]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/execQual.c
Get rid of the separate EState for subplans, and just let them share the
[postgresql] / src / backend / executor / execQual.c
index e4362c38f58437e90aa13057d528b119fccfc967..234441c0f6c80cce5e2fccf430062b9647197d58 100644 (file)
@@ -3,12 +3,12 @@
  * execQual.c
  *       Routines to evaluate qualification and targetlist expressions
  *
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.164 2004/06/09 19:08:14 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.214 2007/02/27 01:11:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #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 "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 */
@@ -58,52 +60,71 @@ static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
                                 ExprContext *econtext,
                                 bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalAggref(AggrefExprState *aggref,
-                                                       ExprContext *econtext,
-                                                       bool *isNull, ExprDoneCond *isDone);
+                          ExprContext *econtext,
+                          bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
-                                                bool *isNull, ExprDoneCond *isDone);
+                       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 ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
-                                                bool *isNull, ExprDoneCond *isDone);
+                         bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
-                                                  bool *isNull, ExprDoneCond *isDone);
+                         bool *isNull, ExprDoneCond *isDone);
+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 Datum ExecMakeFunctionResultNoSets(FuncExprState *fcache,
-                                                                                 ExprContext *econtext,
-                                                                                 bool *isNull, ExprDoneCond *isDone);
+                                                        ExprContext *econtext,
+                                                        bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
                         bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
                         bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
-                                                         bool *isNull, ExprDoneCond *isDone);
+                                bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
-                                                                  ExprContext *econtext,
-                                                                  bool *isNull, ExprDoneCond *isDone);
+                                         ExprContext *econtext,
+                                         bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
-                                                bool *isNull, ExprDoneCond *isDone);
+                       bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
-                                               bool *isNull, ExprDoneCond *isDone);
+                  bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
-                                                bool *isNull, ExprDoneCond *isDone);
+                       bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
+                                          ExprContext *econtext,
+                                          bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
                         bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
-                                                                 ExprContext *econtext,
-                                                                 bool *isNull, ExprDoneCond *isDone);
+                                        ExprContext *econtext,
+                                        bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalArray(ArrayExprState *astate,
-                                                  ExprContext *econtext,
-                                                  bool *isNull, ExprDoneCond *isDone);
+                         ExprContext *econtext,
+                         bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalRow(RowExprState *rstate,
-                                                ExprContext *econtext,
-                                                bool *isNull, ExprDoneCond *isDone);
+                       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);
+                                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,
+                          ExprContext *econtext,
+                          bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalNullTest(NullTestState *nstate,
                                 ExprContext *econtext,
                                 bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalBooleanTest(GenericExprState *bstate,
@@ -113,14 +134,14 @@ static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate,
                                           ExprContext *econtext,
                                           bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
-                                                                                ExprContext *econtext,
-                                                                                bool *isNull, ExprDoneCond *isDone);
+                                                       ExprContext *econtext,
+                                                       bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
                                        ExprContext *econtext,
                                        bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalFieldStore(FieldStoreState *fstate,
-                                       ExprContext *econtext,
-                                       bool *isNull, ExprDoneCond *isDone);
+                                  ExprContext *econtext,
+                                  bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalRelabelType(GenericExprState *exprstate,
                                        ExprContext *econtext,
                                        bool *isNull, ExprDoneCond *isDone);
@@ -144,7 +165,7 @@ static Datum ExecEvalRelabelType(GenericExprState *exprstate,
  *
  * Note: for notational simplicity we declare these functions as taking the
  * specific type of ExprState that they work on.  This requires casting when
- * assigning the function pointer in ExecInitExpr.  Be careful that the
+ * assigning the function pointer in ExecInitExpr.     Be careful that the
  * function signature is declared correctly, because the cast suppresses
  * automatic checking!
  *
@@ -192,16 +213,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
@@ -235,13 +248,13 @@ 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)
        {
                if (isDone && *isDone == ExprEndResult)
-                       return (Datum) NULL; /* end of set result */
+                       return (Datum) NULL;    /* end of set result */
                if (!isAssignment)
                        return (Datum) NULL;
        }
@@ -260,15 +273,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;
                }
        }
 
@@ -288,19 +301,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 */
@@ -320,9 +329,9 @@ 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
+                * 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,
@@ -331,29 +340,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;
                }
 
@@ -361,20 +364,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);
        }
 
@@ -392,8 +395,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                                                                          astate->refattrlength,
                                                                          astate->refelemlength,
                                                                          astate->refelembyval,
-                                                                         astate->refelemalign,
-                                                                         isNull);
+                                                                         astate->refelemalign);
                return PointerGetDatum(resultArray);
        }
 }
@@ -425,28 +427,30 @@ ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext,
  *
  *             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 or ExecEvalWholeRowVar after making one-time checks.
