]> 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 87cc201a3e3cf5412c2ebd4d3a523415b880bbdf..c7bfe7c76ca6b926f2dcbe1eee05b3a0b8b6ecc0 100644 (file)
@@ -3,12 +3,12 @@
  * execQual.c
  *       Routines to evaluate qualification and targetlist expressions
  *
- * Portions Copyright (c) 1996-2005, 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.172 2005/03/14 04:41:12 tgl 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"
 #include "executor/execdebug.h"
-#include "executor/functions.h"
 #include "executor/nodeSubplan.h"
 #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/array.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/memutils.h"
 #include "utils/typcache.h"
+#include "utils/xml.h"
 
 
 /* static function decls */
@@ -61,14 +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);
@@ -88,8 +113,8 @@ static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
 static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
                        bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
-                                                                       ExprContext *econtext,
-                                                                       bool *isNull, ExprDoneCond *isDone);
+                                          ExprContext *econtext,
+                                          bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
                         bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
@@ -101,13 +126,21 @@ static Datum ExecEvalArray(ArrayExprState *astate,
 static Datum ExecEvalRow(RowExprState *rstate,
                        ExprContext *econtext,
                        bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalRowCompare(RowCompareExprState *rstate,
+                                  ExprContext *econtext,
+                                  bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
                                 ExprContext *econtext,
                                 bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr,
+                          ExprContext *econtext,
+                          bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
+                       bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalNullIf(FuncExprState *nullIfExpr,
                           ExprContext *econtext,
                           bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalNullTest(GenericExprState *nstate,
+static Datum ExecEvalNullTest(NullTestState *nstate,
                                 ExprContext *econtext,
                                 bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalBooleanTest(GenericExprState *bstate,
@@ -128,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);
 
 
 /* ----------------------------------------------------------------
@@ -196,16 +237,8 @@ static Datum ExecEvalRelabelType(GenericExprState *exprstate,
  *        if it's a simple reference, or the modified array value if it's
  *        an array assignment (i.e., array element or slice insertion).
  *
- * NOTE: if we get a NULL result from a subexpression, we return NULL when
- * it's an array reference, or the unmodified source array when it's an
- * array assignment.  This may seem peculiar, but if we return NULL (as was
- * done in versions up through 7.0) then an assignment like
- *                     UPDATE table SET arrayfield[4] = NULL
- * will result in setting the whole array to NULL, which is certainly not
- * very desirable.     By returning the source array we make the assignment
- * into a no-op, instead.  (Eventually we need to redesign arrays so that
- * individual elements can be NULL, but for now, let's try to protect users
- * from shooting themselves in the foot.)
+ * NOTE: if we get a NULL result from a subscript expression, we return NULL
+ * when it's an array reference, or raise an error when it's an assignment.
  *
  * NOTE: we deliberately refrain from applying DatumGetArrayTypeP() here,
  * even though that might seem natural, because this code needs to support
@@ -239,8 +272,8 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                                                                         isDone));
 
        /*
-        * If refexpr yields NULL, and it's a fetch, then result is NULL. In
-        * the assignment case, we'll cons up something below.
+        * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
+        * assignment case, we'll cons up something below.
         */
        if (*isNull)
        {
@@ -264,15 +297,15 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                                                                                                         econtext,
                                                                                                         &eisnull,
                                                                                                         NULL));
-               /* If any index expr yields NULL, result is NULL or source array */
+               /* If any index expr yields NULL, result is NULL or error */
                if (eisnull)
                {
-                       if (!isAssignment)
-                       {
-                               *isNull = true;
-                               return (Datum) NULL;
-                       }
-                       return PointerGetDatum(array_source);
+                       if (isAssignment)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                                 errmsg("array subscript in assignment must not be null")));
+                       *isNull = true;
+                       return (Datum) NULL;
                }
        }
 
@@ -292,19 +325,15 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                                                                                                                 econtext,
                                                                                                                 &eisnull,
                                                                                                                 NULL));
-
-                       /*
-                        * If any index expr yields NULL, result is NULL or source
-                        * array
-                        */
+                       /* If any index expr yields NULL, result is NULL or error */
                        if (eisnull)
                        {
-                               if (!isAssignment)
-                               {
-                                       *isNull = true;
-                                       return (Datum) NULL;
-                               }
-                               return PointerGetDatum(array_source);
+                               if (isAssignment)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                                                        errmsg("array subscript in assignment must not be null")));
+                               *isNull = true;
+                               return (Datum) NULL;
                        }
                }
                /* this can't happen unless parser messed up */
@@ -324,11 +353,10 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                 *
                 * XXX At some point we'll need to look into making the old value of
                 * the array element available via CaseTestExpr, as is done by
-                * ExecEvalFieldStore.  This is not needed now but will be needed
-                * to support arrays of composite types; in an assignment to a
-                * field of an array member, the parser would generate a
-                * FieldStore that expects to fetch its input tuple via
-                * CaseTestExpr.
+                * ExecEvalFieldStore.  This is not needed now but will be needed to
+                * support arrays of composite types; in an assignment to a field of
+                * an array member, the parser would generate a FieldStore that
+                * expects to fetch its input tuple via CaseTestExpr.
                 */
                sourceData = ExecEvalExpr(astate->refassgnexpr,
                                                                  econtext,
@@ -336,30 +364,23 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                                                                  NULL);
 
                /*
-                * For now, can't cope with inserting NULL into an array, so make
-                * it a no-op per discussion above...
+                * For an assignment to a fixed-length array type, both the original
+                * array and the value to be assigned into it must be non-NULL, else
+                * we punt and return the original array.
                 */
-               if (eisnull)
-                       return PointerGetDatum(array_source);
+               if (astate->refattrlength > 0)  /* fixed-length array? */
+                       if (eisnull || *isNull)
+                               return PointerGetDatum(array_source);
 
                /*
-                * For an assignment, if all the subscripts and the input
-                * expression are non-null but the original array is null, then
-                * substitute an empty (zero-dimensional) array and proceed with
-                * the assignment. This only works for varlena arrays, though; for
-                * fixed-length array types we punt and return the null input
-                * array.
+                * For assignment to varlena arrays, we handle a NULL original array
+                * by substituting an empty (zero-dimensional) array; insertion of the
+                * new element will result in a singleton array value.  It does not
+                * matter whether the new element is NULL.
                 */
                if (*isNull)
                {
-                       if (astate->refattrlength > 0)          /* fixed-length array? */
-                               return PointerGetDatum(array_source);
-
-                       array_source = construct_md_array(NULL, 0, NULL, NULL,
-                                                                                         arrayRef->refelemtype,
-                                                                                         astate->refelemlength,
-                                                                                         astate->refelembyval,
-                                                                                         astate->refelemalign);
+                       array_source = construct_empty_array(arrayRef->refelemtype);
                        *isNull = false;
                }
 
@@ -367,20 +388,20 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                        resultArray = array_set(array_source, i,
                                                                        upper.indx,
                                                                        sourceData,
+                                                                       eisnull,
                                                                        astate->refattrlength,
                                                                        astate->refelemlength,
                                                                        astate->refelembyval,
-                                                                       astate->refelemalign,
-                                                                       isNull);
+                                                                       astate->refelemalign);
                else
                        resultArray = array_set_slice(array_source, i,
                                                                                  upper.indx, lower.indx,
-                                                          (ArrayType *) DatumGetPointer(sourceData),
+                                                                  (ArrayType *) DatumGetPointer(sourceData),
+                                                                                 eisnull,
                                                                                  astate->refattrlength,
                                                                                  astate->refelemlength,
                                                                                  astate->refelembyval,
-                                                                                 astate->refelemalign,
-                                                                                 isNull);
+                                                                                 astate->refelemalign);
                return PointerGetDatum(resultArray);
        }
 
@@ -398,8 +419,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                                                                          astate->refattrlength,
                                                                          astate->refelemlength,
                                                                          astate->refelembyval,
-                                                                         astate->refelemalign,
-                                                                         isNull);
+                                                                         astate->refelemalign);
                return PointerGetDatum(resultArray);
        }
 }
@@ -426,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
@@ -445,12 +491,12 @@ 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 be treated as ordinary variables (since we no longer have
-        * access to the original tuple).
+        * the level of a relation scan; at higher levels, system attributes must
+        * be treated as ordinary variables (since we no longer have access to the
+        * original tuple).
         */
        attnum = variable->varattno;
 
@@ -472,38 +518,295 @@ 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->ttc_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.
                 *
-                * Note that we can't check dropped columns, since their atttypid has
-                * been zeroed.
+                * 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.
+                *
+                * 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);
 }
 
+/* ----------------------------------------------------------------
+ *             ExecEvalWholeRowVar
+ *
+ *             Returns a Datum for a whole-row variable.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
+                                       bool *isNull, ExprDoneCond *isDone)
+{
+       Var                *variable = (Var *) exprstate->expr;
+       TupleTableSlot *slot = econtext->ecxt_scantuple;
+       HeapTuple       tuple;
+       TupleDesc       tupleDesc;
+       HeapTupleHeader dtuple;
+
+       if (isDone)
+               *isDone = ExprSingleResult;
+       *isNull = false;
+
+       tuple = ExecFetchSlotTuple(slot);
+       tupleDesc = slot->tts_tupleDescriptor;
+
+       /*
+        * 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.
+        */
+       dtuple = (HeapTupleHeader) palloc(tuple->t_len);
+       memcpy((char *) dtuple, (char *) tuple->t_data, tuple->t_len);
+
+       HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len);
+
+       /*
+        * If the Var identifies a named composite type, label the tuple with that
+        * type; otherwise use what is in the tupleDesc.
+        */
+       if (variable->vartype != RECORDOID)
+       {
+               HeapTupleHeaderSetTypeId(dtuple, variable->vartype);
+               HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod);
+       }
+       else
+       {
+               HeapTupleHeaderSetTypeId(dtuple, tupleDesc->tdtypeid);
+               HeapTupleHeaderSetTypMod(dtuple, tupleDesc->tdtypmod);
+       }
+
+       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
  *
@@ -534,11 +837,6 @@ ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
  *             something like ($.name) and the expression context contains
  *             the current parameter bindings (name = "sam") (age = 34)...
  *             so our job is to find and return the appropriate datum ("sam").
- *
- *             Q: if we have a parameter ($.foo) without a binding, i.e.
- *                there is no (foo = xxx) in the parameter list info,
- *                is this a fatal error or should this be a "not available"
- *                (in which case we could return NULL)?        -cim 10/13/89
  * ----------------------------------------------------------------
  */
 static Datum
@@ -546,18 +844,16 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
                          bool *isNull, ExprDoneCond *isDone)
 {
        Param      *expression = (Param *) exprstate->expr;
-       int                     thisParamKind = expression->paramkind;
-       AttrNumber      thisParamId = expression->paramid;
+       int                     thisParamId = expression->paramid;
 
        if (isDone)
                *isDone = ExprSingleResult;
 
-       if (thisParamKind == PARAM_EXEC)
+       if (expression->paramkind == PARAM_EXEC)
        {
                /*
-                * PARAM_EXEC params (internal executor parameters) are stored in
-                * the ecxt_param_exec_vals array, and can be accessed by array
-                * index.
+                * PARAM_EXEC params (internal executor parameters) are stored in the
+                * ecxt_param_exec_vals array, and can be accessed by array index.
                 */
                ParamExecData *prm;
 
@@ -575,19 +871,27 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
        else
        {
                /*
-                * All other parameter types must be sought in
-                * ecxt_param_list_info.
+                * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
                 */
-               ParamListInfo paramInfo;
+               ParamListInfo paramInfo = econtext->ecxt_param_list_info;
+
+               Assert(expression->paramkind == PARAM_EXTERN);
+               if (paramInfo &&
+                       thisParamId > 0 && thisParamId <= paramInfo->numParams)
+               {
+                       ParamExternData *prm = &paramInfo->params[thisParamId - 1];
 
-               paramInfo = lookupParam(econtext->ecxt_param_list_info,
-                                                               thisParamKind,
-                                                               expression->paramname,
-                                                               thisParamId,
-                                                               false);
-               Assert(paramInfo->ptype == expression->paramtype);
-               *isNull = paramInfo->isnull;
-               return paramInfo->value;
+                       if (OidIsValid(prm->ptype))
+                       {
+                               Assert(prm->ptype == expression->paramtype);
+                               *isNull = prm->isnull;
+                               return prm->value;
+                       }
+               }
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("no value found for parameter %d", thisParamId)));
+               return (Datum) 0;               /* keep compiler quiet */
        }
 }
 
