Refactor transformExpr() by creating separate functions for most of the
authorNeil Conway <neilc@samurai.com>
Wed, 19 Jan 2005 23:45:24 +0000 (23:45 +0000)
committerNeil Conway <neilc@samurai.com>
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.*".
  *