+ * ----------------------------------------------------------------
+ */
 static Datum
 ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
                        bool *isNull, ExprDoneCond *isDone)
 {
        Var                *variable = (Var *) exprstate->expr;
-       Datum           result;
        TupleTableSlot *slot;
        AttrNumber      attnum;
-       HeapTuple       heapTuple;
-       TupleDesc       tuple_type;
 
        if (isDone)
                *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 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).
         */
        attnum = variable->varattno;
 
@@ -468,54 +472,222 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
                        break;
        }
 
-       /*
-        * extract tuple information from the slot
-        */
-       heapTuple = slot->val;
-       tuple_type = slot->ttc_tupleDescriptor;
-
-       /*
-        * Some checks that are only applied for user attribute numbers
-        * (bogus system attnums will be caught inside heap_getattr).
-        */
-       if (attnum > 0)
+       if (attnum != InvalidAttrNumber)
        {
                /*
-                * This assert checks that the attnum is valid.
-                */
-               Assert(attnum <= tuple_type->natts &&
-                          tuple_type->attrs[attnum - 1] != NULL);
-
-               /*
-                * If the attribute's column has been dropped, we force a NULL result.
-                * This case should not happen in normal use, but it could happen if
-                * we are executing a plan cached before the column was dropped.
+                * 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.
                 */
-               if (tuple_type->attrs[attnum - 1]->attisdropped)
+               if (attnum > 0)
                {
-                       *isNull = true;
-                       return (Datum) 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);
+               TupleDesc       slot_tupdesc = slot->tts_tupleDescriptor;
+
+               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.
+                        */
+                       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("Table row contains %d attributes, but query expects %d.",
+                                                                  slot_tupdesc->natts, var_tupdesc->natts)));
+
+                       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 */
+               exprstate->evalfunc = ExecEvalWholeRowVar;
+
+               /* Fetch the value */
+               return ExecEvalWholeRowVar(exprstate, econtext, isNull, isDone);
        }
+}
 
-       result = heap_getattr(heapTuple,        /* tuple containing attribute */
-                                                 attnum,               /* attribute number of desired
-                                                                                * attribute */
-                                                 tuple_type,   /* tuple descriptor of tuple */
-                                                 isNull);              /* return: is attribute null? */
+/* ----------------------------------------------------------------
+ *             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;
 
-       return result;
+       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;
+       }
+
+       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);
 }
 
 /* ----------------------------------------------------------------
@@ -548,11 +720,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
@@ -560,18 +727,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;
 
@@ -589,56 +754,27 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
        else
        {
                /*
-                * All other parameter types must be sought in
-                * ecxt_param_list_info. NOTE: The last entry in the param array
-                * is always an entry with kind == PARAM_INVALID.
+                * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
                 */
-               ParamListInfo paramList = econtext->ecxt_param_list_info;
-               char       *thisParamName = expression->paramname;
-               bool            matchFound = false;
+               ParamListInfo paramInfo = econtext->ecxt_param_list_info;
 
-               if (paramList != NULL)
+               Assert(expression->paramkind == PARAM_EXTERN);
+               if (paramInfo &&
+                       thisParamId > 0 && thisParamId <= paramInfo->numParams)
                {
-                       while (paramList->kind != PARAM_INVALID && !matchFound)
-                       {
-                               if (thisParamKind == paramList->kind)
-                               {
-                                       switch (thisParamKind)
-                                       {
-                                               case PARAM_NAMED:
-                                                       if (strcmp(paramList->name, thisParamName) == 0)
-                                                               matchFound = true;
-                                                       break;
-                                               case PARAM_NUM:
-                                                       if (paramList->id == thisParamId)
-                                                               matchFound = true;
-                                                       break;
-                                               default:
-                                                       elog(ERROR, "unrecognized paramkind: %d",
-                                                                thisParamKind);
-                                       }
-                               }
-                               if (!matchFound)
-                                       paramList++;
-                       }                                       /* while */
-               }                                               /* if */
+                       ParamExternData *prm = &paramInfo->params[thisParamId - 1];
 
-               if (!matchFound)
-               {
-                       if (thisParamKind == PARAM_NAMED)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                                errmsg("no value found for parameter \"%s\"",
-                                                               thisParamName)));
-                       else
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                                errmsg("no value found for parameter %d",
-                                                               thisParamId)));
+                       if (OidIsValid(prm->ptype))
+                       {
+                               Assert(prm->ptype == expression->paramtype);
+                               *isNull = prm->isnull;
+                               return prm->value;
+                       }
                }
-
-               *isNull = paramList->isnull;
-               return paramList->value;
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("no value found for parameter %d", thisParamId)));
+               return (Datum) 0;               /* keep compiler quiet */
        }
 }
 