@@ -637,9 +941,9 @@ GetAttributeByNum(HeapTupleHeader tuple,
        tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 
        /*
-        * 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 columns.
+        * 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
+        * columns.
         */
        tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
        ItemPointerSetInvalid(&(tmptup.t_self));
@@ -650,6 +954,9 @@ GetAttributeByNum(HeapTupleHeader tuple,
                                                  attrno,
                                                  tupDesc,
                                                  isNull);
+
+       ReleaseTupleDesc(tupDesc);
+
        return result;
 }
 
@@ -695,9 +1002,9 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
                elog(ERROR, "attribute \"%s\" does not exist", attname);
 
        /*
-        * 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 columns.
+        * 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
+        * columns.
         */
        tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
        ItemPointerSetInvalid(&(tmptup.t_self));
@@ -708,14 +1015,18 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
                                                  attrno,
                                                  tupDesc,
                                                  isNull);
+
+       ReleaseTupleDesc(tupDesc);
+
        return result;
 }
 
 /*
  * 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;
 
@@ -724,17 +1035,76 @@ init_fcache(Oid foid, FuncExprState *fcache, MemoryContext fcacheCxt)
        if (aclresult != ACLCHECK_OK)
                aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid));
 
-       /* Safety check (should never fail, as parser should check sooner) */
+       /*
+        * Safety check on nargs.  Under normal circumstances this should never
+        * fail, as parser should check sooner.  But possibly it might fail if
+        * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+        * declared in pg_proc?
+        */
        if (list_length(fcache->args) > FUNC_MAX_ARGS)
-               elog(ERROR, "too many arguments");
+               ereport(ERROR,
+                               (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+                                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;
+               }
+
+               MemoryContextSwitchTo(oldcontext);
+       }
+       else
+               fcache->funcResultDesc = NULL;
 
-       /* Initialize additional info */
+       /* Initialize additional state */
+       fcache->funcResultStore = NULL;
+       fcache->funcResultSlot = NULL;
        fcache->setArgsValid = false;
        fcache->shutdown_reg = false;
-       fcache->func.fn_expr = (Node *) fcache->xprstate.expr;
 }
 
 /*
@@ -746,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;
 
@@ -753,6 +1132,61 @@ ShutdownFuncExpr(Datum arg)
        fcache->shutdown_reg = false;
 }
 
+/*
+ * get_cached_rowtype: utility function to lookup a rowtype tupdesc
+ *
+ * type_id, typmod: identity of the rowtype
+ * cache_field: where to cache the TupleDesc pointer in expression state node
+ *             (field must be initialized to NULL)
+ * econtext: expression context we are executing in
+ *
+ * NOTE: because the shutdown callback will be called during plan rescan,
+ * must be prepared to re-do this during any node execution; cannot call
+ * just once during expression initialization
+ */
+static TupleDesc
+get_cached_rowtype(Oid type_id, int32 typmod,
+                                  TupleDesc *cache_field, ExprContext *econtext)
+{
+       TupleDesc       tupDesc = *cache_field;
+
+       /* Do lookup if no cached value or if requested type changed */
+       if (tupDesc == NULL ||
+               type_id != tupDesc->tdtypeid ||
+               typmod != tupDesc->tdtypmod)
+       {
+               tupDesc = lookup_rowtype_tupdesc(type_id, typmod);
+
+               if (*cache_field)
+               {
+                       /* Release old tupdesc; but callback is already registered */
+                       ReleaseTupleDesc(*cache_field);
+               }
+               else
+               {
+                       /* Need to register shutdown callback to release tupdesc */
+                       RegisterExprContextCallback(econtext,
+                                                                               ShutdownTupleDescRef,
+                                                                               PointerGetDatum(cache_field));
+               }
+               *cache_field = tupDesc;
+       }
+       return tupDesc;
+}
+
+/*
+ * Callback function to release a tupdesc refcount at expression tree shutdown
+ */
+static void
+ShutdownTupleDescRef(Datum arg)
+{
+       TupleDesc  *cache_field = (TupleDesc *) DatumGetPointer(arg);
+
+       if (*cache_field)
+               ReleaseTupleDesc(*cache_field);
+       *cache_field = NULL;
+}
+
 /*
  * Evaluate arguments for a function.
  */
@@ -781,10 +1215,9 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
                if (thisArgIsDone != ExprSingleResult)
                {
                        /*
-                        * We allow only one argument to have a set value; we'd need
-                        * much more complexity to keep track of multiple set
-                        * arguments (cf. ExecTargetList) and it doesn't seem worth
-                        * it.
+                        * We allow only one argument to have a set value; we'd need much
+                        * more complexity to keep track of multiple set arguments (cf.
+                        * ExecTargetList) and it doesn't seem worth it.
                         */
                        if (argIsDone != ExprSingleResult)
                                ereport(ERROR,
@@ -801,40 +1234,214 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
 }
 
 /*
- *             ExecMakeFunctionResult
+ *             ExecPrepareTuplestoreResult
  *
- * Evaluate the arguments to a function and then the function itself.
+ * 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.
  */
-Datum
-ExecMakeFunctionResult(FuncExprState *fcache,
-                                          ExprContext *econtext,
-                                          bool *isNull,
-                                          ExprDoneCond *isDone)
+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)
 {
-       List       *arguments = fcache->args;
-       Datum           result;
-       FunctionCallInfoData fcinfo;
-       ReturnSetInfo rsinfo;           /* for functions returning sets */
-       ExprDoneCond argDone;
-       bool            hasSetArg;
        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.
+ */
+static Datum
+ExecMakeFunctionResult(FuncExprState *fcache,
+                                          ExprContext *econtext,
+                                          bool *isNull,
+                                          ExprDoneCond *isDone)
+{
+       List       *arguments;
+       Datum           result;
+       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();
 
        /*
-        * 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.
+        * 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 */
-               MemSet(&fcinfo, 0, sizeof(fcinfo));
-               fcinfo.flinfo = &(fcache->func);
-               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. */
@@ -851,39 +1458,20 @@ 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)
        {
                /*
-                * We need to return a set result.      Complain if caller not ready
-                * to accept one.
+                * We need to return a set result.      Complain if caller not ready to
+                * accept one.
                 */
                if (isDone == NULL)
                        ereport(ERROR,
@@ -891,26 +1479,43 @@ ExecMakeFunctionResult(FuncExprState *fcache,
                                         errmsg("set-valued function called in context that cannot accept a set")));
 
                /*
-                * This loop handles the situation where we have both a set
-                * argument and a set-valued function.  Once we have exhausted the
-                * function's value(s) for a particular argument value, we have to
-                * get the next argument value and start the function over again.
-                * We might have to do it more than once, if the function produces
-                * an empty result set for a particular input value.
+                * 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
+                * value(s) for a particular argument value, we have to get the next
+                * argument value and start the function over again. We might have to
+                * do it more than once, if the function produces an empty result set
+                * for a particular input value.
                 */
                for (;;)
                {
                        /*
-                        * If function is strict, and there are any NULL arguments,
-                        * skip calling the function (at least for this set of args).
+                        * If function is strict, and there are any NULL arguments, skip
+                        * calling the function (at least for this set of args).
                         */
                        bool            callit = true;
 
                        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;
@@ -920,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
                        {
@@ -933,42 +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)
+                               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.
-                                */
-                               *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)
                        {
@@ -980,8 +1624,8 @@ ExecMakeFunctionResult(FuncExprState *fcache,
                        }
 
                        /*
-                        * If we reach here, loop around to run the function on the
-                        * new argument.
+                        * If we reach here, loop around to run the function on the new
+                        * argument.
                         */
                }
        }
@@ -991,9 +1635,9 @@ ExecMakeFunctionResult(FuncExprState *fcache,
                 * Non-set case: much easier.
                 *
                 * We change the ExprState function pointer to use the simpler
-                * ExecMakeFunctionResultNoSets on subsequent calls.  This amounts
-                * to assuming that no argument can return a set if it didn't do
-                * so the first time.
+                * ExecMakeFunctionResultNoSets on subsequent calls.  This amounts to
+                * assuming that no argument can return a set if it didn't do so the
+                * first time.
                 */
                fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
 
@@ -1006,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;
@@ -1038,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 */
@@ -1046,32 +1696,24 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
        if (isDone)
                *isDone = ExprSingleResult;
 
-       MemSet(&fcinfo, 0, sizeof(fcinfo));
-       fcinfo.flinfo = &(fcache->func);
-
        /* inlined, simplified version of ExecEvalFuncArgs */
        i = 0;
        foreach(arg, fcache->args)
        {
                ExprState  *argstate = (ExprState *) lfirst(arg);
-               ExprDoneCond thisArgIsDone;
 
                fcinfo.arg[i] = ExecEvalExpr(argstate,
                                                                         econtext,
                                                                         &fcinfo.argnull[i],
-                                                                        &thisArgIsDone);
-
-               if (thisArgIsDone != ExprSingleResult)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("set-valued function called in context that cannot accept a set")));
+                                                                        NULL);
                i++;
        }
-       fcinfo.nargs = i;
+
+       InitFunctionCallInfoData(fcinfo, &(fcache->func), i, NULL, NULL);
 
        /*
-        * If function is strict, and there are any NULL arguments, skip
-        * calling the function and return NULL.
+        * If function is strict, and there are any NULL arguments, skip calling
+        * the function and return NULL.
         */
        if (fcache->func.fn_strict)
        {
@@ -1084,10 +1726,15 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
                        }
                }
        }
-       /* fcinfo.isnull = false; */    /* handled by MemSet */
+
+       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;
 }
 
@@ -1096,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;
@@ -1111,6 +1757,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
        bool            returnsTuple;
        bool            returnsSet = false;
        FunctionCallInfoData fcinfo;
+       PgStat_FunctionCallUsage fcusage;
        ReturnSetInfo rsinfo;
        HeapTupleData tmptup;
        MemoryContext callerContext;
@@ -1122,36 +1769,36 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 
        funcrettype = exprType((Node *) funcexpr->expr);
 
-       returnsTuple = (funcrettype == RECORDOID ||
-                                       get_typtype(funcrettype) == 'c');
+       returnsTuple = type_is_rowtype(funcrettype);
 
        /*
-        * Prepare a resultinfo node for communication.  We always do this
-        * even if not expecting a set result, so that we can pass
-        * expectedDesc.  In the generic-expression case, the expression
-        * doesn't actually get to see the resultinfo, but set it up anyway
-        * because we use some of the fields as our own state variables.
+        * Prepare a resultinfo node for communication.  We always do this even if
+        * not expecting a set result, so that we can pass expectedDesc.  In the
+        * generic-expression case, the expression doesn't actually get to see the
+        * resultinfo, but set it up anyway because we use some of the fields as
+        * our own state variables.
         */
