]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/execQual.c
Improve the recently-added support for properly pluralized error messages
[postgresql] / src / backend / executor / execQual.c
index 8052f8b2d76590715ce199eb29c32fd2830a2709..c7bfe7c76ca6b926f2dcbe1eee05b3a0b8b6ecc0 100644 (file)
@@ -3,12 +3,12 @@
  * execQual.c
  *       Routines to evaluate qualification and targetlist expressions
  *
- * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.206 2007/01/12 21:47:26 petere Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.247 2009/06/04 18:33:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  *             instead of doing needless copying.      -cim 5/31/91
  *
  *             During expression evaluation, we check_stack_depth only in
- *             ExecMakeFunctionResult rather than at every single node.  This
- *             is a compromise that trades off precision of the stack limit setting
- *             to gain speed.
+ *             ExecMakeFunctionResult (and substitute routines) rather than at every
+ *             single node.  This is a compromise that trades off precision of the
+ *             stack limit setting to gain speed.
  */
 
 #include "postgres.h"
 
-#include "access/heapam.h"
 #include "access/nbtree.h"
 #include "catalog/pg_type.h"
 #include "commands/typecmds.h"
@@ -45,8 +44,9 @@
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
-#include "optimizer/planmain.h"
-#include "parser/parse_expr.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "pgstat.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
@@ -62,20 +62,38 @@ static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
 static Datum ExecEvalAggref(AggrefExprState *aggref,
                           ExprContext *econtext,
                           bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalWindowFunc(WindowFuncExprState *wfunc,
+                          ExprContext *econtext,
+                          bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
                        bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
+                                 bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
                                        bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
+                                        bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
                          bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
                          bool *isNull, ExprDoneCond *isDone);
+static void init_fcache(Oid foid, FuncExprState *fcache,
+                                               MemoryContext fcacheCxt, bool needDescForSets);
 static void ShutdownFuncExpr(Datum arg);
 static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
                                   TupleDesc *cache_field, ExprContext *econtext);
 static void ShutdownTupleDescRef(Datum arg);
 static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
                                 List *argList, ExprContext *econtext);
+static void ExecPrepareTuplestoreResult(FuncExprState *fcache,
+                                                       ExprContext *econtext,
+                                                       Tuplestorestate *resultStore,
+                                                       TupleDesc resultDesc);
+static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
+static Datum ExecMakeFunctionResult(FuncExprState *fcache,
+                                          ExprContext *econtext,
+                                          bool *isNull,
+                                          ExprDoneCond *isDone);
 static Datum ExecMakeFunctionResultNoSets(FuncExprState *fcache,
                                                         ExprContext *econtext,
                                                         bool *isNull, ExprDoneCond *isDone);
@@ -118,7 +136,7 @@ static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr,
                           ExprContext *econtext,
                           bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
-                                                bool *isNull, ExprDoneCond *isDone);
+                       bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalNullIf(FuncExprState *nullIfExpr,
                           ExprContext *econtext,
                           bool *isNull, ExprDoneCond *isDone);
@@ -143,6 +161,14 @@ static Datum ExecEvalFieldStore(FieldStoreState *fstate,
 static Datum ExecEvalRelabelType(GenericExprState *exprstate,
                                        ExprContext *econtext,
                                        bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
+                                       ExprContext *econtext,
+                                       bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
+                                               ExprContext *econtext,
+                                               bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
+                                         bool *isNull, ExprDoneCond *isDone);
 
 
 /* ----------------------------------------------------------------
@@ -420,11 +446,37 @@ ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext,
        return econtext->ecxt_aggvalues[aggref->aggno];
 }
 
+/* ----------------------------------------------------------------
+ *             ExecEvalWindowFunc
+ *
+ *             Returns a Datum whose value is the value of the precomputed
+ *             window function found in the given expression context.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalWindowFunc(WindowFuncExprState *wfunc, ExprContext *econtext,
+                                  bool *isNull, ExprDoneCond *isDone)
+{
+       if (isDone)
+               *isDone = ExprSingleResult;
+
+       if (econtext->ecxt_aggvalues == NULL)           /* safety check */
+               elog(ERROR, "no window functions in this expression context");
+
+       *isNull = econtext->ecxt_aggnulls[wfunc->wfuncno];
+       return econtext->ecxt_aggvalues[wfunc->wfuncno];
+}
+
 /* ----------------------------------------------------------------
  *             ExecEvalVar
  *
  *             Returns a Datum whose value is the value of a range
  *             variable with respect to given expression context.
+ *
+ * Note: ExecEvalVar is executed only the first time through in a given plan;
+ * it changes the ExprState's function pointer to pass control directly to
+ * ExecEvalScalarVar, ExecEvalWholeRowVar, or ExecEvalWholeRowSlow after
+ * making one-time checks.
  * ----------------------------------------------------------------
  */
 static Datum
@@ -439,7 +491,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
                *isDone = ExprSingleResult;
 
        /*
-        * Get the slot and attribute number we want
+        * Get the input slot and attribute number we want
         *
         * The asserts check that references to system attributes only appear at
         * the level of a relation scan; at higher levels, system attributes must
@@ -466,35 +518,188 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
                        break;
        }
 
-#ifdef USE_ASSERT_CHECKING
-
-       /*
-        * Some checks that are only applied for user attribute numbers (bogus
-        * system attnums will be caught inside slot_getattr).
-        */
-       if (attnum > 0)
+       if (attnum != InvalidAttrNumber)
        {
-               TupleDesc       tuple_type = slot->tts_tupleDescriptor;
-
                /*
-                * This assert checks that the attnum is valid.
+                * Scalar variable case.
+                *
+                * If it's a user attribute, check validity (bogus system attnums will
+                * be caught inside slot_getattr).      What we have to check for here is
+                * the possibility of an attribute having been changed in type since
+                * the plan tree was created.  Ideally the plan would get invalidated
+                * and not re-used, but until that day arrives, we need defenses.
+                * Fortunately it's sufficient to check once on the first time
+                * through.
+                *
+                * Note: we allow a reference to a dropped attribute.  slot_getattr
+                * will force a NULL result in such cases.
+                *
+                * Note: ideally we'd check typmod as well as typid, but that seems
+                * impractical at the moment: in many cases the tupdesc will have been
+                * generated by ExecTypeFromTL(), and that can't guarantee to generate
+                * an accurate typmod in all cases, because some expression node types
+                * don't carry typmod.
                 */
-               Assert(attnum <= tuple_type->natts);
+               if (attnum > 0)
+               {
+                       TupleDesc       slot_tupdesc = slot->tts_tupleDescriptor;
+                       Form_pg_attribute attr;
+
+                       if (attnum > slot_tupdesc->natts)       /* should never happen */
+                               elog(ERROR, "attribute number %d exceeds number of columns %d",
+                                        attnum, slot_tupdesc->natts);
 
+                       attr = slot_tupdesc->attrs[attnum - 1];
+
+                       /* can't check type if dropped, since atttypid is probably 0 */
+                       if (!attr->attisdropped)
+                       {
+                               if (variable->vartype != attr->atttypid)
+                                       ereport(ERROR,
+                                                       (errmsg("attribute %d has wrong type", attnum),
+                                               errdetail("Table has type %s, but query expects %s.",
+                                                                 format_type_be(attr->atttypid),
+                                                                 format_type_be(variable->vartype))));
+                       }
+               }
+
+               /* Skip the checking on future executions of node */
+               exprstate->evalfunc = ExecEvalScalarVar;
+
+               /* Fetch the value from the slot */
+               return slot_getattr(slot, attnum, isNull);
+       }
+       else
+       {
                /*
-                * This assert checks that the datatype the plan expects to get (as
-                * told by our "variable" argument) is in fact the datatype of the
-                * attribute being fetched (as seen in the current context, identified
-                * by our "econtext" argument).  Otherwise crashes are likely.
+                * Whole-row variable.
+                *
+                * If it's a RECORD Var, we'll use the slot's type ID info.  It's
+                * likely that the slot's type is also RECORD; if so, make sure it's
+                * been "blessed", so that the Datum can be interpreted later.
                 *
-                * Note that we can't check dropped columns, since their atttypid has
-                * been zeroed.
+                * If the Var identifies a named composite type, we must check that
+                * the actual tuple type is compatible with it.
                 */
-               Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid ||
-                          tuple_type->attrs[attnum - 1]->attisdropped);
+               TupleDesc       slot_tupdesc = slot->tts_tupleDescriptor;
+               bool            needslow = false;
+
+               if (variable->vartype == RECORDOID)
+               {
+                       if (slot_tupdesc->tdtypeid == RECORDOID &&
+                               slot_tupdesc->tdtypmod < 0)
+                               assign_record_type_typmod(slot_tupdesc);
+               }
+               else
+               {
+                       TupleDesc       var_tupdesc;
+                       int                     i;
+
+                       /*
+                        * We really only care about number of attributes and data type.
+                        * Also, we can ignore type mismatch on columns that are dropped
+                        * in the destination type, so long as the physical storage
+                        * matches.  This is helpful in some cases involving out-of-date
+                        * cached plans.  Also, we have to allow the case that the slot
+                        * has more columns than the Var's type, because we might be
+                        * looking at the output of a subplan that includes resjunk
+                        * columns.  (XXX it would be nice to verify that the extra
+                        * columns are all marked resjunk, but we haven't got access to
+                        * the subplan targetlist here...)      Resjunk columns should always
+                        * be at the end of a targetlist, so it's sufficient to ignore
+                        * them here; but we need to use ExecEvalWholeRowSlow to get rid
+                        * of them in the eventual output tuples.
+                        */
+                       var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
+
+                       if (var_tupdesc->natts > slot_tupdesc->natts)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                errmsg("table row type and query-specified row type do not match"),
+                                                errdetail_plural("Table row contains %d attribute, but query expects %d.",
+                                                                                 "Table row contains %d attributes, but query expects %d.",
+                                                                                 slot_tupdesc->natts,
+                                                                                 slot_tupdesc->natts,
+                                                                                 var_tupdesc->natts)));
+                       else if (var_tupdesc->natts < slot_tupdesc->natts)
+                               needslow = true;
+
+                       for (i = 0; i < var_tupdesc->natts; i++)
+                       {
+                               Form_pg_attribute vattr = var_tupdesc->attrs[i];
+                               Form_pg_attribute sattr = slot_tupdesc->attrs[i];
+
+                               if (vattr->atttypid == sattr->atttypid)
+                                       continue;       /* no worries */
+                               if (!vattr->attisdropped)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                        errmsg("table row type and query-specified row type do not match"),
+                                                        errdetail("Table has type %s at ordinal position %d, but query expects %s.",
+                                                                          format_type_be(sattr->atttypid),
+                                                                          i + 1,
+                                                                          format_type_be(vattr->atttypid))));
+
+                               if (vattr->attlen != sattr->attlen ||
+                                       vattr->attalign != sattr->attalign)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                        errmsg("table row type and query-specified row type do not match"),
+                                                        errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
+                                                                          i + 1)));
+                       }
+
+                       ReleaseTupleDesc(var_tupdesc);
+               }
+
+               /* Skip the checking on future executions of node */
+               if (needslow)
+                       exprstate->evalfunc = ExecEvalWholeRowSlow;
+               else
+                       exprstate->evalfunc = ExecEvalWholeRowVar;
+
+               /* Fetch the value */
+               return ExecEvalWholeRowVar(exprstate, econtext, isNull, isDone);
+       }
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalScalarVar
+ *
+ *             Returns a Datum for a scalar variable.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
+                                 bool *isNull, ExprDoneCond *isDone)
+{
+       Var                *variable = (Var *) exprstate->expr;
+       TupleTableSlot *slot;
+       AttrNumber      attnum;
+
+       if (isDone)
+               *isDone = ExprSingleResult;
+
+       /* Get the input slot and attribute number we want */
+       switch (variable->varno)
+       {
+               case INNER:                             /* get the tuple from the inner node */
+                       slot = econtext->ecxt_innertuple;
+                       break;
+
+               case OUTER:                             /* get the tuple from the outer node */
+                       slot = econtext->ecxt_outertuple;
+                       break;
+
+               default:                                /* get the tuple from the relation being
+                                                                * scanned */
+                       slot = econtext->ecxt_scantuple;
+                       break;
        }
-#endif   /* USE_ASSERT_CHECKING */
 
+       attnum = variable->varattno;
+
+       /* Fetch the value from the slot */
        return slot_getattr(slot, attnum, isNull);
 }
 