@@ -688,9 +824,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));
@@ -701,6 +837,9 @@ GetAttributeByNum(HeapTupleHeader tuple,
                                                  attrno,
                                                  tupDesc,
                                                  isNull);
+
+       ReleaseTupleDesc(tupDesc);
+
        return result;
 }
 
@@ -746,9 +885,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));
@@ -759,6 +898,9 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
                                                  attrno,
                                                  tupDesc,
                                                  isNull);
+
+       ReleaseTupleDesc(tupDesc);
+
        return result;
 }
 
@@ -775,9 +917,17 @@ 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("cannot pass more than %d arguments to a function",
+                                               FUNC_MAX_ARGS)));
 
        /* Set up the primary fmgr lookup information */
        fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
@@ -804,6 +954,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.
  */
@@ -832,10 +1037,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,
@@ -874,17 +1078,15 @@ ExecMakeFunctionResult(FuncExprState *fcache,
        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.
+        * 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 (!fcache->setArgsValid)
        {
                /* Need to prep callinfo structure */
-               MemSet(&fcinfo, 0, sizeof(fcinfo));
-               fcinfo.flinfo = &(fcache->func);
+               InitFunctionCallInfoData(fcinfo, &(fcache->func), 0, NULL, NULL);
                argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext);
                if (argDone == ExprEndResult)
                {
@@ -910,8 +1112,7 @@ ExecMakeFunctionResult(FuncExprState *fcache,
        }
 
        /*
-        * If function returns set, prepare a resultinfo node for
-        * communication
+        * If function returns set, prepare a resultinfo node for communication
         */
        if (fcache->func.fn_retset)
        {
@@ -927,14 +1128,14 @@ ExecMakeFunctionResult(FuncExprState *fcache,
        }
 
        /*
-        * now return the value gotten by calling the function manager,
-        * passing the function the evaluated parameter values.
+        * now return the value gotten by calling the function manager, passing
+        * the function 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,
@@ -942,18 +1143,18 @@ 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.
+                * 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;
 
@@ -988,10 +1189,10 @@ ExecMakeFunctionResult(FuncExprState *fcache,
                        {
                                /*
                                 * Got a result from current argument.  If function itself
-                                * returns set, save the current argument values to re-use
-                                * on the next call.
+                                * returns set, save the current argument values to re-use on
+                                * the next call.
                                 */
-                               if (fcache->func.fn_retset)
+                               if (fcache->func.fn_retset && *isDone == ExprMultipleResult)
                                {
                                        memcpy(&fcache->setArgs, &fcinfo, sizeof(fcinfo));
                                        fcache->setHasSetArg = hasSetArg;
@@ -1010,7 +1211,8 @@ ExecMakeFunctionResult(FuncExprState *fcache,
                                 * Make sure we say we are returning a set, even if the
                                 * function itself doesn't return sets.
                                 */
-                               *isDone = ExprMultipleResult;
+                               if (hasSetArg)
+                                       *isDone = ExprMultipleResult;
                                break;
                        }
 
@@ -1031,8 +1233,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.
                         */
                }
        }
@@ -1042,9 +1244,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;
 
@@ -1097,32 +1299,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)
        {
@@ -1135,7 +1329,7 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
                        }
                }
        }
-       /* fcinfo.isnull = false; */            /* handled by MemSet */
+       /* fcinfo.isnull = false; */    /* handled by InitFunctionCallInfoData */
        result = FunctionCallInvoke(&fcinfo);
        *isNull = fcinfo.isnull;
 
@@ -1147,7 +1341,8 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
  *             ExecMakeTableFunctionResult
  *
  * Evaluate a table function, producing a materialized result in a Tuplestore
- * object.     (If function returns an empty set, we just return NULL instead.)
+ * object.     *returnDesc is set to the tupledesc actually returned by the
+ * function, or NULL if it didn't provide one.
  */
 Tuplestorestate *
 ExecMakeTableFunctionResult(ExprState *funcexpr,
@@ -1159,6 +1354,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
        TupleDesc       tupdesc = NULL;
        Oid                     funcrettype;
        bool            returnsTuple;
+       bool            returnsSet = false;
        FunctionCallInfoData fcinfo;
        ReturnSetInfo rsinfo;
        HeapTupleData tmptup;
@@ -1167,15 +1363,38 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
        bool            direct_function_call;
        bool            first_time = true;
 
+       callerContext = CurrentMemoryContext;
+
+       funcrettype = exprType((Node *) funcexpr->expr);
+
+       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.
+        */
+       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.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))
@@ -1197,16 +1416,16 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 
                        init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory);
                }
+               returnsSet = fcache->func.fn_retset;
 
                /*
                 * 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?
                 */
-               MemSet(&fcinfo, 0, sizeof(fcinfo));
                fcinfo.flinfo = &(fcache->func);
                argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext);
                /* We don't allow sets in the arguments of the table function */