-       MemSet(&fcinfo, 0, sizeof(fcinfo));
-       fcinfo.resultinfo = (Node *) &rsinfo;
+       InitFunctionCallInfoData(fcinfo, NULL, 0, NULL, (Node *) &rsinfo);
        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;
        rsinfo.setDesc = NULL;
 
        /*
-        * Normally the passed expression tree will be a FuncExprState, since
-        * the grammar only allows a function call at the top level of a table
-        * function reference.  However, if the function doesn't return set
-        * then the planner might have replaced the function call via
-        * constant-folding or inlining.  So if we see any other kind of
-        * expression node, execute it via the general ExecEvalExpr() code;
-        * the only difference is that we don't get a chance to pass a special
-        * ReturnSetInfo to any functions buried in the expression.
+        * Normally the passed expression tree will be a FuncExprState, since the
+        * grammar only allows a function call at the top level of a table
+        * function reference.  However, if the function doesn't return set then
+        * the planner might have replaced the function call via constant-folding
+        * or inlining.  So if we see any other kind of expression node, execute
+        * it via the general ExecEvalExpr() code; the only difference is that we
+        * don't get a chance to pass a special ReturnSetInfo to any functions
+        * buried in the expression.
         */
        if (funcexpr && IsA(funcexpr, FuncExprState) &&
                IsA(funcexpr->expr, FuncExpr))
@@ -1171,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;
 
@@ -1179,9 +1827,9 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                 * Evaluate the function's argument list.
                 *
                 * Note: ideally, we'd do this in the per-tuple context, but then the
-                * argument values would disappear when we reset the context in
-                * the inner loop.      So do it in caller context.  Perhaps we should
-                * make a separate context just to hold the evaluated arguments?
+                * argument values would disappear when we reset the context in the
+                * inner loop.  So do it in caller context.  Perhaps we should make a
+                * separate context just to hold the evaluated arguments?
                 */
                fcinfo.flinfo = &(fcache->func);
                argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext);
@@ -1214,8 +1862,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
        }
 
        /*
-        * Switch to short-lived context for calling the function or
-        * expression.
+        * Switch to short-lived context for calling the function or expression.
         */
        MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
 
@@ -1226,21 +1873,27 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
        for (;;)
        {
                Datum           result;
-               HeapTuple       tuple;
+
+               CHECK_FOR_INTERRUPTS();
 
                /*
-                * reset per-tuple memory context before each call of the function
-                * or expression. This cleans up any local memory the function may
-                * leak when called.
+                * reset per-tuple memory context before each call of the function or
+                * expression. This cleans up any local memory the function may leak
+                * when called.
                 */
                ResetExprContext(econtext);
 
                /* 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
                {
@@ -1258,12 +1911,12 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                                break;
 
                        /*
-                        * Can't do anything very useful with NULL rowtype values.
-                        * For a function returning set, we consider this a protocol
-                        * violation (but another alternative would be to just ignore
-                        * the result and "continue" to get another row).  For a function
-                        * not returning set, we fall out of the loop; we'll cons up
-                        * an all-nulls result row below.
+                        * Can't do anything very useful with NULL rowtype values. For a
+                        * function returning set, we consider this a protocol violation
+                        * (but another alternative would be to just ignore the result and
+                        * "continue" to get another row).      For a function not returning
+                        * set, we fall out of the loop; we'll cons up an all-nulls result
+                        * row below.
                         */
                        if (returnsTuple && fcinfo.isnull)
                        {
@@ -1275,8 +1928,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                        }
 
                        /*
-                        * If first time through, build tupdesc and tuplestore for
-                        * result
+                        * If first time through, build tupdesc and tuplestore for result
                         */
                        if (first_time)
                        {
@@ -1284,16 +1936,14 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                                if (returnsTuple)
                                {
                                        /*
-                                        * Use the type info embedded in the rowtype Datum to
-                                        * look up the needed tupdesc.  Make a copy for the
-                                        * query.
+                                        * Use the type info embedded in the rowtype Datum to look
+                                        * up the needed tupdesc.  Make a copy for the query.
                                         */
                                        HeapTupleHeader td;
 
                                        td = DatumGetHeapTupleHeader(result);
-                                       tupdesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(td),
-                                                                                  HeapTupleHeaderGetTypMod(td));
-                                       tupdesc = CreateTupleDescCopy(tupdesc);
+                                       tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td),
+                                                                                          HeapTupleHeaderGetTypMod(td));
                                }
                                else
                                {
@@ -1308,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;
@@ -1329,18 +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
                        {
-                               char            nullflag;
-
-                               nullflag = fcinfo.isnull ? 'n' : ' ';
-                               tuple = heap_formtuple(tupdesc, &result, &nullflag);
+                               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);
 
                        /*
@@ -1378,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;
-                       char       *nullflags;
-                       HeapTuple       tuple;
+                       bool       *nullflags;
 
                        MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
                        nulldatums = (Datum *) palloc0(natts * sizeof(Datum));
-                       nullflags = (char *) palloc(natts * sizeof(char));
-                       memset(nullflags, 'n', natts * sizeof(char));
-                       tuple = heap_formtuple(expectedDesc, nulldatums, nullflags);
+                       nullflags = (bool *) palloc(natts * sizeof(bool));
+                       memset(nullflags, true, natts * sizeof(bool));
                        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;
 }
 
@@ -1428,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;
@@ -1450,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;
@@ -1492,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);
        }
 
@@ -1502,13 +2165,12 @@ ExecEvalDistinct(FuncExprState *fcache,
        argList = fcache->args;
 
        /* Need to prep callinfo structure */
-       MemSet(&fcinfo, 0, sizeof(fcinfo));
-       fcinfo.flinfo = &(fcache->func);
+       InitFunctionCallInfoData(fcinfo, &(fcache->func), 0, NULL, NULL);
        argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
        if (argDone != ExprSingleResult)
                ereport(ERROR,
                                (errcode(ERRCODE_DATATYPE_MISMATCH),
-                        errmsg("IS DISTINCT FROM does not support set arguments")));
+                                errmsg("IS DISTINCT FROM does not support set arguments")));
        Assert(fcinfo.nargs == 2);
 
        if (fcinfo.argnull[0] && fcinfo.argnull[1])
@@ -1559,6 +2221,8 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
        bool            typbyval;
        char            typalign;
        char       *s;
+       bits8      *bitmap;
+       int                     bitmask;
 
        /* Set default values for result flags: non-null, not a set result */
        *isNull = false;
@@ -1571,23 +2235,22 @@ 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);
        }
 
        /* Need to prep callinfo structure */
-       MemSet(&fcinfo, 0, sizeof(fcinfo));
-       fcinfo.flinfo = &(sstate->fxprstate.func);
+       InitFunctionCallInfoData(fcinfo, &(sstate->fxprstate.func), 0, NULL, NULL);
        argDone = ExecEvalFuncArgs(&fcinfo, sstate->fxprstate.args, econtext);
        if (argDone != ExprSingleResult)
                ereport(ERROR,
                                (errcode(ERRCODE_DATATYPE_MISMATCH),
-                  errmsg("op ANY/ALL (array) does not support set arguments")));
+                          errmsg("op ANY/ALL (array) does not support set arguments")));
        Assert(fcinfo.nargs == 2);
 
        /*
-        * If the array is NULL then we return NULL --- it's not very
-        * meaningful to do anything else, even if the operator isn't strict.
+        * If the array is NULL then we return NULL --- it's not very meaningful
+        * to do anything else, even if the operator isn't strict.
         */
        if (fcinfo.argnull[1])
        {
@@ -1600,18 +2263,16 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
        /*
         * If the array is empty, we return either FALSE or TRUE per the useOr
         * flag.  This is correct even if the scalar is NULL; since we would
-        * evaluate the operator zero times, it matters not whether it would
-        * want to return NULL.
+        * evaluate the operator zero times, it matters not whether it would want
+        * to return NULL.
         */
        nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
        if (nitems <= 0)
                return BoolGetDatum(!useOr);
 
        /*
-        * If the scalar is NULL, and the function is strict, return NULL.
-        * This is just to avoid having to test for strictness inside the
-        * loop.  (XXX but if arrays could have null elements, we'd need a
-        * test anyway.)
+        * If the scalar is NULL, and the function is strict, return NULL; no
+        * point in iterating the loop.
         */
        if (fcinfo.argnull[0] && sstate->fxprstate.func.fn_strict)
        {
@@ -1620,9 +2281,8 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
        }
 
        /*
-        * We arrange to look up info about the element type only once per
-        * series of calls, assuming the element type doesn't change
-        * underneath us.
+        * We arrange to look up info about the element type only once per series
+        * of calls, assuming the element type doesn't change underneath us.
         */
        if (sstate->element_type != ARR_ELEMTYPE(arr))
        {
@@ -1641,22 +2301,40 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
 
        /* Loop over the array elements */
        s = (char *) ARR_DATA_PTR(arr);
+       bitmap = ARR_NULLBITMAP(arr);
+       bitmask = 1;
+
        for (i = 0; i < nitems; i++)
        {
                Datum           elt;
                Datum           thisresult;
 
-               /* Get array element */
-               elt = fetch_att(s, typbyval, typlen);
-
-               s = att_addlength(s, typlen, PointerGetDatum(s));
-               s = (char *) att_align(s, typalign);
+               /* Get array element, checking for NULL */
+               if (bitmap && (*bitmap & bitmask) == 0)
+               {
+                       fcinfo.arg[1] = (Datum) 0;
+                       fcinfo.argnull[1] = true;
+               }
+               else
+               {
+                       elt = fetch_att(s, typbyval, typlen);
+                       s = att_addlength_pointer(s, typlen, s);
+                       s = (char *) att_align_nominal(s, typalign);
+                       fcinfo.arg[1] = elt;
+                       fcinfo.argnull[1] = false;
+               }
 
                /* Call comparison function */
-               fcinfo.arg[1] = elt;
-               fcinfo.argnull[1] = false;
-               fcinfo.isnull = false;
-               thisresult = FunctionCallInvoke(&fcinfo);
+               if (fcinfo.argnull[1] && sstate->fxprstate.func.fn_strict)
+               {
+                       fcinfo.isnull = true;
+                       thisresult = (Datum) 0;
+               }
+               else
+               {
+                       fcinfo.isnull = false;
+                       thisresult = FunctionCallInvoke(&fcinfo);
+               }
 
                /* Combine results per OR or AND semantics */
                if (fcinfo.isnull)
@@ -1679,6 +2357,17 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
                                break;                  /* needn't look at any more elements */
                        }
                }
+
+               /* advance bitmap pointer if any */
+               if (bitmap)
+               {
+                       bitmask <<= 1;
+                       if (bitmask == 0x100)
+                       {
+                               bitmap++;
+                               bitmask = 1;
+                       }
+               }
        }
 
        *isNull = resultnull;
@@ -1713,15 +2402,15 @@ ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
        expr_value = ExecEvalExpr(clause, econtext, isNull, NULL);
 
        /*
-        * if the expression evaluates to null, then we just cascade the null
-        * back to whoever called us.
+        * if the expression evaluates to null, then we just cascade the null back
+        * to whoever called us.
         */
        if (*isNull)
                return expr_value;
 
        /*
-        * evaluation of 'not' is simple.. expr is false, then return 'true'
-        * and vice versa.
+        * evaluation of 'not' is simple.. expr is false, then return 'true' and
+        * vice versa.
         */
        return BoolGetDatum(!DatumGetBool(expr_value));
 }