@@ -502,10 +707,6 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
  *             ExecEvalWholeRowVar
  *
  *             Returns a Datum for a whole-row variable.
- *
- *             This could be folded into ExecEvalVar, but we make it a separate
- *             routine so as not to slow down ExecEvalVar with tests for this
- *             uncommon case.
  * ----------------------------------------------------------------
  */
 static Datum
@@ -513,7 +714,7 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
                                        bool *isNull, ExprDoneCond *isDone)
 {
        Var                *variable = (Var *) exprstate->expr;
-       TupleTableSlot *slot;
+       TupleTableSlot *slot = econtext->ecxt_scantuple;
        HeapTuple       tuple;
        TupleDesc       tupleDesc;
        HeapTupleHeader dtuple;
@@ -522,16 +723,6 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
                *isDone = ExprSingleResult;
        *isNull = false;
 
-       Assert(variable->varattno == InvalidAttrNumber);
-
-       /*
-        * Whole-row Vars can only appear at the level of a relation scan, never
-        * in a join.
-        */
-       Assert(variable->varno != INNER);
-       Assert(variable->varno != OUTER);
-       slot = econtext->ecxt_scantuple;
-
        tuple = ExecFetchSlotTuple(slot);
        tupleDesc = slot->tts_tupleDescriptor;
 
@@ -547,9 +738,6 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
        /*
         * If the Var identifies a named composite type, label the tuple with that
         * type; otherwise use what is in the tupleDesc.
-        *
-        * It's likely that the slot's tupleDesc is a record type; if so, make
-        * sure it's been "blessed", so that the Datum can be interpreted later.
         */
        if (variable->vartype != RECORDOID)
        {
@@ -558,9 +746,6 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
        }
        else
        {
-               if (tupleDesc->tdtypeid == RECORDOID &&
-                       tupleDesc->tdtypmod < 0)
-                       assign_record_type_typmod(tupleDesc);
                HeapTupleHeaderSetTypeId(dtuple, tupleDesc->tdtypeid);
                HeapTupleHeaderSetTypMod(dtuple, tupleDesc->tdtypmod);
        }
@@ -568,6 +753,60 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
        return PointerGetDatum(dtuple);
 }
 
+/* ----------------------------------------------------------------
+ *             ExecEvalWholeRowSlow
+ *
+ *             Returns a Datum for a whole-row variable, in the "slow" case where
+ *             we can't just copy the subplan's output.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
+                                        bool *isNull, ExprDoneCond *isDone)
+{
+       Var                *variable = (Var *) exprstate->expr;
+       TupleTableSlot *slot = econtext->ecxt_scantuple;
+       HeapTuple       tuple;
+       TupleDesc       var_tupdesc;
+       HeapTupleHeader dtuple;
+
+       if (isDone)
+               *isDone = ExprSingleResult;
+       *isNull = false;
+
+       /*
+        * Currently, the only case handled here is stripping of trailing resjunk
+        * fields, which we do in a slightly chintzy way by just adjusting the
+        * tuple's natts header field.  Possibly there will someday be a need for
+        * more-extensive rearrangements, in which case it'd be worth
+        * disassembling and reassembling the tuple (perhaps use a JunkFilter for
+        * that?)
+        */
+       Assert(variable->vartype != RECORDOID);
+       var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
+
+       tuple = ExecFetchSlotTuple(slot);
+
+       /*
+        * We have to make a copy of the tuple so we can safely insert the Datum
+        * overhead fields, which are not set in on-disk tuples; not to mention
+        * fooling with its natts field.
+        */
+       dtuple = (HeapTupleHeader) palloc(tuple->t_len);
+       memcpy((char *) dtuple, (char *) tuple->t_data, tuple->t_len);
+
+       HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len);
+       HeapTupleHeaderSetTypeId(dtuple, variable->vartype);
+       HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod);
+
+       Assert(HeapTupleHeaderGetNatts(dtuple) >= var_tupdesc->natts);
+       HeapTupleHeaderSetNatts(dtuple, var_tupdesc->natts);
+
+       ReleaseTupleDesc(var_tupdesc);
+
+       return PointerGetDatum(dtuple);
+}
+
 /* ----------------------------------------------------------------
  *             ExecEvalConst
  *
@@ -785,8 +1024,9 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
 /*
  * init_fcache - initialize a FuncExprState node during first use
  */
-void
-init_fcache(Oid foid, FuncExprState *fcache, MemoryContext fcacheCxt)
+static void
+init_fcache(Oid foid, FuncExprState *fcache,
+                       MemoryContext fcacheCxt, bool needDescForSets)
 {
        AclResult       aclresult;
 
@@ -804,16 +1044,67 @@ init_fcache(Oid foid, FuncExprState *fcache, MemoryContext fcacheCxt)
        if (list_length(fcache->args) > FUNC_MAX_ARGS)
                ereport(ERROR,
                                (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
-                                errmsg("cannot pass more than %d arguments to a function",
-                                               FUNC_MAX_ARGS)));
+                                errmsg_plural("cannot pass more than %d argument to a function",
+                                                          "cannot pass more than %d arguments to a function",
+                                                          FUNC_MAX_ARGS,
+                                                          FUNC_MAX_ARGS)));
 
        /* Set up the primary fmgr lookup information */
        fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
+       fcache->func.fn_expr = (Node *) fcache->xprstate.expr;
+
+       /* If function returns set, prepare expected tuple descriptor */
+       if (fcache->func.fn_retset && needDescForSets)
+       {
+               TypeFuncClass functypclass;
+               Oid                     funcrettype;
+               TupleDesc       tupdesc;
+               MemoryContext oldcontext;
+
+               functypclass = get_expr_result_type(fcache->func.fn_expr,
+                                                                                       &funcrettype,
+                                                                                       &tupdesc);
+
+               /* Must save tupdesc in fcache's context */
+               oldcontext = MemoryContextSwitchTo(fcacheCxt);
+
+               if (functypclass == TYPEFUNC_COMPOSITE)
+               {
+                       /* Composite data type, e.g. a table's row type */
+                       Assert(tupdesc);
+                       /* Must copy it out of typcache for safety */
+                       fcache->funcResultDesc = CreateTupleDescCopy(tupdesc);
+                       fcache->funcReturnsTuple = true;
+               }
+               else if (functypclass == TYPEFUNC_SCALAR)
+               {
+                       /* Base data type, i.e. scalar */
+                       tupdesc = CreateTemplateTupleDesc(1, false);
+                       TupleDescInitEntry(tupdesc,
+                                                          (AttrNumber) 1,
+                                                          NULL,
+                                                          funcrettype,
+                                                          -1,
+                                                          0);
+                       fcache->funcResultDesc = tupdesc;
+                       fcache->funcReturnsTuple = false;
+               }
+               else
+               {
+                       /* Else, we will complain if function wants materialize mode */
+                       fcache->funcResultDesc = NULL;
+               }
 
-       /* Initialize additional info */
+               MemoryContextSwitchTo(oldcontext);
+       }
+       else
+               fcache->funcResultDesc = NULL;
+
+       /* Initialize additional state */
+       fcache->funcResultStore = NULL;
+       fcache->funcResultSlot = NULL;
        fcache->setArgsValid = false;
        fcache->shutdown_reg = false;
