]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/execQual.c
pgindent run for 8.3.
[postgresql] / src / backend / executor / execQual.c
index 1cbe70571a9764c3a011a70037957e4997780884..8c917c8418978b8406557192c1262348d56507a2 100644 (file)
@@ -3,12 +3,12 @@
  * execQual.c
  *       Routines to evaluate qualification and targetlist expressions
  *
- * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-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.179 2005/05/12 20:41:56 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.224 2007/11/15 21:14:34 momjian 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 "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 */
@@ -64,10 +64,20 @@ static Datum ExecEvalAggref(AggrefExprState *aggref,
                           bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
                        bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
+                                 bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
+                                       bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
+                                        bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
                          bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
                          bool *isNull, ExprDoneCond *isDone);
+static void 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,
@@ -89,8 +99,8 @@ static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
 static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
                        bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
-                                                                       ExprContext *econtext,
-                                                                       bool *isNull, ExprDoneCond *isDone);
+                                          ExprContext *econtext,
+                                          bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
                         bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
@@ -102,13 +112,21 @@ static Datum ExecEvalArray(ArrayExprState *astate,
 static Datum ExecEvalRow(RowExprState *rstate,
                        ExprContext *econtext,
                        bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalRowCompare(RowCompareExprState *rstate,
+                                  ExprContext *econtext,
+                                  bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
                                 ExprContext *econtext,
                                 bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr,
+                          ExprContext *econtext,
+                          bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalXml(XmlExprState * xmlExpr, ExprContext *econtext,
+                       bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalNullIf(FuncExprState *nullIfExpr,
                           ExprContext *econtext,
                           bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalNullTest(GenericExprState *nstate,
+static Datum ExecEvalNullTest(NullTestState *nstate,
                                 ExprContext *econtext,
                                 bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalBooleanTest(GenericExprState *bstate,
@@ -129,6 +147,14 @@ static Datum ExecEvalFieldStore(FieldStoreState *fstate,
 static Datum ExecEvalRelabelType(GenericExprState *exprstate,
                                        ExprContext *econtext,
                                        bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalCoerceViaIO(CoerceViaIOState * iostate,
+                                       ExprContext *econtext,
+                                       bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState * astate,
+                                               ExprContext *econtext,
+                                               bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
+                                         bool *isNull, ExprDoneCond *isDone);
 
 
 /* ----------------------------------------------------------------
@@ -197,16 +223,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
@@ -240,8 +258,8 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                                                                         isDone));
 
        /*
-        * If refexpr yields NULL, and it's a fetch, then result is NULL. In
-        * the assignment case, we'll cons up something below.
+        * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
+        * assignment case, we'll cons up something below.
         */
        if (*isNull)
        {
@@ -265,15 +283,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;
                }
        }
 
@@ -293,19 +311,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 */
@@ -325,11 +339,10 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                 *
                 * XXX At some point we'll need to look into making the old value of
                 * the array element available via CaseTestExpr, as is done by
-                * ExecEvalFieldStore.  This is not needed now but will be needed
-                * to support arrays of composite types; in an assignment to a
-                * field of an array member, the parser would generate a
-                * FieldStore that expects to fetch its input tuple via
-                * CaseTestExpr.
+                * ExecEvalFieldStore.  This is not needed now but will be needed to
+                * support arrays of composite types; in an assignment to a field of
+                * an array member, the parser would generate a FieldStore that
+                * expects to fetch its input tuple via CaseTestExpr.
                 */
                sourceData = ExecEvalExpr(astate->refassgnexpr,
                                                                  econtext,
@@ -337,30 +350,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;
                }
 
@@ -368,20 +374,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);
        }
 
@@ -399,8 +405,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                                                                          astate->refattrlength,
                                                                          astate->refelemlength,
                                                                          astate->refelembyval,
-                                                                         astate->refelemalign,
-                                                                         isNull);
+                                                                         astate->refelemalign);
                return PointerGetDatum(resultArray);
        }
 }
@@ -432,6 +437,11 @@ 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, ExecEvalWholeRowVar, or ExecEvalWholeRowSlow after
+ * making one-time checks.
  * ----------------------------------------------------------------
  */
 static Datum
@@ -446,12 +456,12 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
                *isDone = ExprSingleResult;
 
        /*
-        * Get the slot and attribute number we want
+        * Get the input slot and attribute number we want
         *
         * The asserts check that references to system attributes only appear at
-        * the level of a relation scan; at higher levels, system attributes
-        * must be treated as ordinary variables (since we no longer have
-        * access to the original tuple).
+        * the level of a relation scan; at higher levels, system attributes must
+        * be treated as ordinary variables (since we no longer have access to the
+        * original tuple).
         */
        attnum = variable->varattno;
 
@@ -473,38 +483,292 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
                        break;
        }
 
-#ifdef USE_ASSERT_CHECKING
-       /*
-        * Some checks that are only applied for user attribute numbers (bogus
-        * system attnums will be caught inside slot_getattr).
-        */
-       if (attnum > 0)
+       if (attnum != InvalidAttrNumber)
        {
-               TupleDesc       tuple_type = slot->tts_tupleDescriptor;
-
                /*
-                * This assert checks that the attnum is valid.
+                * Scalar variable case.
+                *
+                * If it's a user attribute, check validity (bogus system attnums will
+                * be caught inside slot_getattr).      What we have to check for here is
+                * the possibility of an attribute having been changed in type since
+                * the plan tree was created.  Ideally the plan would get invalidated
+                * and not re-used, but until that day arrives, we need defenses.
+                * Fortunately it's sufficient to check once on the first time
+                * through.
+                *
+                * Note: we allow a reference to a dropped attribute.  slot_getattr
+                * will force a NULL result in such cases.
+                *
+                * Note: ideally we'd check typmod as well as typid, but that seems
+                * impractical at the moment: in many cases the tupdesc will have been
+                * generated by ExecTypeFromTL(), and that can't guarantee to generate
+                * an accurate typmod in all cases, because some expression node types
+                * don't carry typmod.
                 */
-               Assert(attnum <= tuple_type->natts);
+               if (attnum > 0)
+               {
+                       TupleDesc       slot_tupdesc = slot->tts_tupleDescriptor;
+                       Form_pg_attribute attr;
+
+                       if (attnum > slot_tupdesc->natts)       /* should never happen */
+                               elog(ERROR, "attribute number %d exceeds number of columns %d",
+                                        attnum, slot_tupdesc->natts);
+
+                       attr = slot_tupdesc->attrs[attnum - 1];
+
+                       /* can't check type if dropped, since atttypid is probably 0 */
+                       if (!attr->attisdropped)
+                       {
+                               if (variable->vartype != attr->atttypid)
+                                       ereport(ERROR,
+                                                       (errmsg("attribute %d has wrong type", attnum),
+                                               errdetail("Table has type %s, but query expects %s.",
+                                                                 format_type_be(attr->atttypid),
+                                                                 format_type_be(variable->vartype))));
+                       }
+               }
+
+               /* Skip the checking on future executions of node */
+               exprstate->evalfunc = ExecEvalScalarVar;
 
+               /* Fetch the value from the slot */
+               return slot_getattr(slot, attnum, isNull);
+       }
+       else
+       {
                /*
-                * This assert checks that the datatype the plan expects to get
-                * (as told by our "variable" argument) is in fact the datatype of
-                * the attribute being fetched (as seen in the current context,
-                * identified by our "econtext" argument).      Otherwise crashes are
-                * likely.
+                * Whole-row variable.
+                *
+                * If it's a RECORD Var, we'll use the slot's type ID info.  It's
+                * likely that the slot's type is also RECORD; if so, make sure it's
+                * been "blessed", so that the Datum can be interpreted later.
                 *
-                * Note that we can't check dropped columns, since their atttypid has
-                * been zeroed.
+                * If the Var identifies a named composite type, we must check that
+                * the actual tuple type is compatible with it.
                 */
-               Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid ||
-                          tuple_type->attrs[attnum - 1]->attisdropped);
+               TupleDesc       slot_tupdesc = slot->tts_tupleDescriptor;
+               bool            needslow = false;
+
+               if (variable->vartype == RECORDOID)
+               {
+                       if (slot_tupdesc->tdtypeid == RECORDOID &&
+                               slot_tupdesc->tdtypmod < 0)
+                               assign_record_type_typmod(slot_tupdesc);
+               }
+               else
+               {
+                       TupleDesc       var_tupdesc;
+                       int                     i;
+
+                       /*
+                        * We really only care about number of attributes and data type.
+                        * Also, we can ignore type mismatch on columns that are dropped
+                        * in the destination type, so long as the physical storage
+                        * matches.  This is helpful in some cases involving out-of-date
+                        * cached plans.  Also, we have to allow the case that the slot
+                        * has more columns than the Var's type, because we might be
+                        * looking at the output of a subplan that includes resjunk
+                        * columns.  (XXX it would be nice to verify that the extra
+                        * columns are all marked resjunk, but we haven't got access to
+                        * the subplan targetlist here...)      Resjunk columns should always
+                        * be at the end of a targetlist, so it's sufficient to ignore
+                        * them here; but we need to use ExecEvalWholeRowSlow to get rid
+                        * of them in the eventual output tuples.
+                        */
+                       var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
+
+                       if (var_tupdesc->natts > slot_tupdesc->natts)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                errmsg("table row type and query-specified row type do not match"),
+                                                errdetail("Table row contains %d attributes, but query expects %d.",
+                                                                  slot_tupdesc->natts, var_tupdesc->natts)));
+                       else if (var_tupdesc->natts < slot_tupdesc->natts)
+                               needslow = true;
+
+                       for (i = 0; i < var_tupdesc->natts; i++)
+                       {
+                               Form_pg_attribute vattr = var_tupdesc->attrs[i];
+                               Form_pg_attribute sattr = slot_tupdesc->attrs[i];
+
+                               if (vattr->atttypid == sattr->atttypid)
+                                       continue;       /* no worries */
+                               if (!vattr->attisdropped)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                        errmsg("table row type and query-specified row type do not match"),
+                                                        errdetail("Table has type %s at ordinal position %d, but query expects %s.",
+                                                                          format_type_be(sattr->atttypid),
+                                                                          i + 1,
+                                                                          format_type_be(vattr->atttypid))));
+
+                               if (vattr->attlen != sattr->attlen ||
+                                       vattr->attalign != sattr->attalign)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                        errmsg("table row type and query-specified row type do not match"),
+                                                        errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
+                                                                          i + 1)));
+                       }
+
+                       ReleaseTupleDesc(var_tupdesc);
+               }
+
+               /* Skip the checking on future executions of node */
+               if (needslow)
+                       exprstate->evalfunc = ExecEvalWholeRowSlow;
+               else
+                       exprstate->evalfunc = ExecEvalWholeRowVar;
+
+               /* Fetch the value */
+               return ExecEvalWholeRowVar(exprstate, econtext, isNull, isDone);
+       }
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalScalarVar
+ *
+ *             Returns a Datum for a scalar variable.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
+                                 bool *isNull, ExprDoneCond *isDone)
+{
+       Var                *variable = (Var *) exprstate->expr;
+       TupleTableSlot *slot;
+       AttrNumber      attnum;
+
+       if (isDone)
+               *isDone = ExprSingleResult;
+
+       /* Get the input slot and attribute number we want */
+       switch (variable->varno)
+       {
+               case INNER:                             /* get the tuple from the inner node */
+                       slot = econtext->ecxt_innertuple;
+                       break;
+
+               case OUTER:                             /* get the tuple from the outer node */
+                       slot = econtext->ecxt_outertuple;
+                       break;
+
+               default:                                /* get the tuple from the relation being
+                                                                * scanned */
+                       slot = econtext->ecxt_scantuple;
+                       break;
        }
-#endif /* USE_ASSERT_CHECKING */
 
+       attnum = variable->varattno;
+
+       /* Fetch the value from the slot */
        return slot_getattr(slot, attnum, isNull);
 }
 