@@ -1744,18 +2433,17 @@ ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
        AnyNull = false;
 
        /*
-        * If any of the clauses is TRUE, the OR result is TRUE regardless of
-        * the states of the rest of the clauses, so we can stop evaluating
-        * and return TRUE immediately.  If none are TRUE and one or more is
-        * NULL, we return NULL; otherwise we return FALSE.  This makes sense
-        * when you interpret NULL as "don't know": if we have a TRUE then the
-        * OR is TRUE even if we aren't sure about some of the other inputs.
-        * If all the known inputs are FALSE, but we have one or more "don't
-        * knows", then we have to report that we "don't know" what the OR's
-        * result should be --- perhaps one of the "don't knows" would have
-        * been TRUE if we'd known its value.  Only when all the inputs are
-        * known to be FALSE can we state confidently that the OR's result is
-        * FALSE.
+        * If any of the clauses is TRUE, the OR result is TRUE regardless of the
+        * states of the rest of the clauses, so we can stop evaluating and return
+        * TRUE immediately.  If none are TRUE and one or more is NULL, we return
+        * NULL; otherwise we return FALSE.  This makes sense when you interpret
+        * NULL as "don't know": if we have a TRUE then the OR is TRUE even if we
+        * aren't sure about some of the other inputs. If all the known inputs are
+        * FALSE, but we have one or more "don't knows", then we have to report
+        * that we "don't know" what the OR's result should be --- perhaps one of
+        * the "don't knows" would have been TRUE if we'd known its value.  Only
+        * when all the inputs are known to be FALSE can we state confidently that
+        * the OR's result is FALSE.
         */
        foreach(clause, clauses)
        {
@@ -1796,12 +2484,12 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
        AnyNull = false;
 
        /*
-        * If any of the clauses is FALSE, the AND result is FALSE regardless
-        * of the states of the rest of the clauses, so we can stop evaluating
-        * and return FALSE immediately.  If none are FALSE and one or more is
-        * NULL, we return NULL; otherwise we return TRUE.      This makes sense
-        * when you interpret NULL as "don't know", using the same sort of
-        * reasoning as for OR, above.
+        * If any of the clauses is FALSE, the AND result is FALSE regardless of
+        * the states of the rest of the clauses, so we can stop evaluating and
+        * return FALSE immediately.  If none are FALSE and one or more is NULL,
+        * we return NULL; otherwise we return TRUE.  This makes sense when you
+        * interpret NULL as "don't know", using the same sort of reasoning as for
+        * OR, above.
         */
 
        foreach(clause, clauses)
@@ -1828,7 +2516,7 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
 /* ----------------------------------------------------------------
  *             ExecEvalConvertRowtype
  *
- *             Evaluate a rowtype coercion operation.  This may require
+ *             Evaluate a rowtype coercion operation.  This may require
  *             rearranging field positions.
  * ----------------------------------------------------------------
  */
@@ -1837,17 +2525,18 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
                                           ExprContext *econtext,
                                           bool *isNull, ExprDoneCond *isDone)
 {
+       ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) cstate->xprstate.expr;
        HeapTuple       result;
        Datum           tupDatum;
        HeapTupleHeader tuple;
        HeapTupleData tmptup;
-       AttrNumber *attrMap = cstate->attrMap;
-       Datum      *invalues = cstate->invalues;
-       char       *innulls = cstate->innulls;
-       Datum      *outvalues = cstate->outvalues;
-       char       *outnulls = cstate->outnulls;
+       AttrNumber *attrMap;
+       Datum      *invalues;
+       bool       *inisnull;
+       Datum      *outvalues;
+       bool       *outisnull;
        int                     i;
-       int                     outnatts = cstate->outdesc->natts;
+       int                     outnatts;
 
        tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
 
@@ -1857,24 +2546,96 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
 
        tuple = DatumGetHeapTupleHeader(tupDatum);
 
+       /* Lookup tupdescs if first time through or after rescan */
+       if (cstate->indesc == NULL)
+               get_cached_rowtype(exprType((Node *) convert->arg), -1,
+                                                  &cstate->indesc, econtext);
+       if (cstate->outdesc == NULL)
+               get_cached_rowtype(convert->resulttype, -1,
+                                                  &cstate->outdesc, econtext);
+
        Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid);
        Assert(HeapTupleHeaderGetTypMod(tuple) == cstate->indesc->tdtypmod);
 
+       /* if first time through, initialize */
+       if (cstate->attrMap == NULL)
+       {
+               MemoryContext old_cxt;
+               int                     n;
+
+               /* allocate state in long-lived memory context */
+               old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+               /* prepare map from old to new attribute numbers */
+               n = cstate->outdesc->natts;
+               cstate->attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
+               for (i = 0; i < n; i++)
+               {
+                       Form_pg_attribute att = cstate->outdesc->attrs[i];
+                       char       *attname;
+                       Oid                     atttypid;
+                       int32           atttypmod;
+                       int                     j;
+
+                       if (att->attisdropped)
+                               continue;               /* attrMap[i] is already 0 */
+                       attname = NameStr(att->attname);
+                       atttypid = att->atttypid;
+                       atttypmod = att->atttypmod;
+                       for (j = 0; j < cstate->indesc->natts; j++)
+                       {
+                               att = cstate->indesc->attrs[j];
+                               if (att->attisdropped)
+                                       continue;
+                               if (strcmp(attname, NameStr(att->attname)) == 0)
+                               {
+                                       /* Found it, check type */
+                                       if (atttypid != att->atttypid || atttypmod != att->atttypmod)
+                                               elog(ERROR, "attribute \"%s\" of type %s does not match corresponding attribute of type %s",
+                                                        attname,
+                                                        format_type_be(cstate->indesc->tdtypeid),
+                                                        format_type_be(cstate->outdesc->tdtypeid));
+                                       cstate->attrMap[i] = (AttrNumber) (j + 1);
+                                       break;
+                               }
+                       }
+                       if (cstate->attrMap[i] == 0)
+                               elog(ERROR, "attribute \"%s\" of type %s does not exist",
+                                        attname,
+                                        format_type_be(cstate->indesc->tdtypeid));
+               }
+               /* preallocate workspace for Datum arrays */
+               n = cstate->indesc->natts + 1;  /* +1 for NULL */
+               cstate->invalues = (Datum *) palloc(n * sizeof(Datum));
+               cstate->inisnull = (bool *) palloc(n * sizeof(bool));
+               n = cstate->outdesc->natts;
+               cstate->outvalues = (Datum *) palloc(n * sizeof(Datum));
+               cstate->outisnull = (bool *) palloc(n * sizeof(bool));
+
+               MemoryContextSwitchTo(old_cxt);
+       }
+
+       attrMap = cstate->attrMap;
+       invalues = cstate->invalues;
+       inisnull = cstate->inisnull;
+       outvalues = cstate->outvalues;
+       outisnull = cstate->outisnull;
+       outnatts = cstate->outdesc->natts;
+
        /*
-        * heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader.
+        * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader.
         */
        tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
        tmptup.t_data = tuple;
 
        /*
-        * Extract all the values of the old tuple, offsetting the arrays
-        * so that invalues[0] is NULL and invalues[1] is the first
-        * source attribute; this exactly matches the numbering convention
-        * in attrMap.
+        * Extract all the values of the old tuple, offsetting the arrays so that
+        * invalues[0] is NULL and invalues[1] is the first source attribute; this
+        * exactly matches the numbering convention in attrMap.
         */
-       heap_deformtuple(&tmptup, cstate->indesc, invalues + 1, innulls + 1);
+       heap_deform_tuple(&tmptup, cstate->indesc, invalues + 1, inisnull + 1);
        invalues[0] = (Datum) 0;
-       innulls[0] = 'n';
+       inisnull[0] = true;
 
        /*
         * Transpose into proper fields of the new tuple.
@@ -1884,13 +2645,13 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
                int                     j = attrMap[i];
 
                outvalues[i] = invalues[j];
-               outnulls[i] = innulls[j];
+               outisnull[i] = inisnull[j];
        }
 
        /*
         * Now form the new tuple.
         */
-       result = heap_formtuple(cstate->outdesc, outvalues, outnulls);
+       result = heap_form_tuple(cstate->outdesc, outvalues, outisnull);
 
        return HeapTupleGetDatum(result);
 }
@@ -1917,10 +2678,10 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
                *isDone = ExprSingleResult;
 
        /*
-        * If there's a test expression, we have to evaluate it and save the
-        * value where the CaseTestExpr placeholders can find it. We must save
-        * and restore prior setting of econtext's caseValue fields, in case
-        * this node is itself within a larger CASE.
+        * If there's a test expression, we have to evaluate it and save the value
+        * where the CaseTestExpr placeholders can find it. We must save and
+        * restore prior setting of econtext's caseValue fields, in case this node
+        * is itself within a larger CASE.
         */
        save_datum = econtext->caseValue_datum;
        save_isNull = econtext->caseValue_isNull;
@@ -1929,14 +2690,14 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
        {
                econtext->caseValue_datum = ExecEvalExpr(caseExpr->arg,
                                                                                                 econtext,
-                                                                                        &econtext->caseValue_isNull,
+                                                                                                &econtext->caseValue_isNull,
                                                                                                 NULL);
        }
 
        /*
-        * we evaluate each of the WHEN clauses in turn, as soon as one is
-        * true we return the corresponding result. If none are true then we
-        * return the value of the default clause, or NULL if there is none.
+        * we evaluate each of the WHEN clauses in turn, as soon as one is true we
+        * return the corresponding result. If none are true then we return the
+        * value of the default clause, or NULL if there is none.
         */
        foreach(clause, clauses)
        {
@@ -1949,9 +2710,9 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
                                                                        NULL);
 
                /*
-                * if we have a true test, then we return the result, since the
-                * case statement is satisfied.  A NULL result from the test is
-                * not considered true.
+                * if we have a true test, then we return the result, since the case
+                * statement is satisfied.      A NULL result from the test is not
+                * considered true.
                 */
                if (DatumGetBool(clause_value) && !*isNull)
                {
@@ -1997,10 +2758,6 @@ ExecEvalCaseTestExpr(ExprState *exprstate,
 
 /* ----------------------------------------------------------------
  *             ExecEvalArray - ARRAY[] expressions
- *
- * NOTE: currently, if any input value is NULL then we return a NULL array,
- * so the ARRAY[] construct can be considered strict.  Eventually this will
- * change; when it does, be sure to fix contain_nonstrict_functions().
  * ----------------------------------------------------------------
  */
 static Datum
@@ -2025,39 +2782,33 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
                /* Elements are presumably of scalar type */
                int                     nelems;
                Datum      *dvalues;
+               bool       *dnulls;
                int                     i = 0;
 
                ndims = 1;
                nelems = list_length(astate->elements);
 
-               /* Shouldn't happen here, but if length is 0, return NULL */
+               /* Shouldn't happen here, but if length is 0, return empty array */
                if (nelems == 0)
-               {
-                       *isNull = true;
-                       return (Datum) 0;
-               }
+                       return PointerGetDatum(construct_empty_array(element_type));
 
                dvalues = (Datum *) palloc(nelems * sizeof(Datum));
+               dnulls = (bool *) palloc(nelems * sizeof(bool));
 
                /* loop through and build array of datums */
                foreach(element, astate->elements)
                {
                        ExprState  *e = (ExprState *) lfirst(element);
-                       bool            eisnull;
 
-                       dvalues[i++] = ExecEvalExpr(e, econtext, &eisnull, NULL);
-                       if (eisnull)
-                       {
-                               *isNull = true;
-                               return (Datum) 0;
-                       }
+                       dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i], NULL);
+                       i++;
                }
 
                /* setup for 1-D array of the given length */
                dims[0] = nelems;
                lbs[0] = 1;
 