-       fcache->func.fn_expr = (Node *) fcache->xprstate.expr;
 }
 
 /*
@@ -825,6 +1116,15 @@ ShutdownFuncExpr(Datum arg)
 {
        FuncExprState *fcache = (FuncExprState *) DatumGetPointer(arg);
 
+       /* If we have a slot, make sure it's let go of any tuplestore pointer */
+       if (fcache->funcResultSlot)
+               ExecClearTuple(fcache->funcResultSlot);
+
+       /* Release any open tuplestore */
+       if (fcache->funcResultStore)
+               tuplestore_end(fcache->funcResultStore);
+       fcache->funcResultStore = NULL;
+
        /* Clear any active set-argument state */
        fcache->setArgsValid = false;
 
@@ -933,39 +1233,215 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
        return argIsDone;
 }
 
+/*
+ *             ExecPrepareTuplestoreResult
+ *
+ * Subroutine for ExecMakeFunctionResult: prepare to extract rows from a
+ * tuplestore function result.  We must set up a funcResultSlot (unless
+ * already done in a previous call cycle) and verify that the function
+ * returned the expected tuple descriptor.
+ */
+static void
+ExecPrepareTuplestoreResult(FuncExprState *fcache,
+                                                       ExprContext *econtext,
+                                                       Tuplestorestate *resultStore,
+                                                       TupleDesc resultDesc)
+{
+       fcache->funcResultStore = resultStore;
+
+       if (fcache->funcResultSlot == NULL)
+       {
+               /* Create a slot so we can read data out of the tuplestore */
+               MemoryContext oldcontext;
+
+               /* We must have been able to determine the result rowtype */
+               if (fcache->funcResultDesc == NULL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("function returning setof record called in "
+                                                       "context that cannot accept type record")));
+
+               oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt);
+               fcache->funcResultSlot =
+                       MakeSingleTupleTableSlot(fcache->funcResultDesc);
+               MemoryContextSwitchTo(oldcontext);
+       }
+
+       /*
+        * If function provided a tupdesc, cross-check it.      We only really
+        * need to do this for functions returning RECORD, but might as well
+        * do it always.
+        */
+       if (resultDesc)
+       {
+               if (fcache->funcResultDesc)
+                       tupledesc_match(fcache->funcResultDesc, resultDesc);
+
+               /*
+                * If it is a dynamically-allocated TupleDesc, free it: it is
+                * typically allocated in a per-query context, so we must avoid
+                * leaking it across multiple usages.
+                */
+               if (resultDesc->tdrefcount == -1)
+                       FreeTupleDesc(resultDesc);
+       }
+
+       /* Register cleanup callback if we didn't already */
+       if (!fcache->shutdown_reg)
+       {
+               RegisterExprContextCallback(econtext,
+                                                                       ShutdownFuncExpr,
+                                                                       PointerGetDatum(fcache));
+               fcache->shutdown_reg = true;
+       }
+}
+
+/*
+ * Check that function result tuple type (src_tupdesc) matches or can
+ * be considered to match what the query expects (dst_tupdesc). If
+ * they don't match, ereport.
+ *
+ * We really only care about number of attributes and data type.
+ * Also, we can ignore type mismatch on columns that are dropped in the
+ * destination type, so long as the physical storage matches.  This is
+ * helpful in some cases involving out-of-date cached plans.
+ */
+static void
+tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
+{
+       int                     i;
+
+       if (dst_tupdesc->natts != src_tupdesc->natts)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("function return row and query-specified return row do not match"),
+                                errdetail_plural("Returned row contains %d attribute, but query expects %d.",
+                                                                 "Returned row contains %d attributes, but query expects %d.",
+                                                                 src_tupdesc->natts,
+                                                                 src_tupdesc->natts, dst_tupdesc->natts)));
+
+       for (i = 0; i < dst_tupdesc->natts; i++)
+       {
+               Form_pg_attribute dattr = dst_tupdesc->attrs[i];
+               Form_pg_attribute sattr = src_tupdesc->attrs[i];
+
+               if (dattr->atttypid == sattr->atttypid)
+                       continue;                       /* no worries */
+               if (!dattr->attisdropped)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg("function return row and query-specified return row do not match"),
+                                        errdetail("Returned type %s at ordinal position %d, but query expects %s.",
+                                                          format_type_be(sattr->atttypid),
+                                                          i + 1,
+                                                          format_type_be(dattr->atttypid))));
+
+               if (dattr->attlen != sattr->attlen ||
+                       dattr->attalign != sattr->attalign)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg("function return row and query-specified return row do not match"),
+                                        errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
+                                                          i + 1)));
+       }
+}
+
 /*
  *             ExecMakeFunctionResult
  *
  * Evaluate the arguments to a function and then the function itself.
+ * init_fcache is presumed already run on the FuncExprState.
+ *
+ * This function handles the most general case, wherein the function or
+ * one of its arguments might (or might not) return a set.  If we find
+ * no sets involved, we will change the FuncExprState's function pointer
+ * to use a simpler method on subsequent calls.
  */
