On Tue, 2005-01-18 at 00:54 -0500, Tom Lane wrote:
> I won't stand in the way of you doing this

Attached is a revised patch. Barring any objections, I intend to apply
this sometime tomorrow.

-Neil

Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /var/lib/cvs/pgsql/src/backend/parser/parse_expr.c,v
retrieving revision 1.179
diff -c -r1.179 parse_expr.c
*** src/backend/parser/parse_expr.c	12 Jan 2005 17:32:36 -0000	1.179
--- src/backend/parser/parse_expr.c	19 Jan 2005 07:07:48 -0000
***************
*** 37,45 ****
--- 37,63 ----
  
  bool		Transform_null_equals = false;
  
+ static Node *transformParamRef(ParseState *pstate, ParamRef *pref);
+ static Node *transformAExprOp(ParseState *pstate, A_Expr *a);
+ static Node *transformAExprAnd(ParseState *pstate, A_Expr *a);
+ static Node *transformAExprOr(ParseState *pstate, A_Expr *a);
+ static Node *transformAExprNot(ParseState *pstate, A_Expr *a);
+ static Node *transformAExprOpAny(ParseState *pstate, A_Expr *a);
+ static Node *transformAExprOpAll(ParseState *pstate, A_Expr *a);
+ static Node *transformAExprDistinct(ParseState *pstate, A_Expr *a);
+ static Node *transformAExprNullIf(ParseState *pstate, A_Expr *a);
+ static Node *transformAExprOf(ParseState *pstate, A_Expr *a);
+ static Node *transformFuncCall(ParseState *pstate, FuncCall *fn);
+ static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c);
+ static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
+ static Node *transformArrayExpr(ParseState *pstate, ArrayExpr *a);
+ static Node *transformRowExpr(ParseState *pstate, RowExpr *r);
+ static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
+ static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
  static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
  static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
  					 char *relname);