-               result = construct_md_array(dvalues, ndims, dims, lbs,
+               result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
                                                                        element_type,
                                                                        astate->elemlength,
                                                                        astate->elembyval,
@@ -2066,15 +2817,29 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
        else
        {
                /* Must be nested array expressions */
-               char       *dat = NULL;
-               Size            ndatabytes = 0;
-               int                     nbytes;
-               int                     outer_nelems = list_length(astate->elements);
+               int                     nbytes = 0;
+               int                     nitems = 0;
+               int                     outer_nelems = 0;
                int                     elem_ndims = 0;
                int                *elem_dims = NULL;
                int                *elem_lbs = NULL;
                bool            firstone = true;
+               bool            havenulls = false;
+               bool            haveempty = false;
+               char      **subdata;
+               bits8     **subbitmaps;
+               int                *subbytes;
+               int                *subnitems;
                int                     i;
+               int32           dataoffset;
+               char       *dat;
+               int                     iitem;
+
+               i = list_length(astate->elements);
+               subdata = (char **) palloc(i * sizeof(char *));
+               subbitmaps = (bits8 **) palloc(i * sizeof(bits8 *));
+               subbytes = (int *) palloc(i * sizeof(int));
+               subnitems = (int *) palloc(i * sizeof(int));
 
                /* loop through and get data area from each element */
                foreach(element, astate->elements)
@@ -2083,13 +2848,14 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
                        bool            eisnull;
                        Datum           arraydatum;
                        ArrayType  *array;
-                       int                     elem_ndatabytes;
+                       int                     this_ndims;
 
                        arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL);
+                       /* temporarily ignore null subarrays */
                        if (eisnull)
                        {
-                               *isNull = true;
-                               return (Datum) 0;
+                               haveempty = true;
+                               continue;
                        }
 
                        array = DatumGetArrayTypeP(arraydatum);
@@ -2100,20 +2866,28 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
                                                (errcode(ERRCODE_DATATYPE_MISMATCH),
                                                 errmsg("cannot merge incompatible arrays"),
                                                 errdetail("Array with element type %s cannot be "
-                                        "included in ARRAY construct with element type %s.",
+                                                "included in ARRAY construct with element type %s.",
                                                                   format_type_be(ARR_ELEMTYPE(array)),
                                                                   format_type_be(element_type))));
 
+                       this_ndims = ARR_NDIM(array);
+                       /* temporarily ignore zero-dimensional subarrays */
+                       if (this_ndims <= 0)
+                       {
+                               haveempty = true;
+                               continue;
+                       }
+
                        if (firstone)
                        {
                                /* Get sub-array details from first member */
-                               elem_ndims = ARR_NDIM(array);
+                               elem_ndims = this_ndims;
                                ndims = elem_ndims + 1;
                                if (ndims <= 0 || ndims > MAXDIM)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-                                         errmsg("number of array dimensions (%d) exceeds " \
-                                                        "the maximum allowed (%d)", ndims, MAXDIM)));
+                                                 errmsg("number of array dimensions (%d) exceeds " \
+                                                                "the maximum allowed (%d)", ndims, MAXDIM)));
 
                                elem_dims = (int *) palloc(elem_ndims * sizeof(int));
                                memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int));
@@ -2125,27 +2899,42 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
                        else
                        {
                                /* Check other sub-arrays are compatible */
-                               if (elem_ndims != ARR_NDIM(array) ||
+                               if (elem_ndims != this_ndims ||
                                        memcmp(elem_dims, ARR_DIMS(array),
                                                   elem_ndims * sizeof(int)) != 0 ||
                                        memcmp(elem_lbs, ARR_LBOUND(array),
                                                   elem_ndims * sizeof(int)) != 0)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
-                                               errmsg("multidimensional arrays must have array "
-                                                          "expressions with matching dimensions")));
+                                                        errmsg("multidimensional arrays must have array "
+                                                                       "expressions with matching dimensions")));
                        }
 
-                       elem_ndatabytes = ARR_SIZE(array) - ARR_OVERHEAD(elem_ndims);
-                       ndatabytes += elem_ndatabytes;
-                       if (dat == NULL)
-                               dat = (char *) palloc(ndatabytes);
-                       else
-                               dat = (char *) repalloc(dat, ndatabytes);
+                       subdata[outer_nelems] = ARR_DATA_PTR(array);
+                       subbitmaps[outer_nelems] = ARR_NULLBITMAP(array);
+                       subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array);
+                       nbytes += subbytes[outer_nelems];
+                       subnitems[outer_nelems] = ArrayGetNItems(this_ndims,
+                                                                                                        ARR_DIMS(array));
+                       nitems += subnitems[outer_nelems];
+                       havenulls |= ARR_HASNULL(array);
+                       outer_nelems++;
+               }
 
-                       memcpy(dat + (ndatabytes - elem_ndatabytes),
-                                  ARR_DATA_PTR(array),
-                                  elem_ndatabytes);
+               /*
+                * 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...)
+                */
+               if (haveempty)
+               {
+                       if (ndims == 0)         /* didn't find any nonempty array */
+                               return PointerGetDatum(construct_empty_array(element_type));
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+                                        errmsg("multidimensional arrays must have array "
+                                                       "expressions with matching dimensions")));
                }
 
                /* setup for multi-D array */
@@ -2157,20 +2946,37 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
                        lbs[i] = elem_lbs[i - 1];
                }
 
-               nbytes = ndatabytes + ARR_OVERHEAD(ndims);
-               result = (ArrayType *) palloc(nbytes);
+               if (havenulls)
+               {
+                       dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
+                       nbytes += dataoffset;
+               }
+               else
+               {
+                       dataoffset = 0;         /* marker for no null bitmap */
+                       nbytes += ARR_OVERHEAD_NONULLS(ndims);
+               }
 
-               result->size = nbytes;
+               result = (ArrayType *) palloc(nbytes);
+               SET_VARSIZE(result, nbytes);
                result->ndim = ndims;
-               result->flags = 0;
+               result->dataoffset = dataoffset;
                result->elemtype = element_type;
                memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
                memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
-               if (ndatabytes > 0)
-                       memcpy(ARR_DATA_PTR(result), dat, ndatabytes);
 
-               if (dat != NULL)
-                       pfree(dat);
+               dat = ARR_DATA_PTR(result);
+               iitem = 0;
+               for (i = 0; i < outer_nelems; i++)
+               {
+                       memcpy(dat, subdata[i], subbytes[i]);
+                       dat += subbytes[i];
+                       if (havenulls)
+                               array_bitmap_copy(ARR_NULLBITMAP(result), iitem,
+                                                                 subbitmaps[i], 0,
+                                                                 subnitems[i]);
+                       iitem += subnitems[i];
+               }
        }
 
        return PointerGetDatum(result);