-Datum
+static Datum
 ExecMakeFunctionResult(FuncExprState *fcache,
                                           ExprContext *econtext,
                                           bool *isNull,
                                           ExprDoneCond *isDone)
 {
-       List       *arguments = fcache->args;
+       List       *arguments;
        Datum           result;
-       FunctionCallInfoData fcinfo;
+       FunctionCallInfoData fcinfo_data;
+       FunctionCallInfo fcinfo;
+       PgStat_FunctionCallUsage fcusage;
        ReturnSetInfo rsinfo;           /* for functions returning sets */
        ExprDoneCond argDone;
        bool            hasSetArg;
        int                     i;
 
+restart:
+
        /* Guard against stack overflow due to overly complex expressions */
        check_stack_depth();
 
+       /*
+        * If a previous call of the function returned a set result in the form
+        * of a tuplestore, continue reading rows from the tuplestore until it's
+        * empty.
+        */
+       if (fcache->funcResultStore)
+       {
+               Assert(isDone);                         /* it was provided before ... */
+               if (tuplestore_gettupleslot(fcache->funcResultStore, true, false,
+                                                                       fcache->funcResultSlot))
+               {
+                       *isDone = ExprMultipleResult;
+                       if (fcache->funcReturnsTuple)
+                       {
+                               /* We must return the whole tuple as a Datum. */
+                               *isNull = false;
+                               return ExecFetchSlotTupleDatum(fcache->funcResultSlot);
+                       }
+                       else
+                       {
+                               /* Extract the first column and return it as a scalar. */
+                               return slot_getattr(fcache->funcResultSlot, 1, isNull);
+                       }
+               }
+               /* Exhausted the tuplestore, so clean up */
+               tuplestore_end(fcache->funcResultStore);
+               fcache->funcResultStore = NULL;
+               /* We are done unless there was a set-valued argument */
+               if (!fcache->setHasSetArg)
+               {
+                       *isDone = ExprEndResult;
+                       *isNull = true;
+                       return (Datum) 0;
+               }
+               /* If there was, continue evaluating the argument values */
+               Assert(!fcache->setArgsValid);
+       }
+
+       /*
+        * For non-set-returning functions, we just use a local-variable
+        * FunctionCallInfoData.  For set-returning functions we keep the callinfo
+        * record in fcache->setArgs so that it can survive across multiple
+        * value-per-call invocations.  (The reason we don't just do the latter
+        * all the time is that plpgsql expects to be able to use simple expression
+        * trees re-entrantly.  Which might not be a good idea, but the penalty
+        * for not doing so is high.)
+        */
+       if (fcache->func.fn_retset)
+               fcinfo = &fcache->setArgs;
+       else
+               fcinfo = &fcinfo_data;
+
        /*
         * arguments is a list of expressions to evaluate before passing to the
         * function manager.  We skip the evaluation if it was already done in the
         * previous call (ie, we are continuing the evaluation of a set-valued
         * function).  Otherwise, collect the current argument values into fcinfo.
         */
+       arguments = fcache->args;
        if (!fcache->setArgsValid)
        {
                /* Need to prep callinfo structure */
-               InitFunctionCallInfoData(fcinfo, &(fcache->func), 0, NULL, NULL);
-               argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext);
+               InitFunctionCallInfoData(*fcinfo, &(fcache->func), 0, NULL, NULL);
+               argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext);
                if (argDone == ExprEndResult)
                {
                        /* input is an empty set, so return an empty set. */
@@ -982,32 +1458,14 @@ ExecMakeFunctionResult(FuncExprState *fcache,
        }
        else
        {
-               /* Copy callinfo from previous evaluation */
-               memcpy(&fcinfo, &fcache->setArgs, sizeof(fcinfo));
+               /* Re-use callinfo from previous evaluation */
                hasSetArg = fcache->setHasSetArg;
                /* Reset flag (we may set it again below) */
                fcache->setArgsValid = false;
        }
 
        /*
-        * If function returns set, prepare a resultinfo node for communication
-        */
-       if (fcache->func.fn_retset)
-       {
-               fcinfo.resultinfo = (Node *) &rsinfo;
-               rsinfo.type = T_ReturnSetInfo;
-               rsinfo.econtext = econtext;
-               rsinfo.expectedDesc = NULL;
-               rsinfo.allowedModes = (int) SFRM_ValuePerCall;
-               rsinfo.returnMode = SFRM_ValuePerCall;
-               /* isDone is filled below */
-               rsinfo.setResult = NULL;
-               rsinfo.setDesc = NULL;
-       }
-
-       /*
-        * now return the value gotten by calling the function manager, passing
-        * the function the evaluated parameter values.
+        * Now call the function, passing the evaluated parameter values.
         */
        if (fcache->func.fn_retset || hasSetArg)
        {
@@ -1020,6 +1478,23 @@ ExecMakeFunctionResult(FuncExprState *fcache,
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                         errmsg("set-valued function called in context that cannot accept a set")));
 
+               /*
+                * Prepare a resultinfo node for communication.  If the function
+                * doesn't itself return set, we don't pass the resultinfo to the
+                * function, but we need to fill it in anyway for internal use.
+                */
+               if (fcache->func.fn_retset)
+                       fcinfo->resultinfo = (Node *) &rsinfo;
+               rsinfo.type = T_ReturnSetInfo;
+               rsinfo.econtext = econtext;
+               rsinfo.expectedDesc = fcache->funcResultDesc;
+               rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
+               /* note we do not set SFRM_Materialize_Random or _Preferred */
+               rsinfo.returnMode = SFRM_ValuePerCall;
+               /* isDone is filled below */
+               rsinfo.setResult = NULL;
+               rsinfo.setDesc = NULL;
+
                /*
                 * This loop handles the situation where we have both a set argument
                 * and a set-valued function.  Once we have exhausted the function's
@@ -1038,9 +1513,9 @@ ExecMakeFunctionResult(FuncExprState *fcache,
 
                        if (fcache->func.fn_strict)
                        {
-                               for (i = 0; i < fcinfo.nargs; i++)
+                               for (i = 0; i < fcinfo->nargs; i++)
                                {
-                                       if (fcinfo.argnull[i])
+                                       if (fcinfo->argnull[i])
                                        {
                                                callit = false;
                                                break;
@@ -1050,11 +1525,16 @@ ExecMakeFunctionResult(FuncExprState *fcache,
 
                        if (callit)
                        {
-                               fcinfo.isnull = false;
+                               pgstat_init_function_usage(fcinfo, &fcusage);
+
+                               fcinfo->isnull = false;
                                rsinfo.isDone = ExprSingleResult;
-                               result = FunctionCallInvoke(&fcinfo);
-                               *isNull = fcinfo.isnull;
+                               result = FunctionCallInvoke(fcinfo);
+                               *isNull = fcinfo->isnull;
                                *isDone = rsinfo.isDone;
+
+                               pgstat_end_function_usage(&fcusage,
+                                                                                 rsinfo.isDone != ExprMultipleResult);
                        }
                        else
                        {
@@ -1063,43 +1543,76 @@ ExecMakeFunctionResult(FuncExprState *fcache,
                                *isDone = ExprEndResult;
                        }
 
-                       if (*isDone != ExprEndResult)
+                       /* Which protocol does function want to use? */
+                       if (rsinfo.returnMode == SFRM_ValuePerCall)
                        {
-                               /*
-                                * Got a result from current argument.  If function itself
-                                * returns set, save the current argument values to re-use on
-                                * the next call.
-                                */
-                               if (fcache->func.fn_retset && *isDone == ExprMultipleResult)
+                               if (*isDone != ExprEndResult)
                                {
-                                       memcpy(&fcache->setArgs, &fcinfo, sizeof(fcinfo));
-                                       fcache->setHasSetArg = hasSetArg;
-                                       fcache->setArgsValid = true;
-                                       /* Register cleanup callback if we didn't already */
-                                       if (!fcache->shutdown_reg)
+                                       /*
+                                        * Got a result from current argument. If function itself
+                                        * returns set, save the current argument values to re-use
+                                        * on the next call.
+                                        */
+                                       if (fcache->func.fn_retset &&
+                                               *isDone == ExprMultipleResult)
                                        {
-                                               RegisterExprContextCallback(econtext,
-                                                                                                       ShutdownFuncExpr,
-                                                                                                       PointerGetDatum(fcache));
-                                               fcache->shutdown_reg = true;
+                                               Assert(fcinfo == &fcache->setArgs);
+                                               fcache->setHasSetArg = hasSetArg;
+                                               fcache->setArgsValid = true;
+                                               /* Register cleanup callback if we didn't already */
+                                               if (!fcache->shutdown_reg)
+                                               {
+                                                       RegisterExprContextCallback(econtext,
+                                                                                                               ShutdownFuncExpr,
+                                                                                                               PointerGetDatum(fcache));
+                                                       fcache->shutdown_reg = true;
+                                               }
                                        }
-                               }
 
-                               /*
-                                * Make sure we say we are returning a set, even if the
-                                * function itself doesn't return sets.
-                                */
-                               if (hasSetArg)
-                                       *isDone = ExprMultipleResult;
-                               break;
+                                       /*
+                                        * Make sure we say we are returning a set, even if the
+                                        * function itself doesn't return sets.
+                                        */
+                                       if (hasSetArg)
+                                               *isDone = ExprMultipleResult;
+                                       break;
+                               }
                        }
+                       else if (rsinfo.returnMode == SFRM_Materialize)
+                       {
+                               /* check we're on the same page as the function author */
+                               if (rsinfo.isDone != ExprSingleResult)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
+                                                        errmsg("table-function protocol for materialize mode was not followed")));
+                               if (rsinfo.setResult != NULL)
+                               {
+                                       /* prepare to return values from the tuplestore */
+                                       ExecPrepareTuplestoreResult(fcache, econtext,
+                                                                                               rsinfo.setResult,
+                                                                                               rsinfo.setDesc);
+                                       /* remember whether we had set arguments */
+                                       fcache->setHasSetArg = hasSetArg;
+                                       /* loop back to top to start returning from tuplestore */
+                                       goto restart;
+                               }
+                               /* if setResult was left null, treat it as empty set */
+                               *isDone = ExprEndResult;
+                               *isNull = true;
+                               result = (Datum) 0;
+                       }
+                       else
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
+                                                errmsg("unrecognized table-function returnMode: %d",
+                                                               (int) rsinfo.returnMode)));
 
                        /* Else, done with this argument */
                        if (!hasSetArg)
                                break;                  /* input not a set, so done */
 
                        /* Re-eval args to get the next element of the input set */
-                       argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext);
+                       argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext);
 
                        if (argDone != ExprMultipleResult)
                        {
@@ -1137,18 +1650,23 @@ ExecMakeFunctionResult(FuncExprState *fcache,
                 */
                if (fcache->func.fn_strict)
                {
-                       for (i = 0; i < fcinfo.nargs; i++)
+                       for (i = 0; i < fcinfo->nargs; i++)
                        {
-                               if (fcinfo.argnull[i])
+                               if (fcinfo->argnull[i])
                                {
                                        *isNull = true;
                                        return (Datum) 0;
                                }
                        }
                }
-               fcinfo.isnull = false;
-               result = FunctionCallInvoke(&fcinfo);
-               *isNull = fcinfo.isnull;
+
+               pgstat_init_function_usage(fcinfo, &fcusage);
+
+               fcinfo->isnull = false;
+               result = FunctionCallInvoke(fcinfo);
+               *isNull = fcinfo->isnull;
+
+               pgstat_end_function_usage(&fcusage, true);
        }
 
        return result;
@@ -1169,6 +1687,7 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
        ListCell   *arg;
        Datum           result;
        FunctionCallInfoData fcinfo;
+       PgStat_FunctionCallUsage fcusage;
        int                     i;
 
        /* Guard against stack overflow due to overly complex expressions */
@@ -1207,10 +1726,15 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
                        }
                }
        }
+
+       pgstat_init_function_usage(&fcinfo, &fcusage);
+
        /* fcinfo.isnull = false; */    /* handled by InitFunctionCallInfoData */
        result = FunctionCallInvoke(&fcinfo);
        *isNull = fcinfo.isnull;
 
+       pgstat_end_function_usage(&fcusage, true);
+
        return result;
 }
 
@@ -1219,14 +1743,13 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
  *             ExecMakeTableFunctionResult
  *
  * Evaluate a table function, producing a materialized result in a Tuplestore
- * object.     *returnDesc is set to the tupledesc actually returned by the
- * function, or NULL if it didn't provide one.
+ * object.
  */
 Tuplestorestate *
 ExecMakeTableFunctionResult(ExprState *funcexpr,
                                                        ExprContext *econtext,
                                                        TupleDesc expectedDesc,
-                                                       TupleDesc *returnDesc)
+                                                       bool randomAccess)
 {
        Tuplestorestate *tupstore = NULL;
        TupleDesc       tupdesc = NULL;
@@ -1234,6 +1757,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
        bool            returnsTuple;
        bool            returnsSet = false;
        FunctionCallInfoData fcinfo;
+       PgStat_FunctionCallUsage fcusage;
        ReturnSetInfo rsinfo;
        HeapTupleData tmptup;
        MemoryContext callerContext;
@@ -1258,7 +1782,9 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
        rsinfo.type = T_ReturnSetInfo;
        rsinfo.econtext = econtext;
        rsinfo.expectedDesc = expectedDesc;
-       rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
+       rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred);
+       if (randomAccess)
+               rsinfo.allowedModes |= (int) SFRM_Materialize_Random;
        rsinfo.returnMode = SFRM_ValuePerCall;
        /* isDone is filled below */
        rsinfo.setResult = NULL;