@@ -1217,7 +1436,8 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 
                /*
                 * If function is strict, and there are any NULL arguments, skip
-                * calling the function and return NULL (actually an empty set).
+                * calling the function and act like it returned NULL (or an empty
+                * set, in the returns-set case).
                 */
                if (fcache->func.fn_strict)
                {
@@ -1226,10 +1446,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                        for (i = 0; i < fcinfo.nargs; i++)
                        {
                                if (fcinfo.argnull[i])
-                               {
-                                       *returnDesc = NULL;
-                                       return NULL;
-                               }
+                                       goto no_function_result;
                        }
                }
        }
@@ -1239,33 +1456,10 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                direct_function_call = false;
        }
 
-       funcrettype = exprType((Node *) funcexpr->expr);
-
-       returnsTuple = (funcrettype == RECORDOID ||
-                                       get_typtype(funcrettype) == 'c');
-
-       /*
-        * 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.
-        */
-       fcinfo.resultinfo = (Node *) &rsinfo;
-       rsinfo.type = T_ReturnSetInfo;
-       rsinfo.econtext = econtext;
-       rsinfo.expectedDesc = expectedDesc;
-       rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
-       rsinfo.returnMode = SFRM_ValuePerCall;
-       /* isDone is filled below */
-       rsinfo.setResult = NULL;
-       rsinfo.setDesc = NULL;
-
        /*
-        * Switch to short-lived context for calling the function or
-        * expression.
+        * Switch to short-lived context for calling the function or expression.
         */
-       callerContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+       MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
 
        /*
         * Loop to handle the ValuePerCall protocol (which is also the same
@@ -1276,10 +1470,12 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                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);
 
@@ -1301,27 +1497,29 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                {
                        /*
                         * Check for end of result set.
-                        *
-                        * Note: if function returns an empty set, we don't build a
-                        * tupdesc or tuplestore (since we can't get a tupdesc in the
-                        * function-returning-tuple case)
                         */
                        if (rsinfo.isDone == ExprEndResult)
                                break;
 
                        /*
-                        * Can't do anything useful with NULL rowtype values.  Currently
-                        * we raise an error, but another alternative is to just ignore
-                        * the result and "continue" to get another row.
+                        * 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)
+                       {
+                               if (!returnsSet)
+                                       break;
                                ereport(ERROR,
                                                (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-                                                errmsg("function returning row cannot return null value")));
+                                                errmsg("function returning set of rows cannot return null value")));
+                       }
 
                        /*
-                        * If first time through, build tupdesc and tuplestore for
-                        * result
+                        * If first time through, build tupdesc and tuplestore for result
                         */
                        if (first_time)
                        {
@@ -1329,15 +1527,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;
+                                       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
                                {
@@ -1363,7 +1560,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                         */
                        if (returnsTuple)
                        {
-                               HeapTupleHeader td;
+                               HeapTupleHeader td;
 
                                td = DatumGetHeapTupleHeader(result);
 
@@ -1377,10 +1574,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                        }
                        else
                        {
-                               char            nullflag;
-
-                               nullflag = fcinfo.isnull ? 'n' : ' ';
-                               tuple = heap_formtuple(tupdesc, &result, &nullflag);
+                               tuple = heap_form_tuple(tupdesc, &result, &fcinfo.isnull);
                        }
 
                        oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
@@ -1412,9 +1606,38 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                first_time = false;
        }
 
-       MemoryContextSwitchTo(callerContext);
+no_function_result:
 
-       /* The returned pointers are those in rsinfo */
+       /*
+        * If we got nothing from the function (ie, an empty-set or NULL result),
+        * we have to create the tuplestore to return, and if it's a
+        * non-set-returning function then insert a single all-nulls row.
+        */
+       if (rsinfo.setResult == NULL)
+       {
+               MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+               tupstore = tuplestore_begin_heap(true, false, work_mem);
+               rsinfo.setResult = tupstore;
+               if (!returnsSet)
+               {
+                       int                     natts = expectedDesc->natts;
+                       Datum      *nulldatums;
+                       bool       *nullflags;
+                       HeapTuple       tuple;
+
+                       MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+                       nulldatums = (Datum *) palloc0(natts * sizeof(Datum));
+                       nullflags = (bool *) palloc(natts * sizeof(bool));
+                       memset(nullflags, true, natts * sizeof(bool));
+                       tuple = heap_form_tuple(expectedDesc, nulldatums, nullflags);
+                       MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+                       tuplestore_puttuple(tupstore, tuple);
+               }
+       }
+
+       MemoryContextSwitchTo(callerContext);
+
+       /* The returned pointers are those in rsinfo */
        *returnDesc = rsinfo.setDesc;
        return rsinfo.setResult;
 }