@@ -2187,7 +2993,7 @@ ExecEvalRow(RowExprState *rstate,
 {
        HeapTuple       tuple;
        Datum      *values;
-       char       *nulls;
+       bool       *isnull;
        int                     natts;
        ListCell   *arg;
        int                     i;
@@ -2200,31 +3006,99 @@ ExecEvalRow(RowExprState *rstate,
        /* Allocate workspace */
        natts = rstate->tupdesc->natts;
        values = (Datum *) palloc0(natts * sizeof(Datum));
-       nulls = (char *) palloc(natts * sizeof(char));
+       isnull = (bool *) palloc(natts * sizeof(bool));
 
        /* preset to nulls in case rowtype has some later-added columns */
-       memset(nulls, 'n', natts * sizeof(char));
+       memset(isnull, true, natts * sizeof(bool));
 
        /* Evaluate field values */
        i = 0;
        foreach(arg, rstate->args)
        {
                ExprState  *e = (ExprState *) lfirst(arg);
-               bool            eisnull;
 
-               values[i] = ExecEvalExpr(e, econtext, &eisnull, NULL);
-               nulls[i] = eisnull ? 'n' : ' ';
+               values[i] = ExecEvalExpr(e, econtext, &isnull[i], NULL);
                i++;
        }
 
-       tuple = heap_formtuple(rstate->tupdesc, values, nulls);
+       tuple = heap_form_tuple(rstate->tupdesc, values, isnull);
 
        pfree(values);
-       pfree(nulls);
+       pfree(isnull);
 
        return HeapTupleGetDatum(tuple);
 }
 
+/* ----------------------------------------------------------------
+ *             ExecEvalRowCompare - ROW() comparison-op ROW()
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalRowCompare(RowCompareExprState *rstate,
+                                  ExprContext *econtext,
+                                  bool *isNull, ExprDoneCond *isDone)
+{
+       bool            result;
+       RowCompareType rctype = ((RowCompareExpr *) rstate->xprstate.expr)->rctype;
+       int32           cmpresult = 0;
+       ListCell   *l;
+       ListCell   *r;
+       int                     i;
+
+       if (isDone)
+               *isDone = ExprSingleResult;
+       *isNull = true;                         /* until we get a result */
+
+       i = 0;
+       forboth(l, rstate->largs, r, rstate->rargs)
+       {
+               ExprState  *le = (ExprState *) lfirst(l);
+               ExprState  *re = (ExprState *) lfirst(r);
+               FunctionCallInfoData locfcinfo;
+
+               InitFunctionCallInfoData(locfcinfo, &(rstate->funcs[i]), 2,
+                                                                NULL, NULL);
+               locfcinfo.arg[0] = ExecEvalExpr(le, econtext,
+                                                                               &locfcinfo.argnull[0], NULL);
+               locfcinfo.arg[1] = ExecEvalExpr(re, econtext,
+                                                                               &locfcinfo.argnull[1], NULL);
+               if (rstate->funcs[i].fn_strict &&
+                       (locfcinfo.argnull[0] || locfcinfo.argnull[1]))
+                       return (Datum) 0;       /* force NULL result */
+               locfcinfo.isnull = false;
+               cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+               if (locfcinfo.isnull)
+                       return (Datum) 0;       /* force NULL result */
+               if (cmpresult != 0)
+                       break;                          /* no need to compare remaining columns */
+               i++;
+       }
+
+       switch (rctype)
+       {
+                       /* EQ and NE cases aren't allowed here */
+               case ROWCOMPARE_LT:
+                       result = (cmpresult < 0);
+                       break;
+               case ROWCOMPARE_LE:
+                       result = (cmpresult <= 0);
+                       break;
+               case ROWCOMPARE_GE:
+                       result = (cmpresult >= 0);
+                       break;
+               case ROWCOMPARE_GT:
+                       result = (cmpresult > 0);
+                       break;
+               default:
+                       elog(ERROR, "unrecognized RowCompareType: %d", (int) rctype);
+                       result = 0;                     /* keep compiler quiet */
+                       break;
+       }
+
+       *isNull = false;
+       return BoolGetDatum(result);
+}
+
 /* ----------------------------------------------------------------
  *             ExecEvalCoalesce
  * ----------------------------------------------------------------
@@ -2254,6 +3128,282 @@ ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
        return (Datum) 0;
 }
 
+/* ----------------------------------------------------------------
+ *             ExecEvalMinMax
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext,
+                          bool *isNull, ExprDoneCond *isDone)
+{
+       Datum           result = (Datum) 0;
+       MinMaxOp        op = ((MinMaxExpr *) minmaxExpr->xprstate.expr)->op;
+       FunctionCallInfoData locfcinfo;
+       ListCell   *arg;
+
+       if (isDone)
+               *isDone = ExprSingleResult;
+       *isNull = true;                         /* until we get a result */
+
+       InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2, NULL, NULL);
+       locfcinfo.argnull[0] = false;
+       locfcinfo.argnull[1] = false;
+
+       foreach(arg, minmaxExpr->args)
+       {
+               ExprState  *e = (ExprState *) lfirst(arg);
+               Datum           value;
+               bool            valueIsNull;
+               int32           cmpresult;
+
+               value = ExecEvalExpr(e, econtext, &valueIsNull, NULL);
+               if (valueIsNull)
+                       continue;                       /* ignore NULL inputs */
+
+               if (*isNull)
+               {
+                       /* first nonnull input, adopt value */
+                       result = value;
+                       *isNull = false;
+               }
+               else
+               {
+                       /* apply comparison function */
+                       locfcinfo.arg[0] = result;
+                       locfcinfo.arg[1] = value;
+                       locfcinfo.isnull = false;
+                       cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+                       if (locfcinfo.isnull)           /* probably should not happen */
+                               continue;
+                       if (cmpresult > 0 && op == IS_LEAST)
+                               result = value;
+                       else if (cmpresult < 0 && op == IS_GREATEST)
+                               result = value;
+               }
+       }
+
+       return result;
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalXml
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
+                       bool *isNull, ExprDoneCond *isDone)
+{
+       XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
+       Datum           value;
+       bool            isnull;
+       ListCell   *arg;
+       ListCell   *narg;
+
+       if (isDone)
+               *isDone = ExprSingleResult;
+       *isNull = true;                         /* until we get a result */
+
+       switch (xexpr->op)
+       {
+               case IS_XMLCONCAT:
+                       {
+                               List       *values = NIL;
+
+                               foreach(arg, xmlExpr->args)
+                               {
+                                       ExprState  *e = (ExprState *) lfirst(arg);
+
+                                       value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                                       if (!isnull)
+                                               values = lappend(values, DatumGetPointer(value));
+                               }
+
+                               if (list_length(values) > 0)
+                               {
+                                       *isNull = false;
+                                       return PointerGetDatum(xmlconcat(values));
+                               }
+                               else
+                                       return (Datum) 0;
+                       }
+                       break;
+
+               case IS_XMLFOREST:
+               {
+                       StringInfoData buf;
+
+                       initStringInfo(&buf);
+                       forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names)
+                       {
+                               ExprState  *e = (ExprState *) lfirst(arg);
+                               char       *argname = strVal(lfirst(narg));
+
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (!isnull)
+                               {
+                                       appendStringInfo(&buf, "<%s>%s</%s>",
+                                                                        argname,
+                                                                        map_sql_value_to_xml_value(value, exprType((Node *) e->expr)),
+                                                                        argname);
+                                       *isNull = false;
+                               }
+                       }
+
+                       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;
+
+               case IS_XMLELEMENT:
+                       *isNull = false;
+                       return PointerGetDatum(xmlelement(xmlExpr, econtext));
+                       break;
+
+               case IS_XMLPARSE:
+                       {
+                               ExprState  *e;
+                               text       *data;
+                               bool            preserve_whitespace;
+
+                               /* arguments are known to be text, bool */
+                               Assert(list_length(xmlExpr->args) == 2);
+
+                               e = (ExprState *) linitial(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (isnull)
+                                       return (Datum) 0;
+                               data = DatumGetTextP(value);
+
+                               e = (ExprState *) lsecond(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (isnull)             /* probably can't happen */
+                                       return (Datum) 0;
+                               preserve_whitespace = DatumGetBool(value);
+
+                               *isNull = false;
+
+                               return PointerGetDatum(xmlparse(data,
+                                                                                               xexpr->xmloption,
+                                                                                               preserve_whitespace));
+                       }
+                       break;
+
+               case IS_XMLPI:
+                       {
+                               ExprState  *e;
+                               text       *arg;
+
+                               /* optional argument is known to be text */
+                               Assert(list_length(xmlExpr->args) <= 1);
+
+                               if (xmlExpr->args)
+                               {
+                                       e = (ExprState *) linitial(xmlExpr->args);
+                                       value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                                       if (isnull)
+                                               arg = NULL;
+                                       else
+                                               arg = DatumGetTextP(value);
+                               }
+                               else
+                               {
+                                       arg = NULL;
+                                       isnull = false;
+                               }
+
+                               return PointerGetDatum(xmlpi(xexpr->name, arg, isnull, isNull));
+                       }
+                       break;
+
+               case IS_XMLROOT:
+                       {
+                               ExprState  *e;
+                               xmltype    *data;
+                               text       *version;
+                               int                     standalone;
+
+                               /* arguments are known to be xml, text, int */
+                               Assert(list_length(xmlExpr->args) == 3);
+
+                               e = (ExprState *) linitial(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (isnull)
+                                       return (Datum) 0;
+                               data = DatumGetXmlP(value);
+
+                               e = (ExprState *) lsecond(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (isnull)
+                                       version = NULL;
+                               else
+                                       version = DatumGetTextP(value);
+
+                               e = (ExprState *) lthird(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               standalone = DatumGetInt32(value);
+
+                               *isNull = false;
+
+                               return PointerGetDatum(xmlroot(data,
+                                                                                          version,
+                                                                                          standalone));
+                       }
+                       break;
+
+               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;
+
+               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;
+       }
+
+       elog(ERROR, "unrecognized XML operation");
+       return (Datum) 0;
+}
+
 /* ----------------------------------------------------------------
  *             ExecEvalNullIf
  *
@@ -2282,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);
        }
 
@@ -2292,8 +3443,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
        argList = nullIfExpr->args;
 
        /* Need to prep callinfo structure */
-       MemSet(&fcinfo, 0, sizeof(fcinfo));
-       fcinfo.flinfo = &(nullIfExpr->func);
+       InitFunctionCallInfoData(fcinfo, &(nullIfExpr->func), 0, NULL, NULL);
        argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
        if (argDone != ExprSingleResult)
                ereport(ERROR,
@@ -2326,7 +3476,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalNullTest(GenericExprState *nstate,
+ExecEvalNullTest(NullTestState *nstate,
                                 ExprContext *econtext,
                                 bool *isNull,
                                 ExprDoneCond *isDone)
@@ -2339,28 +3489,77 @@ ExecEvalNullTest(GenericExprState *nstate,
        if (isDone && *isDone == ExprEndResult)
                return result;                  /* nothing to check */
 
-       switch (ntest->nulltesttype)
+       if (nstate->argisrow && !(*isNull))
        {
-               case IS_NULL:
-                       if (*isNull)
+               HeapTupleHeader tuple;
+               Oid                     tupType;
+               int32           tupTypmod;
+               TupleDesc       tupDesc;
+               HeapTupleData tmptup;
+               int                     att;
+
+               tuple = DatumGetHeapTupleHeader(result);
+
+               tupType = HeapTupleHeaderGetTypeId(tuple);
+               tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+
+               /* Lookup tupdesc if first time through or if type changes */
+               tupDesc = get_cached_rowtype(tupType, tupTypmod,
+                                                                        &nstate->argdesc, econtext);
+
+               /*
+                * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
+                */
+               tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+               tmptup.t_data = tuple;
+
+               for (att = 1; att <= tupDesc->natts; att++)
+               {
+                       /* ignore dropped columns */
+                       if (tupDesc->attrs[att - 1]->attisdropped)
+                               continue;
+                       if (heap_attisnull(&tmptup, att))
                        {
-                               *isNull = false;
-                               return BoolGetDatum(true);
+                               /* null field disproves IS NOT NULL */
+                               if (ntest->nulltesttype == IS_NOT_NULL)
+                                       return BoolGetDatum(false);
                        }
                        else
-                               return BoolGetDatum(false);
-               case IS_NOT_NULL:
-                       if (*isNull)
                        {
-                               *isNull = false;
-                               return BoolGetDatum(false);
+                               /* non-null field disproves IS NULL */
+                               if (ntest->nulltesttype == IS_NULL)
+                                       return BoolGetDatum(false);
                        }
-                       else
-                               return BoolGetDatum(true);
-               default:
-                       elog(ERROR, "unrecognized nulltesttype: %d",
-                                (int) ntest->nulltesttype);
-                       return (Datum) 0;       /* keep compiler quiet */
+               }
+
+               return BoolGetDatum(true);
+       }
+       else
+       {
+               /* Simple scalar-argument case, or a null rowtype datum */
+               switch (ntest->nulltesttype)
+               {
+                       case IS_NULL:
+                               if (*isNull)
+                               {
+                                       *isNull = false;
+                                       return BoolGetDatum(true);
+                               }
+                               else
+                                       return BoolGetDatum(false);
+                       case IS_NOT_NULL:
+                               if (*isNull)
+                               {
+                                       *isNull = false;
+                                       return BoolGetDatum(false);
+                               }
+                               else
+                                       return BoolGetDatum(true);
+                       default:
+                               elog(ERROR, "unrecognized nulltesttype: %d",
+                                        (int) ntest->nulltesttype);
+                               return (Datum) 0;               /* keep compiler quiet */
+               }
        }
 }
 
@@ -2479,8 +3678,8 @@ ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
                                if (*isNull)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_NOT_NULL_VIOLATION),
-                                                  errmsg("domain %s does not allow null values",
-                                                                 format_type_be(ctest->resulttype))));
+                                                        errmsg("domain %s does not allow null values",
+                                                                       format_type_be(ctest->resulttype))));
                                break;
                        case DOM_CONSTRAINT_CHECK:
                                {
@@ -2493,8 +3692,7 @@ ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
                                         * Set up value to be returned by CoerceToDomainValue
                                         * nodes. We must save and restore prior setting of
                                         * econtext's domainValue fields, in case this node is
-                                        * itself within a check expression for another
-                                        * domain.
+                                        * itself within a check expression for another domain.
                                         */
                                        save_datum = econtext->domainValue_datum;
                                        save_isNull = econtext->domainValue_isNull;
@@ -2557,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);
@@ -2577,27 +3777,34 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
        tupTypmod = HeapTupleHeaderGetTypMod(tuple);
 
        /* Lookup tupdesc if first time through or if type changes */
-       tupDesc = fstate->argdesc;
-       if (tupDesc == NULL ||
-               tupType != tupDesc->tdtypeid ||
-               tupTypmod != tupDesc->tdtypmod)
+       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)
        {
-               MemoryContext oldcontext;
-
-               tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
-               /* Copy the tupdesc into query storage for safety */
-               oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-               tupDesc = CreateTupleDescCopy(tupDesc);
-               if (fstate->argdesc)
-                       FreeTupleDesc(fstate->argdesc);
-               fstate->argdesc = tupDesc;
-               MemoryContextSwitchTo(oldcontext);
+               *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 columns.
+        * 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
+        * columns.
         */
        tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
        ItemPointerSetInvalid(&(tmptup.t_self));
@@ -2605,7 +3812,7 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
        tmptup.t_data = tuple;
 
        result = heap_getattr(&tmptup,
-                                                 fselect->fieldnum,
+                                                 fieldnum,
                                                  tupDesc,
                                                  isNull);
        return result;
@@ -2628,7 +3835,7 @@ ExecEvalFieldStore(FieldStoreState *fstate,
        Datum           tupDatum;
        TupleDesc       tupDesc;
        Datum      *values;
-       char       *nulls;
+       bool       *isnull;
        Datum           save_datum;
        bool            save_isNull;
        ListCell   *l1,
@@ -2639,32 +3846,19 @@ ExecEvalFieldStore(FieldStoreState *fstate,
        if (isDone && *isDone == ExprEndResult)
                return tupDatum;
 
-       /* Lookup tupdesc if first time through or if type changes */
-       tupDesc = fstate->argdesc;
-       if (tupDesc == NULL ||
-               fstore->resulttype != tupDesc->tdtypeid)
-       {
-               MemoryContext oldcontext;
-
-               tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1);
-               /* Copy the tupdesc into query storage for safety */
-               oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-               tupDesc = CreateTupleDescCopy(tupDesc);
-               if (fstate->argdesc)
-                       FreeTupleDesc(fstate->argdesc);
-               fstate->argdesc = tupDesc;
-               MemoryContextSwitchTo(oldcontext);
-       }
+       /* Lookup tupdesc if first time through or after rescan */
+       tupDesc = get_cached_rowtype(fstore->resulttype, -1,
+                                                                &fstate->argdesc, econtext);
 
        /* Allocate workspace */
        values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
-       nulls = (char *) palloc(tupDesc->natts * sizeof(char));
+       isnull = (bool *) palloc(tupDesc->natts * sizeof(bool));
 
        if (!*isNull)
        {
                /*
-                * heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader.
-                * We set all the fields in the struct just in case.
+                * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
+                * set all the fields in the struct just in case.
                 */
                HeapTupleHeader tuphdr;
                HeapTupleData tmptup;
@@ -2675,12 +3869,12 @@ ExecEvalFieldStore(FieldStoreState *fstate,
                tmptup.t_tableOid = InvalidOid;
                tmptup.t_data = tuphdr;
 
-               heap_deformtuple(&tmptup, tupDesc, values, nulls);
+               heap_deform_tuple(&tmptup, tupDesc, values, isnull);
        }
        else
        {
                /* Convert null input tuple into an all-nulls row */
-               memset(nulls, 'n', tupDesc->natts * sizeof(char));
+               memset(isnull, true, tupDesc->natts * sizeof(bool));
        }
 
        /* Result is never null */
@@ -2693,34 +3887,32 @@ ExecEvalFieldStore(FieldStoreState *fstate,
        {
                ExprState  *newval = (ExprState *) lfirst(l1);
                AttrNumber      fieldnum = lfirst_int(l2);
-               bool            eisnull;
 
                Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
 
                /*
-                * Use the CaseTestExpr mechanism to pass down the old value of
-                * the field being replaced; this is useful in case we have a
-                * nested field update situation.  It's safe to reuse the CASE
-                * mechanism because there cannot be a CASE between here and where
-                * the value would be needed.
+                * Use the CaseTestExpr mechanism to pass down the old value of the
+                * field being replaced; this is useful in case we have a nested field
+                * update situation.  It's safe to reuse the CASE mechanism because
+                * there cannot be a CASE between here and where the value would be
+                * needed.
                 */
                econtext->caseValue_datum = values[fieldnum - 1];
-               econtext->caseValue_isNull = (nulls[fieldnum - 1] == 'n');
+               econtext->caseValue_isNull = isnull[fieldnum - 1];
 
                values[fieldnum - 1] = ExecEvalExpr(newval,
                                                                                        econtext,
-                                                                                       &eisnull,
+                                                                                       &isnull[fieldnum - 1],
                                                                                        NULL);
-               nulls[fieldnum - 1] = eisnull ? 'n' : ' ';
        }
 
        econtext->caseValue_datum = save_datum;
        econtext->caseValue_isNull = save_isNull;
 
-       tuple = heap_formtuple(tupDesc, values, nulls);
+       tuple = heap_form_tuple(tupDesc, values, isnull);
 
        pfree(values);
-       pfree(nulls);
+       pfree(isnull);
 
        return HeapTupleGetDatum(tuple);
 }
@@ -2739,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
@@ -2774,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
@@ -2840,27 +4159,66 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                        aggstate->aggs = lcons(astate, aggstate->aggs);
                                        naggs = ++aggstate->numaggs;
 
-                                       astate->target = ExecInitExpr(aggref->target, parent);
+                                       astate->args = (List *) ExecInitExpr((Expr *) aggref->args,
+                                                                                                                parent);
 
                                        /*
-                                        * Complain if the aggregate's argument contains any
+                                        * Complain if the aggregate's arguments contain any
                                         * aggregates; nested agg functions are semantically
-                                        * nonsensical.  (This should have been caught
-                                        * earlier, but we defend against it here anyway.)
+                                        * nonsensical.  (This should have been caught earlier,
+                                        * but we defend against it here anyway.)
                                         */
                                        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;
@@ -2960,32 +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->exprs = (List *)
-                                       ExecInitExpr((Expr *) subplan->exprs, 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;
@@ -3019,65 +4378,53 @@ 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;
                                ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
-                               int             i;
-                               int             n;
 
                                cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
                                cstate->arg = ExecInitExpr(convert->arg, parent);
-                               /* save copies of needed tuple descriptors */
-                               cstate->indesc = lookup_rowtype_tupdesc(exprType((Node *) convert->arg), -1);
-                               cstate->indesc = CreateTupleDescCopy(cstate->indesc);
-                               cstate->outdesc = lookup_rowtype_tupdesc(convert->resulttype, -1);
-                               cstate->outdesc = CreateTupleDescCopy(cstate->outdesc);
-                               /* prepare map from old to new attribute numbers */
-                               n = cstate->outdesc->natts;
-                               cstate->attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
-                               for (i = 0; i < n; i++)
-                               {
-                                       Form_pg_attribute att = cstate->outdesc->attrs[i];
-                                       char       *attname;
-                                       Oid                     atttypid;
-                                       int32           atttypmod;
-                                       int                     j;
-
-                                       if (att->attisdropped)
-                                               continue;       /* attrMap[i] is already 0 */
-                                       attname = NameStr(att->attname);
-                                       atttypid = att->atttypid;
-                                       atttypmod = att->atttypmod;
-                                       for (j = 0; j < cstate->indesc->natts; j++)
-                                       {
-                                               att = cstate->indesc->attrs[j];
-                                               if (att->attisdropped)
-                                                       continue;
-                                               if (strcmp(attname, NameStr(att->attname)) == 0)
-                                               {
-                                                       /* Found it, check type */
-                                                       if (atttypid != att->atttypid || atttypmod != att->atttypmod)
-                                                               elog(ERROR, "attribute \"%s\" of type %s does not match corresponding attribute of type %s",
-                                                                        attname,
-                                                                        format_type_be(cstate->indesc->tdtypeid),
-                                                                        format_type_be(cstate->outdesc->tdtypeid));
-                                                       cstate->attrMap[i] = (AttrNumber) (j + 1);
-                                                       break;
-                                               }
-                                       }
-                                       if (cstate->attrMap[i] == 0)
-                                               elog(ERROR, "attribute \"%s\" of type %s does not exist",
-                                                        attname,
-                                                        format_type_be(cstate->indesc->tdtypeid));
-                               }
-                               /* preallocate workspace for Datum arrays */
-                               n = cstate->indesc->natts + 1;  /* +1 for NULL */
-                               cstate->invalues = (Datum *) palloc(n * sizeof(Datum));
-                               cstate->innulls = (char *) palloc(n * sizeof(char));
-                               n = cstate->outdesc->natts;
-                               cstate->outvalues = (Datum *) palloc(n * sizeof(Datum));
-                               cstate->outnulls = (char *) palloc(n * sizeof(char));
                                state = (ExprState *) cstate;
                        }
                        break;
@@ -3147,13 +4494,13 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                {
                                        /* generic record, use runtime type assignment */
                                        rstate->tupdesc = ExecTypeFromExprList(rowexpr->args);
-                                       rstate->tupdesc = BlessTupleDesc(rstate->tupdesc);
+                                       BlessTupleDesc(rstate->tupdesc);
+                                       /* we won't need to redo this at runtime */
                                }
                                else
                                {
                                        /* it's been cast to a named type, use that */
-                                       rstate->tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
-                                       rstate->tupdesc = CreateTupleDescCopy(rstate->tupdesc);
+                                       rstate->tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
                                }
                                /* Set up evaluation, skipping any deleted columns */
                                Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts);
@@ -3167,26 +4514,26 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                        if (!attrs[i]->attisdropped)
                                        {
                                                /*
-                                                * Guard against ALTER COLUMN TYPE on rowtype
-                                                * since the RowExpr was created.  XXX should we
-                                                * check typmod too?  Not sure we can be sure
-                                                * it'll be the same.
+                                                * Guard against ALTER COLUMN TYPE on rowtype since
+                                                * the RowExpr was created.  XXX should we check
+                                                * typmod too?  Not sure we can be sure it'll be the
+                                                * same.
                                                 */
                                                if (exprType((Node *) e) != attrs[i]->atttypid)
                                                        ereport(ERROR,
                                                                        (errcode(ERRCODE_DATATYPE_MISMATCH),
                                                                         errmsg("ROW() column has type %s instead of type %s",
-                                                                       format_type_be(exprType((Node *) e)),
-                                                                  format_type_be(attrs[i]->atttypid))));
+                                                                               format_type_be(exprType((Node *) e)),
+                                                                          format_type_be(attrs[i]->atttypid))));
                                        }
                                        else
                                        {
                                                /*
-                                                * Ignore original expression and insert a NULL.
-                                                * We don't really care what type of NULL it is,
-                                                * so always make an int4 NULL.
+                                                * Ignore original expression and insert a NULL. We
+                                                * 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);
@@ -3196,6 +4543,72 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) rstate;
                        }
                        break;
+               case T_RowCompareExpr:
+                       {
+                               RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+                               RowCompareExprState *rstate = makeNode(RowCompareExprState);
+                               int                     nopers = list_length(rcexpr->opnos);
+                               List       *outlist;
+                               ListCell   *l;
+                               ListCell   *l2;
+                               int                     i;
+
+                               rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRowCompare;
+                               Assert(list_length(rcexpr->largs) == nopers);
+                               outlist = NIL;
+                               foreach(l, rcexpr->largs)
+                               {
+                                       Expr       *e = (Expr *) lfirst(l);
+                                       ExprState  *estate;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       outlist = lappend(outlist, estate);
+                               }
+                               rstate->largs = outlist;
+                               Assert(list_length(rcexpr->rargs) == nopers);
+                               outlist = NIL;
+                               foreach(l, rcexpr->rargs)
+                               {
+                                       Expr       *e = (Expr *) lfirst(l);
+                                       ExprState  *estate;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       outlist = lappend(outlist, estate);
+                               }
+                               rstate->rargs = outlist;
+                               Assert(list_length(rcexpr->opfamilies) == nopers);
+                               rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo));
+                               i = 0;
+                               forboth(l, rcexpr->opnos, l2, rcexpr->opfamilies)
+                               {
+                                       Oid                     opno = lfirst_oid(l);
+                                       Oid                     opfamily = lfirst_oid(l2);
+                                       int                     strategy;
+                                       Oid                     lefttype;
+                                       Oid                     righttype;
+                                       Oid                     proc;
+
+                                       get_op_opfamily_properties(opno, opfamily,
+                                                                                          &strategy,
+                                                                                          &lefttype,
+                                                                                          &righttype);
+                                       proc = get_opfamily_proc(opfamily,
+                                                                                        lefttype,
+                                                                                        righttype,
+                                                                                        BTORDER_PROC);
+
+                                       /*
+                                        * If we enforced permissions checks on index support
+                                        * functions, we'd need to make a check here.  But the
+                                        * index support machinery doesn't do that, and neither
+                                        * does this code.
+                                        */
+                                       fmgr_info(proc, &(rstate->funcs[i]));
+                                       i++;
+                               }
+                               state = (ExprState *) rstate;
+                       }
+                       break;
                case T_CoalesceExpr:
                        {
                                CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
@@ -3216,6 +4629,76 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) cstate;
                        }
                        break;