@@ -1292,7 +1818,8 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                {
                        FuncExpr   *func = (FuncExpr *) fcache->xprstate.expr;
 
-                       init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory);
+                       init_fcache(func->funcid, fcache,
+                                               econtext->ecxt_per_query_memory, false);
                }
                returnsSet = fcache->func.fn_retset;
 
@@ -1346,7 +1873,6 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
        for (;;)
        {
                Datum           result;
-               HeapTuple       tuple;
 
                CHECK_FOR_INTERRUPTS();
 
@@ -1360,9 +1886,14 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                /* Call the function or expression one time */
                if (direct_function_call)
                {
+                       pgstat_init_function_usage(&fcinfo, &fcusage);
+
                        fcinfo.isnull = false;
                        rsinfo.isDone = ExprSingleResult;
                        result = FunctionCallInvoke(&fcinfo);
+
+                       pgstat_end_function_usage(&fcusage,
+                                                                         rsinfo.isDone != ExprMultipleResult);
                }
                else
                {
@@ -1427,7 +1958,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                                                                           -1,
                                                                           0);
                                }
-                               tupstore = tuplestore_begin_heap(true, false, work_mem);
+                               tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
                                MemoryContextSwitchTo(oldcontext);
                                rsinfo.setResult = tupstore;
                                rsinfo.setDesc = tupdesc;
@@ -1448,15 +1979,15 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                                 */
                                tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
                                tmptup.t_data = td;
-                               tuple = &tmptup;
+
+                               oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+                               tuplestore_puttuple(tupstore, &tmptup);
                        }
                        else
                        {
-                               tuple = heap_form_tuple(tupdesc, &result, &fcinfo.isnull);
+                               oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+                               tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull);
                        }
-
-                       oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-                       tuplestore_puttuple(tupstore, tuple);
                        MemoryContextSwitchTo(oldcontext);
 
                        /*
@@ -1494,29 +2025,44 @@ no_function_result:
        if (rsinfo.setResult == NULL)
        {
                MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-               tupstore = tuplestore_begin_heap(true, false, work_mem);
+               tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
                rsinfo.setResult = tupstore;
                if (!returnsSet)
                {
                        int                     natts = expectedDesc->natts;
                        Datum      *nulldatums;
                        bool       *nullflags;
-                       HeapTuple       tuple;
 
                        MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
                        nulldatums = (Datum *) palloc0(natts * sizeof(Datum));
                        nullflags = (bool *) palloc(natts * sizeof(bool));
                        memset(nullflags, true, natts * sizeof(bool));
-                       tuple = heap_form_tuple(expectedDesc, nulldatums, nullflags);
                        MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-                       tuplestore_puttuple(tupstore, tuple);
+                       tuplestore_putvalues(tupstore, expectedDesc, nulldatums, nullflags);
                }
        }
 
+       /*
+        * If function provided a tupdesc, cross-check it.      We only really
+        * need to do this for functions returning RECORD, but might as well
+        * do it always.
+        */
+       if (rsinfo.setDesc)
+       {
+               tupledesc_match(expectedDesc, rsinfo.setDesc);
+
+               /*
+                * If it is a dynamically-allocated TupleDesc, free it: it is
+                * typically allocated in a per-query context, so we must avoid
+                * leaking it across multiple usages.
+                */
+               if (rsinfo.setDesc->tdrefcount == -1)
+                       FreeTupleDesc(rsinfo.setDesc);
+       }
+
        MemoryContextSwitchTo(callerContext);
 
-       /* The returned pointers are those in rsinfo */
-       *returnDesc = rsinfo.setDesc;
+       /* All done, pass back the tuplestore */
        return rsinfo.setResult;
 }
 
@@ -1544,7 +2090,7 @@ ExecEvalFunc(FuncExprState *fcache,
        FuncExpr   *func = (FuncExpr *) fcache->xprstate.expr;
 
        /* Initialize function lookup info */
-       init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory);
+       init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory, true);
 
        /* Go directly to ExecMakeFunctionResult on subsequent uses */
        fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult;
@@ -1566,7 +2112,7 @@ ExecEvalOper(FuncExprState *fcache,
        OpExpr     *op = (OpExpr *) fcache->xprstate.expr;
 
        /* Initialize function lookup info */
-       init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory);
+       init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory, true);
 
        /* Go directly to ExecMakeFunctionResult on subsequent uses */
        fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult;
@@ -1608,7 +2154,8 @@ ExecEvalDistinct(FuncExprState *fcache,
        {
                DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr;
 
-               init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory);
+               init_fcache(op->opfuncid, fcache,
+                                       econtext->ecxt_per_query_memory, true);
                Assert(!fcache->func.fn_retset);
        }
 
@@ -1688,7 +2235,7 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
        if (sstate->fxprstate.func.fn_oid == InvalidOid)
        {
                init_fcache(opexpr->opfuncid, &sstate->fxprstate,
-                                       econtext->ecxt_per_query_memory);
+                                       econtext->ecxt_per_query_memory, true);
                Assert(!sstate->fxprstate.func.fn_retset);
        }
 
@@ -1771,8 +2318,8 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
                else
                {
                        elt = fetch_att(s, typbyval, typlen);
-                       s = att_addlength(s, typlen, PointerGetDatum(s));
-                       s = (char *) att_align(s, typalign);
+                       s = att_addlength_pointer(s, typlen, s);
+                       s = (char *) att_align_nominal(s, typalign);
                        fcinfo.arg[1] = elt;
                        fcinfo.argnull[1] = false;
                }
@@ -2376,9 +2923,9 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
 
                /*
                 * If all items were null or empty arrays, return an empty array;
-                * otherwise, if some were and some weren't, raise error.  (Note:
-                * we must special-case this somehow to avoid trying to generate
-                * a 1-D array formed from empty arrays.  It's not ideal...)
+                * otherwise, if some were and some weren't, raise error.  (Note: we
+                * must special-case this somehow to avoid trying to generate a 1-D
+                * array formed from empty arrays.      It's not ideal...)
                 */
                if (haveempty)
                {
@@ -2411,7 +2958,7 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
                }
 
                result = (ArrayType *) palloc(nbytes);
-               result->size = nbytes;
+               SET_VARSIZE(result, nbytes);
                result->ndim = ndims;
                result->dataoffset = dataoffset;
                result->elemtype = element_type;
@@ -2646,15 +3193,11 @@ static Datum
 ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
                        bool *isNull, ExprDoneCond *isDone)
 {
-       XmlExpr            *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
-       text               *result;
-       StringInfoData  buf;
-       Datum                   value;
-       bool                    isnull;
-       char               *str;
-       ListCell           *arg;
+       XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
+       Datum           value;
+       bool            isnull;
+       ListCell   *arg;
        ListCell   *narg;
-       int                     i;
 
        if (isDone)
                *isDone = ExprSingleResult;
@@ -2663,31 +3206,37 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
        switch (xexpr->op)
        {
                case IS_XMLCONCAT:
-                       initStringInfo(&buf);
-                       foreach(arg, xmlExpr->args)
                        {
-                               ExprState       *e = (ExprState *) lfirst(arg);
+                               List       *values = NIL;
+
+                               foreach(arg, xmlExpr->args)
+                               {
+                                       ExprState  *e = (ExprState *) lfirst(arg);
 
-                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
-                               if (!isnull)
+                                       value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                                       if (!isnull)
+                                               values = lappend(values, DatumGetPointer(value));
+                               }
+
+                               if (list_length(values) > 0)
                                {
-                                       /* we know the value is XML type */
-                                       str = DatumGetCString(DirectFunctionCall1(xml_out,
-                                                                                                                         value));
-                                       appendStringInfoString(&buf, str);
-                                       pfree(str);
                                        *isNull = false;
+                                       return PointerGetDatum(xmlconcat(values));
                                }
+                               else
+                                       return (Datum) 0;
                        }
                        break;
 
                case IS_XMLFOREST:
+               {
+                       StringInfoData buf;
+
                        initStringInfo(&buf);
-                       i = 0;
                        forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names)
                        {
-                               ExprState       *e = (ExprState *) lfirst(arg);
-                               char    *argname = strVal(lfirst(narg));
+                               ExprState  *e = (ExprState *) lfirst(arg);
+                               char       *argname = strVal(lfirst(narg));
 
                                value = ExecEvalExpr(e, econtext, &isnull, NULL);
                                if (!isnull)
@@ -2698,11 +3247,25 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
                                                                         argname);
                                        *isNull = false;
                                }
-                               i++;
                        }
+
+                       if (*isNull)
+                       {
+                               pfree(buf.data);
+                               return (Datum) 0;
+                       }
+                       else
+                       {
+                               text       *result;
+
+                               result = cstring_to_text_with_len(buf.data, buf.len);
+                               pfree(buf.data);
+
+                               return PointerGetDatum(result);
+                       }
+               }
                        break;
 
-                       /* The remaining cases don't need to set up buf */
                case IS_XMLELEMENT:
                        *isNull = false;
                        return PointerGetDatum(xmlelement(xmlExpr, econtext));