+/* ----------------------------------------------------------------
+ *             ExecEvalWholeRowVar
+ *
+ *             Returns a Datum for a whole-row variable.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
+                                       bool *isNull, ExprDoneCond *isDone)
+{
+       Var                *variable = (Var *) exprstate->expr;
+       TupleTableSlot *slot = econtext->ecxt_scantuple;
+       HeapTuple       tuple;
+       TupleDesc       tupleDesc;
+       HeapTupleHeader dtuple;
+
+       if (isDone)
+               *isDone = ExprSingleResult;
+       *isNull = false;
+
+       tuple = ExecFetchSlotTuple(slot);
+       tupleDesc = slot->tts_tupleDescriptor;
+
+       /*
+        * We have to make a copy of the tuple so we can safely insert the Datum
+        * overhead fields, which are not set in on-disk tuples.
+        */
+       dtuple = (HeapTupleHeader) palloc(tuple->t_len);
+       memcpy((char *) dtuple, (char *) tuple->t_data, tuple->t_len);
+
+       HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len);
+
+       /*
+        * If the Var identifies a named composite type, label the tuple with that
+        * type; otherwise use what is in the tupleDesc.
+        */
+       if (variable->vartype != RECORDOID)
+       {
+               HeapTupleHeaderSetTypeId(dtuple, variable->vartype);
+               HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod);
+       }
+       else
+       {
+               HeapTupleHeaderSetTypeId(dtuple, tupleDesc->tdtypeid);
+               HeapTupleHeaderSetTypMod(dtuple, tupleDesc->tdtypmod);
+       }
+
+       return PointerGetDatum(dtuple);
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalWholeRowSlow
+ *
+ *             Returns a Datum for a whole-row variable, in the "slow" case where
+ *             we can't just copy the subplan's output.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
+                                        bool *isNull, ExprDoneCond *isDone)
+{
+       Var                *variable = (Var *) exprstate->expr;
+       TupleTableSlot *slot = econtext->ecxt_scantuple;
+       HeapTuple       tuple;
+       TupleDesc       var_tupdesc;
+       HeapTupleHeader dtuple;
+
+       if (isDone)
+               *isDone = ExprSingleResult;
+       *isNull = false;
+
+       /*
+        * Currently, the only case handled here is stripping of trailing resjunk
+        * fields, which we do in a slightly chintzy way by just adjusting the
+        * tuple's natts header field.  Possibly there will someday be a need for
+        * more-extensive rearrangements, in which case it'd be worth
+        * disassembling and reassembling the tuple (perhaps use a JunkFilter for
+        * that?)
+        */
+       Assert(variable->vartype != RECORDOID);
+       var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
+
+       tuple = ExecFetchSlotTuple(slot);
+
+       /*
+        * We have to make a copy of the tuple so we can safely insert the Datum
+        * overhead fields, which are not set in on-disk tuples; not to mention
+        * fooling with its natts field.
+        */
+       dtuple = (HeapTupleHeader) palloc(tuple->t_len);
+       memcpy((char *) dtuple, (char *) tuple->t_data, tuple->t_len);
+
+       HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len);
+       HeapTupleHeaderSetTypeId(dtuple, variable->vartype);
+       HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod);
+
+       Assert(HeapTupleHeaderGetNatts(dtuple) >= var_tupdesc->natts);
+       HeapTupleHeaderSetNatts(dtuple, var_tupdesc->natts);
+
+       ReleaseTupleDesc(var_tupdesc);
+
+       return PointerGetDatum(dtuple);
+}
+
 /* ----------------------------------------------------------------
  *             ExecEvalConst
  *
@@ -535,11 +799,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
@@ -547,18 +806,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;
 
@@ -576,19 +833,27 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
        else
        {
                /*
-                * All other parameter types must be sought in
-                * ecxt_param_list_info.
+                * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
                 */
