Refactor transformExpr() by creating separate functions for most of the
authorNeil Conway
Wed, 19 Jan 2005 23:45:24 +0000 (23:45 +0000)
committerNeil Conway
Wed, 19 Jan 2005 23:45:24 +0000 (23:45 +0000)
expression types.

src/backend/parser/parse_expr.c

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