@@ -2710,13 +3273,12 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 
                case IS_XMLPARSE:
                        {
-                               ExprState       *e;
-                               text        *data;
-                               bool            is_document;
+                               ExprState  *e;
+                               text       *data;
                                bool            preserve_whitespace;
 
-                               /* arguments are known to be text, bool, bool */
-                               Assert(list_length(xmlExpr->args) == 3);
+                               /* arguments are known to be text, bool */
+                               Assert(list_length(xmlExpr->args) == 2);
 
                                e = (ExprState *) linitial(xmlExpr->args);
                                value = ExecEvalExpr(e, econtext, &isnull, NULL);
@@ -2726,12 +3288,6 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 
                                e = (ExprState *) lsecond(xmlExpr->args);
                                value = ExecEvalExpr(e, econtext, &isnull, NULL);
-                               if (isnull)             /* probably can't happen */
-                                       return (Datum) 0;
-                               is_document = DatumGetBool(value);
-
-                               e = (ExprState *) lthird(xmlExpr->args);
-                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
                                if (isnull)             /* probably can't happen */
                                        return (Datum) 0;
                                preserve_whitespace = DatumGetBool(value);
@@ -2739,15 +3295,15 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
                                *isNull = false;
 
                                return PointerGetDatum(xmlparse(data,
-                                                                                               is_document,
+                                                                                               xexpr->xmloption,
                                                                                                preserve_whitespace));
                        }
                        break;
 
                case IS_XMLPI:
                        {
-                               ExprState       *e;
-                               text        *arg;
+                               ExprState  *e;
+                               text       *arg;
 
                                /* optional argument is known to be text */
                                Assert(list_length(xmlExpr->args) <= 1);
@@ -2773,12 +3329,12 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 
                case IS_XMLROOT:
                        {
-                               ExprState       *e;
-                               xmltype         *data;
-                               text            *version;
+                               ExprState  *e;
+                               xmltype    *data;
+                               text       *version;
                                int                     standalone;
 
-                               /* arguments are known to be xml, text, bool */
+                               /* arguments are known to be xml, text, int */
                                Assert(list_length(xmlExpr->args) == 3);
 
                                e = (ExprState *) linitial(xmlExpr->args);
@@ -2796,10 +3352,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 
                                e = (ExprState *) lthird(xmlExpr->args);
                                value = ExecEvalExpr(e, econtext, &isnull, NULL);
-                               if (isnull)
-                                       standalone = 0;
-                               else
-                                       standalone = (DatumGetBool(value) ? 1 : -1);
+                               standalone = DatumGetInt32(value);
 
                                *isNull = false;
 
@@ -2808,21 +3361,47 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
                                                                                           standalone));
                        }
                        break;
-       }
 
-       if (*isNull)
-               result = NULL;
-       else
-       {
-               int             len = buf.len + VARHDRSZ;
+               case IS_XMLSERIALIZE:
+                       {
+                               ExprState  *e;
+
+                               /* argument type is known to be xml */
+                               Assert(list_length(xmlExpr->args) == 1);
+
+                               e = (ExprState *) linitial(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (isnull)
+                                       return (Datum) 0;
+
+                               *isNull = false;
+
+                               return PointerGetDatum(xmltotext_with_xmloption(DatumGetXmlP(value), xexpr->xmloption));
+                       }
+                       break;
 
-               result = palloc(len);
-               VARATT_SIZEP(result) = len;
-               memcpy(VARDATA(result), buf.data, buf.len);
+               case IS_DOCUMENT:
+                       {
+                               ExprState  *e;
+
+                               /* optional argument is known to be xml */
+                               Assert(list_length(xmlExpr->args) == 1);
+
+                               e = (ExprState *) linitial(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (isnull)
+                                       return (Datum) 0;
+                               else
+                               {
+                                       *isNull = false;
+                                       return BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
+                               }
+                       }
+                       break;
        }
 
-       pfree(buf.data);
-       return PointerGetDatum(result);
+       elog(ERROR, "unrecognized XML operation");
+       return (Datum) 0;
 }
 
 /* ----------------------------------------------------------------
@@ -2853,7 +3432,8 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
        {
                NullIfExpr *op = (NullIfExpr *) nullIfExpr->xprstate.expr;
 
-               init_fcache(op->opfuncid, nullIfExpr, econtext->ecxt_per_query_memory);
+               init_fcache(op->opfuncid, nullIfExpr,
+                                       econtext->ecxt_per_query_memory, true);
                Assert(!nullIfExpr->func.fn_retset);
        }
 
@@ -3175,12 +3755,14 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
                                        ExprDoneCond *isDone)
 {
        FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
+       AttrNumber      fieldnum = fselect->fieldnum;
        Datum           result;
        Datum           tupDatum;
        HeapTupleHeader tuple;
        Oid                     tupType;
        int32           tupTypmod;
        TupleDesc       tupDesc;
+       Form_pg_attribute attr;
        HeapTupleData tmptup;
 
        tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
@@ -3198,6 +3780,27 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
        tupDesc = get_cached_rowtype(tupType, tupTypmod,
                                                                 &fstate->argdesc, econtext);
 
+       /* Check for dropped column, and force a NULL result if so */
+       if (fieldnum <= 0 ||
+               fieldnum > tupDesc->natts)              /* should never happen */
+               elog(ERROR, "attribute number %d exceeds number of columns %d",
+                        fieldnum, tupDesc->natts);
+       attr = tupDesc->attrs[fieldnum - 1];
+       if (attr->attisdropped)
+       {
+               *isNull = true;
+               return (Datum) 0;
+       }
+
+       /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
+       /* As in ExecEvalVar, we should but can't check typmod */
+       if (fselect->resulttype != attr->atttypid)
+               ereport(ERROR,
+                               (errmsg("attribute %d has wrong type", fieldnum),
+                                errdetail("Table has type %s, but query expects %s.",
+                                                  format_type_be(attr->atttypid),
+                                                  format_type_be(fselect->resulttype))));
+
        /*
         * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set all
         * the fields in the struct just in case user tries to inspect system
@@ -3209,7 +3812,7 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
        tmptup.t_data = tuple;
 
        result = heap_getattr(&tmptup,
-                                                 fselect->fieldnum,
+                                                 fieldnum,
                                                  tupDesc,
                                                  isNull);
        return result;
@@ -3328,6 +3931,133 @@ ExecEvalRelabelType(GenericExprState *exprstate,
        return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
 }
 
+/* ----------------------------------------------------------------
+ *             ExecEvalCoerceViaIO
+ *
+ *             Evaluate a CoerceViaIO node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
+                                       ExprContext *econtext,
+                                       bool *isNull, ExprDoneCond *isDone)
+{
+       Datum           result;
+       Datum           inputval;
+       char       *string;
+
+       inputval = ExecEvalExpr(iostate->arg, econtext, isNull, isDone);
+
+       if (isDone && *isDone == ExprEndResult)
+               return inputval;                /* nothing to do */
+
+       if (*isNull)
+               string = NULL;                  /* output functions are not called on nulls */
+       else
+               string = OutputFunctionCall(&iostate->outfunc, inputval);
+
+       result = InputFunctionCall(&iostate->infunc,
+                                                          string,
+                                                          iostate->intypioparam,
+                                                          -1);
+
+       /* The input function cannot change the null/not-null status */
+       return result;
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalArrayCoerceExpr
+ *
+ *             Evaluate an ArrayCoerceExpr node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
+                                               ExprContext *econtext,
+                                               bool *isNull, ExprDoneCond *isDone)
+{
+       ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) astate->xprstate.expr;
+       Datum           result;
+       ArrayType  *array;
+       FunctionCallInfoData locfcinfo;
+
+       result = ExecEvalExpr(astate->arg, econtext, isNull, isDone);
+
+       if (isDone && *isDone == ExprEndResult)
+               return result;                  /* nothing to do */
+       if (*isNull)
+               return result;                  /* nothing to do */
+
+       /*
+        * If it's binary-compatible, modify the element type in the array header,
+        * but otherwise leave the array as we received it.
+        */
+       if (!OidIsValid(acoerce->elemfuncid))
+       {
+               /* Detoast input array if necessary, and copy in any case */
+               array = DatumGetArrayTypePCopy(result);
+               ARR_ELEMTYPE(array) = astate->resultelemtype;
+               PG_RETURN_ARRAYTYPE_P(array);
+       }
+
+       /* Detoast input array if necessary, but don't make a useless copy */
+       array = DatumGetArrayTypeP(result);
+
+       /* Initialize function cache if first time through */
+       if (astate->elemfunc.fn_oid == InvalidOid)
+       {
+               AclResult       aclresult;
+
+               /* Check permission to call function */
+               aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(),
+                                                                        ACL_EXECUTE);
+               if (aclresult != ACLCHECK_OK)
+                       aclcheck_error(aclresult, ACL_KIND_PROC,
+                                                  get_func_name(acoerce->elemfuncid));
+
+               /* Set up the primary fmgr lookup information */
+               fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
+                                         econtext->ecxt_per_query_memory);
+
+               /* Initialize additional info */
+               astate->elemfunc.fn_expr = (Node *) acoerce;
+       }
+
+       /*
+        * Use array_map to apply the function to each array element.
+        *
+        * We pass on the desttypmod and isExplicit flags whether or not the
+        * function wants them.
+        */
+       InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3,
+                                                        NULL, NULL);
+       locfcinfo.arg[0] = PointerGetDatum(array);
+       locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
+       locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
+       locfcinfo.argnull[0] = false;
+       locfcinfo.argnull[1] = false;
+       locfcinfo.argnull[2] = false;
+
+       return array_map(&locfcinfo, ARR_ELEMTYPE(array), astate->resultelemtype,
+                                        astate->amstate);
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalCurrentOfExpr
+ *
+ * The planner must convert CURRENT OF into a TidScan qualification.
+ * So, we have to be able to do ExecInitExpr on a CurrentOfExpr,
+ * but we shouldn't ever actually execute it.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
+                                         bool *isNull, ExprDoneCond *isDone)
+{
+       elog(ERROR, "CURRENT OF cannot be executed");
+       return 0;                                       /* keep compiler quiet */
+}
+
 
 /*
  * ExecEvalExprSwitchContext
@@ -3363,12 +4093,12 @@ ExecEvalExprSwitchContext(ExprState *expression,
  * executions of the expression are needed.  Typically the context will be
  * the same as the per-query context of the associated ExprContext.
  *
- * Any Aggref and SubPlan nodes found in the tree are added to the lists
- * of such nodes held by the parent PlanState. Otherwise, we do very little
- * initialization here other than building the state-node tree.  Any nontrivial
- * work associated with initializing runtime info for a node should happen
- * during the first actual evaluation of that node.  (This policy lets us
- * avoid work if the node is never actually evaluated.)
+ * Any Aggref, WindowFunc, or SubPlan nodes found in the tree are added to the
+ * lists of such nodes held by the parent PlanState. Otherwise, we do very
+ * little initialization here other than building the state-node tree.  Any
+ * nontrivial work associated with initializing runtime info for a node should
+ * happen during the first actual evaluation of that node.  (This policy lets
+ * us avoid work if the node is never actually evaluated.)
  *
  * Note: there is no ExecEndExpr function; we assume that any resource
  * cleanup needed will be handled by just releasing the memory context
@@ -3396,15 +4126,8 @@ ExecInitExpr(Expr *node, PlanState *parent)
        switch (nodeTag(node))
        {
                case T_Var:
-                       {
-                               Var                *var = (Var *) node;
-
-                               state = (ExprState *) makeNode(ExprState);
-                               if (var->varattno != InvalidAttrNumber)
-                                       state->evalfunc = ExecEvalVar;
-                               else
-                                       state->evalfunc = ExecEvalWholeRowVar;
-                       }
+                       state = (ExprState *) makeNode(ExprState);
+                       state->evalfunc = ExecEvalVar;
                        break;
                case T_Const:
                        state = (ExprState *) makeNode(ExprState);
@@ -3448,16 +4171,54 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                        if (naggs != aggstate->numaggs)
                                                ereport(ERROR,
                                                                (errcode(ERRCODE_GROUPING_ERROR),
-                                                                errmsg("aggregate function calls may not be nested")));
+                                               errmsg("aggregate function calls cannot be nested")));
                                }
                                else
                                {
                                        /* planner messed up */