-               ParamListInfo paramInfo;
+               ParamListInfo paramInfo = econtext->ecxt_param_list_info;
+
+               Assert(expression->paramkind == PARAM_EXTERN);
+               if (paramInfo &&
+                       thisParamId > 0 && thisParamId <= paramInfo->numParams)
+               {
+                       ParamExternData *prm = &paramInfo->params[thisParamId - 1];
 
-               paramInfo = lookupParam(econtext->ecxt_param_list_info,
-                                                               thisParamKind,
-                                                               expression->paramname,
-                                                               thisParamId,
-                                                               false);
-               Assert(paramInfo->ptype == expression->paramtype);
-               *isNull = paramInfo->isnull;
-               return paramInfo->value;
+                       if (OidIsValid(prm->ptype))
+                       {
+                               Assert(prm->ptype == expression->paramtype);
+                               *isNull = prm->isnull;
+                               return prm->value;
+                       }
+               }
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("no value found for parameter %d", thisParamId)));
+               return (Datum) 0;               /* keep compiler quiet */
        }
 }
 
@@ -638,9 +903,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));
@@ -651,6 +916,9 @@ GetAttributeByNum(HeapTupleHeader tuple,
                                                  attrno,
                                                  tupDesc,
                                                  isNull);
+
+       ReleaseTupleDesc(tupDesc);
+
        return result;
 }
 
@@ -696,9 +964,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));
@@ -709,6 +977,9 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
                                                  attrno,
                                                  tupDesc,
                                                  isNull);
+
+       ReleaseTupleDesc(tupDesc);
+
        return result;
 }
 
@@ -727,9 +998,9 @@ init_fcache(Oid foid, FuncExprState *fcache, MemoryContext fcacheCxt)
 
        /*
         * 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?
+        * 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)
                ereport(ERROR,
@@ -762,6 +1033,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.
  */
@@ -790,10 +1116,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,
@@ -832,11 +1157,10 @@ 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)
        {
@@ -867,8 +1191,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)
        {
@@ -884,14 +1207,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,
@@ -899,18 +1222,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;
 
@@ -945,8 +1268,8 @@ 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 && *isDone == ExprMultipleResult)
                                {
@@ -958,7 +1281,7 @@ ExecMakeFunctionResult(FuncExprState *fcache,
                                        {
                                                RegisterExprContextCallback(econtext,
                                                                                                        ShutdownFuncExpr,
-                                                                                               PointerGetDatum(fcache));
+                                                                                                       PointerGetDatum(fcache));
                                                fcache->shutdown_reg = true;
                                        }
                                }
@@ -989,8 +1312,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.
                         */
                }
        }
@@ -1000,9 +1323,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;
 
@@ -1071,8 +1394,8 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
        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)
        {
@@ -1097,7 +1420,7 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
  *             ExecMakeTableFunctionResult
  *
  * Evaluate a table function, producing a materialized result in a Tuplestore
- * object.  *returnDesc is set to the tupledesc actually returned by the
+ * object.     *returnDesc is set to the tupledesc actually returned by the
  * function, or NULL if it didn't provide one.
  */
 Tuplestorestate *
@@ -1123,15 +1446,14 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 
        funcrettype = exprType((Node *) funcexpr->expr);
 
-       returnsTuple = (funcrettype == RECORDOID ||
-                                       get_typtype(funcrettype) == 'c');
+       returnsTuple = type_is_rowtype(funcrettype);
 
        /*
-        * Prepare a resultinfo node for communication.  We always do this
-        * even if not expecting a set result, so that we can pass
-        * expectedDesc.  In the generic-expression case, the expression
-        * doesn't actually get to see the resultinfo, but set it up anyway
-        * because we use some of the fields as our own state variables.
+        * Prepare a resultinfo node for communication.  We always do this even if
+        * not expecting a set result, so that we can pass expectedDesc.  In the
+        * generic-expression case, the expression doesn't actually get to see the
+        * resultinfo, but set it up anyway because we use some of the fields as
+        * our own state variables.
         */
        InitFunctionCallInfoData(fcinfo, NULL, 0, NULL, (Node *) &rsinfo);
        rsinfo.type = T_ReturnSetInfo;
@@ -1144,14 +1466,14 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
        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))