@@ -1517,13 +1740,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])
@@ -1574,6 +1796,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;
@@ -1591,18 +1815,17 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
        }
 
        /* 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])
        {
@@ -1615,18 +1838,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)
        {
@@ -1635,9 +1856,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))
        {
@@ -1656,22 +1876,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(s, typlen, PointerGetDatum(s));
+                       s = (char *) att_align(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)
@@ -1694,6 +1932,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;
@@ -1728,15 +1977,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));
 }
@@ -1759,18 +2008,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)
        {
@@ -1811,12 +2059,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)
@@ -1840,6 +2088,148 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
        return BoolGetDatum(!AnyNull);
 }
 
+/* ----------------------------------------------------------------
+ *             ExecEvalConvertRowtype
+ *
+ *             Evaluate a rowtype coercion operation.  This may require
+ *             rearranging field positions.
+ * ----------------------------------------------------------------
+ */
+static Datum
+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;
+       Datum      *invalues;
+       bool       *inisnull;
+       Datum      *outvalues;
+       bool       *outisnull;
+       int                     i;
+       int                     outnatts;
+
+       tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
+
+       /* this test covers the isDone exception too: */
+       if (*isNull)
+               return tupDatum;
+
+       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_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.
+        */
+       heap_deform_tuple(&tmptup, cstate->indesc, invalues + 1, inisnull + 1);
+       invalues[0] = (Datum) 0;
+       inisnull[0] = true;
+
+       /*
+        * Transpose into proper fields of the new tuple.
+        */
+       for (i = 0; i < outnatts; i++)
+       {
+               int                     j = attrMap[i];
+
+               outvalues[i] = invalues[j];
+               outisnull[i] = inisnull[j];
+       }
+
+       /*
+        * Now form the new tuple.
+        */
+       result = heap_form_tuple(cstate->outdesc, outvalues, outisnull);
+
+       return HeapTupleGetDatum(result);
+}
 
 /* ----------------------------------------------------------------
  *             ExecEvalCase
@@ -1863,10 +2253,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;
@@ -1880,9 +2270,9 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
        }
 
        /*
-        * 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)
        {
@@ -1895,9 +2285,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)
                {
@@ -1943,10 +2333,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
@@ -1971,39 +2357,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,
@@ -2012,15 +2392,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)
@@ -2029,13 +2423,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);
@@ -2046,20 +2441,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));
@@ -2071,27 +2474,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 */
@@ -2103,20 +2521,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 = (ArrayType *) palloc(nbytes);
                result->size = 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);
@@ -2133,68 +2568,411 @@ ExecEvalRow(RowExprState *rstate,
 {
        HeapTuple       tuple;
        Datum      *values;
-       char       *nulls;
-       int                     nargs;
+       bool       *isnull;
+       int                     natts;
        ListCell   *arg;
        int                     i;
 
-       /* Set default values for result flags: non-null, not a set result */
-       *isNull = false;
-       if (isDone)
-               *isDone = ExprSingleResult;
+       /* Set default values for result flags: non-null, not a set result */
+       *isNull = false;
+       if (isDone)
+               *isDone = ExprSingleResult;
+
+       /* Allocate workspace */
+       natts = rstate->tupdesc->natts;
+       values = (Datum *) palloc0(natts * sizeof(Datum));
+       isnull = (bool *) palloc(natts * sizeof(bool));
+
+       /* preset to nulls in case rowtype has some later-added columns */
+       memset(isnull, true, natts * sizeof(bool));
+
+       /* Evaluate field values */
+       i = 0;
+       foreach(arg, rstate->args)
+       {
+               ExprState  *e = (ExprState *) lfirst(arg);
+
+               values[i] = ExecEvalExpr(e, econtext, &isnull[i], NULL);
+               i++;
+       }
+
+       tuple = heap_form_tuple(rstate->tupdesc, values, isnull);
+
+       pfree(values);
+       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
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
+                                bool *isNull, ExprDoneCond *isDone)
+{
+       ListCell   *arg;
+
+       if (isDone)
+               *isDone = ExprSingleResult;
+
+       /* Simply loop through until something NOT NULL is found */
+       foreach(arg, coalesceExpr->args)
+       {
+               ExprState  *e = (ExprState *) lfirst(arg);
+               Datum           value;
+
+               value = ExecEvalExpr(e, econtext, isNull, NULL);
+               if (!*isNull)
+                       return value;
+       }
+
+       /* Else return NULL */
+       *isNull = true;
+       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;
+       text               *result;
+       StringInfoData  buf;
+       Datum                   value;
+       bool                    isnull;
+       ListCell           *arg;
+       ListCell   *narg;
+       int                     i;
+
+       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));
+                               }
+                       }
+                       break;
+
+               case IS_XMLFOREST:
+                       initStringInfo(&buf);
+                       i = 0;
+                       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;
+                               }
+                               i++;
+                       }
+                       break;
+
+                       /* The remaining cases don't need to set up buf */
+               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;
 