+ static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
  static Node *transformIndirection(ParseState *pstate, Node *basenode,
  					 List *indirection);
  static Node *typecast_expression(ParseState *pstate, Node *expr,
***************
*** 90,154 ****
  	switch (nodeTag(expr))
  	{
  		case T_ColumnRef:
! 			{
! 				result = transformColumnRef(pstate, (ColumnRef *) expr);
! 				break;
! 			}
! 		case T_ParamRef:
! 			{
! 				ParamRef   *pref = (ParamRef *) expr;
! 				int			paramno = pref->number;
! 				ParseState *toppstate;
! 				Param	   *param;
! 
! 				/*
! 				 * Find topmost ParseState, which is where paramtype info
! 				 * lives.
! 				 */
! 				toppstate = pstate;
! 				while (toppstate->parentParseState != NULL)
! 					toppstate = toppstate->parentParseState;
  
! 				/* Check parameter number is in range */
! 				if (paramno <= 0)		/* probably can't happen? */
! 					ereport(ERROR,
! 							(errcode(ERRCODE_UNDEFINED_PARAMETER),
! 						  errmsg("there is no parameter $%d", paramno)));
! 				if (paramno > toppstate->p_numparams)
! 				{
! 					if (!toppstate->p_variableparams)
! 						ereport(ERROR,
! 								(errcode(ERRCODE_UNDEFINED_PARAMETER),
! 								 errmsg("there is no parameter $%d",
! 										paramno)));
! 					/* Okay to enlarge param array */
! 					if (toppstate->p_paramtypes)
! 						toppstate->p_paramtypes =
! 							(Oid *) repalloc(toppstate->p_paramtypes,
! 											 paramno * sizeof(Oid));
! 					else
! 						toppstate->p_paramtypes =
! 							(Oid *) palloc(paramno * sizeof(Oid));
! 					/* Zero out the previously-unreferenced slots */
! 					MemSet(toppstate->p_paramtypes + toppstate->p_numparams,
! 						   0,
! 					   (paramno - toppstate->p_numparams) * sizeof(Oid));
! 					toppstate->p_numparams = paramno;
! 				}
! 				if (toppstate->p_variableparams)
! 				{
! 					/* If not seen before, initialize to UNKNOWN type */
! 					if (toppstate->p_paramtypes[paramno - 1] == InvalidOid)
! 						toppstate->p_paramtypes[paramno - 1] = UNKNOWNOID;
! 				}
  
- 				param = makeNode(Param);
- 				param->paramkind = PARAM_NUM;
- 				param->paramid = (AttrNumber) paramno;
- 				param->paramtype = toppstate->p_paramtypes[paramno - 1];
- 				result = (Node *) param;
- 				break;
- 			}
  		case T_A_Const:
  			{
  				A_Const    *con = (A_Const *) expr;
--- 108,120 ----
  	switch (nodeTag(expr))
  	{
  		case T_ColumnRef:
! 			result = transformColumnRef(pstate, (ColumnRef *) expr);
! 			break;
  
! 		case T_ParamRef:
! 			result = transformParamRef(pstate, (ParamRef *) expr);
! 			break;
  
  		case T_A_Const:
  			{
  				A_Const    *con = (A_Const *) expr;
***************
*** 160,165 ****
--- 126,132 ----
  												 con->typename);
  				break;
  			}
+ 
  		case T_A_Indirection:
  			{
  				A_Indirection *ind = (A_Indirection *) expr;
***************
*** 169,174 ****
--- 136,142 ----
  											  ind->indirection);
  				break;
  			}
+ 
  		case T_TypeCast:
  			{
  				TypeCast   *tc = (TypeCast *) expr;
***************
*** 177,182 ****
--- 145,151 ----
  				result = typecast_expression(pstate, arg, tc->typename);
  				break;
  			}
+ 
  		case T_A_Expr:
  			{
  				A_Expr	   *a = (A_Expr *) expr;
***************
*** 184,884 ****
  				switch (a->kind)
  				{
  					case AEXPR_OP:
! 						{
! 							Node	   *lexpr = a->lexpr;
! 							Node	   *rexpr = a->rexpr;
! 
! 							/*
! 							 * Special-case "foo = NULL" and "NULL = foo"
! 							 * for compatibility with standards-broken
! 							 * products (like Microsoft's).  Turn these
! 							 * into IS NULL exprs.
! 							 */
! 							if (Transform_null_equals &&
! 								list_length(a->name) == 1 &&
! 							strcmp(strVal(linitial(a->name)), "=") == 0 &&
! 								(exprIsNullConstant(lexpr) ||
! 								 exprIsNullConstant(rexpr)))
! 							{
! 								NullTest   *n = makeNode(NullTest);
! 
! 								n->nulltesttype = IS_NULL;
! 
! 								if (exprIsNullConstant(lexpr))
! 									n->arg = (Expr *) rexpr;
! 								else
! 									n->arg = (Expr *) lexpr;
! 
! 								result = transformExpr(pstate,
! 													   (Node *) n);
! 							}
! 							else if (lexpr && IsA(lexpr, RowExpr) &&
! 									 rexpr && IsA(rexpr, SubLink) &&
! 									 ((SubLink *) rexpr)->subLinkType == EXPR_SUBLINK)
! 							{
! 								/*
! 								 * Convert "row op subselect" into a
! 								 * MULTIEXPR sublink.  Formerly the
! 								 * grammar did this, but now that a row
! 								 * construct is allowed anywhere in
! 								 * expressions, it's easier to do it here.
! 								 */
! 								SubLink    *s = (SubLink *) rexpr;
! 
! 								s->subLinkType = MULTIEXPR_SUBLINK;
! 								s->lefthand = ((RowExpr *) lexpr)->args;
! 								s->operName = a->name;
! 								result = transformExpr(pstate, (Node *) s);
! 							}
! 							else if (lexpr && IsA(lexpr, RowExpr) &&
! 									 rexpr && IsA(rexpr, RowExpr))
! 							{
! 								/* "row op row" */
! 								result = make_row_op(pstate, a->name,
! 													 lexpr, rexpr);
! 							}
! 							else
! 							{
! 								/* Ordinary scalar operator */
! 								lexpr = transformExpr(pstate, lexpr);
! 								rexpr = transformExpr(pstate, rexpr);
! 
! 								result = (Node *) make_op(pstate,
! 														  a->name,
! 														  lexpr,
! 														  rexpr);
! 							}
! 						}
  						break;
  					case AEXPR_AND:
! 						{
! 							Node	   *lexpr = transformExpr(pstate,
! 															  a->lexpr);
! 							Node	   *rexpr = transformExpr(pstate,
! 															  a->rexpr);
! 
! 							lexpr = coerce_to_boolean(pstate, lexpr, "AND");
! 							rexpr = coerce_to_boolean(pstate, rexpr, "AND");
! 
! 							result = (Node *) makeBoolExpr(AND_EXPR,
! 														list_make2(lexpr,
! 																 rexpr));
! 						}
  						break;
  					case AEXPR_OR:
! 						{
! 							Node	   *lexpr = transformExpr(pstate,
! 															  a->lexpr);
! 							Node	   *rexpr = transformExpr(pstate,
! 															  a->rexpr);
! 
! 							lexpr = coerce_to_boolean(pstate, lexpr, "OR");
! 							rexpr = coerce_to_boolean(pstate, rexpr, "OR");
! 
! 							result = (Node *) makeBoolExpr(OR_EXPR,
! 														list_make2(lexpr,
! 																 rexpr));
! 						}
  						break;
  					case AEXPR_NOT:
! 						{
! 							Node	   *rexpr = transformExpr(pstate,
! 															  a->rexpr);
! 
! 							rexpr = coerce_to_boolean(pstate, rexpr, "NOT");
! 
! 							result = (Node *) makeBoolExpr(NOT_EXPR,
! 													  list_make1(rexpr));
! 						}
  						break;
  					case AEXPR_OP_ANY:
! 						{
! 							Node	   *lexpr = transformExpr(pstate,
! 															  a->lexpr);
! 							Node	   *rexpr = transformExpr(pstate,
! 															  a->rexpr);
! 
! 							result = (Node *) make_scalar_array_op(pstate,
! 																 a->name,
! 																   true,
! 																   lexpr,
! 																   rexpr);
! 						}
  						break;
  					case AEXPR_OP_ALL:
! 						{
! 							Node	   *lexpr = transformExpr(pstate,
! 															  a->lexpr);
! 							Node	   *rexpr = transformExpr(pstate,
! 															  a->rexpr);
! 
! 							result = (Node *) make_scalar_array_op(pstate,
! 																 a->name,
! 																   false,
! 																   lexpr,
! 																   rexpr);
! 						}
  						break;
  					case AEXPR_DISTINCT:
! 						{
! 							Node	   *lexpr = a->lexpr;
! 							Node	   *rexpr = a->rexpr;
! 
! 							if (lexpr && IsA(lexpr, RowExpr) &&
! 								rexpr && IsA(rexpr, RowExpr))
! 							{
! 								/* "row op row" */
! 								result = make_row_distinct_op(pstate, a->name,
! 														   lexpr, rexpr);
! 							}
! 							else
! 							{
! 								/* Ordinary scalar operator */
! 								lexpr = transformExpr(pstate, lexpr);
! 								rexpr = transformExpr(pstate, rexpr);
! 
! 								result = (Node *) make_distinct_op(pstate,
! 																 a->name,
! 																   lexpr,
! 																   rexpr);
! 							}
! 						}
  						break;
  					case AEXPR_NULLIF:
! 						{
! 							Node	   *lexpr = transformExpr(pstate,
! 															  a->lexpr);
! 							Node	   *rexpr = transformExpr(pstate,
! 															  a->rexpr);
! 
! 							result = (Node *) make_op(pstate,
! 													  a->name,
! 													  lexpr,
! 													  rexpr);
! 							if (((OpExpr *) result)->opresulttype != BOOLOID)
! 								ereport(ERROR,
! 									 (errcode(ERRCODE_DATATYPE_MISMATCH),
! 									  errmsg("NULLIF requires = operator to yield boolean")));
! 
! 							/*
! 							 * We rely on NullIfExpr and OpExpr being same
! 							 * struct
! 							 */
! 							NodeSetTag(result, T_NullIfExpr);
! 						}
  						break;
  					case AEXPR_OF:
! 						{
! 							/*
! 							 * Checking an expression for match to type.
! 							 * Will result in a boolean constant node.
! 							 */
! 							ListCell   *telem;
! 							A_Const    *n;
! 							Oid			ltype,
! 										rtype;
! 							bool		matched = FALSE;
! 							Node	   *lexpr = transformExpr(pstate,
! 															  a->lexpr);
! 
! 							ltype = exprType(lexpr);
! 							foreach(telem, (List *) a->rexpr)
! 							{
! 								rtype = LookupTypeName(lfirst(telem));
! 								matched = (rtype == ltype);
! 								if (matched)
! 									break;
! 							}
! 
! 							/*
! 							 * Expect two forms: equals or not equals.
! 							 * Flip the sense of the result for not
! 							 * equals.
! 							 */
! 							if (strcmp(strVal(linitial(a->name)), "!=") == 0)
! 								matched = (!matched);
! 
! 							n = makeNode(A_Const);
! 							n->val.type = T_String;
! 							n->val.val.str = (matched ? "t" : "f");
! 							n->typename = SystemTypeName("bool");
! 
! 							result = transformExpr(pstate, (Node *) n);
! 						}
  						break;
  				}
  				break;
  			}
  		case T_FuncCall:
! 			{
! 				FuncCall   *fn = (FuncCall *) expr;
! 				List	   *targs;
! 				ListCell   *args;
  
- 				/*
- 				 * Transform the list of arguments.  We use a shallow list
- 				 * copy and then transform-in-place to avoid O(N^2)
- 				 * behavior from repeated lappend's.
- 				 *
- 				 * XXX: repeated lappend() would no longer result in O(n^2)
- 				 * behavior; worth reconsidering this design?
- 				 */
- 				targs = list_copy(fn->args);
- 				foreach(args, targs)
- 				{
- 					lfirst(args) = transformExpr(pstate,
- 												 (Node *) lfirst(args));
- 				}
- 				result = ParseFuncOrColumn(pstate,
- 										   fn->funcname,
- 										   targs,
- 										   fn->agg_star,
- 										   fn->agg_distinct,
- 										   false);
- 				break;
- 			}
  		case T_SubLink:
! 			{
! 				SubLink    *sublink = (SubLink *) expr;
! 				List	   *qtrees;
! 				Query	   *qtree;
! 
! 				/* If we already transformed this node, do nothing */
! 				if (IsA(sublink->subselect, Query))
! 				{
! 					result = expr;
! 					break;
! 				}
! 				pstate->p_hasSubLinks = true;
! 				qtrees = parse_sub_analyze(sublink->subselect, pstate);
! 				if (list_length(qtrees) != 1)
! 					elog(ERROR, "bad query in sub-select");
! 				qtree = (Query *) linitial(qtrees);
! 				if (qtree->commandType != CMD_SELECT ||
! 					qtree->resultRelation != 0)
! 					elog(ERROR, "bad query in sub-select");
! 				sublink->subselect = (Node *) qtree;
! 
! 				if (sublink->subLinkType == EXISTS_SUBLINK)
! 				{
! 					/*
! 					 * EXISTS needs no lefthand or combining operator.
! 					 * These fields should be NIL already, but make sure.
! 					 */
! 					sublink->lefthand = NIL;
! 					sublink->operName = NIL;
! 					sublink->operOids = NIL;
! 					sublink->useOr = FALSE;
! 				}
! 				else if (sublink->subLinkType == EXPR_SUBLINK ||
! 						 sublink->subLinkType == ARRAY_SUBLINK)
! 				{
! 					ListCell   *tlist_item = list_head(qtree->targetList);
! 
! 					/*
! 					 * Make sure the subselect delivers a single column
! 					 * (ignoring resjunk targets).
! 					 */
! 					if (tlist_item == NULL ||
! 					((TargetEntry *) lfirst(tlist_item))->resdom->resjunk)
! 						ereport(ERROR,
! 								(errcode(ERRCODE_SYNTAX_ERROR),
! 							   errmsg("subquery must return a column")));
! 					while ((tlist_item = lnext(tlist_item)) != NULL)
! 					{
! 						if (!((TargetEntry *) lfirst(tlist_item))->resdom->resjunk)
! 							ereport(ERROR,
! 									(errcode(ERRCODE_SYNTAX_ERROR),
! 									 errmsg("subquery must return only one column")));
! 					}
! 
! 					/*
! 					 * EXPR and ARRAY need no lefthand or combining
! 					 * operator. These fields should be NIL already, but
! 					 * make sure.
! 					 */
! 					sublink->lefthand = NIL;
! 					sublink->operName = NIL;
! 					sublink->operOids = NIL;
! 					sublink->useOr = FALSE;
! 				}
! 				else
! 				{
! 					/* ALL, ANY, or MULTIEXPR: generate operator list */
! 					List	   *left_list = sublink->lefthand;
! 					List	   *right_list = qtree->targetList;
! 					int			row_length = list_length(left_list);
! 					bool		needNot = false;
! 					List	   *op = sublink->operName;
! 					char	   *opname = strVal(llast(op));
! 					ListCell   *l;
! 					ListCell   *ll_item;
! 
! 					/* transform lefthand expressions */
! 					foreach(l, left_list)
! 						lfirst(l) = transformExpr(pstate, lfirst(l));
! 
! 					/*
! 					 * If the expression is "<> ALL" (with unqualified
! 					 * opname) then convert it to "NOT IN".  This is a
! 					 * hack to improve efficiency of expressions output by
! 					 * pre-7.4 Postgres.
! 					 */
! 					if (sublink->subLinkType == ALL_SUBLINK &&
! 						list_length(op) == 1 && strcmp(opname, "<>") == 0)
! 					{
! 						sublink->subLinkType = ANY_SUBLINK;
! 						opname = pstrdup("=");
! 						op = list_make1(makeString(opname));
! 						sublink->operName = op;
! 						needNot = true;
! 					}
! 
! 					/* Set useOr if op is "<>" (possibly qualified) */
! 					if (strcmp(opname, "<>") == 0)
! 						sublink->useOr = TRUE;
! 					else
! 						sublink->useOr = FALSE;
! 
! 					/* Combining operators other than =/<> is dubious... */
! 					if (row_length != 1 &&
! 						strcmp(opname, "=") != 0 &&
! 						strcmp(opname, "<>") != 0)
! 						ereport(ERROR,
! 								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 						  errmsg("row comparison cannot use operator %s",
! 								 opname)));
! 
! 					/*
! 					 * To build the list of combining operator OIDs, we
! 					 * must scan subquery's targetlist to find values that
! 					 * will be matched against lefthand values.  We need
! 					 * to ignore resjunk targets, so doing the outer
! 					 * iteration over right_list is easier than doing it
! 					 * over left_list.
! 					 */
! 					sublink->operOids = NIL;
! 
! 					ll_item = list_head(left_list);
! 					foreach(l, right_list)
! 					{
! 						TargetEntry *tent = (TargetEntry *) lfirst(l);
! 						Node	   *lexpr;
! 						Operator	optup;
! 						Form_pg_operator opform;
! 
! 						if (tent->resdom->resjunk)
! 							continue;
! 
! 						if (ll_item == NULL)
! 							ereport(ERROR,
! 									(errcode(ERRCODE_SYNTAX_ERROR),
! 							   errmsg("subquery has too many columns")));
! 						lexpr = lfirst(ll_item);
! 						ll_item = lnext(ll_item);
! 
! 						/*
! 						 * It's OK to use oper() not compatible_oper()
! 						 * here, because make_subplan() will insert type
! 						 * coercion calls if needed.
! 						 */
! 						optup = oper(op,
! 									 exprType(lexpr),
! 									 exprType((Node *) tent->expr),
! 									 false);
! 						opform = (Form_pg_operator) GETSTRUCT(optup);
! 
! 						if (opform->oprresult != BOOLOID)
! 							ereport(ERROR,
! 									(errcode(ERRCODE_DATATYPE_MISMATCH),
! 									 errmsg("operator %s must return type boolean, not type %s",
! 											opname,
! 									  format_type_be(opform->oprresult)),
! 									 errhint("The operator of a quantified predicate subquery must return type boolean.")));
! 
! 						if (get_func_retset(opform->oprcode))
! 							ereport(ERROR,
! 									(errcode(ERRCODE_DATATYPE_MISMATCH),
! 							  errmsg("operator %s must not return a set",
! 									 opname),
! 									 errhint("The operator of a quantified predicate subquery must return type boolean.")));
! 
! 						sublink->operOids = lappend_oid(sublink->operOids,
! 														oprid(optup));
! 
! 						ReleaseSysCache(optup);
! 					}
! 					if (ll_item != NULL)
! 						ereport(ERROR,
! 								(errcode(ERRCODE_SYNTAX_ERROR),
! 								 errmsg("subquery has too few columns")));
! 
! 					if (needNot)
! 					{
! 						expr = coerce_to_boolean(pstate, expr, "NOT");
! 						expr = (Node *) makeBoolExpr(NOT_EXPR,
! 													 list_make1(expr));
! 					}
! 				}
! 				result = (Node *) expr;
! 				break;
! 			}
  
  		case T_CaseExpr:
! 			{
! 				CaseExpr   *c = (CaseExpr *) expr;
! 				CaseExpr   *newc;
! 				Node	   *arg;
! 				CaseTestExpr *placeholder;
! 				List	   *newargs;
! 				List	   *typeids;
! 				ListCell   *l;
! 				Node	   *defresult;
! 				Oid			ptype;
! 
! 				/* If we already transformed this node, do nothing */
! 				if (OidIsValid(c->casetype))
! 				{
! 					result = expr;
! 					break;
! 				}
! 				newc = makeNode(CaseExpr);
! 
! 				/* transform the test expression, if any */
! 				arg = transformExpr(pstate, (Node *) c->arg);
! 
! 				/* generate placeholder for test expression */
! 				if (arg)
! 				{
! 					/*
! 					 * If test expression is an untyped literal, force it to
! 					 * text.  We have to do something now because we won't be
! 					 * able to do this coercion on the placeholder.  This is
! 					 * not as flexible as what was done in 7.4 and before,
! 					 * but it's good enough to handle the sort of silly
! 					 * coding commonly seen.
! 					 */
! 					if (exprType(arg) == UNKNOWNOID)
! 						arg = coerce_to_common_type(pstate, arg,
! 													TEXTOID, "CASE");
! 					placeholder = makeNode(CaseTestExpr);
! 					placeholder->typeId = exprType(arg);
! 					placeholder->typeMod = exprTypmod(arg);
! 				}
! 				else
! 					placeholder = NULL;
! 
! 				newc->arg = (Expr *) arg;
! 
! 				/* transform the list of arguments */
! 				newargs = NIL;
! 				typeids = NIL;
! 				foreach(l, c->args)
! 				{
! 					CaseWhen   *w = (CaseWhen *) lfirst(l);
! 					CaseWhen   *neww = makeNode(CaseWhen);
! 					Node	   *warg;
! 
! 					Assert(IsA(w, CaseWhen));
! 
! 					warg = (Node *) w->expr;
! 					if (placeholder)
! 					{
! 						/* shorthand form was specified, so expand... */
! 						warg = (Node *) makeSimpleA_Expr(AEXPR_OP, "=",
! 													(Node *) placeholder,
! 														 warg);
! 					}
! 					neww->expr = (Expr *) transformExpr(pstate, warg);
! 
! 					neww->expr = (Expr *) coerce_to_boolean(pstate,
! 													 (Node *) neww->expr,
! 															"CASE/WHEN");
! 
! 					warg = (Node *) w->result;
! 					neww->result = (Expr *) transformExpr(pstate, warg);
! 
! 					newargs = lappend(newargs, neww);
! 					typeids = lappend_oid(typeids, exprType((Node *) neww->result));
! 				}
! 
! 				newc->args = newargs;
! 
! 				/* transform the default clause */
! 				defresult = (Node *) c->defresult;
! 				if (defresult == NULL)
! 				{
! 					A_Const    *n = makeNode(A_Const);
! 
! 					n->val.type = T_Null;
! 					defresult = (Node *) n;
! 				}
! 				newc->defresult = (Expr *) transformExpr(pstate, defresult);
! 
! 				/*
! 				 * Note: default result is considered the most significant
! 				 * type in determining preferred type.	This is how the
! 				 * code worked before, but it seems a little bogus to me
! 				 * --- tgl
! 				 */
! 				typeids = lcons_oid(exprType((Node *) newc->defresult), typeids);
! 
! 				ptype = select_common_type(typeids, "CASE");
! 				Assert(OidIsValid(ptype));
! 				newc->casetype = ptype;
! 
! 				/* Convert default result clause, if necessary */
! 				newc->defresult = (Expr *)
! 					coerce_to_common_type(pstate,
! 										  (Node *) newc->defresult,
! 										  ptype,
! 										  "CASE/ELSE");
! 
! 				/* Convert when-clause results, if necessary */
! 				foreach(l, newc->args)
! 				{
! 					CaseWhen   *w = (CaseWhen *) lfirst(l);
! 
! 					w->result = (Expr *)
! 						coerce_to_common_type(pstate,
! 											  (Node *) w->result,
! 											  ptype,
! 											  "CASE/WHEN");
! 				}
! 
! 				result = (Node *) newc;
! 				break;
! 			}
  
  		case T_ArrayExpr:
! 			{
! 				ArrayExpr  *a = (ArrayExpr *) expr;
! 				ArrayExpr  *newa = makeNode(ArrayExpr);
! 				List	   *newelems = NIL;
! 				List	   *newcoercedelems = NIL;
! 				List	   *typeids = NIL;
! 				ListCell   *element;
! 				Oid			array_type;
! 				Oid			element_type;
! 
! 				/* Transform the element expressions */
! 				foreach(element, a->elements)
! 				{
! 					Node	   *e = (Node *) lfirst(element);
! 					Node	   *newe;
! 
! 					newe = transformExpr(pstate, e);
! 					newelems = lappend(newelems, newe);
! 					typeids = lappend_oid(typeids, exprType(newe));
! 				}
! 
! 				/* Select a common type for the elements */
! 				element_type = select_common_type(typeids, "ARRAY");
! 
! 				/* Coerce arguments to common type if necessary */
! 				foreach(element, newelems)
! 				{
! 					Node	   *e = (Node *) lfirst(element);
! 					Node	   *newe;
! 
! 					newe = coerce_to_common_type(pstate, e,
! 												 element_type,
! 												 "ARRAY");
! 					newcoercedelems = lappend(newcoercedelems, newe);
! 				}
! 
! 				/* Do we have an array type to use? */
! 				array_type = get_array_type(element_type);
! 				if (array_type != InvalidOid)
! 				{
! 					/* Elements are presumably of scalar type */
! 					newa->multidims = false;
! 				}
! 				else
! 				{
! 					/* Must be nested array expressions */
! 					newa->multidims = true;
! 
! 					array_type = element_type;
! 					element_type = get_element_type(array_type);
! 					if (!OidIsValid(element_type))
! 						ereport(ERROR,
! 								(errcode(ERRCODE_UNDEFINED_OBJECT),
! 								 errmsg("could not find array type for data type %s",
! 										format_type_be(array_type))));
! 				}
! 
! 				newa->array_typeid = array_type;
! 				newa->element_typeid = element_type;
! 				newa->elements = newcoercedelems;
! 
! 				result = (Node *) newa;
! 				break;
! 			}
  
  		case T_RowExpr:
! 			{
! 				RowExpr    *r = (RowExpr *) expr;
! 				RowExpr    *newr = makeNode(RowExpr);
! 				List	   *newargs = NIL;
! 				ListCell   *arg;
! 
! 				/* Transform the field expressions */
! 				foreach(arg, r->args)
! 				{
! 					Node	   *e = (Node *) lfirst(arg);
! 					Node	   *newe;
! 
! 					newe = transformExpr(pstate, e);
! 					newargs = lappend(newargs, newe);
! 				}
! 				newr->args = newargs;
! 
! 				/* Barring later casting, we consider the type RECORD */
! 				newr->row_typeid = RECORDOID;
! 				newr->row_format = COERCE_IMPLICIT_CAST;
! 
! 				result = (Node *) newr;
! 				break;
! 			}
  
  		case T_CoalesceExpr:
! 			{
! 				CoalesceExpr *c = (CoalesceExpr *) expr;
! 				CoalesceExpr *newc = makeNode(CoalesceExpr);
! 				List	   *newargs = NIL;
! 				List	   *newcoercedargs = NIL;
! 				List	   *typeids = NIL;
! 				ListCell   *args;
! 
! 				foreach(args, c->args)
! 				{
! 					Node	   *e = (Node *) lfirst(args);
! 					Node	   *newe;
! 
! 					newe = transformExpr(pstate, e);
! 					newargs = lappend(newargs, newe);
! 					typeids = lappend_oid(typeids, exprType(newe));
! 				}
! 
! 				newc->coalescetype = select_common_type(typeids, "COALESCE");
! 
! 				/* Convert arguments if necessary */
! 				foreach(args, newargs)
! 				{
! 					Node	   *e = (Node *) lfirst(args);
! 					Node	   *newe;
! 
! 					newe = coerce_to_common_type(pstate, e,
! 												 newc->coalescetype,
! 												 "COALESCE");
! 					newcoercedargs = lappend(newcoercedargs, newe);
! 				}
! 
! 				newc->args = newcoercedargs;
! 				result = (Node *) newc;
! 				break;
! 			}
  
  		case T_NullTest:
  			{
--- 153,213 ----
  				switch (a->kind)
  				{
  					case AEXPR_OP:
! 						result = transformAExprOp(pstate, a);
  						break;
  					case AEXPR_AND:
! 						result = transformAExprAnd(pstate, a);
  						break;
  					case AEXPR_OR:
! 						result = transformAExprOr(pstate, a);
  						break;
  					case AEXPR_NOT:
! 						result = transformAExprNot(pstate, a);
  						break;
  					case AEXPR_OP_ANY:
! 						result = transformAExprOpAny(pstate, a);
  						break;
  					case AEXPR_OP_ALL:
! 						result = transformAExprOpAll(pstate, a);
  						break;
  					case AEXPR_DISTINCT:
! 						result = transformAExprDistinct(pstate, a);
  						break;
  					case AEXPR_NULLIF:
! 						result = transformAExprNullIf(pstate, a);
  						break;
  					case AEXPR_OF:
! 						result = transformAExprOf(pstate, a);
  						break;
+ 					default:
+ 						elog(ERROR, "unrecognized A_Expr kind: %d", a->kind);
  				}
  				break;
  			}
+ 
  		case T_FuncCall:
! 			result = transformFuncCall(pstate, (FuncCall *) expr);
! 			break;
  
  		case T_SubLink:
! 			result = transformSubLink(pstate, (SubLink *) expr);
! 			break;
  
  		case T_CaseExpr:
! 			result = transformCaseExpr(pstate, (CaseExpr *) expr);
! 			break;
  
  		case T_ArrayExpr:
! 			result = transformArrayExpr(pstate, (ArrayExpr *) expr);
! 			break;
  
  		case T_RowExpr:
! 			result = transformRowExpr(pstate, (RowExpr *) expr);
! 			break;
  
  		case T_CoalesceExpr:
! 			result = transformCoalesceExpr(pstate, (CoalesceExpr *) expr);
! 			break;
  
  		case T_NullTest:
  			{
***************
*** 891,935 ****
  			}
  
  		case T_BooleanTest:
! 			{
! 				BooleanTest *b = (BooleanTest *) expr;
! 				const char *clausename;
! 
! 				switch (b->booltesttype)
! 				{
! 					case IS_TRUE:
! 						clausename = "IS TRUE";
! 						break;
! 					case IS_NOT_TRUE:
! 						clausename = "IS NOT TRUE";
! 						break;
! 					case IS_FALSE:
! 						clausename = "IS FALSE";
! 						break;
! 					case IS_NOT_FALSE:
! 						clausename = "IS NOT FALSE";
! 						break;
! 					case IS_UNKNOWN:
! 						clausename = "IS UNKNOWN";
! 						break;
! 					case IS_NOT_UNKNOWN:
! 						clausename = "IS NOT UNKNOWN";
! 						break;
! 					default:
! 						elog(ERROR, "unrecognized booltesttype: %d",
! 							 (int) b->booltesttype);
! 						clausename = NULL;		/* keep compiler quiet */
! 				}
! 
! 				b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg);
! 
! 				b->arg = (Expr *) coerce_to_boolean(pstate,
! 													(Node *) b->arg,
! 													clausename);
! 
! 				result = expr;
! 				break;
! 			}
  
  			/*********************************************
  			 * Quietly accept node types that may be presented when we are
--- 220,227 ----
  			}
  
  		case T_BooleanTest:
! 			result = transformBooleanTest(pstate, (BooleanTest *) expr);
! 			break;
  
  			/*********************************************
  			 * Quietly accept node types that may be presented when we are
***************
*** 1203,1208 ****
--- 495,1278 ----
  	return node;
  }
  
+ static Node *
+ transformParamRef(ParseState *pstate, ParamRef *pref)
+ {
+ 	int			paramno = pref->number;
+ 	ParseState *toppstate;
+ 	Param	   *param;
+ 
+ 	/*
+ 	 * Find topmost ParseState, which is where paramtype info lives.
+ 	 */
+ 	toppstate = pstate;
+ 	while (toppstate->parentParseState != NULL)
+ 		toppstate = toppstate->parentParseState;
+ 
+ 	/* Check parameter number is in range */
+ 	if (paramno <= 0)		/* probably can't happen? */
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_PARAMETER),
+ 				 errmsg("there is no parameter $%d", paramno)));
+ 	if (paramno > toppstate->p_numparams)
+ 	{
+ 		if (!toppstate->p_variableparams)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_PARAMETER),
+ 					 errmsg("there is no parameter $%d",
+ 							paramno)));
+ 		/* Okay to enlarge param array */
+ 		if (toppstate->p_paramtypes)
+ 			toppstate->p_paramtypes =
+ 				(Oid *) repalloc(toppstate->p_paramtypes,
+ 								 paramno * sizeof(Oid));
+ 		else
+ 			toppstate->p_paramtypes =
+ 				(Oid *) palloc(paramno * sizeof(Oid));
+ 		/* Zero out the previously-unreferenced slots */
+ 		MemSet(toppstate->p_paramtypes + toppstate->p_numparams,
+ 			   0,
+ 			   (paramno - toppstate->p_numparams) * sizeof(Oid));
+ 		toppstate->p_numparams = paramno;
+ 	}
+ 	if (toppstate->p_variableparams)
+ 	{
+ 		/* If not seen before, initialize to UNKNOWN type */
+ 		if (toppstate->p_paramtypes[paramno - 1] == InvalidOid)
+ 			toppstate->p_paramtypes[paramno - 1] = UNKNOWNOID;
+ 	}
+ 
+ 	param = makeNode(Param);
+ 	param->paramkind = PARAM_NUM;
+ 	param->paramid = (AttrNumber) paramno;
+ 	param->paramtype = toppstate->p_paramtypes[paramno - 1];
+ 
+ 	return (Node *) param;
+ }
+ 
+ static Node *
+ transformAExprOp(ParseState *pstate, A_Expr *a)
+ {
+ 	Node	   *lexpr = a->lexpr;
+ 	Node	   *rexpr = a->rexpr;
+ 	Node	   *result;
+ 
+ 	/*
+ 	 * Special-case "foo = NULL" and "NULL = foo" for compatibility
+ 	 * with standards-broken products (like Microsoft's).  Turn these
+ 	 * into IS NULL exprs.
+ 	 */
+ 	if (Transform_null_equals &&
+ 		list_length(a->name) == 1 &&
+ 		strcmp(strVal(linitial(a->name)), "=") == 0 &&
+ 		(exprIsNullConstant(lexpr) ||
+ 		 exprIsNullConstant(rexpr)))
+ 	{
+ 		NullTest   *n = makeNode(NullTest);
+ 
+ 		n->nulltesttype = IS_NULL;
+ 
+ 		if (exprIsNullConstant(lexpr))
+ 			n->arg = (Expr *) rexpr;
+ 		else
+ 			n->arg = (Expr *) lexpr;
+ 
+ 		result = transformExpr(pstate, (Node *) n);
+ 	}
+ 	else if (lexpr && IsA(lexpr, RowExpr) &&
+ 			 rexpr && IsA(rexpr, SubLink) &&
+ 			 ((SubLink *) rexpr)->subLinkType == EXPR_SUBLINK)
+ 	{
+ 		/*
+ 		 * Convert "row op subselect" into a MULTIEXPR sublink.
+ 		 * Formerly the grammar did this, but now that a row construct
+ 		 * is allowed anywhere in expressions, it's easier to do it
+ 		 * here.
+ 		 */
+ 		SubLink    *s = (SubLink *) rexpr;
+ 
+ 		s->subLinkType = MULTIEXPR_SUBLINK;
+ 		s->lefthand = ((RowExpr *) lexpr)->args;
+ 		s->operName = a->name;
+ 		result = transformExpr(pstate, (Node *) s);
+ 	}
+ 	else if (lexpr && IsA(lexpr, RowExpr) &&
+ 			 rexpr && IsA(rexpr, RowExpr))
+ 	{
+ 		/* "row op row" */
+ 		result = make_row_op(pstate, a->name,
+ 							 lexpr, rexpr);
+ 	}
+ 	else
+ 	{
+ 		/* Ordinary scalar operator */
+ 		lexpr = transformExpr(pstate, lexpr);
+ 		rexpr = transformExpr(pstate, rexpr);
+ 
+ 		result = (Node *) make_op(pstate,
+ 								  a->name,
+ 								  lexpr,
+ 								  rexpr);
+ 	}
+ 
+ 	return result;
+ }
+ 
+ static Node *
+ transformAExprAnd(ParseState *pstate, A_Expr *a)
+ {
+ 	Node	   *lexpr = transformExpr(pstate, a->lexpr);
+ 	Node	   *rexpr = transformExpr(pstate, a->rexpr);
+ 
+ 	lexpr = coerce_to_boolean(pstate, lexpr, "AND");
+ 	rexpr = coerce_to_boolean(pstate, rexpr, "AND");
+ 
+ 	return (Node *) makeBoolExpr(AND_EXPR,
+ 								 list_make2(lexpr,
+ 											rexpr));
+ }
+ 
+ static Node *
+ transformAExprOr(ParseState *pstate, A_Expr *a)
+ {
+ 	Node	   *lexpr = transformExpr(pstate, a->lexpr);
+ 	Node	   *rexpr = transformExpr(pstate, a->rexpr);
+ 
+ 	lexpr = coerce_to_boolean(pstate, lexpr, "OR");
+ 	rexpr = coerce_to_boolean(pstate, rexpr, "OR");
+ 
+ 	return (Node *) makeBoolExpr(OR_EXPR,
+ 								 list_make2(lexpr,
+ 											rexpr));
+ }
+ 
+ static Node *
+ transformAExprNot(ParseState *pstate, A_Expr *a)
+ {
+ 	Node	   *rexpr = transformExpr(pstate, a->rexpr);
+ 
+ 	rexpr = coerce_to_boolean(pstate, rexpr, "NOT");
+ 
+ 	return (Node *) makeBoolExpr(NOT_EXPR,
+ 								 list_make1(rexpr));
+ }
+ 
+ static Node *
+ transformAExprOpAny(ParseState *pstate, A_Expr *a)
+ {
+ 	Node	   *lexpr = transformExpr(pstate, a->lexpr);
+ 	Node	   *rexpr = transformExpr(pstate, a->rexpr);
+ 
+ 	return (Node *) make_scalar_array_op(pstate,
+ 										 a->name,
+ 										 true,
+ 										 lexpr,
+ 										 rexpr);
+ }
+ 
+ static Node *
+ transformAExprOpAll(ParseState *pstate, A_Expr *a)
+ {
+ 	Node	   *lexpr = transformExpr(pstate, a->lexpr);
+ 	Node	   *rexpr = transformExpr(pstate, a->rexpr);
+ 
+ 	return (Node *) make_scalar_array_op(pstate,
+ 										 a->name,
+ 										 false,
+ 										 lexpr,
+ 										 rexpr);
+ }
+ 
+ static Node *
+ transformAExprDistinct(ParseState *pstate, A_Expr *a)
+ {
+ 	Node	   *lexpr = a->lexpr;
+ 	Node	   *rexpr = a->rexpr;
+ 
+ 	if (lexpr && IsA(lexpr, RowExpr) &&
+ 		rexpr && IsA(rexpr, RowExpr))
+ 	{
+ 		/* "row op row" */
+ 		return make_row_distinct_op(pstate, a->name,
+ 									lexpr, rexpr);
+ 	}
+ 	else
+ 	{
+ 		/* Ordinary scalar operator */
+ 		lexpr = transformExpr(pstate, lexpr);
+ 		rexpr = transformExpr(pstate, rexpr);
+ 
+ 		return (Node *) make_distinct_op(pstate,
+ 										 a->name,
+ 										 lexpr,
+ 										 rexpr);
+ 	}
+ }
+ 
+ static Node *
+ transformAExprNullIf(ParseState *pstate, A_Expr *a)
+ {
+ 	Node	   *lexpr = transformExpr(pstate, a->lexpr);
+ 	Node	   *rexpr = transformExpr(pstate, a->rexpr);
+ 	Node	   *result;
+ 
+ 	result = (Node *) make_op(pstate,
+ 							  a->name,
+ 							  lexpr,
+ 							  rexpr);
+ 	if (((OpExpr *) result)->opresulttype != BOOLOID)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 				 errmsg("NULLIF requires = operator to yield boolean")));
+ 
+ 	/*
+ 	 * We rely on NullIfExpr and OpExpr being the same struct
+ 	 */
+ 	NodeSetTag(result, T_NullIfExpr);
+ 
+ 	return result;
+ }
+ 
+ static Node *
+ transformAExprOf(ParseState *pstate, A_Expr *a)
+ {
+ 	/*
+ 	 * Checking an expression for match to type.  Will result in a
+ 	 * boolean constant node.
+ 	 */
+ 	ListCell   *telem;
+ 	A_Const    *n;
+ 	Oid			ltype,
+ 				rtype;
+ 	bool		matched = false;
+ 	Node	   *lexpr = transformExpr(pstate, a->lexpr);
+ 
+ 	ltype = exprType(lexpr);
+ 	foreach(telem, (List *) a->rexpr)
+ 	{
+ 		rtype = LookupTypeName(lfirst(telem));
+ 		matched = (rtype == ltype);
+ 		if (matched)
+ 			break;
+ 	}
+ 
+ 	/*
+ 	 * Expect two forms: equals or not equals.  Flip the sense of the
+ 	 * result for not equals.
+ 	 */
+ 	if (strcmp(strVal(linitial(a->name)), "!=") == 0)
+ 		matched = (!matched);
+ 
+ 	n = makeNode(A_Const);
+ 	n->val.type = T_String;
+ 	n->val.val.str = (matched ? "t" : "f");
+ 	n->typename = SystemTypeName("bool");
+ 
+ 	return transformExpr(pstate, (Node *) n);
+ }
+ 
+ static Node *
+ transformFuncCall(ParseState *pstate, FuncCall *fn)
+ {
+ 	List	   *targs;
+ 	ListCell   *args;
+ 
+ 	/*
+ 	 * Transform the list of arguments.  We use a shallow list copy
+ 	 * and then transform-in-place to avoid O(N^2) behavior from
+ 	 * repeated lappend's.
+ 	 *
+ 	 * XXX: repeated lappend() would no longer result in O(n^2)
+ 	 * behavior; worth reconsidering this design?
+ 	 */
+ 	targs = list_copy(fn->args);
+ 	foreach(args, targs)
+ 	{
+ 		lfirst(args) = transformExpr(pstate,
+ 									 (Node *) lfirst(args));
+ 	}
+ 
+ 	return ParseFuncOrColumn(pstate,
+ 							 fn->funcname,
+ 							 targs,
+ 							 fn->agg_star,
+ 							 fn->agg_distinct,
+ 							 false);
+ }
+ 
+ static Node *
+ transformCaseExpr(ParseState *pstate, CaseExpr *c)
+ {
+ 	CaseExpr   *newc;
+ 	Node	   *arg;
+ 	CaseTestExpr *placeholder;
+ 	List	   *newargs;
+ 	List	   *typeids;
+ 	ListCell   *l;
+ 	Node	   *defresult;
+ 	Oid			ptype;
+ 
+ 	/* If we already transformed this node, do nothing */
+ 	if (OidIsValid(c->casetype))
+ 		return (Node *) c;
+ 
+ 	newc = makeNode(CaseExpr);
+ 
+ 	/* transform the test expression, if any */
+ 	arg = transformExpr(pstate, (Node *) c->arg);
+ 
+ 	/* generate placeholder for test expression */
+ 	if (arg)
+ 	{
+ 		/*
+ 		 * If test expression is an untyped literal, force it to text.
+ 		 * We have to do something now because we won't be able to do
+ 		 * this coercion on the placeholder.  This is not as flexible
+ 		 * as what was done in 7.4 and before, but it's good enough to
+ 		 * handle the sort of silly coding commonly seen.
+ 		 */
+ 		if (exprType(arg) == UNKNOWNOID)
+ 			arg = coerce_to_common_type(pstate, arg,
+ 										TEXTOID, "CASE");
+ 		placeholder = makeNode(CaseTestExpr);
+ 		placeholder->typeId = exprType(arg);
+ 		placeholder->typeMod = exprTypmod(arg);
+ 	}
+ 	else
+ 		placeholder = NULL;
+ 
+ 	newc->arg = (Expr *) arg;
+ 
+ 	/* transform the list of arguments */
+ 	newargs = NIL;
+ 	typeids = NIL;
+ 	foreach(l, c->args)
+ 	{
+ 		CaseWhen   *w = (CaseWhen *) lfirst(l);
+ 		CaseWhen   *neww = makeNode(CaseWhen);
+ 		Node	   *warg;
+ 
+ 		Assert(IsA(w, CaseWhen));
+ 
+ 		warg = (Node *) w->expr;
+ 		if (placeholder)
+ 		{
+ 			/* shorthand form was specified, so expand... */
+ 			warg = (Node *) makeSimpleA_Expr(AEXPR_OP, "=",
+ 											 (Node *) placeholder,
+ 											 warg);
+ 		}
+ 		neww->expr = (Expr *) transformExpr(pstate, warg);
+ 
+ 		neww->expr = (Expr *) coerce_to_boolean(pstate,
+ 												(Node *) neww->expr,
+ 												"CASE/WHEN");
+ 
+ 		warg = (Node *) w->result;
+ 		neww->result = (Expr *) transformExpr(pstate, warg);
+ 
+ 		newargs = lappend(newargs, neww);
+ 		typeids = lappend_oid(typeids, exprType((Node *) neww->result));
+ 	}
+ 
+ 	newc->args = newargs;
+ 
+ 	/* transform the default clause */
+ 	defresult = (Node *) c->defresult;
+ 	if (defresult == NULL)
+ 	{
+ 		A_Const    *n = makeNode(A_Const);
+ 
+ 		n->val.type = T_Null;
+ 		defresult = (Node *) n;
+ 	}
+ 	newc->defresult = (Expr *) transformExpr(pstate, defresult);
+ 
+ 	/*
+ 	 * Note: default result is considered the most significant type in
+ 	 * determining preferred type. This is how the code worked before,
+ 	 * but it seems a little bogus to me
+ 	 * --- tgl
+ 	 */
+ 	typeids = lcons_oid(exprType((Node *) newc->defresult), typeids);
+ 
+ 	ptype = select_common_type(typeids, "CASE");
+ 	Assert(OidIsValid(ptype));
+ 	newc->casetype = ptype;
+ 
+ 	/* Convert default result clause, if necessary */
+ 	newc->defresult = (Expr *)
+ 		coerce_to_common_type(pstate,
+ 							  (Node *) newc->defresult,
+ 							  ptype,
+ 							  "CASE/ELSE");
+ 
+ 	/* Convert when-clause results, if necessary */
+ 	foreach(l, newc->args)
+ 	{
+ 		CaseWhen   *w = (CaseWhen *) lfirst(l);
+ 
+ 		w->result = (Expr *)
+ 			coerce_to_common_type(pstate,
+ 								  (Node *) w->result,
+ 								  ptype,
+ 								  "CASE/WHEN");
+ 	}
+ 
+ 	return (Node *) newc;
+ }
+ 
+ static Node *
+ transformSubLink(ParseState *pstate, SubLink *sublink)
+ {
+ 	List	   *qtrees;
+ 	Query	   *qtree;
+ 	Node	   *result = (Node *) sublink;
+ 
+ 	/* If we already transformed this node, do nothing */
+ 	if (IsA(sublink->subselect, Query))
+ 		return result;
+ 
+ 	pstate->p_hasSubLinks = true;
+ 	qtrees = parse_sub_analyze(sublink->subselect, pstate);
+ 	if (list_length(qtrees) != 1)
+ 		elog(ERROR, "bad query in sub-select");
+ 	qtree = (Query *) linitial(qtrees);
+ 	if (qtree->commandType != CMD_SELECT ||
+ 		qtree->resultRelation != 0)
+ 		elog(ERROR, "bad query in sub-select");
+ 	sublink->subselect = (Node *) qtree;
+ 
+ 	if (sublink->subLinkType == EXISTS_SUBLINK)
+ 	{
+ 		/*
+ 		 * EXISTS needs no lefthand or combining operator.  These
+ 		 * fields should be NIL already, but make sure.
+ 		 */
+ 		sublink->lefthand = NIL;
+ 		sublink->operName = NIL;
+ 		sublink->operOids = NIL;
+ 		sublink->useOr = FALSE;
+ 	}
+ 	else if (sublink->subLinkType == EXPR_SUBLINK ||
+ 			 sublink->subLinkType == ARRAY_SUBLINK)
+ 	{
+ 		ListCell   *tlist_item = list_head(qtree->targetList);
+ 
+ 		/*
+ 		 * Make sure the subselect delivers a single column (ignoring
+ 		 * resjunk targets).
+ 		 */
+ 		if (tlist_item == NULL ||
+ 			((TargetEntry *) lfirst(tlist_item))->resdom->resjunk)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 					 errmsg("subquery must return a column")));
+ 		while ((tlist_item = lnext(tlist_item)) != NULL)
+ 		{
+ 			if (!((TargetEntry *) lfirst(tlist_item))->resdom->resjunk)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("subquery must return only one column")));
+ 		}
+ 
+ 		/*
+ 		 * EXPR and ARRAY need no lefthand or combining
+ 		 * operator. These fields should be NIL already, but make
+ 		 * sure.
+ 		 */
+ 		sublink->lefthand = NIL;
+ 		sublink->operName = NIL;
+ 		sublink->operOids = NIL;
+ 		sublink->useOr = FALSE;
+ 	}
+ 	else
+ 	{
+ 		/* ALL, ANY, or MULTIEXPR: generate operator list */
+ 		List	   *left_list = sublink->lefthand;
+ 		List	   *right_list = qtree->targetList;
+ 		int			row_length = list_length(left_list);
+ 		bool		needNot = false;
+ 		List	   *op = sublink->operName;
+ 		char	   *opname = strVal(llast(op));
+ 		ListCell   *l;
+ 		ListCell   *ll_item;
+ 
+ 		/* transform lefthand expressions */
+ 		foreach(l, left_list)
+ 			lfirst(l) = transformExpr(pstate, lfirst(l));
+ 
+ 		/*
+ 		 * If the expression is "<> ALL" (with unqualified opname)
+ 		 * then convert it to "NOT IN".  This is a hack to improve
+ 		 * efficiency of expressions output by pre-7.4 Postgres.
+ 		 */
+ 		if (sublink->subLinkType == ALL_SUBLINK &&
+ 			list_length(op) == 1 && strcmp(opname, "<>") == 0)
+ 		{
+ 			sublink->subLinkType = ANY_SUBLINK;
+ 			opname = pstrdup("=");
+ 			op = list_make1(makeString(opname));
+ 			sublink->operName = op;
+ 			needNot = true;
+ 		}
+ 
+ 		/* Set useOr if op is "<>" (possibly qualified) */
+ 		if (strcmp(opname, "<>") == 0)
+ 			sublink->useOr = TRUE;
+ 		else
+ 			sublink->useOr = FALSE;
+ 
+ 		/* Combining operators other than =/<> is dubious... */
+ 		if (row_length != 1 &&
+ 			strcmp(opname, "=") != 0 &&
+ 			strcmp(opname, "<>") != 0)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 					 errmsg("row comparison cannot use operator %s",
+ 							opname)));
+ 
+ 		/*
+ 		 * To build the list of combining operator OIDs, we must scan
+ 		 * subquery's targetlist to find values that will be matched
+ 		 * against lefthand values.  We need to ignore resjunk
+ 		 * targets, so doing the outer iteration over right_list is
+ 		 * easier than doing it over left_list.
+ 		 */
+ 		sublink->operOids = NIL;
+ 
+ 		ll_item = list_head(left_list);
+ 		foreach(l, right_list)
+ 		{
+ 			TargetEntry *tent = (TargetEntry *) lfirst(l);
+ 			Node	   *lexpr;
+ 			Operator	optup;
+ 			Form_pg_operator opform;
+ 
+ 			if (tent->resdom->resjunk)
+ 				continue;
+ 
+ 			if (ll_item == NULL)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("subquery has too many columns")));
+ 			lexpr = lfirst(ll_item);
+ 			ll_item = lnext(ll_item);
+ 
+ 			/*
+ 			 * It's OK to use oper() not compatible_oper() here,
+ 			 * because make_subplan() will insert type coercion calls
+ 			 * if needed.
+ 			 */
+ 			optup = oper(op,
+ 						 exprType(lexpr),
+ 						 exprType((Node *) tent->expr),
+ 						 false);
+ 			opform = (Form_pg_operator) GETSTRUCT(optup);
+ 
+ 			if (opform->oprresult != BOOLOID)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 						 errmsg("operator %s must return type boolean, not type %s",
+ 								opname,
+ 								format_type_be(opform->oprresult)),
+ 						 errhint("The operator of a quantified predicate subquery must return type boolean.")));
+ 
+ 			if (get_func_retset(opform->oprcode))
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 						 errmsg("operator %s must not return a set",
+ 								opname),
+ 						 errhint("The operator of a quantified predicate subquery must return type boolean.")));
+ 
+ 			sublink->operOids = lappend_oid(sublink->operOids,
+ 											oprid(optup));
+ 
+ 			ReleaseSysCache(optup);
+ 		}
+ 		if (ll_item != NULL)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 					 errmsg("subquery has too few columns")));
+ 
+ 		if (needNot)
+ 		{
+ 			result = coerce_to_boolean(pstate, result, "NOT");
+ 			result = (Node *) makeBoolExpr(NOT_EXPR,
+ 										   list_make1(result));
+ 		}
+ 	}
+ 
+ 	return result;
+ }
+ 
+ static Node *
+ transformArrayExpr(ParseState *pstate, ArrayExpr *a)
+ {
+ 	ArrayExpr  *newa = makeNode(ArrayExpr);
+ 	List	   *newelems = NIL;
+ 	List	   *newcoercedelems = NIL;
+ 	List	   *typeids = NIL;
+ 	ListCell   *element;
+ 	Oid			array_type;
+ 	Oid			element_type;
+ 
+ 	/* Transform the element expressions */
+ 	foreach(element, a->elements)
+ 	{
+ 		Node	   *e = (Node *) lfirst(element);
+ 		Node	   *newe;
+ 
+ 		newe = transformExpr(pstate, e);
+ 		newelems = lappend(newelems, newe);
+ 		typeids = lappend_oid(typeids, exprType(newe));
+ 	}
+ 
+ 	/* Select a common type for the elements */
+ 	element_type = select_common_type(typeids, "ARRAY");
+ 
+ 	/* Coerce arguments to common type if necessary */
+ 	foreach(element, newelems)
+ 	{
+ 		Node	   *e = (Node *) lfirst(element);
+ 		Node	   *newe;
+ 
+ 		newe = coerce_to_common_type(pstate, e,
+ 									 element_type,
+ 									 "ARRAY");
+ 		newcoercedelems = lappend(newcoercedelems, newe);
+ 	}
+ 
+ 	/* Do we have an array type to use? */
+ 	array_type = get_array_type(element_type);
+ 	if (array_type != InvalidOid)
+ 	{
+ 		/* Elements are presumably of scalar type */
+ 		newa->multidims = false;
+ 	}
+ 	else
+ 	{
+ 		/* Must be nested array expressions */
+ 		newa->multidims = true;
+ 
+ 		array_type = element_type;
+ 		element_type = get_element_type(array_type);
+ 		if (!OidIsValid(element_type))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("could not find array type for data type %s",
+ 							format_type_be(array_type))));
+ 	}
+ 
+ 	newa->array_typeid = array_type;
+ 	newa->element_typeid = element_type;
+ 	newa->elements = newcoercedelems;
+ 
+ 	return (Node *) newa;
+ }
+ 
+ static Node *
+ transformRowExpr(ParseState *pstate, RowExpr *r)
+ {
+ 	RowExpr    *newr = makeNode(RowExpr);
+ 	List	   *newargs = NIL;
+ 	ListCell   *arg;
+ 
+ 	/* Transform the field expressions */
+ 	foreach(arg, r->args)
+ 	{
+ 		Node	   *e = (Node *) lfirst(arg);
+ 		Node	   *newe;
+ 
+ 		newe = transformExpr(pstate, e);
+ 		newargs = lappend(newargs, newe);
+ 	}
+ 	newr->args = newargs;
+ 
+ 	/* Barring later casting, we consider the type RECORD */
+ 	newr->row_typeid = RECORDOID;
+ 	newr->row_format = COERCE_IMPLICIT_CAST;
+ 
+ 	return (Node *) newr;
+ }
+ 
+ static Node *
+ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
+ {
+ 	CoalesceExpr *newc = makeNode(CoalesceExpr);
+ 	List	   *newargs = NIL;
+ 	List	   *newcoercedargs = NIL;
+ 	List	   *typeids = NIL;
+ 	ListCell   *args;
+ 
+ 	foreach(args, c->args)
+ 	{
+ 		Node	   *e = (Node *) lfirst(args);
+ 		Node	   *newe;
+ 
+ 		newe = transformExpr(pstate, e);
+ 		newargs = lappend(newargs, newe);
+ 		typeids = lappend_oid(typeids, exprType(newe));
+ 	}
+ 
+ 	newc->coalescetype = select_common_type(typeids, "COALESCE");
+ 
+ 	/* Convert arguments if necessary */
+ 	foreach(args, newargs)
+ 	{
+ 		Node	   *e = (Node *) lfirst(args);
+ 		Node	   *newe;
+ 
+ 		newe = coerce_to_common_type(pstate, e,
+ 									 newc->coalescetype,
+ 									 "COALESCE");
+ 		newcoercedargs = lappend(newcoercedargs, newe);
+ 	}
+ 
+ 	newc->args = newcoercedargs;
+ 	return (Node *) newc;
+ }
+ 
+ static Node *
+ transformBooleanTest(ParseState *pstate, BooleanTest *b)
+ {
+ 	const char *clausename;
+ 
+ 	switch (b->booltesttype)
+ 	{
+ 		case IS_TRUE:
+ 			clausename = "IS TRUE";
+ 			break;
+ 		case IS_NOT_TRUE:
+ 			clausename = "IS NOT TRUE";
+ 			break;
+ 		case IS_FALSE:
+ 			clausename = "IS FALSE";
+ 			break;
+ 		case IS_NOT_FALSE:
+ 			clausename = "IS NOT FALSE";
+ 			break;
+ 		case IS_UNKNOWN:
+ 			clausename = "IS UNKNOWN";
+ 			break;
+ 		case IS_NOT_UNKNOWN:
+ 			clausename = "IS NOT UNKNOWN";
+ 			break;
+ 		default:
+ 			elog(ERROR, "unrecognized booltesttype: %d",
+ 				 (int) b->booltesttype);
+ 			clausename = NULL;		/* keep compiler quiet */
+ 	}
+ 
+ 	b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg);
+ 
+ 	b->arg = (Expr *) coerce_to_boolean(pstate,
+ 										(Node *) b->arg,
+ 										clausename);
+ 
+ 	return (Node *) b;
+ }
+ 
  /*
   * Construct a whole-row reference to represent the notation "relation.*".
   *
---------------------------(end of broadcast)---------------------------
TIP 3: if posting/reading through Usenet, please send an appropriate
      subscribe-nomail command to [EMAIL PROTECTED] so that your
      message can get through to the mailing list cleanly

Reply via email to