@@ -1179,9 +1501,9 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                 * Evaluate the function's argument list.
                 *
                 * Note: ideally, we'd do this in the per-tuple context, but then the
-                * argument values would disappear when we reset the context in
-                * the inner loop.      So do it in caller context.  Perhaps we should
-                * make a separate context just to hold the evaluated arguments?
+                * argument values would disappear when we reset the context in the
+                * inner loop.  So do it in caller context.  Perhaps we should make a
+                * separate context just to hold the evaluated arguments?
                 */
                fcinfo.flinfo = &(fcache->func);
                argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext);
@@ -1214,8 +1536,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
        }
 
        /*
-        * Switch to short-lived context for calling the function or
-        * expression.
+        * Switch to short-lived context for calling the function or expression.
         */
        MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
 
@@ -1228,10 +1549,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);
 
@@ -1258,12 +1581,12 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                                break;
 
                        /*
-                        * Can't do anything very useful with NULL rowtype values.
-                        * For a function returning set, we consider this a protocol
-                        * violation (but another alternative would be to just ignore
-                        * the result and "continue" to get another row).  For a function
-                        * not returning set, we fall out of the loop; we'll cons up
-                        * an all-nulls result row below.
+                        * Can't do anything very useful with NULL rowtype values. For a
+                        * function returning set, we consider this a protocol violation
+                        * (but another alternative would be to just ignore the result and
+                        * "continue" to get another row).      For a function not returning
+                        * set, we fall out of the loop; we'll cons up an all-nulls result
+                        * row below.
                         */
                        if (returnsTuple && fcinfo.isnull)
                        {
@@ -1275,8 +1598,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                        }
 
                        /*
-                        * If first time through, build tupdesc and tuplestore for
-                        * result
+                        * If first time through, build tupdesc and tuplestore for result
                         */
                        if (first_time)
                        {
@@ -1284,16 +1606,14 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                                if (returnsTuple)
                                {
                                        /*
-                                        * Use the type info embedded in the rowtype Datum to
-                                        * look up the needed tupdesc.  Make a copy for the
-                                        * query.
+                                        * Use the type info embedded in the rowtype Datum to look
+                                        * up the needed tupdesc.  Make a copy for the query.
                                         */
                                        HeapTupleHeader td;
 
                                        td = DatumGetHeapTupleHeader(result);
-                                       tupdesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(td),
-                                                                                  HeapTupleHeaderGetTypMod(td));
-                                       tupdesc = CreateTupleDescCopy(tupdesc);
+                                       tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td),
+                                                                                          HeapTupleHeaderGetTypMod(td));
                                }
                                else
                                {
@@ -1504,7 +1824,7 @@ ExecEvalDistinct(FuncExprState *fcache,
        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])
@@ -1555,6 +1875,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;
@@ -1577,12 +1899,12 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
        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])
        {
@@ -1595,18 +1917,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)
        {
@@ -1615,9 +1935,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))
        {
@@ -1636,22 +1955,40 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
 
        /* Loop over the array elements */
        s = (char *) ARR_DATA_PTR(arr);
+       bitmap = ARR_NULLBITMAP(arr);
+       bitmask = 1;
+
        for (i = 0; i < nitems; i++)
        {
                Datum           elt;
                Datum           thisresult;
 
-               /* Get array element */
-               elt = fetch_att(s, typbyval, typlen);
-
-               s = att_addlength(s, typlen, PointerGetDatum(s));
-               s = (char *) att_align(s, typalign);
+               /* Get array element, checking for NULL */
+               if (bitmap && (*bitmap & bitmask) == 0)
+               {
+                       fcinfo.arg[1] = (Datum) 0;
+                       fcinfo.argnull[1] = true;
+               }
+               else
+               {
+                       elt = fetch_att(s, typbyval, typlen);
+                       s = att_addlength_pointer(s, typlen, s);
+                       s = (char *) att_align_nominal(s, typalign);
+                       fcinfo.arg[1] = elt;
+                       fcinfo.argnull[1] = false;
+               }
 
                /* Call comparison function */
-               fcinfo.arg[1] = elt;
-               fcinfo.argnull[1] = false;
-               fcinfo.isnull = false;
-               thisresult = FunctionCallInvoke(&fcinfo);
+               if (fcinfo.argnull[1] && sstate->fxprstate.func.fn_strict)
+               {
+                       fcinfo.isnull = true;
+                       thisresult = (Datum) 0;
+               }
+               else
+               {
+                       fcinfo.isnull = false;
+                       thisresult = FunctionCallInvoke(&fcinfo);
+               }
 
                /* Combine results per OR or AND semantics */
                if (fcinfo.isnull)
@@ -1674,6 +2011,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;
@@ -1708,15 +2056,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));
 }
@@ -1739,18 +2087,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)
        {
@@ -1791,12 +2138,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)
@@ -1823,7 +2170,7 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
 /* ----------------------------------------------------------------
  *             ExecEvalConvertRowtype
  *
- *             Evaluate a rowtype coercion operation.  This may require
+ *             Evaluate a rowtype coercion operation.  This may require
  *             rearranging field positions.
  * ----------------------------------------------------------------
  */
@@ -1832,17 +2179,18 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
                                           ExprContext *econtext,
                                           bool *isNull, ExprDoneCond *isDone)
 {
+       ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) cstate->xprstate.expr;
        HeapTuple       result;
        Datum           tupDatum;
        HeapTupleHeader tuple;
        HeapTupleData tmptup;
-       AttrNumber *attrMap = cstate->attrMap;
-       Datum      *invalues = cstate->invalues;
-       bool       *inisnull = cstate->inisnull;
-       Datum      *outvalues = cstate->outvalues;
-       bool       *outisnull = cstate->outisnull;
+       AttrNumber *attrMap;
+       Datum      *invalues;
+       bool       *inisnull;
+       Datum      *outvalues;
+       bool       *outisnull;
        int                     i;
-       int                     outnatts = cstate->outdesc->natts;
+       int                     outnatts;
 
        tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
 
@@ -1852,9 +2200,82 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
 
        tuple = DatumGetHeapTupleHeader(tupDatum);
 
+       /* Lookup tupdescs if first time through or after rescan */
+       if (cstate->indesc == NULL)
+               get_cached_rowtype(exprType((Node *) convert->arg), -1,
+                                                  &cstate->indesc, econtext);
+       if (cstate->outdesc == NULL)
+               get_cached_rowtype(convert->resulttype, -1,
+                                                  &cstate->outdesc, econtext);
+
        Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid);
        Assert(HeapTupleHeaderGetTypMod(tuple) == cstate->indesc->tdtypmod);
 