-       /* Allocate workspace */
-       nargs = list_length(rstate->args);
-       values = (Datum *) palloc(nargs * sizeof(Datum));
-       nulls = (char *) palloc(nargs * sizeof(char));
+               case IS_XMLROOT:
+                       {
+                               ExprState       *e;
+                               xmltype         *data;
+                               text            *version;
+                               int                     standalone;
 
-       /* Evaluate field values */
-       i = 0;
-       foreach(arg, rstate->args)
-       {
-               ExprState  *e = (ExprState *) lfirst(arg);
-               bool            eisnull;
+                               /* arguments are known to be xml, text, int */
+                               Assert(list_length(xmlExpr->args) == 3);
 
-               values[i] = ExecEvalExpr(e, econtext, &eisnull, NULL);
-               nulls[i] = eisnull ? 'n' : ' ';
-               i++;
-       }
+                               e = (ExprState *) linitial(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (isnull)
+                                       return (Datum) 0;
+                               data = DatumGetXmlP(value);
 
-       tuple = heap_formtuple(rstate->tupdesc, values, nulls);
+                               e = (ExprState *) lsecond(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (isnull)
+                                       version = NULL;
+                               else
+                                       version = DatumGetTextP(value);
 
-       pfree(values);
-       pfree(nulls);
+                               e = (ExprState *) lthird(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               standalone = DatumGetInt32(value);
 
-       return HeapTupleGetDatum(tuple);
-}
+                               *isNull = false;
 
-/* ----------------------------------------------------------------
- *             ExecEvalCoalesce
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
-                                bool *isNull, ExprDoneCond *isDone)
-{
-       ListCell   *arg;
+                               return PointerGetDatum(xmlroot(data,
+                                                                                          version,
+                                                                                          standalone));
+                       }
+                       break;
 
-       if (isDone)
-               *isDone = ExprSingleResult;
+               case IS_XMLSERIALIZE:
+                       {
+                               ExprState       *e;
 
-       /* Simply loop through until something NOT NULL is found */
-       foreach(arg, coalesceExpr->args)
+                               /* 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;
+       }
+
+       if (*isNull)
+               result = NULL;
+       else
        {
-               ExprState  *e = (ExprState *) lfirst(arg);
-               Datum           value;
+               int             len = buf.len + VARHDRSZ;
 
-               value = ExecEvalExpr(e, econtext, isNull, NULL);
-               if (!*isNull)
-                       return value;
+               result = palloc(len);
+               VARATT_SIZEP(result) = len;
+               memcpy(VARDATA(result), buf.data, buf.len);
        }
 
-       /* Else return NULL */
-       *isNull = true;
-       return (Datum) 0;
+       pfree(buf.data);
+       return PointerGetDatum(result);
 }
 
 /* ----------------------------------------------------------------
@@ -2235,8 +3013,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,
@@ -2269,7 +3046,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalNullTest(GenericExprState *nstate,
+ExecEvalNullTest(NullTestState *nstate,
                                 ExprContext *econtext,
                                 bool *isNull,
                                 ExprDoneCond *isDone)
@@ -2282,28 +3059,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 */
+               }
        }
 }
 
@@ -2422,8 +3248,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:
                                {
@@ -2436,8 +3262,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;
@@ -2500,12 +3325,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);
@@ -2520,27 +3347,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));
@@ -2548,7 +3382,7 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
        tmptup.t_data = tuple;
 
        result = heap_getattr(&tmptup,
-                                                 fselect->fieldnum,
+                                                 fieldnum,
                                                  tupDesc,
                                                  isNull);
        return result;
@@ -2571,7 +3405,7 @@ ExecEvalFieldStore(FieldStoreState *fstate,
        Datum           tupDatum;
        TupleDesc       tupDesc;
        Datum      *values;
-       char       *nulls;
+       bool       *isnull;
        Datum           save_datum;
        bool            save_isNull;
        ListCell   *l1,
@@ -2582,32 +3416,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;
@@ -2618,12 +3439,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 */
@@ -2634,9 +3455,8 @@ ExecEvalFieldStore(FieldStoreState *fstate,
 
        forboth(l1, fstate->newvals, l2, fstore->fieldnums)
        {
-               ExprState *newval = (ExprState *) lfirst(l1);
-               AttrNumber fieldnum = lfirst_int(l2);
-               bool            eisnull;
+               ExprState  *newval = (ExprState *) lfirst(l1);
+               AttrNumber      fieldnum = lfirst_int(l2);
 
                Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
 
@@ -2648,22 +3468,21 @@ ExecEvalFieldStore(FieldStoreState *fstate,
                 * 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);
 }
@@ -2783,18 +3602,19 @@ 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
                                {
@@ -2903,28 +3723,16 @@ 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 = lcons(sstate, parent->subPlan);
 
                                state = (ExprState *) sstate;
                        }
@@ -2962,6 +3770,16 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) gstate;
                        }
                        break;