-                                       elog(ERROR, "aggref found in non-Agg plan node");
+                                       elog(ERROR, "Aggref found in non-Agg plan node");
                                }
                                state = (ExprState *) astate;
                        }
                        break;
+               case T_WindowFunc:
+                       {
+                               WindowFunc *wfunc = (WindowFunc *) node;
+                               WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
+
+                               wfstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWindowFunc;
+                               if (parent && IsA(parent, WindowAggState))
+                               {
+                                       WindowAggState *winstate = (WindowAggState *) parent;
+                                       int                     nfuncs;
+
+                                       winstate->funcs = lcons(wfstate, winstate->funcs);
+                                       nfuncs = ++winstate->numfuncs;
+                                       if (wfunc->winagg)
+                                               winstate->numaggs++;
+
+                                       wfstate->args = (List *) ExecInitExpr((Expr *) wfunc->args,
+                                                                                                                 parent);
+
+                                       /*
+                                        * Complain if the windowfunc's arguments contain any
+                                        * windowfuncs; nested window functions are semantically
+                                        * nonsensical.  (This should have been caught earlier,
+                                        * but we defend against it here anyway.)
+                                        */
+                                       if (nfuncs != winstate->numfuncs)
+                                               ereport(ERROR,
+                                                               (errcode(ERRCODE_WINDOWING_ERROR),
+                                               errmsg("window function calls cannot be nested")));
+                               }
+                               else
+                               {
+                                       /* planner messed up */
+                                       elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
+                               }
+                               state = (ExprState *) wfstate;
+                       }
+                       break;
                case T_ArrayRef:
                        {
                                ArrayRef   *aref = (ArrayRef *) node;
@@ -3557,31 +4318,33 @@ ExecInitExpr(Expr *node, PlanState *parent)
                        break;
                case T_SubPlan:
                        {
-                               /* Keep this in sync with ExecInitExprInitPlan, below */
                                SubPlan    *subplan = (SubPlan *) node;
-                               SubPlanState *sstate = makeNode(SubPlanState);
-
-                               sstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecSubPlan;
+                               SubPlanState *sstate;
 
                                if (!parent)
                                        elog(ERROR, "SubPlan found with no parent plan");
 
-                               /*
-                                * Here we just add the SubPlanState nodes to parent->subPlan.
-                                * The subplans will be initialized later.
-                                */
-                               parent->subPlan = lcons(sstate, parent->subPlan);
-                               sstate->sub_estate = NULL;
-                               sstate->planstate = NULL;
+                               sstate = ExecInitSubPlan(subplan, parent);
 
-                               sstate->testexpr =
-                                       ExecInitExpr((Expr *) subplan->testexpr, parent);
-                               sstate->args = (List *)
-                                       ExecInitExpr((Expr *) subplan->args, parent);
+                               /* Add SubPlanState nodes to parent->subPlan */
+                               parent->subPlan = lappend(parent->subPlan, sstate);
 
                                state = (ExprState *) sstate;
                        }
                        break;
+               case T_AlternativeSubPlan:
+                       {
+                               AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
+                               AlternativeSubPlanState *asstate;
+
+                               if (!parent)
+                                       elog(ERROR, "AlternativeSubPlan found with no parent plan");
+
+                               asstate = ExecInitAlternativeSubPlan(asplan, parent);
+
+                               state = (ExprState *) asstate;
+                       }
+                       break;
                case T_FieldSelect:
                        {
                                FieldSelect *fselect = (FieldSelect *) node;
@@ -3615,6 +4378,46 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) gstate;
                        }
                        break;
+               case T_CoerceViaIO:
+                       {
+                               CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+                               CoerceViaIOState *iostate = makeNode(CoerceViaIOState);
+                               Oid                     iofunc;
+                               bool            typisvarlena;
+
+                               iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO;
+                               iostate->arg = ExecInitExpr(iocoerce->arg, parent);
+                               /* lookup the result type's input function */
+                               getTypeInputInfo(iocoerce->resulttype, &iofunc,
+                                                                &iostate->intypioparam);
+                               fmgr_info(iofunc, &iostate->infunc);
+                               /* lookup the input type's output function */
+                               getTypeOutputInfo(exprType((Node *) iocoerce->arg),
+                                                                 &iofunc, &typisvarlena);
+                               fmgr_info(iofunc, &iostate->outfunc);
+                               state = (ExprState *) iostate;
+                       }
+                       break;
+               case T_ArrayCoerceExpr:
+                       {
+                               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+                               ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
+
+                               astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
+                               astate->arg = ExecInitExpr(acoerce->arg, parent);
+                               astate->resultelemtype = get_element_type(acoerce->resulttype);
+                               if (astate->resultelemtype == InvalidOid)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                        errmsg("target type is not an array")));
+                               /* Arrays over domains aren't supported yet */
+                               Assert(getBaseType(astate->resultelemtype) ==
+                                          astate->resultelemtype);
+                               astate->elemfunc.fn_oid = InvalidOid;   /* not initialized */
+                               astate->amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState));
+                               state = (ExprState *) astate;
+                       }
+                       break;
                case T_ConvertRowtypeExpr:
                        {
                                ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
@@ -3730,7 +4533,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                                 * don't really care what type of NULL it is, so
                                                 * always make an int4 NULL.
                                                 */
-                                               e = (Expr *) makeNullConst(INT4OID);
+                                               e = (Expr *) makeNullConst(INT4OID, -1);
                                        }
                                        estate = ExecInitExpr(e, parent);
                                        outlist = lappend(outlist, estate);
@@ -3783,14 +4586,12 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                        int                     strategy;
                                        Oid                     lefttype;
                                        Oid                     righttype;