+       /* if first time through, initialize */
+       if (cstate->attrMap == NULL)
+       {
+               MemoryContext old_cxt;
+               int                     n;
+
+               /* allocate state in long-lived memory context */
+               old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+               /* prepare map from old to new attribute numbers */
+               n = cstate->outdesc->natts;
+               cstate->attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
+               for (i = 0; i < n; i++)
+               {
+                       Form_pg_attribute att = cstate->outdesc->attrs[i];
+                       char       *attname;
+                       Oid                     atttypid;
+                       int32           atttypmod;
+                       int                     j;
+
+                       if (att->attisdropped)
+                               continue;               /* attrMap[i] is already 0 */
+                       attname = NameStr(att->attname);
+                       atttypid = att->atttypid;
+                       atttypmod = att->atttypmod;
+                       for (j = 0; j < cstate->indesc->natts; j++)
+                       {
+                               att = cstate->indesc->attrs[j];
+                               if (att->attisdropped)
+                                       continue;
+                               if (strcmp(attname, NameStr(att->attname)) == 0)
+                               {
+                                       /* Found it, check type */
+                                       if (atttypid != att->atttypid || atttypmod != att->atttypmod)
+                                               elog(ERROR, "attribute \"%s\" of type %s does not match corresponding attribute of type %s",
+                                                        attname,
+                                                        format_type_be(cstate->indesc->tdtypeid),
+                                                        format_type_be(cstate->outdesc->tdtypeid));
+                                       cstate->attrMap[i] = (AttrNumber) (j + 1);
+                                       break;
+                               }
+                       }
+                       if (cstate->attrMap[i] == 0)
+                               elog(ERROR, "attribute \"%s\" of type %s does not exist",
+                                        attname,
+                                        format_type_be(cstate->indesc->tdtypeid));
+               }
+               /* preallocate workspace for Datum arrays */
+               n = cstate->indesc->natts + 1;  /* +1 for NULL */
+               cstate->invalues = (Datum *) palloc(n * sizeof(Datum));
+               cstate->inisnull = (bool *) palloc(n * sizeof(bool));
+               n = cstate->outdesc->natts;
+               cstate->outvalues = (Datum *) palloc(n * sizeof(Datum));
+               cstate->outisnull = (bool *) palloc(n * sizeof(bool));
+
+               MemoryContextSwitchTo(old_cxt);
+       }
+
+       attrMap = cstate->attrMap;
+       invalues = cstate->invalues;
+       inisnull = cstate->inisnull;
+       outvalues = cstate->outvalues;
+       outisnull = cstate->outisnull;
+       outnatts = cstate->outdesc->natts;
+
        /*
         * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader.
         */
@@ -1862,10 +2283,9 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
        tmptup.t_data = tuple;
 
        /*
-        * Extract all the values of the old tuple, offsetting the arrays
-        * so that invalues[0] is NULL and invalues[1] is the first
-        * source attribute; this exactly matches the numbering convention
-        * in attrMap.
+        * Extract all the values of the old tuple, offsetting the arrays so that
+        * invalues[0] is NULL and invalues[1] is the first source attribute; this
+        * exactly matches the numbering convention in attrMap.
         */
        heap_deform_tuple(&tmptup, cstate->indesc, invalues + 1, inisnull + 1);
        invalues[0] = (Datum) 0;
@@ -1912,10 +2332,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;
@@ -1924,14 +2344,14 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
        {
                econtext->caseValue_datum = ExecEvalExpr(caseExpr->arg,
                                                                                                 econtext,
-                                                                                        &econtext->caseValue_isNull,
+                                                                                                &econtext->caseValue_isNull,
                                                                                                 NULL);
        }
 
        /*
-        * we evaluate each of the WHEN clauses in turn, as soon as one is
-        * true we return the corresponding result. If none are true then we
-        * return the value of the default clause, or NULL if there is none.
+        * we evaluate each of the WHEN clauses in turn, as soon as one is true we
+        * return the corresponding result. If none are true then we return the
+        * value of the default clause, or NULL if there is none.
         */
        foreach(clause, clauses)
        {
@@ -1944,9 +2364,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)
                {
@@ -1992,10 +2412,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
@@ -2020,39 +2436,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,
@@ -2061,15 +2471,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)
@@ -2078,13 +2502,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);
@@ -2095,20 +2520,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));
@@ -2120,52 +2553,84 @@ 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);
-
-                       memcpy(dat + (ndatabytes - elem_ndatabytes),
-                                  ARR_DATA_PTR(array),
-                                  elem_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++;
                }
 
-               /* setup for multi-D array */
-               dims[0] = outer_nelems;
-               lbs[0] = 1;
+               /*
+                * 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 */
+               dims[0] = outer_nelems;
+               lbs[0] = 1;
                for (i = 1; i < ndims; i++)
                {
                        dims[i] = elem_dims[i - 1];
                        lbs[i] = elem_lbs[i - 1];
                }
 
-               nbytes = ndatabytes + ARR_OVERHEAD(ndims);
-               result = (ArrayType *) palloc(nbytes);
+               if (havenulls)
+               {
+                       dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
+                       nbytes += dataoffset;
+               }
+               else
+               {
+                       dataoffset = 0;         /* marker for no null bitmap */
+                       nbytes += ARR_OVERHEAD_NONULLS(ndims);
+               }
 
-               result->size = nbytes;
+               result = (ArrayType *) palloc(nbytes);
+               SET_VARSIZE(result, nbytes);
                result->ndim = ndims;
-               result->flags = 0;
+               result->dataoffset = dataoffset;
                result->elemtype = element_type;
                memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
                memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
-               if (ndatabytes > 0)
-                       memcpy(ARR_DATA_PTR(result), dat, ndatabytes);
 
-               if (dat != NULL)
-                       pfree(dat);
+               dat = ARR_DATA_PTR(result);
+               iitem = 0;
+               for (i = 0; i < outer_nelems; i++)
+               {
+                       memcpy(dat, subdata[i], subbytes[i]);
+                       dat += subbytes[i];
+                       if (havenulls)
+                               array_bitmap_copy(ARR_NULLBITMAP(result), iitem,
+                                                                 subbitmaps[i], 0,
+                                                                 subnitems[i]);
+                       iitem += subnitems[i];
+               }
        }
 
        return PointerGetDatum(result);
@@ -2218,6 +2683,76 @@ ExecEvalRow(RowExprState *rstate,
        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
  * ----------------------------------------------------------------
@@ -2247,6 +2782,278 @@ ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
        return (Datum) 0;
 }
 