+               case T_MinMaxExpr:
+                       {
+                               MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
+                               MinMaxExprState *mstate = makeNode(MinMaxExprState);
+                               List       *outlist = NIL;
+                               ListCell   *l;
+                               TypeCacheEntry *typentry;
+
+                               mstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalMinMax;
+                               foreach(l, minmaxexpr->args)
+                               {
+                                       Expr       *e = (Expr *) lfirst(l);
+                                       ExprState  *estate;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       outlist = lappend(outlist, estate);
+                               }
+                               mstate->args = outlist;
+                               /* Look up the btree comparison function for the datatype */
+                               typentry = lookup_type_cache(minmaxexpr->minmaxtype,
+                                                                                        TYPECACHE_CMP_PROC);
+                               if (!OidIsValid(typentry->cmp_proc))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                                        errmsg("could not identify a comparison function for type %s",
+                                                                       format_type_be(minmaxexpr->minmaxtype))));
+
+                               /*
+                                * If we enforced permissions checks on index support
+                                * functions, we'd need to make a check here.  But the index
+                                * support machinery doesn't do that, and neither does this
+                                * code.
+                                */
+                               fmgr_info(typentry->cmp_proc, &(mstate->cfunc));
+                               state = (ExprState *) mstate;
+                       }
+                       break;
+               case T_XmlExpr:
+                       {
+                               XmlExpr    *xexpr = (XmlExpr *) node;
+                               XmlExprState *xstate = makeNode(XmlExprState);
+                               List       *outlist;
+                               ListCell   *arg;
+
+                               xstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalXml;
+                               outlist = NIL;
+                               foreach(arg, xexpr->named_args)
+                               {
+                                       Expr       *e = (Expr *) lfirst(arg);
+                                       ExprState  *estate;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       outlist = lappend(outlist, estate);
+                               }
+                               xstate->named_args = outlist;
+
+                               outlist = NIL;
+                               foreach(arg, xexpr->args)
+                               {
+                                       Expr       *e = (Expr *) lfirst(arg);
+                                       ExprState  *estate;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       outlist = lappend(outlist, estate);
+                               }
+                               xstate->args = outlist;
+
+                               state = (ExprState *) xstate;
+                       }
+                       break;
                case T_NullIfExpr:
                        {
                                NullIfExpr *nullifexpr = (NullIfExpr *) node;
@@ -3231,11 +4714,13 @@ ExecInitExpr(Expr *node, PlanState *parent)
                case T_NullTest:
                        {
                                NullTest   *ntest = (NullTest *) node;
-                               GenericExprState *gstate = makeNode(GenericExprState);
+                               NullTestState *nstate = makeNode(NullTestState);
 
-                               gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
-                               gstate->arg = ExecInitExpr(ntest->arg, parent);
-                               state = (ExprState *) gstate;
+                               nstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
+                               nstate->arg = ExecInitExpr(ntest->arg, parent);
+                               nstate->argisrow = type_is_rowtype(exprType((Node *) ntest->arg));
+                               nstate->argdesc = NULL;
+                               state = (ExprState *) nstate;
                        }
                        break;
                case T_BooleanTest:
@@ -3259,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;
@@ -3296,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->exprs = (List *) ExecInitExpr((Expr *) node->exprs, 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)
@@ -3338,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);
@@ -3405,16 +4869,16 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
        oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
 
        /*
-        * Evaluate the qual conditions one at a time.  If we find a FALSE
-        * result, we can stop evaluating and return FALSE --- the AND result
-        * must be FALSE.  Also, if we find a NULL result when resultForNull
-        * is FALSE, we can stop and return FALSE --- the AND result must be
-        * FALSE or NULL in that case, and the caller doesn't care which.
+        * Evaluate the qual conditions one at a time.  If we find a FALSE result,
+        * we can stop evaluating and return FALSE --- the AND result must be
+        * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
+        * can stop and return FALSE --- the AND result must be FALSE or NULL in
+        * that case, and the caller doesn't care which.
         *
-        * If we get to the end of the list, we can return TRUE.  This will
-        * happen when the AND result is indeed TRUE, or when the AND result
-        * is NULL (one or more NULL subresult, with all the rest TRUE) and
-        * the caller has specified resultForNull = TRUE.
+        * If we get to the end of the list, we can return TRUE.  This will happen
+        * when the AND result is indeed TRUE, or when the AND result is NULL (one
+        * or more NULL subresult, with all the rest TRUE) and the caller has
+        * specified resultForNull = TRUE.
         */
        result = true;
 