-                                       bool            recheck;
                                        Oid                     proc;
 
                                        get_op_opfamily_properties(opno, opfamily,
                                                                                           &strategy,
                                                                                           &lefttype,
-                                                                                          &righttype,
-                                                                                          &recheck);
+                                                                                          &righttype);
                                        proc = get_opfamily_proc(opfamily,
                                                                                         lefttype,
                                                                                         righttype,
@@ -3867,39 +4668,28 @@ ExecInitExpr(Expr *node, PlanState *parent)
                        break;
                case T_XmlExpr:
                        {
-                               XmlExpr                 *xexpr = (XmlExpr *) node;
-                               XmlExprState    *xstate = makeNode(XmlExprState);
-                               List                    *outlist;
-                               ListCell                *arg;
-                               int                             i;
+                               XmlExpr    *xexpr = (XmlExpr *) node;
+                               XmlExprState *xstate = makeNode(XmlExprState);
+                               List       *outlist;
+                               ListCell   *arg;
 
                                xstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalXml;
-                               xstate->named_outfuncs = (FmgrInfo *)
-                                       palloc0(list_length(xexpr->named_args) * sizeof(FmgrInfo));
                                outlist = NIL;
-                               i = 0;
                                foreach(arg, xexpr->named_args)
                                {
-                                       Expr            *e = (Expr *) lfirst(arg);
-                                       ExprState       *estate;
-                                       Oid                     typOutFunc;
-                                       bool            typIsVarlena;
+                                       Expr       *e = (Expr *) lfirst(arg);
+                                       ExprState  *estate;
 
                                        estate = ExecInitExpr(e, parent);
                                        outlist = lappend(outlist, estate);
-
-                                       getTypeOutputInfo(exprType((Node *) e),
-                                                                         &typOutFunc, &typIsVarlena);
-                                       fmgr_info(typOutFunc, &xstate->named_outfuncs[i]);
-                                       i++;
                                }
                                xstate->named_args = outlist;
 
                                outlist = NIL;
                                foreach(arg, xexpr->args)
                                {
-                                       Expr            *e = (Expr *) lfirst(arg);
-                                       ExprState       *estate;
+                                       Expr       *e = (Expr *) lfirst(arg);
+                                       ExprState  *estate;
 
                                        estate = ExecInitExpr(e, parent);
                                        outlist = lappend(outlist, estate);
@@ -3954,6 +4744,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) cstate;
                        }
                        break;
+               case T_CurrentOfExpr:
+                       state = (ExprState *) makeNode(ExprState);
+                       state->evalfunc = ExecEvalCurrentOfExpr;
+                       break;
                case T_TargetEntry:
                        {
                                TargetEntry *tle = (TargetEntry *) node;
@@ -3991,41 +4785,16 @@ ExecInitExpr(Expr *node, PlanState *parent)
        return state;
 }
 
-/*
- * ExecInitExprInitPlan --- initialize a subplan expr that's being handled
- * as an InitPlan.     This is identical to ExecInitExpr's handling of a regular
- * subplan expr, except we do NOT want to add the node to the parent's
- * subplan list.
- */
-SubPlanState *
-ExecInitExprInitPlan(SubPlan *node, PlanState *parent)
-{
-       SubPlanState *sstate = makeNode(SubPlanState);
-
-       if (!parent)
-               elog(ERROR, "SubPlan found with no parent plan");
-
-       /* The subplan's state will be initialized later */
-       sstate->sub_estate = NULL;
-       sstate->planstate = NULL;
-
-       sstate->testexpr = ExecInitExpr((Expr *) node->testexpr, parent);
-       sstate->args = (List *) ExecInitExpr((Expr *) node->args, parent);
-
-       sstate->xprstate.expr = (Expr *) node;
-
-       return sstate;
-}
-
 /*
  * ExecPrepareExpr --- initialize for expression execution outside a normal
  * Plan tree context.
  *
  * This differs from ExecInitExpr in that we don't assume the caller is
- * already running in the EState's per-query context.  Also, we apply
- * fix_opfuncids() to the passed expression tree to be sure it is ready
- * to run.     (In ordinary Plan trees the planner will have fixed opfuncids,
- * but callers outside the executor will not have done this.)
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
  */
 ExprState *
 ExecPrepareExpr(Expr *node, EState *estate)
@@ -4033,10 +4802,10 @@ ExecPrepareExpr(Expr *node, EState *estate)
        ExprState  *result;
        MemoryContext oldcontext;
 
-       fix_opfuncids((Node *) node);
-
        oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
 
+       node = expression_planner(node);
+
        result = ExecInitExpr(node, NULL);
 
        MemoryContextSwitchTo(oldcontext);
@@ -4187,6 +4956,7 @@ ExecCleanTargetListLength(List *targetlist)
  * prepared to deal with sets of result tuples.  Otherwise, a return
  * of *isDone = ExprMultipleResult signifies a set element, and a return
  * of *isDone = ExprEndResult signifies end of the set of tuple.
+ * We assume that *isDone has been initialized to ExprSingleResult by caller.
  */
 static bool
 ExecTargetList(List *targetlist,
@@ -4208,9 +4978,6 @@ ExecTargetList(List *targetlist,
        /*
         * evaluate all the expressions in the target list
         */
-       if (isDone)
-               *isDone = ExprSingleResult;             /* until proven otherwise */
-
        haveDoneSets = false;           /* any exhausted set exprs in tlist? */
 
        foreach(tl, targetlist)
@@ -4325,50 +5092,6 @@ ExecTargetList(List *targetlist,
        return true;
 }
 
-/*
- * ExecVariableList
- *             Evaluates a simple-Variable-list projection.
- *
- * Results are stored into the passed values and isnull arrays.
- */
-static void
-ExecVariableList(ProjectionInfo *projInfo,
-                                Datum *values,
-                                bool *isnull)
-{
-       ExprContext *econtext = projInfo->pi_exprContext;
-       int                *varSlotOffsets = projInfo->pi_varSlotOffsets;
-       int                *varNumbers = projInfo->pi_varNumbers;
-       int                     i;
-
-       /*
-        * Force extraction of all input values that we need.
-        */
-       if (projInfo->pi_lastInnerVar > 0)
-               slot_getsomeattrs(econtext->ecxt_innertuple,
-                                                 projInfo->pi_lastInnerVar);
-       if (projInfo->pi_lastOuterVar > 0)
-               slot_getsomeattrs(econtext->ecxt_outertuple,
-                                                 projInfo->pi_lastOuterVar);
-       if (projInfo->pi_lastScanVar > 0)
-               slot_getsomeattrs(econtext->ecxt_scantuple,
-                                                 projInfo->pi_lastScanVar);
-
-       /*
-        * Assign to result by direct extraction of fields from source slots ... a
-        * mite ugly, but fast ...
-        */
-       for (i = list_length(projInfo->pi_targetlist) - 1; i >= 0; i--)
-       {
-               char       *slotptr = ((char *) econtext) + varSlotOffsets[i];
-               TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-               int                     varNumber = varNumbers[i] - 1;
-
-               values[i] = varSlot->tts_values[varNumber];
-               isnull[i] = varSlot->tts_isnull[varNumber];
-       }
-}
-
 /*
  * ExecProject
  *
@@ -4386,6 +5109,8 @@ TupleTableSlot *
 ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
 {
        TupleTableSlot *slot;
+       ExprContext *econtext;
+       int                     numSimpleVars;
 
        /*
         * sanity checks
@@ -4396,6 +5121,11 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
         * get the projection info we want
         */
        slot = projInfo->pi_slot;
+       econtext = projInfo->pi_exprContext;
+
+       /* Assume single result row until proven otherwise */
+       if (isDone)
+               *isDone = ExprSingleResult;
 
        /*
         * Clear any former contents of the result slot.  This makes it safe for
@@ -4405,29 +5135,84 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
        ExecClearTuple(slot);
 
        /*
-        * form a new result tuple (if possible); if successful, mark the result
-        * slot as containing a valid virtual tuple
+        * Force extraction of all input values that we'll need.  The
+        * Var-extraction loops below depend on this, and we are also prefetching
+        * all attributes that will be referenced in the generic expressions.
+        */
+       if (projInfo->pi_lastInnerVar > 0)
+               slot_getsomeattrs(econtext->ecxt_innertuple,
+                                                 projInfo->pi_lastInnerVar);
+       if (projInfo->pi_lastOuterVar > 0)
+               slot_getsomeattrs(econtext->ecxt_outertuple,
+                                                 projInfo->pi_lastOuterVar);
+       if (projInfo->pi_lastScanVar > 0)
+               slot_getsomeattrs(econtext->ecxt_scantuple,
+                                                 projInfo->pi_lastScanVar);
+
+       /*
+        * Assign simple Vars to result by direct extraction of fields from source
+        * slots ... a mite ugly, but fast ...
         */
-       if (projInfo->pi_isVarList)
+       numSimpleVars = projInfo->pi_numSimpleVars;
+       if (numSimpleVars > 0)
        {
-               /* simple Var list: this always succeeds with one result row */
-               if (isDone)
-                       *isDone = ExprSingleResult;
-               ExecVariableList(projInfo,
-                                                slot->tts_values,
-                                                slot->tts_isnull);
-               ExecStoreVirtualTuple(slot);
+               Datum  *values = slot->tts_values;
+               bool   *isnull = slot->tts_isnull;
+               int        *varSlotOffsets = projInfo->pi_varSlotOffsets;
+               int        *varNumbers = projInfo->pi_varNumbers;
+               int             i;
+
+               if (projInfo->pi_directMap)
+               {
+                       /* especially simple case where vars go to output in order */
+                       for (i = 0; i < numSimpleVars; i++)
+                       {
+                               char       *slotptr = ((char *) econtext) + varSlotOffsets[i];
+                               TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
+                               int                     varNumber = varNumbers[i] - 1;
+
+                               values[i] = varSlot->tts_values[varNumber];
+                               isnull[i] = varSlot->tts_isnull[varNumber];
+                       }
+               }
+               else
+               {
+                       /* we have to pay attention to varOutputCols[] */
+                       int        *varOutputCols = projInfo->pi_varOutputCols;
+
+                       for (i = 0; i < numSimpleVars; i++)
+                       {
+                               char       *slotptr = ((char *) econtext) + varSlotOffsets[i];
+                               TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
+                               int                     varNumber = varNumbers[i] - 1;
+                               int                     varOutputCol = varOutputCols[i] - 1;
+
+                               values[varOutputCol] = varSlot->tts_values[varNumber];
+                               isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
+                       }
+               }
        }
-       else
+
+       /*
+        * If there are any generic expressions, evaluate them.  It's possible
+        * that there are set-returning functions in such expressions; if so
+        * and we have reached the end of the set, we return the result slot,
+        * which we already marked empty.
+        */
+       if (projInfo->pi_targetlist)
        {
-               if (ExecTargetList(projInfo->pi_targetlist,
-                                                  projInfo->pi_exprContext,
-                                                  slot->tts_values,
-                                                  slot->tts_isnull,
-                                                  projInfo->pi_itemIsDone,
-                                                  isDone))
-                       ExecStoreVirtualTuple(slot);
+               if (!ExecTargetList(projInfo->pi_targetlist,
+                                                       econtext,
+                                                       slot->tts_values,
+                                                       slot->tts_isnull,
+                                                       projInfo->pi_itemIsDone,
+                                                       isDone))
+                       return slot;            /* no more result rows, return empty slot */
        }
 
-       return slot;
+       /*
+        * Successfully formed a result row.  Mark the result slot as containing a
+        * valid virtual tuple.
+        */
+       return ExecStoreVirtualTuple(slot);
 }