+/* ----------------------------------------------------------------
+ *             ExecEvalMinMax
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext,
+                          bool *isNull, ExprDoneCond *isDone)
+{
+       Datum           result = (Datum) 0;
+       MinMaxOp        op = ((MinMaxExpr *) minmaxExpr->xprstate.expr)->op;
+       FunctionCallInfoData locfcinfo;
+       ListCell   *arg;
+
+       if (isDone)
+               *isDone = ExprSingleResult;
+       *isNull = true;                         /* until we get a result */
+
+       InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2, NULL, NULL);
+       locfcinfo.argnull[0] = false;
+       locfcinfo.argnull[1] = false;
+
+       foreach(arg, minmaxExpr->args)
+       {
+               ExprState  *e = (ExprState *) lfirst(arg);
+               Datum           value;
+               bool            valueIsNull;
+               int32           cmpresult;
+
+               value = ExecEvalExpr(e, econtext, &valueIsNull, NULL);
+               if (valueIsNull)
+                       continue;                       /* ignore NULL inputs */
+
+               if (*isNull)
+               {
+                       /* first nonnull input, adopt value */
+                       result = value;
+                       *isNull = false;
+               }
+               else
+               {
+                       /* apply comparison function */
+                       locfcinfo.arg[0] = result;
+                       locfcinfo.arg[1] = value;
+                       locfcinfo.isnull = false;
+                       cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+                       if (locfcinfo.isnull)           /* probably should not happen */
+                               continue;
+                       if (cmpresult > 0 && op == IS_LEAST)
+                               result = value;
+                       else if (cmpresult < 0 && op == IS_GREATEST)
+                               result = value;
+               }
+       }
+
+       return result;
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalXml
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalXml(XmlExprState * xmlExpr, ExprContext *econtext,
+                       bool *isNull, ExprDoneCond *isDone)
+{
+       XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
+       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;
+
+               case IS_XMLROOT:
+                       {
+                               ExprState  *e;
+                               xmltype    *data;
+                               text       *version;
+                               int                     standalone;
+
+                               /* arguments are known to be xml, text, int */
+                               Assert(list_length(xmlExpr->args) == 3);
+
+                               e = (ExprState *) linitial(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (isnull)
+                                       return (Datum) 0;
+                               data = DatumGetXmlP(value);
+
+                               e = (ExprState *) lsecond(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (isnull)
+                                       version = NULL;
+                               else
+                                       version = DatumGetTextP(value);
+
+                               e = (ExprState *) lthird(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               standalone = DatumGetInt32(value);
+
+                               *isNull = false;
+
+                               return PointerGetDatum(xmlroot(data,
+                                                                                          version,
+                                                                                          standalone));
+                       }
+                       break;
+
+               case IS_XMLSERIALIZE:
+                       {
+                               ExprState  *e;
+
+                               /* argument type is known to be xml */
+                               Assert(list_length(xmlExpr->args) == 1);
+
+                               e = (ExprState *) linitial(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (isnull)
+                                       return (Datum) 0;
+
+                               *isNull = false;
+
+                               return PointerGetDatum(xmltotext_with_xmloption(DatumGetXmlP(value), xexpr->xmloption));
+                       }
+                       break;
+
+               case IS_DOCUMENT:
+                       {
+                               ExprState  *e;
+
+                               /* optional argument is known to be xml */
+                               Assert(list_length(xmlExpr->args) == 1);
+
+                               e = (ExprState *) linitial(xmlExpr->args);
+                               value = ExecEvalExpr(e, econtext, &isnull, NULL);
+                               if (isnull)
+                                       return (Datum) 0;
+                               else
+                               {
+                                       *isNull = false;
+                                       return BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
+                               }
+                       }
+                       break;
+       }
+
+       if (*isNull)
+               result = NULL;
+       else
+       {
+               int                     len = buf.len + VARHDRSZ;
+
+               result = palloc(len);
+               SET_VARSIZE(result, len);
+               memcpy(VARDATA(result), buf.data, buf.len);
+       }
+
+       pfree(buf.data);
+       return PointerGetDatum(result);
+}
+
 /* ----------------------------------------------------------------
  *             ExecEvalNullIf
  *
@@ -2318,7 +3125,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalNullTest(GenericExprState *nstate,
+ExecEvalNullTest(NullTestState *nstate,
                                 ExprContext *econtext,
                                 bool *isNull,
                                 ExprDoneCond *isDone)
@@ -2331,28 +3138,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 */
+               }
        }
 }
 
@@ -2471,8 +3327,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:
                                {
@@ -2485,8 +3341,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;
@@ -2549,12 +3404,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);
@@ -2569,27 +3426,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));
@@ -2597,7 +3461,7 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
        tmptup.t_data = tuple;
 
        result = heap_getattr(&tmptup,
-                                                 fselect->fieldnum,
+                                                 fieldnum,
                                                  tupDesc,
                                                  isNull);
        return result;