+               case T_ConvertRowtypeExpr:
+                       {
+                               ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
+                               ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
+
+                               cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
+                               cstate->arg = ExecInitExpr(convert->arg, parent);
+                               state = (ExprState *) cstate;
+                       }
+                       break;
                case T_CaseExpr:
                        {
                                CaseExpr   *caseexpr = (CaseExpr *) node;
@@ -3015,32 +3833,132 @@ ExecInitExpr(Expr *node, PlanState *parent)
                        break;
                case T_RowExpr:
                        {
-                               RowExpr    *rowexpr = (RowExpr *) node;
+                               RowExpr    *rowexpr = (RowExpr *) node;
                                RowExprState *rstate = makeNode(RowExprState);
+                               Form_pg_attribute *attrs;
                                List       *outlist = NIL;
                                ListCell   *l;
+                               int                     i;
 
                                rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRow;
+                               /* Build tupdesc to describe result tuples */
+                               if (rowexpr->row_typeid == RECORDOID)
+                               {
+                                       /* generic record, use runtime type assignment */
+                                       rstate->tupdesc = ExecTypeFromExprList(rowexpr->args);
+                                       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_copy(rowexpr->row_typeid, -1);
+                               }
+                               /* Set up evaluation, skipping any deleted columns */
+                               Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts);
+                               attrs = rstate->tupdesc->attrs;
+                               i = 0;
                                foreach(l, rowexpr->args)
                                {
                                        Expr       *e = (Expr *) lfirst(l);
                                        ExprState  *estate;
 
+                                       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.
+                                                */
+                                               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))));
+                                       }
+                                       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.
+                                                */
+                                               e = (Expr *) makeNullConst(INT4OID);
+                                       }
                                        estate = ExecInitExpr(e, parent);
                                        outlist = lappend(outlist, estate);
+                                       i++;
                                }
                                rstate->args = outlist;
-                               /* Build tupdesc to describe result tuples */
-                               if (rowexpr->row_typeid == RECORDOID)
+                               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)
                                {
-                                       /* generic record, use runtime type assignment */
-                                       rstate->tupdesc = ExecTypeFromExprList(rowexpr->args);
-                                       rstate->tupdesc = BlessTupleDesc(rstate->tupdesc);
+                                       Expr       *e = (Expr *) lfirst(l);
+                                       ExprState  *estate;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       outlist = lappend(outlist, estate);
                                }
-                               else
+                               rstate->largs = outlist;
+                               Assert(list_length(rcexpr->rargs) == nopers);
+                               outlist = NIL;
+                               foreach(l, rcexpr->rargs)
                                {
-                                       /* it's been cast to a named type, use that */
-                                       rstate->tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
+                                       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;
+                                       bool            recheck;
+                                       Oid                     proc;
+
+                                       get_op_opfamily_properties(opno, opfamily,
+                                                                                          &strategy,
+                                                                                          &lefttype,
+                                                                                          &righttype,
+                                                                                          &recheck);
+                                       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;
                        }
@@ -3065,6 +3983,87 @@ 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;
+                               int                             i;
+
+                               xstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalXml;
+                               xstate->named_outfuncs = (FmgrInfo *)
+                                       palloc0(list_length(xexpr->named_args) * sizeof(FmgrInfo));
+                               outlist = NIL;
+                               i = 0;
+                               foreach(arg, xexpr->named_args)
+                               {
+                                       Expr            *e = (Expr *) lfirst(arg);
+                                       ExprState       *estate;
+                                       Oid                     typOutFunc;
+                                       bool            typIsVarlena;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       outlist = lappend(outlist, estate);
+
+                                       getTypeOutputInfo(exprType((Node *) e),
+                                                                         &typOutFunc, &typIsVarlena);
+                                       fmgr_info(typOutFunc, &xstate->named_outfuncs[i]);
+                                       i++;
+                               }
+                               xstate->named_args = outlist;
+
+                               outlist = NIL;
+                               foreach(arg, xexpr->args)
+                               {
+                                       Expr            *e = (Expr *) lfirst(arg);
+                                       ExprState       *estate;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       outlist = lappend(outlist, estate);
+                               }
+                               xstate->args = outlist;
+
+                               state = (ExprState *) xstate;
+                       }
+                       break;
                case T_NullIfExpr:
                        {
                                NullIfExpr *nullifexpr = (NullIfExpr *) node;
@@ -3080,11 +4079,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:
@@ -3113,7 +4114,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                TargetEntry *tle = (TargetEntry *) node;
                                GenericExprState *gstate = makeNode(GenericExprState);
 
-                               gstate->xprstate.evalfunc = NULL;       /* not used */
+                               gstate->xprstate.evalfunc = NULL;               /* not used */
                                gstate->arg = ExecInitExpr(tle->expr, parent);
                                state = (ExprState *) gstate;
                        }
