On Thu, 2004-10-28 at 16:49 +1000, Neil Conway wrote:
> 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 still think this patch is worth applying. Sadly, I will need to
basically rewrite it due to code drift. I'm happy to do that, although
I'd like to resolve whether we want to accept it or not. Tom and Bruce
objected when I posted it originally, although I don't think we reached
a conclusion.

Why I think the patch is a good idea: 900 line functions are almost
universally bad (in fact, I'd be tempted to remove the "almost"). A 900
line function that divides distinct functionality into different
branches of a "switch" statement isn't _that_ evil, but it is still a
large hunk of code for someone to understand as a single, monolithic
unit. Because each branch of the "switch" statement is independent, we
can trivially split each branch off into a function -- each branch does
something distinct, so it ought to be a distinct function. That means
the independence of each branch of the "switch" statement is guaranteed
(the function can't modify a local variable in the calling function, for
example), and it means that the code is conceptually easier to read.
With the present layout, 

It does mean that you'll need to jump to the function definition if you
want to see what a particular branch of the "switch" statement does. But
(a) use tags, it's not tough (b) this is a _good_ thing. If I want to
understand what the parent function does (transformExpr(), the one with
the "switch"), I don't want to have to grok through a 700 line "switch"
statement. If each branch of the switch just invokes a function, the
intent of transformExpr() is perfectly clear.

For refresh everyone's memory, I've attached the original version of the
patch. It won't apply cleanly to current sources, but it should apply to
HEAD as of approximately Oct. 28, 2004.

-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 8: explain analyze is your friend

Reply via email to