@@ -2631,22 +3495,9 @@ 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));
@@ -2655,8 +3506,8 @@ ExecEvalFieldStore(FieldStoreState *fstate,
        if (!*isNull)
        {
                /*
-                * heap_deform_tuple 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;
@@ -2689,11 +3540,11 @@ ExecEvalFieldStore(FieldStoreState *fstate,
                Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
 
                /*
-                * Use the CaseTestExpr mechanism to pass down the old value of
-                * the field being replaced; this is useful in case we have a
-                * nested field update situation.  It's safe to reuse the CASE
-                * mechanism because there cannot be a CASE between here and where
-                * the value would be needed.
+                * Use the CaseTestExpr mechanism to pass down the old value of the
+                * field being replaced; this is useful in case we have a nested field
+                * update situation.  It's safe to reuse the CASE mechanism because
+                * there cannot be a CASE between here and where the value would be
+                * needed.
                 */
                econtext->caseValue_datum = values[fieldnum - 1];
                econtext->caseValue_isNull = isnull[fieldnum - 1];
@@ -2729,6 +3580,133 @@ ExecEvalRelabelType(GenericExprState *exprstate,
        return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
 }
 
+/* ----------------------------------------------------------------
+ *             ExecEvalCoerceViaIO
+ *
+ *             Evaluate a CoerceViaIO node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalCoerceViaIO(CoerceViaIOState * iostate,
+                                       ExprContext *econtext,
+                                       bool *isNull, ExprDoneCond *isDone)
+{
+       Datum           result;
+       Datum           inputval;
+       char       *string;
+
+       inputval = ExecEvalExpr(iostate->arg, econtext, isNull, isDone);
+
+       if (isDone && *isDone == ExprEndResult)
+               return inputval;                /* nothing to do */
+
+       if (*isNull)
+               string = NULL;                  /* output functions are not called on nulls */
+       else
+               string = OutputFunctionCall(&iostate->outfunc, inputval);
+
+       result = InputFunctionCall(&iostate->infunc,
+                                                          string,
+                                                          iostate->intypioparam,
+                                                          -1);
+
+       /* The input function cannot change the null/not-null status */
+       return result;
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalArrayCoerceExpr
+ *
+ *             Evaluate an ArrayCoerceExpr node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalArrayCoerceExpr(ArrayCoerceExprState * astate,
+                                               ExprContext *econtext,
+                                               bool *isNull, ExprDoneCond *isDone)
+{
+       ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) astate->xprstate.expr;
+       Datum           result;
+       ArrayType  *array;
+       FunctionCallInfoData locfcinfo;
+
+       result = ExecEvalExpr(astate->arg, econtext, isNull, isDone);
+
+       if (isDone && *isDone == ExprEndResult)
+               return result;                  /* nothing to do */
+       if (*isNull)
+               return result;                  /* nothing to do */
+
+       /*
+        * If it's binary-compatible, modify the element type in the array header,
+        * but otherwise leave the array as we received it.
+        */
+       if (!OidIsValid(acoerce->elemfuncid))
+       {
+               /* Detoast input array if necessary, and copy in any case */
+               array = DatumGetArrayTypePCopy(result);
+               ARR_ELEMTYPE(array) = astate->resultelemtype;
+               PG_RETURN_ARRAYTYPE_P(array);
+       }
+
+       /* Detoast input array if necessary, but don't make a useless copy */
+       array = DatumGetArrayTypeP(result);
+
+       /* Initialize function cache if first time through */
+       if (astate->elemfunc.fn_oid == InvalidOid)
+       {
+               AclResult       aclresult;
+
+               /* Check permission to call function */
+               aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(),
+                                                                        ACL_EXECUTE);
+               if (aclresult != ACLCHECK_OK)
+                       aclcheck_error(aclresult, ACL_KIND_PROC,
+                                                  get_func_name(acoerce->elemfuncid));
+
+               /* Set up the primary fmgr lookup information */
+               fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
+                                         econtext->ecxt_per_query_memory);
+
+               /* Initialize additional info */
+               astate->elemfunc.fn_expr = (Node *) acoerce;
+       }
+
+       /*
+        * Use array_map to apply the function to each array element.
+        *
+        * We pass on the desttypmod and isExplicit flags whether or not the
+        * function wants them.
+        */
+       InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3,
+                                                        NULL, NULL);
+       locfcinfo.arg[0] = PointerGetDatum(array);
+       locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
+       locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
+       locfcinfo.argnull[0] = false;
+       locfcinfo.argnull[1] = false;
+       locfcinfo.argnull[2] = false;
+
+       return array_map(&locfcinfo, ARR_ELEMTYPE(array), astate->resultelemtype,
+                                        astate->amstate);
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalCurrentOfExpr
+ *
+ * The planner must convert CURRENT OF into a TidScan qualification.
+ * So, we have to be able to do ExecInitExpr on a CurrentOfExpr,
+ * but we shouldn't ever actually execute it.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
+                                         bool *isNull, ExprDoneCond *isDone)
+{
+       elog(ERROR, "CURRENT OF cannot be executed");
+       return 0;                                       /* keep compiler quiet */
+}
+
 
 /*
  * ExecEvalExprSwitchContext
@@ -2830,18 +3808,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
                                {
@@ -2950,28 +3929,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;
                        }
@@ -3009,65 +3976,53 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) gstate;
                        }
                        break;
+               case T_CoerceViaIO:
+                       {
+                               CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+                               CoerceViaIOState *iostate = makeNode(CoerceViaIOState);
+                               Oid                     iofunc;
+                               bool            typisvarlena;
+
+                               iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO;
+                               iostate->arg = ExecInitExpr(iocoerce->arg, parent);
+                               /* lookup the result type's input function */
+                               getTypeInputInfo(iocoerce->resulttype, &iofunc,
+                                                                &iostate->intypioparam);
+                               fmgr_info(iofunc, &iostate->infunc);
+                               /* lookup the input type's output function */
+                               getTypeOutputInfo(exprType((Node *) iocoerce->arg),
+                                                                 &iofunc, &typisvarlena);
+                               fmgr_info(iofunc, &iostate->outfunc);
+                               state = (ExprState *) iostate;
+                       }
+                       break;
+               case T_ArrayCoerceExpr:
+                       {
+                               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+                               ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
+
+                               astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
+                               astate->arg = ExecInitExpr(acoerce->arg, parent);
+                               astate->resultelemtype = get_element_type(acoerce->resulttype);
+                               if (astate->resultelemtype == InvalidOid)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                        errmsg("target type is not an array")));
+                               /* Arrays over domains aren't supported yet */
+                               Assert(getBaseType(astate->resultelemtype) ==
+                                          astate->resultelemtype);
+                               astate->elemfunc.fn_oid = InvalidOid;   /* not initialized */
+                               astate->amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState));
+                               state = (ExprState *) astate;
+                       }
+                       break;
                case T_ConvertRowtypeExpr:
                        {
                                ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
                                ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
-                               int             i;
-                               int             n;
 
                                cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
                                cstate->arg = ExecInitExpr(convert->arg, parent);
-                               /* save copies of needed tuple descriptors */
-                               cstate->indesc = lookup_rowtype_tupdesc(exprType((Node *) convert->arg), -1);
-                               cstate->indesc = CreateTupleDescCopy(cstate->indesc);
-                               cstate->outdesc = lookup_rowtype_tupdesc(convert->resulttype, -1);
-                               cstate->outdesc = CreateTupleDescCopy(cstate->outdesc);
-                               /* prepare map from old to new attribute numbers */
-                               n = cstate->outdesc->natts;
-                               cstate->attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
-                               for (i = 0; i < n; i++)
-                               {
-                                       Form_pg_attribute att = cstate->outdesc->attrs[i];
-                                       char       *attname;
-                                       Oid                     atttypid;
-                                       int32           atttypmod;
-                                       int                     j;
-
-                                       if (att->attisdropped)
-                                               continue;       /* attrMap[i] is already 0 */
-                                       attname = NameStr(att->attname);
-                                       atttypid = att->atttypid;
-                                       atttypmod = att->atttypmod;
-                                       for (j = 0; j < cstate->indesc->natts; j++)
-                                       {
-                                               att = cstate->indesc->attrs[j];
-                                               if (att->attisdropped)
-                                                       continue;
-                                               if (strcmp(attname, NameStr(att->attname)) == 0)
-                                               {
-                                                       /* Found it, check type */
-                                                       if (atttypid != att->atttypid || atttypmod != att->atttypmod)
-                                                               elog(ERROR, "attribute \"%s\" of type %s does not match corresponding attribute of type %s",
-                                                                        attname,
-                                                                        format_type_be(cstate->indesc->tdtypeid),
-                                                                        format_type_be(cstate->outdesc->tdtypeid));
-                                                       cstate->attrMap[i] = (AttrNumber) (j + 1);
-                                                       break;
-                                               }
-                                       }
-                                       if (cstate->attrMap[i] == 0)
-                                               elog(ERROR, "attribute \"%s\" of type %s does not exist",
-                                                        attname,
-                                                        format_type_be(cstate->indesc->tdtypeid));
-                               }
-                               /* preallocate workspace for Datum arrays */
-                               n = cstate->indesc->natts + 1;  /* +1 for NULL */
-                               cstate->invalues = (Datum *) palloc(n * sizeof(Datum));
-                               cstate->inisnull = (bool *) palloc(n * sizeof(bool));
-                               n = cstate->outdesc->natts;
-                               cstate->outvalues = (Datum *) palloc(n * sizeof(Datum));
-                               cstate->outisnull = (bool *) palloc(n * sizeof(bool));
                                state = (ExprState *) cstate;
                        }
                        break;