@@ -3145,32 +4146,6 @@ 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.
@@ -3254,16 +4229,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;
 
@@ -3322,61 +4297,43 @@ 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.
- * ----------------------------------------------------------------
  */
-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
         */
@@ -3389,13 +4346,12 @@ 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;
 
                values[resind] = ExecEvalExpr(gstate->arg,
                                                                          econtext,
-                                                                         &isNull,
+                                                                         &isnull[resind],
                                                                          &itemIsDone[resind]);
-               nulls[resind] = isNull ? 'n' : ' ';
 
                if (itemIsDone[resind] != ExprSingleResult)
                {
@@ -3425,38 +4381,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;
@@ -3465,9 +4419,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?
                         */
@@ -3477,79 +4431,132 @@ 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;
                        }
                }
        }
 
+       /* Report success */
+       MemoryContextSwitchTo(oldContext);
+
+       return true;
+}
+
+/*
+ * ExecVariableList
+ *             Evaluates a simple-Variable-list projection.
+ *
+ * Results are stored into the passed values and isnull arrays.
+ */
+static void
+ExecVariableList(ProjectionInfo *projInfo,
+                                Datum *values,
+                                bool *isnull)
+{
+       ExprContext *econtext = projInfo->pi_exprContext;
+       int                *varSlotOffsets = projInfo->pi_varSlotOffsets;
+       int                *varNumbers = projInfo->pi_varNumbers;
+       int                     i;
+
        /*
-        * form the new result tuple (in the caller's memory context!)
+        * Force extraction of all input values that we need.
         */
-       MemoryContextSwitchTo(oldContext);
+       if (projInfo->pi_lastInnerVar > 0)
+               slot_getsomeattrs(econtext->ecxt_innertuple,
+                                                 projInfo->pi_lastInnerVar);
+       if (projInfo->pi_lastOuterVar > 0)
+               slot_getsomeattrs(econtext->ecxt_outertuple,
+                                                 projInfo->pi_lastOuterVar);
+       if (projInfo->pi_lastScanVar > 0)
+               slot_getsomeattrs(econtext->ecxt_scantuple,
+                                                 projInfo->pi_lastScanVar);
+
+       /*
+        * Assign to result by direct extraction of fields from source slots ... a
+        * mite ugly, but fast ...
+        */
+       for (i = list_length(projInfo->pi_targetlist) - 1; i >= 0; i--)
+       {
+               char       *slotptr = ((char *) econtext) + varSlotOffsets[i];
+               TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
+               int                     varNumber = varNumbers[i] - 1;
 
-       return heap_formtuple(targettype, values, nulls);
+               values[i] = varSlot->tts_values[varNumber];
+               isnull[i] = varSlot->tts_isnull[varNumber];
+       }
 }
 
-/* ----------------------------------------------------------------
- *             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;
 
        /*
         * sanity checks
         */
-       if (projInfo == NULL)
-               return NULL;
+       Assert(projInfo != NULL);
 
        /*
         * get the projection info we want
         */
        slot = projInfo->pi_slot;
-       tupType = slot->ttc_tupleDescriptor;
 
        /*
-        * form a new result tuple (if possible --- result can be NULL)
+        * 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.)
         */
-       newTuple = ExecTargetList(projInfo->pi_targetlist,
-                                                         tupType,
-                                                         projInfo->pi_exprContext,
-                                                         projInfo->pi_tupValues,
-                                                         projInfo->pi_tupNulls,
-                                                         projInfo->pi_itemIsDone,
-                                                         isDone);
+       ExecClearTuple(slot);
 
        /*
-        * store the tuple in the projection slot and return the slot.
+        * form a new result tuple (if possible); if successful, 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);
+       if (projInfo->pi_isVarList)
+       {
+               /* simple Var list: this always succeeds with one result row */
+               if (isDone)
+                       *isDone = ExprSingleResult;
+               ExecVariableList(projInfo,
+                                                slot->tts_values,
+                                                slot->tts_isnull);
+               ExecStoreVirtualTuple(slot);
+       }
+       else
+       {
+               if (ExecTargetList(projInfo->pi_targetlist,
+                                                  projInfo->pi_exprContext,
+                                                  slot->tts_values,
+                                                  slot->tts_isnull,
+                                                  projInfo->pi_itemIsDone,
+                                                  isDone))
+                       ExecStoreVirtualTuple(slot);
+       }
+
+       return slot;
 }