This patch refactors transformExpr(): rather than being a monsterous 900
line function, it breaks it up into numerous sub-functions that are
invoked by transformExpr() for individual expression types, in the style
of transformStmt().

I think this patch is reasonably safe for HEAD (it mostly just
rearranges code), but I don't mind holding on to it for 8.1. Comments?

-Neil

Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /var/lib/cvs/pgsql/src/backend/parser/parse_expr.c,v
retrieving revision 1.176
diff -c -r1.176 parse_expr.c
*** src/backend/parser/parse_expr.c	29 Aug 2004 05:06:44 -0000	1.176
--- src/backend/parser/parse_expr.c	28 Oct 2004 06:46:54 -0000
***************
*** 37,45 ****
--- 37,62 ----
  
  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 *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;
--- 107,117 ----
  	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;
***************
*** 184,961 ****
  				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);
! 				newc->arg = (Expr *) arg;
! 				/* generate placeholder for test expression */
! 				if (arg)
! 				{
! 					placeholder = makeNode(CaseTestExpr);
! 					placeholder->typeId = exprType(arg);
! 					placeholder->typeMod = exprTypmod(arg);
! 				}
! 				else
! 					placeholder = NULL;
  
! 				/* 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:
! 			{
! 				NullTest   *n = (NullTest *) expr;
  
! 				n->arg = (Expr *) transformExpr(pstate, (Node *) n->arg);
! 				/* the argument can be any type, so don't coerce it */
! 				result = expr;
! 				break;
! 			}
  
! 		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
! 			 * called on an already-transformed tree.
! 			 *
! 			 * Do any other node types need to be accepted?  For now we are
! 			 * taking a conservative approach, and only accepting node
! 			 * types that are demonstrably necessary to accept.
! 			 *********************************************/
! 		case T_Var:
! 		case T_Const:
! 		case T_Param:
! 		case T_Aggref:
! 		case T_ArrayRef:
! 		case T_FuncExpr:
! 		case T_OpExpr:
! 		case T_DistinctExpr:
! 		case T_ScalarArrayOpExpr:
! 		case T_NullIfExpr:
! 		case T_BoolExpr:
! 		case T_FieldSelect:
! 		case T_FieldStore:
! 		case T_RelabelType:
! 		case T_CaseTestExpr:
! 		case T_CoerceToDomain:
! 		case T_CoerceToDomainValue:
! 		case T_SetToDefault:
! 			{
! 				result = (Node *) expr;
! 				break;
! 			}
  
! 		default:
! 			/* should not reach here */
! 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
! 			break;
  	}
  
! 	return result;
  }
  
  static Node *
--- 147,984 ----
  				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:
  			{
! 				NullTest   *n = (NullTest *) expr;
  
! 				n->arg = (Expr *) transformExpr(pstate, (Node *) n->arg);
! 				/* the argument can be any type, so don't coerce it */
! 				result = expr;
  				break;
  			}
! 
! 		case T_BooleanTest:
! 			result = transformBooleanTest(pstate, (BooleanTest *) expr);
! 			break;
! 
! 			/*********************************************
! 			 * Quietly accept node types that may be presented when we are
! 			 * called on an already-transformed tree.
! 			 *
! 			 * Do any other node types need to be accepted?  For now we are
! 			 * taking a conservative approach, and only accepting node
! 			 * types that are demonstrably necessary to accept.
! 			 *********************************************/
! 		case T_Var:
! 		case T_Const:
! 		case T_Param:
! 		case T_Aggref:
! 		case T_ArrayRef:
! 		case T_FuncExpr:
! 		case T_OpExpr:
! 		case T_DistinctExpr:
! 		case T_ScalarArrayOpExpr:
! 		case T_NullIfExpr:
! 		case T_BoolExpr:
! 		case T_FieldSelect:
! 		case T_FieldStore:
! 		case T_RelabelType:
! 		case T_CaseTestExpr:
! 		case T_CoerceToDomain:
! 		case T_CoerceToDomainValue:
! 		case T_SetToDefault:
  			{
! 				result = (Node *) expr;
! 				break;
! 			}
  
! 		default:
! 			/* should not reach here */
! 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
! 			break;
! 	}
  
! 	return result;
! }
  
! 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;
! 	Node	   *result;
  
! 	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);
! 	}
  
! 	return result;
! }
  
! 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 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 *
! 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 *
! 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);
! 	newc->arg = (Expr *) arg;
! 	/* generate placeholder for test expression */
! 	if (arg)
! 	{
! 		placeholder = makeNode(CaseTestExpr);
! 		placeholder->typeId = exprType(arg);
! 		placeholder->typeMod = exprTypmod(arg);
! 	}
! 	else
! 		placeholder = NULL;
  
! 	/* 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 *
! 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 *
***************
*** 1278,1283 ****
--- 1301,1346 ----
  	return result;
  }
  
+ 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;
+ }
+ 
  /*
   *	exprType -
   *	  returns the Oid of the type of the expression. (Used for typechecking.)
---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
    (send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])

Reply via email to