@@ -3137,13 +4092,13 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                {
                                        /* generic record, use runtime type assignment */
                                        rstate->tupdesc = ExecTypeFromExprList(rowexpr->args);
-                                       rstate->tupdesc = BlessTupleDesc(rstate->tupdesc);
+                                       BlessTupleDesc(rstate->tupdesc);
+                                       /* we won't need to redo this at runtime */
                                }
                                else
                                {
                                        /* it's been cast to a named type, use that */
-                                       rstate->tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
-                                       rstate->tupdesc = CreateTupleDescCopy(rstate->tupdesc);
+                                       rstate->tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
                                }
                                /* Set up evaluation, skipping any deleted columns */
                                Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts);
@@ -3157,26 +4112,26 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                        if (!attrs[i]->attisdropped)
                                        {
                                                /*
-                                                * Guard against ALTER COLUMN TYPE on rowtype
-                                                * since the RowExpr was created.  XXX should we
-                                                * check typmod too?  Not sure we can be sure
-                                                * it'll be the same.
+                                                * Guard against ALTER COLUMN TYPE on rowtype since
+                                                * the RowExpr was created.  XXX should we check
+                                                * typmod too?  Not sure we can be sure it'll be the
+                                                * same.
                                                 */
                                                if (exprType((Node *) e) != attrs[i]->atttypid)
                                                        ereport(ERROR,
                                                                        (errcode(ERRCODE_DATATYPE_MISMATCH),
                                                                         errmsg("ROW() column has type %s instead of type %s",
-                                                                       format_type_be(exprType((Node *) e)),
-                                                                  format_type_be(attrs[i]->atttypid))));
+                                                                               format_type_be(exprType((Node *) e)),
+                                                                          format_type_be(attrs[i]->atttypid))));
                                        }
                                        else
                                        {
                                                /*
-                                                * Ignore original expression and insert a NULL.
-                                                * We don't really care what type of NULL it is,
-                                                * so always make an int4 NULL.
+                                                * Ignore original expression and insert a NULL. We
+                                                * don't really care what type of NULL it is, so
+                                                * always make an int4 NULL.
                                                 */
-                                               e = (Expr *) makeNullConst(INT4OID);
+                                               e = (Expr *) makeNullConst(INT4OID, -1);
                                        }
                                        estate = ExecInitExpr(e, parent);
                                        outlist = lappend(outlist, estate);
@@ -3186,6 +4141,74 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) rstate;
                        }
                        break;
+               case T_RowCompareExpr:
+                       {
+                               RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+                               RowCompareExprState *rstate = makeNode(RowCompareExprState);
+                               int                     nopers = list_length(rcexpr->opnos);
+                               List       *outlist;
+                               ListCell   *l;
+                               ListCell   *l2;
+                               int                     i;
+
+                               rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRowCompare;
+                               Assert(list_length(rcexpr->largs) == nopers);
+                               outlist = NIL;
+                               foreach(l, rcexpr->largs)
+                               {
+                                       Expr       *e = (Expr *) lfirst(l);
+                                       ExprState  *estate;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       outlist = lappend(outlist, estate);
+                               }
+                               rstate->largs = outlist;
+                               Assert(list_length(rcexpr->rargs) == nopers);
+                               outlist = NIL;
+                               foreach(l, rcexpr->rargs)
+                               {
+                                       Expr       *e = (Expr *) lfirst(l);
+                                       ExprState  *estate;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       outlist = lappend(outlist, estate);
+                               }
+                               rstate->rargs = outlist;
+                               Assert(list_length(rcexpr->opfamilies) == nopers);
+                               rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo));
+                               i = 0;
+                               forboth(l, rcexpr->opnos, l2, rcexpr->opfamilies)
+                               {
+                                       Oid                     opno = lfirst_oid(l);
+                                       Oid                     opfamily = lfirst_oid(l2);
+                                       int                     strategy;
+                                       Oid                     lefttype;
+                                       Oid                     righttype;
+                                       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;
+                       }
+                       break;
                case T_CoalesceExpr:
                        {
                                CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
@@ -3206,6 +4229,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;
@@ -3221,11 +4325,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:
@@ -3249,6 +4355,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) cstate;
                        }
                        break;
+               case T_CurrentOfExpr:
+                       state = (ExprState *) makeNode(ExprState);
+                       state->evalfunc = ExecEvalCurrentOfExpr;
+                       break;
                case T_TargetEntry:
                        {
                                TargetEntry *tle = (TargetEntry *) node;
@@ -3286,32 +4396,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.
@@ -3395,16 +4479,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;
 
@@ -3547,8 +4631,7 @@ 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);
@@ -3557,8 +4640,8 @@ ExecTargetList(List *targetlist,
                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)
                        {
@@ -3576,8 +4659,8 @@ ExecTargetList(List *targetlist,
                                        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;
@@ -3586,9 +4669,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?
                         */
@@ -3651,8 +4734,8 @@ ExecVariableList(ProjectionInfo *projInfo,
                                                  projInfo->pi_lastScanVar);
 
        /*
-        * Assign to result by direct extraction of fields from source
-        * slots ... a mite ugly, but fast ...
+        * 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--)
        {
@@ -3694,10 +4777,9 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
        slot = projInfo->pi_slot;
 
        /*
-        * 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.)
+        * Clear any former contents of the result slot.  This makes it safe for
+        * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+        * return the slot as-is if we decide no rows can be projected.)
         */
        ExecClearTuple(slot);