@@ -3473,80 +4937,59 @@ ExecCleanTargetListLength(List *targetlist)
                TargetEntry *curTle = (TargetEntry *) lfirst(tl);
 
                Assert(IsA(curTle, TargetEntry));
-               if (!curTle->resdom->resjunk)
+               if (!curTle->resjunk)
                        len++;
        }
        return len;
 }
 
-/* ----------------------------------------------------------------
- *             ExecTargetList
- *
+/*
+ * ExecTargetList
  *             Evaluates a targetlist with respect to the given
- *             expression context and returns a tuple.
+ *             expression context.  Returns TRUE if we were able to create
+ *             a result, FALSE if we have exhausted a set-valued expression.
  *
- * The caller must pass workspace for the values and nulls arrays
- * as well as the itemIsDone array.  This convention saves palloc'ing
- * workspace on each call, and some callers may find it useful to examine
- * the values array directly.
+ * Results are stored into the passed values and isnull arrays.
+ * The caller must provide an itemIsDone array that persists across calls.
  *
  * As with ExecEvalExpr, the caller should pass isDone = NULL if not
  * 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 HeapTuple
+static bool
 ExecTargetList(List *targetlist,
-                          TupleDesc targettype,
                           ExprContext *econtext,
                           Datum *values,
-                          char *nulls,
+                          bool *isnull,
                           ExprDoneCond *itemIsDone,
                           ExprDoneCond *isDone)
 {
        MemoryContext oldContext;
        ListCell   *tl;
-       bool            isNull;
        bool            haveDoneSets;
 
-       /*
-        * debugging stuff
-        */
-       EV_printf("ExecTargetList: tl is ");
-       EV_nodeDisplay(targetlist);
-       EV_printf("\n");
-
        /*
         * Run in short-lived per-tuple context while computing expressions.
         */
        oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
 
-       /*
-        * There used to be some klugy and demonstrably broken code here that
-        * special-cased the situation where targetlist == NIL.  Now we just
-        * fall through and return an empty-but-valid tuple.
-        */
-
        /*
         * 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)
        {
                GenericExprState *gstate = (GenericExprState *) lfirst(tl);
                TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-               AttrNumber      resind = tle->resdom->resno - 1;
+               AttrNumber      resind = tle->resno - 1;
 
                values[resind] = ExecEvalExpr(gstate->arg,
                                                                          econtext,
-                                                                         &isNull,
+                                                                         &isnull[resind],
                                                                          &itemIsDone[resind]);
-               nulls[resind] = isNull ? 'n' : ' ';
 
                if (itemIsDone[resind] != ExprSingleResult)
                {
@@ -3576,38 +5019,36 @@ ExecTargetList(List *targetlist,
                if (*isDone == ExprSingleResult)
                {
                        /*
-                        * all sets are done, so report that tlist expansion is
-                        * complete.
+                        * all sets are done, so report that tlist expansion is complete.
                         */
                        *isDone = ExprEndResult;
                        MemoryContextSwitchTo(oldContext);
-                       return NULL;
+                       return false;
                }
                else
                {
                        /*
-                        * We have some done and some undone sets.      Restart the done
-                        * ones so that we can deliver a tuple (if possible).
+                        * We have some done and some undone sets.      Restart the done ones
+                        * so that we can deliver a tuple (if possible).
                         */
                        foreach(tl, targetlist)
                        {
                                GenericExprState *gstate = (GenericExprState *) lfirst(tl);
                                TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-                               AttrNumber      resind = tle->resdom->resno - 1;
+                               AttrNumber      resind = tle->resno - 1;
 
                                if (itemIsDone[resind] == ExprEndResult)
                                {
                                        values[resind] = ExecEvalExpr(gstate->arg,
                                                                                                  econtext,
-                                                                                                 &isNull,
+                                                                                                 &isnull[resind],
                                                                                                  &itemIsDone[resind]);
-                                       nulls[resind] = isNull ? 'n' : ' ';
 
                                        if (itemIsDone[resind] == ExprEndResult)
                                        {
                                                /*
-                                                * Oh dear, this item is returning an empty set.
-                                                * Guess we can't make a tuple after all.
+                                                * Oh dear, this item is returning an empty set. Guess
+                                                * we can't make a tuple after all.
                                                 */
                                                *isDone = ExprEndResult;
                                                break;
@@ -3616,9 +5057,9 @@ ExecTargetList(List *targetlist,
                        }
 
                        /*
-                        * If we cannot make a tuple because some sets are empty, we
-                        * still have to cycle the nonempty sets to completion, else
-                        * resources will not be released from subplans etc.
+                        * If we cannot make a tuple because some sets are empty, we still
+                        * have to cycle the nonempty sets to completion, else resources
+                        * will not be released from subplans etc.
                         *
                         * XXX is that still necessary?
                         */
@@ -3628,79 +5069,150 @@ ExecTargetList(List *targetlist,
                                {
                                        GenericExprState *gstate = (GenericExprState *) lfirst(tl);
                                        TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-                                       AttrNumber      resind = tle->resdom->resno - 1;
+                                       AttrNumber      resind = tle->resno - 1;
 
                                        while (itemIsDone[resind] == ExprMultipleResult)
                                        {
-                                               (void) ExecEvalExpr(gstate->arg,
-                                                                                       econtext,
-                                                                                       &isNull,
-                                                                                       &itemIsDone[resind]);
+                                               values[resind] = ExecEvalExpr(gstate->arg,
+                                                                                                         econtext,
+                                                                                                         &isnull[resind],
+                                                                                                         &itemIsDone[resind]);
                                        }
                                }
 
                                MemoryContextSwitchTo(oldContext);
-                               return NULL;
+                               return false;
                        }
                }
        }
 
-       /*
-        * form the new result tuple (in the caller's memory context!)
-        */
+       /* Report success */
        MemoryContextSwitchTo(oldContext);
 
-       return heap_formtuple(targettype, values, nulls);
+       return true;
 }
 
-/* ----------------------------------------------------------------
- *             ExecProject
+/*
+ * ExecProject
  *
  *             projects a tuple based on projection info and stores
- *             it in the specified tuple table slot.
+ *             it in the previously specified tuple table slot.
  *
- *             Note: someday soon the executor can be extended to eliminate
- *                       redundant projections by storing pointers to datums
- *                       in the tuple table and then passing these around when
- *                       possible.  this should make things much quicker.
- *                       -cim 6/3/91
- * ----------------------------------------------------------------
+ *             Note: the result is always a virtual tuple; therefore it
+ *             may reference the contents of the exprContext's scan tuples
+ *             and/or temporary results constructed in the exprContext.
+ *             If the caller wishes the result to be valid longer than that
+ *             data will be valid, he must call ExecMaterializeSlot on the
+ *             result slot.
  */
 TupleTableSlot *
 ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
 {
        TupleTableSlot *slot;
-       TupleDesc       tupType;
-       HeapTuple       newTuple;
+       ExprContext *econtext;
+       int                     numSimpleVars;
 
        /*
         * sanity checks
         */
-       if (projInfo == NULL)
-               return NULL;
+       Assert(projInfo != NULL);
 
        /*
         * get the projection info we want
         */
        slot = projInfo->pi_slot;
-       tupType = slot->ttc_tupleDescriptor;
+       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
+        * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+        * return the slot as-is if we decide no rows can be projected.)
+        */
+       ExecClearTuple(slot);
+
+       /*
+        * 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 ...
+        */
+       numSimpleVars = projInfo->pi_numSimpleVars;
+       if (numSimpleVars > 0)
+       {
+               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];
+                       }
+               }
+       }
 
        /*
-        * form a new result tuple (if possible --- result can be NULL)
+        * 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.
         */
-       newTuple = ExecTargetList(projInfo->pi_targetlist,
-                                                         tupType,
-                                                         projInfo->pi_exprContext,
-                                                         projInfo->pi_tupValues,
-                                                         projInfo->pi_tupNulls,
-                                                         projInfo->pi_itemIsDone,
-                                                         isDone);
+       if (projInfo->pi_targetlist)
+       {
+               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 */
+       }
 
        /*
-        * store the tuple in the projection slot and return the slot.
+        * Successfully formed a result row.  Mark the result slot as containing a
+        * valid virtual tuple.
         */
-       return ExecStoreTuple(newTuple,         /* tuple to store */
-                                                 slot, /* slot to store in */
-                                                 InvalidBuffer,                /* tuple has no buffer */
-                                                 true);
+       return ExecStoreVirtualTuple(slot);
 }