]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/execQual.c
Cause ARRAY[] construct to return a NULL array, rather than raising an
[postgresql] / src / backend / executor / execQual.c
index 83117d836ebef83d7d5f9fb3233a40fb09c32708..d2efab0e36fa459b5991387cf0d2fda832604d3a 100644 (file)
@@ -3,12 +3,12 @@
  * execQual.c
  *       Routines to evaluate qualification and targetlist expressions
  *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.78 2000/08/21 20:55:30 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.137 2003/07/30 19:02:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "access/heapam.h"
-#include "catalog/pg_language.h"
-#include "executor/execFlatten.h"
+#include "catalog/pg_type.h"
+#include "commands/typecmds.h"
 #include "executor/execdebug.h"
 #include "executor/functions.h"
 #include "executor/nodeSubplan.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/fmgroids.h"
-#include "utils/fcache2.h"
+#include "utils/lsyscache.h"
 
 
 /* static function decls */
-static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
-                                bool *isNull, bool *isDone);
-static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
-                        bool *isNull);
-static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
-                        bool *isNull, bool *isDone);
-static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
-                                                        List *argList, FunctionCallInfo fcinfo,
-                                                        bool *argIsDone);
-static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
+static Datum ExecEvalAggref(AggrefExprState *aggref,
+                                                       ExprContext *econtext,
+                                                       bool *isNull);
+static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
+                                                         ExprContext *econtext,
+                                                         bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
-static Datum ExecMakeFunctionResult(Node *node, List *arguments,
-                                          ExprContext *econtext, bool *isNull, bool *isDone);
+static Datum ExecEvalParam(Param *expression, ExprContext *econtext,
+                                                  bool *isNull);
+static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
+                        bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
+                        bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
+                                bool *isNull);
+static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
+                                                                  ExprContext *econtext, bool *isNull);
+static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
+                                List *argList, ExprContext *econtext);
+static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
+                                                bool *isNull);
+static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
+                                               bool *isNull);
+static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
+                                                bool *isNull);
+static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
+                        bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalArray(ArrayExprState *astate,
+                                                  ExprContext *econtext,
+                                                  bool *isNull);
+static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
+                                                         ExprContext *econtext,
+                                                         bool *isNull);
+static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext,
+                                                       bool *isNull);
+static Datum ExecEvalNullTest(GenericExprState *nstate,
+                                                         ExprContext *econtext,
+                                                         bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalBooleanTest(GenericExprState *bstate,
+                                                                ExprContext *econtext,
+                                                                bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate,
+                                          ExprContext *econtext,
+                                          bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalCoerceToDomainValue(CoerceToDomainValue *conVal,
+                                          ExprContext *econtext, bool *isNull);
+static Datum ExecEvalFieldSelect(GenericExprState *fstate,
+                                                                ExprContext *econtext,
+                                                                bool *isNull, ExprDoneCond *isDone);
+
 
 /*----------
  *       ExecEvalArrayRef
@@ -77,7 +114,7 @@ static Datum ExecMakeFunctionResult(Node *node, List *arguments,
  * 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
+ * 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.)
@@ -90,11 +127,12 @@ static Datum ExecMakeFunctionResult(Node *node, List *arguments,
  *----------
  */
 static Datum
-ExecEvalArrayRef(ArrayRef *arrayRef,
+ExecEvalArrayRef(ArrayRefExprState *astate,
                                 ExprContext *econtext,
                                 bool *isNull,
-                                bool *isDone)
+                                ExprDoneCond *isDone)
 {
+       ArrayRef   *arrayRef = (ArrayRef *) astate->xprstate.expr;
        ArrayType  *array_source;
        ArrayType  *resultArray;
        bool            isAssignment = (arrayRef->refassgnexpr != NULL);
@@ -104,28 +142,25 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
        IntArray        upper,
                                lower;
        int                *lIndex;
-       bool            dummy;
-
-       *isNull = false;
 
        if (arrayRef->refexpr != NULL)
        {
                array_source = (ArrayType *)
-                       DatumGetPointer(ExecEvalExpr(arrayRef->refexpr,
+                       DatumGetPointer(ExecEvalExpr(astate->refexpr,
                                                                                 econtext,
                                                                                 isNull,
                                                                                 isDone));
+
                /*
                 * If refexpr yields NULL, result is always NULL, for now anyway.
-                * (This means you cannot assign to an element or slice of an array
-                * that's NULL; it'll just stay NULL.)
+                * (This means you cannot assign to an element or slice of an
+                * array that's NULL; it'll just stay NULL.)
                 */
                if (*isNull)
                        return (Datum) NULL;
        }
        else
        {
-
                /*
                 * Empty refexpr indicates we are doing an INSERT into an array
                 * column. For now, we just take the refassgnexpr (which the
@@ -137,50 +172,58 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
                array_source = NULL;
        }
 
-       foreach(elt, arrayRef->refupperindexpr)
+       foreach(elt, astate->refupperindexpr)
        {
                if (i >= MAXDIM)
-                       elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
-                                MAXDIM);
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                        errmsg("number of array dimensions exceeds the maximum allowed, %d",
+                                                       MAXDIM)));
 
-               upper.indx[i++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
+               upper.indx[i++] = DatumGetInt32(ExecEvalExpr((ExprState *) lfirst(elt),
                                                                                                         econtext,
                                                                                                         isNull,
-                                                                                                        &dummy));
+                                                                                                        NULL));
                /* If any index expr yields NULL, result is NULL or source array */
                if (*isNull)
                {
-                       if (! isAssignment || array_source == NULL)
+                       if (!isAssignment || array_source == NULL)
                                return (Datum) NULL;
                        *isNull = false;
                        return PointerGetDatum(array_source);
                }
        }
 
-       if (arrayRef->reflowerindexpr != NIL)
+       if (astate->reflowerindexpr != NIL)
        {
-               foreach(elt, arrayRef->reflowerindexpr)
+               foreach(elt, astate->reflowerindexpr)
                {
                        if (j >= MAXDIM)
-                               elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
-                                        MAXDIM);
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                                errmsg("number of array dimensions exceeds the maximum allowed, %d",
+                                                               MAXDIM)));
 
-                       lower.indx[j++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
+                       lower.indx[j++] = DatumGetInt32(ExecEvalExpr((ExprState *) lfirst(elt),
                                                                                                                 econtext,
                                                                                                                 isNull,
-                                                                                                                &dummy));
-                       /* If any index expr yields NULL, result is NULL or source array */
+                                                                                                                NULL));
+
+                       /*
+                        * If any index expr yields NULL, result is NULL or source
+                        * array
+                        */
                        if (*isNull)
                        {
-                               if (! isAssignment || array_source == NULL)
+                               if (!isAssignment || array_source == NULL)
                                        return (Datum) NULL;
                                *isNull = false;
                                return PointerGetDatum(array_source);
                        }
                }
+               /* this can't happen unless parser messed up */
                if (i != j)
-                       elog(ERROR,
-                                "ExecEvalArrayRef: upper and lower indices mismatch");
+                       elog(ERROR, "upper and lower index lists are not same length");
                lIndex = lower.indx;
        }
        else
@@ -188,13 +231,14 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 
        if (isAssignment)
        {
-               Datum           sourceData = ExecEvalExpr(arrayRef->refassgnexpr,
+               Datum           sourceData = ExecEvalExpr(astate->refassgnexpr,
                                                                                          econtext,
                                                                                          isNull,
-                                                                                         &dummy);
+                                                                                         NULL);
+
                /*
-                * For now, can't cope with inserting NULL into an array,
-                * so make it a no-op per discussion above...
+                * For now, can't cope with inserting NULL into an array, so make
+                * it a no-op per discussion above...
                 */
                if (*isNull)
                {
@@ -205,41 +249,44 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
                }
 
                if (array_source == NULL)
-                       return sourceData;              /* XXX do something else? */
+                       return sourceData;      /* XXX do something else? */
 
                if (lIndex == NULL)
                        resultArray = array_set(array_source, i,
                                                                        upper.indx,
                                                                        sourceData,
-                                                                       arrayRef->refelembyval,
-                                                                       arrayRef->refelemlength,
-                                                                       arrayRef->refattrlength,
+                                                                       astate->refattrlength,
+                                                                       astate->refelemlength,
+                                                                       astate->refelembyval,
+                                                                       astate->refelemalign,
                                                                        isNull);
                else
                        resultArray = array_set_slice(array_source, i,
                                                                                  upper.indx, lower.indx,
-                                                                                 (ArrayType *) DatumGetPointer(sourceData),
-                                                                                 arrayRef->refelembyval,
-                                                                                 arrayRef->refelemlength,
-                                                                                 arrayRef->refattrlength,
+                                                          (ArrayType *) DatumGetPointer(sourceData),
+                                                                                 astate->refattrlength,
+                                                                                 astate->refelemlength,
+                                                                                 astate->refelembyval,
+                                                                                 astate->refelemalign,
                                                                                  isNull);
                return PointerGetDatum(resultArray);
        }
 
        if (lIndex == NULL)
-               return array_ref(array_source, i,
-                                                upper.indx,
-                                                arrayRef->refelembyval,
-                                                arrayRef->refelemlength,
-                                                arrayRef->refattrlength,
+               return array_ref(array_source, i, upper.indx,
+                                                astate->refattrlength,
+                                                astate->refelemlength,
+                                                astate->refelembyval,
+                                                astate->refelemalign,
                                                 isNull);
        else
        {
                resultArray = array_get_slice(array_source, i,
                                                                          upper.indx, lower.indx,
-                                                                         arrayRef->refelembyval,
-                                                                         arrayRef->refelemlength,
-                                                                         arrayRef->refattrlength,
+                                                                         astate->refattrlength,
+                                                                         astate->refelemlength,
+                                                                         astate->refelembyval,
+                                                                         astate->refelemalign,
                                                                          isNull);
                return PointerGetDatum(resultArray);
        }
@@ -254,10 +301,10 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull)
+ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, bool *isNull)
 {
        if (econtext->ecxt_aggvalues == NULL)           /* safety check */
-               elog(ERROR, "ExecEvalAggref: no aggregates in this expression context");
+               elog(ERROR, "no aggregates in this expression context");
 
        *isNull = econtext->ecxt_aggnulls[aggref->aggno];
        return econtext->ecxt_aggvalues[aggref->aggno];
@@ -330,28 +377,32 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
 
        /*
         * If the attribute number is invalid, then we are supposed to return
-        * the entire tuple, we give back a whole slot so that callers know
+        * the entire tuple; we give back a whole slot so that callers know
         * what the tuple looks like.
+        *
+        * XXX this is a horrid crock: since the pointer to the slot might live
+        * longer than the current evaluation context, we are forced to copy
+        * the tuple and slot into a long-lived context --- we use
+        * the econtext's per-query memory which should be safe enough.  This
+        * represents a serious memory leak if many such tuples are processed
+        * in one command, however.  We ought to redesign the representation
+        * of whole-tuple datums so that this is not necessary.
+        *
+        * We assume it's OK to point to the existing tupleDescriptor, rather
+        * than copy that too.
         */
        if (attnum == InvalidAttrNumber)
        {
+               MemoryContext oldContext;
                TupleTableSlot *tempSlot;
-               TupleDesc       td;
                HeapTuple       tup;
 
-               tempSlot = makeNode(TupleTableSlot);
-               tempSlot->ttc_shouldFree = false;
-               tempSlot->ttc_descIsNew = true;
-               tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL;
-               tempSlot->ttc_buffer = InvalidBuffer;
-               tempSlot->ttc_whichplan = -1;
-
+               oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+               tempSlot = MakeTupleTableSlot();
                tup = heap_copytuple(heapTuple);
-               td = CreateTupleDescCopy(tuple_type);
-
-               ExecSetSlotDescriptor(tempSlot, td);
-
                ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
+               ExecSetSlotDescriptor(tempSlot, tuple_type, false);
+               MemoryContextSwitchTo(oldContext);
                return PointerGetDatum(tempSlot);
        }
 
@@ -370,40 +421,32 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
  *             Returns the value of a parameter.  A param node contains
  *             something like ($.name) and the expression context contains
  *             the current parameter bindings (name = "sam") (age = 34)...
- *             so our job is to replace the param node with the datum
- *             containing the appropriate information ("sam").
+ *             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 shoud return a Const node with the
- *                     isnull flag) ?  -cim 10/13/89
- *
- *             Minor modification: Param nodes now have an extra field,
- *             `paramkind' which specifies the type of parameter
- *             (see params.h). So while searching the paramList for
- *             a paramname/value pair, we have also to check for `kind'.
- *
- *             NOTE: The last entry in `paramList' is always an
- *             entry with kind == PARAM_INVALID.
+ *                (in which case we could return NULL)?        -cim 10/13/89
  * ----------------------------------------------------------------
  */
-Datum
+static Datum
 ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
 {
-       char       *thisParameterName;
-       int                     thisParameterKind = expression->paramkind;
-       AttrNumber      thisParameterId = expression->paramid;
-       int                     matchFound;
-       ParamListInfo paramList;
+       int                     thisParamKind = expression->paramkind;
+       AttrNumber      thisParamId = expression->paramid;
 
-       if (thisParameterKind == PARAM_EXEC)
+       if (thisParamKind == PARAM_EXEC)
        {
+               /*
+                * PARAM_EXEC params (internal executor parameters) are stored in
+                * the ecxt_param_exec_vals array, and can be accessed by array index.
+                */
                ParamExecData *prm;
 
-               prm = &(econtext->ecxt_param_exec_vals[thisParameterId]);
+               prm = &(econtext->ecxt_param_exec_vals[thisParamId]);
                if (prm->execPlan != NULL)
                {
+                       /* Parameter not evaluated yet, so go do it */
                        ExecSetParamPlan(prm->execPlan, econtext);
                        /* ExecSetParamPlan should have processed this param... */
                        Assert(prm->execPlan == NULL);
@@ -411,84 +454,60 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
                *isNull = prm->isnull;
                return prm->value;
        }
-
-       thisParameterName = expression->paramname;
-       paramList = econtext->ecxt_param_list_info;
-
-       *isNull = false;
-
-       /*
-        * search the list with the parameter info to find a matching name. An
-        * entry with an InvalidName denotes the last element in the array.
-        */
-       matchFound = 0;
-       if (paramList != NULL)
+       else
        {
-
                /*
-                * search for an entry in 'paramList' that matches the
-                * `expression'.
+                * All other parameter types must be sought in ecxt_param_list_info.
+                * NOTE: The last entry in the param array is always an
+                * entry with kind == PARAM_INVALID.
                 */
-               while (paramList->kind != PARAM_INVALID && !matchFound)
+               ParamListInfo paramList = econtext->ecxt_param_list_info;
+               char       *thisParamName = expression->paramname;
+               bool            matchFound = false;
+
+               if (paramList != NULL)
                {
-                       switch (thisParameterKind)
+                       while (paramList->kind != PARAM_INVALID && !matchFound)
                        {
-                               case PARAM_NAMED:
-                                       if (thisParameterKind == paramList->kind &&
-                                               strcmp(paramList->name, thisParameterName) == 0)
-                                               matchFound = 1;
-                                       break;
-                               case PARAM_NUM:
-                                       if (thisParameterKind == paramList->kind &&
-                                               paramList->id == thisParameterId)
-                                               matchFound = 1;
-                                       break;
-                               case PARAM_OLD:
-                               case PARAM_NEW:
-                                       if (thisParameterKind == paramList->kind &&
-                                               paramList->id == thisParameterId)
+                               if (thisParamKind == paramList->kind)
+                               {
+                                       switch (thisParamKind)
                                        {
-                                               matchFound = 1;
-
-                                               /*
-                                                * sanity check
-                                                */
-                                               if (strcmp(paramList->name, thisParameterName) != 0)
-                                               {
-                                                       elog(ERROR,
-                                                                "ExecEvalParam: new/old params with same id & diff names");
-                                               }
+                                               case PARAM_NAMED:
+                                                       if (strcmp(paramList->name, thisParamName) == 0)
+                                                               matchFound = true;
+                                                       break;
+                                               case PARAM_NUM:
+                                                       if (paramList->id == thisParamId)
+                                                               matchFound = true;
+                                                       break;
+                                               default:
+                                                       elog(ERROR, "unrecognized paramkind: %d",
+                                                                thisParamKind);
                                        }
-                                       break;
-                               default:
-
-                                       /*
-                                        * oops! this is not supposed to happen!
-                                        */
-                                       elog(ERROR, "ExecEvalParam: invalid paramkind %d",
-                                                thisParameterKind);
-                       }
-                       if (!matchFound)
-                               paramList++;
-               }                                               /* while */
-       }                                                       /* if */
+                               }
+                               if (!matchFound)
+                                       paramList++;
+                       } /* while */
+               } /* if */
 
-       if (!matchFound)
-       {
+               if (!matchFound)
+               {
+                       if (thisParamKind == PARAM_NAMED)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                                errmsg("no value found for parameter \"%s\"",
+                                                               thisParamName)));
+                       else
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                                errmsg("no value found for parameter %d",
+                                                               thisParamId)));
+               }
 
-               /*
-                * ooops! we couldn't find this parameter in the parameter list.
-                * Signal an error
-                */
-               elog(ERROR, "ExecEvalParam: Unknown value for parameter %s",
-                        thisParameterName);
+               *isNull = paramList->isnull;
+               return paramList->value;
        }
-
-       /*
-        * return the value.
-        */
-       *isNull = paramList->isnull;
-       return paramList->value;
 }
 
 
@@ -505,13 +524,8 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
  *             named attribute out of the tuple from the arg slot.  User defined
  *             C functions which take a tuple as an argument are expected
  *             to use this.  Ex: overpaid(EMP) might call GetAttributeByNum().
- *
- * XXX these two functions are misdeclared: they should be declared to
- * return Datum.  They are not used anywhere in the backend proper, and
- * exist only for use by user-defined functions.  Should we change their
- * definitions, at risk of breaking user code?
  */
-char *
+Datum
 GetAttributeByNum(TupleTableSlot *slot,
                                  AttrNumber attrno,
                                  bool *isNull)
@@ -519,18 +533,15 @@ GetAttributeByNum(TupleTableSlot *slot,
        Datum           retval;
 
        if (!AttributeNumberIsValid(attrno))
-               elog(ERROR, "GetAttributeByNum: Invalid attribute number");
-
-       if (!AttrNumberIsForUserDefinedAttr(attrno))
-               elog(ERROR, "GetAttributeByNum: cannot access system attributes here");
+               elog(ERROR, "invalid attribute number %d", attrno);
 
        if (isNull == (bool *) NULL)
-               elog(ERROR, "GetAttributeByNum: a NULL isNull flag was passed");
+               elog(ERROR, "a NULL isNull pointer was passed");
 
        if (TupIsNull(slot))
        {
                *isNull = true;
-               return (char *) NULL;
+               return (Datum) 0;
        }
 
        retval = heap_getattr(slot->val,
@@ -538,11 +549,12 @@ GetAttributeByNum(TupleTableSlot *slot,
                                                  slot->ttc_tupleDescriptor,
                                                  isNull);
        if (*isNull)
-               return (char *) NULL;
-       return (char *) retval;
+               return (Datum) 0;
+
+       return retval;
 }
 
-char *
+Datum
 GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
 {
        AttrNumber      attrno;
@@ -552,15 +564,15 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
        int                     i;
 
        if (attname == NULL)
-               elog(ERROR, "GetAttributeByName: Invalid attribute name");
+               elog(ERROR, "invalid attribute name");
 
        if (isNull == (bool *) NULL)
-               elog(ERROR, "GetAttributeByName: a NULL isNull flag was passed");
+               elog(ERROR, "a NULL isNull pointer was passed");
 
        if (TupIsNull(slot))
        {
                *isNull = true;
-               return (char *) NULL;
+               return (Datum) 0;
        }
 
        tupdesc = slot->ttc_tupleDescriptor;
@@ -577,173 +589,183 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
        }
 
        if (attrno == InvalidAttrNumber)
-               elog(ERROR, "GetAttributeByName: attribute %s not found", attname);
+               elog(ERROR, "attribute \"%s\" does not exist", attname);
 
        retval = heap_getattr(slot->val,
                                                  attrno,
                                                  tupdesc,
                                                  isNull);
        if (*isNull)
-               return (char *) NULL;
-       return (char *) retval;
+               return (Datum) 0;
+
+       return retval;
 }
 
+/*
+ * init_fcache - initialize a FuncExprState node during first use
+ */
+void
+init_fcache(Oid foid, FuncExprState *fcache, MemoryContext fcacheCxt)
+{
+       AclResult       aclresult;
+
+       /* Check permission to call function */
+       aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, get_func_name(foid));
 
-static void
-ExecEvalFuncArgs(FunctionCachePtr fcache,
-                                ExprContext *econtext,
+       /* Safety check (should never fail, as parser should check sooner) */
+       if (length(fcache->args) > FUNC_MAX_ARGS)
+               elog(ERROR, "too many arguments");
+
+       /* Set up the primary fmgr lookup information */
+       fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
+
+       /* Initialize additional info */
+       fcache->setArgsValid = false;
+       fcache->func.fn_expr = (Node *) fcache->xprstate.expr;
+}
+
+/*
+ * Evaluate arguments for a function.
+ */
+static ExprDoneCond
+ExecEvalFuncArgs(FunctionCallInfo fcinfo,
                                 List *argList,
-                                FunctionCallInfo fcinfo,
-                                bool *argIsDone)
+                                ExprContext *econtext)
 {
+       ExprDoneCond argIsDone;
        int                     i;
        List       *arg;
 
+       argIsDone = ExprSingleResult;           /* default assumption */
+
        i = 0;
        foreach(arg, argList)
        {
+               ExprDoneCond thisArgIsDone;
 
-               /*
-                * evaluate the expression, in general functions cannot take sets
-                * as arguments but we make an exception in the case of nested dot
-                * expressions.  We have to watch out for this case here.
-                */
-               fcinfo->arg[i] = ExecEvalExpr((Node *) lfirst(arg),
+               fcinfo->arg[i] = ExecEvalExpr((ExprState *) lfirst(arg),
                                                                          econtext,
                                                                          &fcinfo->argnull[i],
-                                                                         argIsDone);
+                                                                         &thisArgIsDone);
 
-               if (!(*argIsDone))
+               if (thisArgIsDone != ExprSingleResult)
                {
-                       if (i != 0)
-                               elog(ERROR, "functions can only take sets in their first argument");
-                       fcache->setArg = fcinfo->arg[0];
-                       fcache->hasSetArg = true;
+                       /*
+                        * 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,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("functions and operators can take at most one set argument")));
+                       argIsDone = thisArgIsDone;
                }
                i++;
        }
+
+       fcinfo->nargs = i;
+
+       return argIsDone;
 }
 
 /*
  *             ExecMakeFunctionResult
+ *
+ * Evaluate the arguments to a function and then the function itself.
  */
-static Datum
-ExecMakeFunctionResult(Node *node,
-                                          List *arguments,
+Datum
+ExecMakeFunctionResult(FuncExprState *fcache,
                                           ExprContext *econtext,
                                           bool *isNull,
-                                          bool *isDone)
+                                          ExprDoneCond *isDone)
 {
-       FunctionCallInfoData    fcinfo;
-       FunctionCachePtr                fcache;
-       bool                                    funcisset;
-       Datum                                   result;
-       bool                                    argDone;
-
-       MemSet(&fcinfo, 0, sizeof(fcinfo));
-
-       /*
-        * This is kind of ugly, Func nodes now have targetlists so that we
-        * know when and what to project out from postquel function results.
-        * ExecMakeFunctionResult becomes a little bit more of a dual personality
-        * as a result.
-        */
-       if (IsA(node, Func))
-       {
-               fcache = ((Func *) node)->func_fcache;
-               funcisset = (((Func *) node)->funcid == F_SETEVAL);
-       }
-       else
-       {
-               fcache = ((Oper *) node)->op_fcache;
-               funcisset = false;
-       }
-
-       fcinfo.flinfo = &fcache->func;
-       fcinfo.nargs = fcache->nargs;
+       List       *arguments = fcache->args;
+       Datum           result;
+       FunctionCallInfoData fcinfo;
+       ReturnSetInfo rsinfo;           /* for functions returning sets */
+       ExprDoneCond argDone;
+       bool            hasSetArg;
+       int                     i;
 
        /*
         * arguments is a list of expressions to evaluate before passing to
-        * the function manager.  We collect the results of evaluating the
-        * expressions into the FunctionCallInfo struct.  Note we assume that
-        * fcache->nargs is the correct length of the arguments list!
+        * 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->nargs > 0)
+       if (!fcache->setArgsValid)
        {
-               if (fcache->nargs > FUNC_MAX_ARGS)
-                       elog(ERROR, "ExecMakeFunctionResult: too many arguments");
-
-               /*
-                * If the setArg in the fcache is set we have an argument
-                * returning a set of tuples (i.e. a nested dot expression).  We
-                * don't want to evaluate the arguments again until the function
-                * is done. hasSetArg will always be false until we eval the args
-                * for the first time.
-                */
-               if (fcache->hasSetArg && fcache->setArg != (Datum) 0)
-               {
-                       fcinfo.arg[0] = fcache->setArg;
-                       argDone = false;
-               }
-               else
-                       ExecEvalFuncArgs(fcache, econtext, arguments, &fcinfo, &argDone);
-
-               if (fcache->hasSetArg && argDone)
+               /* Need to prep callinfo structure */
+               MemSet(&fcinfo, 0, sizeof(fcinfo));
+               fcinfo.flinfo = &(fcache->func);
+               argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext);
+               if (argDone == ExprEndResult)
                {
-                       /* can only get here if input is an empty set. */
+                       /* input is an empty set, so return an empty set. */
                        *isNull = true;
-                       *isDone = true;
+                       if (isDone)
+                               *isDone = ExprEndResult;
+                       else
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("set-valued function called in context that cannot accept a set")));
                        return (Datum) 0;
                }
+               hasSetArg = (argDone != ExprSingleResult);
+       }
+       else
+       {
+               /* Copy callinfo from previous evaluation */
+               memcpy(&fcinfo, &fcache->setArgs, sizeof(fcinfo));
+               hasSetArg = fcache->setHasSetArg;
+               /* Reset flag (we may set it again below) */
+               fcache->setArgsValid = false;
        }
 
        /*
-        * If this function is really a set, we have to diddle with things. If
-        * the function has already been called at least once, then the setArg
-        * field of the fcache holds the OID of this set in pg_proc.  (This is
-        * not quite legit, since the setArg field is really for functions
-        * which take sets of tuples as input - set functions take no inputs
-        * at all.      But it's a nice place to stash this value, for now.)
-        *
-        * If this is the first call of the set's function, then the call to
-        * ExecEvalFuncArgs above just returned the OID of the pg_proc tuple
-        * which defines this set.      So replace the existing funcid in the
-        * funcnode with the set's OID.  Also, we want a new fcache which
-        * points to the right function, so get that, now that we have the
-        * right OID.  Also zero out fcinfo.arg, since the real set doesn't take
-        * any arguments.
+        * If function returns set, prepare a resultinfo node for
+        * communication
         */
-       if (funcisset)
+       if (fcache->func.fn_retset)
        {
-               if (fcache->setArg)
-               {
-                       ((Func *) node)->funcid = DatumGetObjectId(fcache->setArg);
-               }
-               else
-               {
-                       ((Func *) node)->funcid = DatumGetObjectId(fcinfo.arg[0]);
-                       setFcache(node, DatumGetObjectId(fcinfo.arg[0]), NIL, econtext);
-                       fcache = ((Func *) node)->func_fcache;
-                       fcache->setArg = fcinfo.arg[0];
-               }
-               fcinfo.arg[0] = (Datum) 0;
+               fcinfo.resultinfo = (Node *) &rsinfo;
+               rsinfo.type = T_ReturnSetInfo;
+               rsinfo.econtext = econtext;
+               rsinfo.expectedDesc = NULL;
+               rsinfo.allowedModes = (int) SFRM_ValuePerCall;
+               rsinfo.returnMode = SFRM_ValuePerCall;
+               /* isDone is filled below */
+               rsinfo.setResult = NULL;
+               rsinfo.setDesc = NULL;
        }
 
        /*
         * now return the value gotten by calling the function manager,
         * passing the function the evaluated parameter values.
         */
-       if (fcache->language == SQLlanguageId)
+       if (fcache->func.fn_retset || hasSetArg)
        {
-               /*--------------------
-                * This loop handles the situation where we are iterating through
-                * all results in a nested dot function (whose argument function
-                * returns a set of tuples) and the current function finally
-                * finishes.  We need to get the next argument in the set and start
-                * the function all over again.  We might have to do it more than
-                * once, if the function produces no results for a particular argument.
-                * This is getting unclean.
-                *--------------------
+               /*
+                * We need to return a set result.      Complain if caller not ready
+                * to accept one.
+                */
+               if (isDone == NULL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        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.
                 */
                for (;;)
                {
@@ -751,12 +773,10 @@ ExecMakeFunctionResult(Node *node,
                         * If function is strict, and there are any NULL arguments,
                         * skip calling the function (at least for this set of args).
                         */
-                       bool    callit = true;
+                       bool            callit = true;
 
-                       if (fcinfo.flinfo->fn_strict)
+                       if (fcache->func.fn_strict)
                        {
-                               int             i;
-
                                for (i = 0; i < fcinfo.nargs; i++)
                                {
                                        if (fcinfo.argnull[i])
@@ -769,35 +789,53 @@ ExecMakeFunctionResult(Node *node,
 
                        if (callit)
                        {
-                               result = postquel_function(&fcinfo, fcache, isDone);
+                               fcinfo.isnull = false;
+                               rsinfo.isDone = ExprSingleResult;
+                               result = FunctionCallInvoke(&fcinfo);
                                *isNull = fcinfo.isnull;
+                               *isDone = rsinfo.isDone;
                        }
                        else
                        {
                                result = (Datum) 0;
                                *isNull = true;
-                               *isDone = true;
+                               *isDone = ExprEndResult;
                        }
 
-                       if (!*isDone)
-                               break;                  /* got a result from current argument */
-                       if (!fcache->hasSetArg)
-                               break;                  /* input not a set, so done */
-
-                       /* OK, get the next argument... */
-                       ExecEvalFuncArgs(fcache, econtext, arguments, &fcinfo, &argDone);
-
-                       if (argDone)
+                       if (*isDone != ExprEndResult)
                        {
+                               /*
+                                * Got a result from current argument.  If function itself
+                                * returns set, save the current argument values to re-use
+                                * on the next call.
+                                */
+                               if (fcache->func.fn_retset)
+                               {
+                                       memcpy(&fcache->setArgs, &fcinfo, sizeof(fcinfo));
+                                       fcache->setHasSetArg = hasSetArg;
+                                       fcache->setArgsValid = true;
+                               }
 
                                /*
-                                * End of arguments, so reset the setArg flag and say
-                                * "Done"
+                                * Make sure we say we are returning a set, even if the
+                                * function itself doesn't return sets.
                                 */
-                               fcache->setArg = (Datum) 0;
-                               fcache->hasSetArg = false;
-                               *isDone = true;
+                               *isDone = ExprMultipleResult;
+                               break;
+                       }
+
+                       /* Else, done with this argument */
+                       if (!hasSetArg)
+                               break;                  /* input not a set, so done */
+
+                       /* Re-eval args to get the next element of the input set */
+                       argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext);
+
+                       if (argDone != ExprMultipleResult)
+                       {
+                               /* End of argument set, so we're done. */
                                *isNull = true;
+                               *isDone = ExprEndResult;
                                result = (Datum) 0;
                                break;
                        }
@@ -807,39 +845,17 @@ ExecMakeFunctionResult(Node *node,
                         * new argument.
                         */
                }
-
-               if (funcisset)
-               {
-
-                       /*
-                        * reset the funcid so that next call to this routine will
-                        * still recognize this func as a set. Note that for now we
-                        * assume that the set function in pg_proc must be a Postquel
-                        * function - the funcid is not reset below for C functions.
-                        */
-                       ((Func *) node)->funcid = F_SETEVAL;
-
-                       /*
-                        * If we're done with the results of this function, get rid of
-                        * its func cache.
-                        */
-                       if (*isDone)
-                               ((Func *) node)->func_fcache = NULL;
-               }
        }
        else
        {
-               /* A non-SQL function cannot return a set, at present. */
-               *isDone = true;
-
                /*
-                * If function is strict, and there are any NULL arguments,
-                * skip calling the function and return NULL.
+                * Non-set case: much easier.
+                *
+                * If function is strict, and there are any NULL arguments, skip
+                * calling the function and return NULL.
                 */
-               if (fcinfo.flinfo->fn_strict)
+               if (fcache->func.fn_strict)
                {
-                       int             i;
-
                        for (i = 0; i < fcinfo.nargs; i++)
                        {
                                if (fcinfo.argnull[i])
@@ -849,6 +865,7 @@ ExecMakeFunctionResult(Node *node,
                                }
                        }
                }
+               fcinfo.isnull = false;
                result = FunctionCallInvoke(&fcinfo);
                *isNull = fcinfo.isnull;
        }
@@ -857,182 +874,616 @@ ExecMakeFunctionResult(Node *node,
 }
 
 
-/* ----------------------------------------------------------------
- *             ExecEvalOper
- *             ExecEvalFunc
+/*
+ *             ExecMakeTableFunctionResult
  *
- *             Evaluate the functional result of a list of arguments by calling the
- *             function manager.
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- *             ExecEvalOper
- * ----------------------------------------------------------------
+ * Evaluate a table function, producing a materialized result in a Tuplestore
+ * object.     (If function returns an empty set, we just return NULL instead.)
  */
-static Datum
-ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
+Tuplestorestate *
+ExecMakeTableFunctionResult(ExprState *funcexpr,
+                                                       ExprContext *econtext,
+                                                       TupleDesc expectedDesc,
+                                                       TupleDesc *returnDesc)
 {
-       Oper       *op;
-       List       *argList;
-       FunctionCachePtr fcache;
-       bool            isDone;
-
-       /*
-        * we extract the oid of the function associated with the op and then
-        * pass the work onto ExecMakeFunctionResult which evaluates the
-        * arguments and returns the result of calling the function on the
-        * evaluated arguments.
-        */
-       op = (Oper *) opClause->oper;
-       argList = opClause->args;
+       Tuplestorestate *tupstore = NULL;
+       TupleDesc       tupdesc = NULL;
+       Oid                     funcrettype;
+       FunctionCallInfoData fcinfo;
+       ReturnSetInfo rsinfo;
+       MemoryContext callerContext;
+       MemoryContext oldcontext;
+       TupleTableSlot *slot;
+       bool            direct_function_call;
+       bool            first_time = true;
+       bool            returnsTuple = false;
 
        /*
-        * get the fcache from the Oper node. If it is NULL, then initialize
-        * it
+        * 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.
         */
-       fcache = op->op_fcache;
-       if (fcache == NULL)
+       if (funcexpr && IsA(funcexpr, FuncExprState) &&
+               IsA(funcexpr->expr, FuncExpr))
        {
-               setFcache((Node *) op, op->opid, argList, econtext);
-               fcache = op->op_fcache;
-       }
+               FuncExprState *fcache = (FuncExprState *) funcexpr;
+               ExprDoneCond argDone;
 
-       /*
-        * call ExecMakeFunctionResult() with a dummy isDone that we ignore.
-        * We don't have operator whose arguments are sets.
-        */
-       return ExecMakeFunctionResult((Node *) op, argList, econtext,
-                                                                 isNull, &isDone);
-}
+               /*
+                * This path is similar to ExecMakeFunctionResult.
+                */
+               direct_function_call = true;
 
-/* ----------------------------------------------------------------
- *             ExecEvalFunc
- * ----------------------------------------------------------------
- */
+               /*
+                * Initialize function cache if first time through
+                */
+               if (fcache->func.fn_oid == InvalidOid)
+               {
+                       FuncExpr *func = (FuncExpr *) fcache->xprstate.expr;
 
-static Datum
-ExecEvalFunc(Expr *funcClause,
-                        ExprContext *econtext,
-                        bool *isNull,
-                        bool *isDone)
-{
-       Func       *func;
-       List       *argList;
-       FunctionCachePtr fcache;
+                       init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory);
+               }
 
-       /*
-        * we extract the oid of the function associated with the func node and
-        * then pass the work onto ExecMakeFunctionResult which evaluates the
-        * arguments and returns the result of calling the function on the
-        * evaluated arguments.
-        *
-        * this is nearly identical to the ExecEvalOper code.
-        */
-       func = (Func *) funcClause->oper;
-       argList = funcClause->args;
+               /*
+                * 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?
+                */
+               MemSet(&fcinfo, 0, sizeof(fcinfo));
+               fcinfo.flinfo = &(fcache->func);
+               argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext);
+               /* We don't allow sets in the arguments of the table function */
+               if (argDone != ExprSingleResult)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("set-valued function called in context that cannot accept a set")));
 
-       /*
-        * get the fcache from the Func node. If it is NULL, then initialize
-        * it
-        */
-       fcache = func->func_fcache;
-       if (fcache == NULL)
+               /*
+                * If function is strict, and there are any NULL arguments, skip
+                * calling the function and return NULL (actually an empty set).
+                */
+               if (fcache->func.fn_strict)
+               {
+                       int                     i;
+
+                       for (i = 0; i < fcinfo.nargs; i++)
+                       {
+                               if (fcinfo.argnull[i])
+                               {
+                                       *returnDesc = NULL;
+                                       return NULL;
+                               }
+                       }
+               }
+       }
+       else
        {
-               setFcache((Node *) func, func->funcid, argList, econtext);
-               fcache = func->func_fcache;
+               /* Treat funcexpr as a generic expression */
+               direct_function_call = false;
        }
 
-       return ExecMakeFunctionResult((Node *) func, argList, econtext,
-                                                                 isNull, isDone);
-}
-
-/* ----------------------------------------------------------------
- *             ExecEvalNot
- *             ExecEvalOr
- *             ExecEvalAnd
- *
- *             Evaluate boolean expressions.  Evaluation of 'or' is
- *             short-circuited when the first true (or null) value is found.
- *
- *             The query planner reformulates clause expressions in the
- *             qualification to conjunctive normal form.  If we ever get
- *             an AND to evaluate, we can be sure that it's not a top-level
- *             clause in the qualification, but appears lower (as a function
- *             argument, for example), or in the target list.  Not that you
- *             need to know this, mind you...
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
-{
-       Node       *clause;
-       Datum           expr_value;
-       bool            isDone;
-
-       clause = lfirst(notclause->args);
-
-       /*
-        * We don't iterate over sets in the quals, so pass in an isDone flag,
-        * but ignore it.
-        */
-       expr_value = ExecEvalExpr(clause, econtext, isNull, &isDone);
+       funcrettype = exprType((Node *) funcexpr->expr);
 
        /*
-        * if the expression evaluates to null, then we just cascade the null
-        * back to whoever called us.
+        * 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.
         */
-       if (*isNull)
-               return expr_value;
+       fcinfo.resultinfo = (Node *) &rsinfo;
+       rsinfo.type = T_ReturnSetInfo;
+       rsinfo.econtext = econtext;
+       rsinfo.expectedDesc = expectedDesc;
+       rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
+       rsinfo.returnMode = SFRM_ValuePerCall;
+       /* isDone is filled below */
+       rsinfo.setResult = NULL;
+       rsinfo.setDesc = NULL;
 
        /*
-        * evaluation of 'not' is simple.. expr is false, then return 'true'
-        * and vice versa.
+        * Switch to short-lived context for calling the function or expression.
         */
-       return BoolGetDatum(! DatumGetBool(expr_value));
-}
-
-/* ----------------------------------------------------------------
- *             ExecEvalOr
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
-{
-       List       *clauses;
-       List       *clause;
-       bool            isDone;
-       bool            AnyNull;
-       Datum           clause_value;
-
-       clauses = orExpr->args;
-       AnyNull = false;
+       callerContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
 
        /*
-        * 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.
+        * Loop to handle the ValuePerCall protocol (which is also the same
+        * behavior needed in the generic ExecEvalExpr path).
         */
-       foreach(clause, clauses)
+       for (;;)
        {
+               Datum           result;
+               HeapTuple       tuple;
 
                /*
-                * We don't iterate over sets in the quals, so pass in an isDone
-                * flag, but ignore it.
+                * 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.
                 */
-               clause_value = ExecEvalExpr((Node *) lfirst(clause),
-                                                                       econtext,
-                                                                       isNull,
-                                                                       &isDone);
+               ResetExprContext(econtext);
+
+               /* Call the function or expression one time */
+               if (direct_function_call)
+               {
+                       fcinfo.isnull = false;
+                       rsinfo.isDone = ExprSingleResult;
+                       result = FunctionCallInvoke(&fcinfo);
+               }
+               else
+               {
+                       result = ExecEvalExpr(funcexpr, econtext,
+                                                                 &fcinfo.isnull, &rsinfo.isDone);
+               }
+
+               /* Which protocol does function want to use? */
+               if (rsinfo.returnMode == SFRM_ValuePerCall)
+               {
+                       /*
+                        * Check for end of result set.
+                        *
+                        * Note: if function returns an empty set, we don't build a
+                        * tupdesc or tuplestore (since we can't get a tupdesc in the
+                        * function-returning-tuple case)
+                        */
+                       if (rsinfo.isDone == ExprEndResult)
+                               break;
+
+                       /*
+                        * If first time through, build tupdesc and tuplestore for
+                        * result
+                        */
+                       if (first_time)
+                       {
+                               oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+                               if (funcrettype == RECORDOID ||
+                                       get_typtype(funcrettype) == 'c')
+                               {
+                                       /*
+                                        * Composite type, so function should have returned a
+                                        * TupleTableSlot; use its descriptor
+                                        */
+                                       slot = (TupleTableSlot *) DatumGetPointer(result);
+                                       if (fcinfo.isnull || !slot)
+                                               ereport(ERROR,
+                                                               (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                                                                errmsg("function returning tuple cannot return NULL")));
+                                       if (!IsA(slot, TupleTableSlot) ||
+                                               !slot->ttc_tupleDescriptor)
+                                               ereport(ERROR,
+                                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                                errmsg("function returning tuple did not return a valid tuple slot")));
+                                       tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
+                                       returnsTuple = true;
+                               }
+                               else
+                               {
+                                       /*
+                                        * Scalar type, so make a single-column descriptor
+                                        */
+                                       tupdesc = CreateTemplateTupleDesc(1, false);
+                                       TupleDescInitEntry(tupdesc,
+                                                                          (AttrNumber) 1,
+                                                                          "column",
+                                                                          funcrettype,
+                                                                          -1,
+                                                                          0,
+                                                                          false);
+                               }
+                               tupstore = tuplestore_begin_heap(true, false, SortMem);
+                               MemoryContextSwitchTo(oldcontext);
+                               rsinfo.setResult = tupstore;
+                               rsinfo.setDesc = tupdesc;
+                       }
+
+                       /*
+                        * Store current resultset item.
+                        */
+                       if (returnsTuple)
+                       {
+                               slot = (TupleTableSlot *) DatumGetPointer(result);
+                               if (fcinfo.isnull ||
+                                       !slot ||
+                                       !IsA(slot, TupleTableSlot) ||
+                                       TupIsNull(slot))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                                                        errmsg("function returning tuple cannot return NULL")));
+                               tuple = slot->val;
+                       }
+                       else
+                       {
+                               char            nullflag;
+
+                               nullflag = fcinfo.isnull ? 'n' : ' ';
+                               tuple = heap_formtuple(tupdesc, &result, &nullflag);
+                       }
+
+                       oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+                       tuplestore_puttuple(tupstore, tuple);
+                       MemoryContextSwitchTo(oldcontext);
+
+                       /*
+                        * Are we done?
+                        */
+                       if (rsinfo.isDone != ExprMultipleResult)
+                               break;
+               }
+               else if (rsinfo.returnMode == SFRM_Materialize)
+               {
+                       /* check we're on the same page as the function author */
+                       if (!first_time || rsinfo.isDone != ExprSingleResult)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
+                                                errmsg("table-function protocol for materialize mode was not followed")));
+                       /* Done evaluating the set result */
+                       break;
+               }
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
+                                        errmsg("unrecognized table-function returnMode: %d",
+                                                       (int) rsinfo.returnMode)));
+
+               first_time = false;
+       }
+
+       MemoryContextSwitchTo(callerContext);
+
+       /* The returned pointers are those in rsinfo */
+       *returnDesc = rsinfo.setDesc;
+       return rsinfo.setResult;
+}
+
+
+/* ----------------------------------------------------------------
+ *             ExecEvalFunc
+ *             ExecEvalOper
+ *
+ *             Evaluate the functional result of a list of arguments by calling the
+ *             function manager.
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ *             ExecEvalFunc
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalFunc(FuncExprState *fcache,
+                        ExprContext *econtext,
+                        bool *isNull,
+                        ExprDoneCond *isDone)
+{
+       /*
+        * Initialize function cache if first time through
+        */
+       if (fcache->func.fn_oid == InvalidOid)
+       {
+               FuncExpr *func = (FuncExpr *) fcache->xprstate.expr;
+
+               init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory);
+       }
+
+       return ExecMakeFunctionResult(fcache, econtext, isNull, isDone);
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalOper
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalOper(FuncExprState *fcache,
+                        ExprContext *econtext,
+                        bool *isNull,
+                        ExprDoneCond *isDone)
+{
+       /*
+        * Initialize function cache if first time through
+        */
+       if (fcache->func.fn_oid == InvalidOid)
+       {
+               OpExpr *op = (OpExpr *) fcache->xprstate.expr;
+
+               init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory);
+       }
+
+       return ExecMakeFunctionResult(fcache, econtext, isNull, isDone);
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalDistinct
+ *
+ * IS DISTINCT FROM must evaluate arguments to determine whether
+ * they are NULL; if either is NULL then the result is already
+ * known. If neither is NULL, then proceed to evaluate the
+ * function. Note that this is *always* derived from the equals
+ * operator, but since we need special processing of the arguments
+ * we can not simply reuse ExecEvalOper() or ExecEvalFunc().
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalDistinct(FuncExprState *fcache,
+                                ExprContext *econtext,
+                                bool *isNull)
+{
+       Datum           result;
+       FunctionCallInfoData fcinfo;
+       ExprDoneCond argDone;
+       List       *argList;
+
+       /*
+        * Initialize function cache if first time through
+        */
+       if (fcache->func.fn_oid == InvalidOid)
+       {
+               DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr;
+
+               init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory);
+               Assert(!fcache->func.fn_retset);
+       }
+
+       /*
+        * extract info from fcache
+        */
+       argList = fcache->args;
+
+       /* Need to prep callinfo structure */
+       MemSet(&fcinfo, 0, sizeof(fcinfo));
+       fcinfo.flinfo = &(fcache->func);
+       argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
+       if (argDone != ExprSingleResult)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("IS DISTINCT FROM does not support set arguments")));
+       Assert(fcinfo.nargs == 2);
+
+       if (fcinfo.argnull[0] && fcinfo.argnull[1])
+       {
+               /* Both NULL? Then is not distinct... */
+               result = BoolGetDatum(FALSE);
+       }
+       else if (fcinfo.argnull[0] || fcinfo.argnull[1])
+       {
+               /* Only one is NULL? Then is distinct... */
+               result = BoolGetDatum(TRUE);
+       }
+       else
+       {
+               fcinfo.isnull = false;
+               result = FunctionCallInvoke(&fcinfo);
+               *isNull = fcinfo.isnull;
+               /* Must invert result of "=" */
+               result = BoolGetDatum(!DatumGetBool(result));
+       }
+
+       return result;
+}
+
+/*
+ * ExecEvalScalarArrayOp
+ *
+ * Evaluate "scalar op ANY/ALL (array)".  The operator always yields boolean,
+ * and we combine the results across all array elements using OR and AND
+ * (for ANY and ALL respectively).  Of course we short-circuit as soon as
+ * the result is known.
+ */
+static Datum
+ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
+                                         ExprContext *econtext, bool *isNull)
+{
+       ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) sstate->fxprstate.xprstate.expr;
+       bool            useOr = opexpr->useOr;
+       ArrayType  *arr;
+       int                     nitems;
+       Datum           result;
+       bool            resultnull;
+       FunctionCallInfoData fcinfo;
+       ExprDoneCond argDone;
+       int                     i;
+       int16           typlen;
+       bool            typbyval;
+       char            typalign;
+       char       *s;
+
+       /*
+        * Initialize function cache if first time through
+        */
+       if (sstate->fxprstate.func.fn_oid == InvalidOid)
+       {
+               init_fcache(opexpr->opfuncid, &sstate->fxprstate,
+                                       econtext->ecxt_per_query_memory);
+               Assert(!sstate->fxprstate.func.fn_retset);
+       }
+
+       /* Need to prep callinfo structure */
+       MemSet(&fcinfo, 0, sizeof(fcinfo));
+       fcinfo.flinfo = &(sstate->fxprstate.func);
+       argDone = ExecEvalFuncArgs(&fcinfo, sstate->fxprstate.args, econtext);
+       if (argDone != ExprSingleResult)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("op ANY/ALL (array) does not support set arguments")));
+       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 (fcinfo.argnull[1])
+       {
+               *isNull = true;
+               return (Datum) 0;
+       }
+       /* Else okay to fetch and detoast the array */
+       arr = DatumGetArrayTypeP(fcinfo.arg[1]);
+
+       /*
+        * 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.
+        */
+       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 (fcinfo.argnull[0] && sstate->fxprstate.func.fn_strict)
+       {
+               *isNull = true;
+               return (Datum) 0;
+       }
+
+       /*
+        * 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))
+       {
+               get_typlenbyvalalign(ARR_ELEMTYPE(arr),
+                                                        &sstate->typlen,
+                                                        &sstate->typbyval,
+                                                        &sstate->typalign);
+               sstate->element_type = ARR_ELEMTYPE(arr);
+       }
+       typlen = sstate->typlen;
+       typbyval = sstate->typbyval;
+       typalign = sstate->typalign;
+
+       result = BoolGetDatum(!useOr);
+       resultnull = false;
+
+       /* Loop over the array elements */
+       s = (char *) ARR_DATA_PTR(arr);
+       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);
+
+               /* Call comparison function */
+               fcinfo.arg[1] = elt;
+               fcinfo.argnull[1] = false;
+               fcinfo.isnull = false;
+               thisresult = FunctionCallInvoke(&fcinfo);
+
+               /* Combine results per OR or AND semantics */
+               if (fcinfo.isnull)
+                       resultnull = true;
+               else if (useOr)
+               {
+                       if (DatumGetBool(thisresult))
+                       {
+                               result = BoolGetDatum(true);
+                               resultnull = false;
+                               break;          /* needn't look at any more elements */
+                       }
+               }
+               else
+               {
+                       if (!DatumGetBool(thisresult))
+                       {
+                               result = BoolGetDatum(false);
+                               resultnull = false;
+                               break;          /* needn't look at any more elements */
+                       }
+               }
+       }
+
+       *isNull = resultnull;
+       return result;
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalNot
+ *             ExecEvalOr
+ *             ExecEvalAnd
+ *
+ *             Evaluate boolean expressions, with appropriate short-circuiting.
+ *
+ *             The query planner reformulates clause expressions in the
+ *             qualification to conjunctive normal form.  If we ever get
+ *             an AND to evaluate, we can be sure that it's not a top-level
+ *             clause in the qualification, but appears lower (as a function
+ *             argument, for example), or in the target list.  Not that you
+ *             need to know this, mind you...
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalNot(BoolExprState *notclause, ExprContext *econtext, bool *isNull)
+{
+       ExprState  *clause;
+       Datum           expr_value;
+
+       clause = lfirst(notclause->args);
+
+       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 (*isNull)
+               return expr_value;
+
+       /*
+        * evaluation of 'not' is simple.. expr is false, then return 'true'
+        * and vice versa.
+        */
+       return BoolGetDatum(!DatumGetBool(expr_value));
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalOr
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, bool *isNull)
+{
+       List       *clauses;
+       List       *clause;
+       bool            AnyNull;
+       Datum           clause_value;
+
+       clauses = orExpr->args;
+       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.
+        */
+       foreach(clause, clauses)
+       {
+               clause_value = ExecEvalExpr((ExprState *) lfirst(clause),
+                                                                       econtext, isNull, NULL);
 
                /*
                 * if we have a non-null true result, then return it.
@@ -1053,11 +1504,10 @@ ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
+ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull)
 {
        List       *clauses;
        List       *clause;
-       bool            isDone;
        bool            AnyNull;
        Datum           clause_value;
 
@@ -1074,22 +1524,15 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
         */
        foreach(clause, clauses)
        {
-
-               /*
-                * We don't iterate over sets in the quals, so pass in an isDone
-                * flag, but ignore it.
-                */
-               clause_value = ExecEvalExpr((Node *) lfirst(clause),
-                                                                       econtext,
-                                                                       isNull,
-                                                                       &isDone);
+               clause_value = ExecEvalExpr((ExprState *) lfirst(clause),
+                                                                       econtext, isNull, NULL);
 
                /*
                 * if we have a non-null false result, then return it.
                 */
                if (*isNull)
                        AnyNull = true;         /* remember we got a null */
-               else if (! DatumGetBool(clause_value))
+               else if (!DatumGetBool(clause_value))
                        return clause_value;
        }
 
@@ -1098,6 +1541,7 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
        return BoolGetDatum(!AnyNull);
 }
 
+
 /* ----------------------------------------------------------------
  *             ExecEvalCase
  *
@@ -1108,12 +1552,12 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
+ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
+                        bool *isNull, ExprDoneCond *isDone)
 {
        List       *clauses;
        List       *clause;
        Datum           clause_value;
-       bool            isDone;
 
        clauses = caseExpr->args;
 
@@ -1124,16 +1568,12 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
         */
        foreach(clause, clauses)
        {
-               CaseWhen   *wclause = lfirst(clause);
+               CaseWhenState *wclause = lfirst(clause);
 
-               /*
-                * We don't iterate over sets in the quals, so pass in an isDone
-                * flag, but ignore it.
-                */
                clause_value = ExecEvalExpr(wclause->expr,
                                                                        econtext,
                                                                        isNull,
-                                                                       &isDone);
+                                                                       NULL);
 
                /*
                 * if we have a true test, then we return the result, since the
@@ -1145,7 +1585,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
                        return ExecEvalExpr(wclause->result,
                                                                econtext,
                                                                isNull,
-                                                               &isDone);
+                                                               isDone);
                }
        }
 
@@ -1154,13 +1594,482 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
                return ExecEvalExpr(caseExpr->defresult,
                                                        econtext,
                                                        isNull,
-                                                       &isDone);
+                                                       isDone);
        }
 
        *isNull = true;
        return (Datum) 0;
 }
 
+/* ----------------------------------------------------------------
+ *             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
+ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
+                         bool *isNull)
+{
+       ArrayExpr   *arrayExpr = (ArrayExpr *) astate->xprstate.expr;
+       ArrayType  *result;
+       List   *element;
+       Oid             element_type = arrayExpr->element_typeid;
+       int             ndims = arrayExpr->ndims;
+       int             dims[MAXDIM];
+       int             lbs[MAXDIM];
+
+       if (ndims == 1)
+       {
+               int             nelems;
+               Datum  *dvalues;
+               int             i = 0;
+
+               nelems = length(astate->elements);
+
+               /* Shouldn't happen here, but if length is 0, return NULL */
+               if (nelems == 0)
+               {
+                       *isNull = true;
+                       return (Datum) 0;
+               }
+
+               dvalues = (Datum *) palloc(nelems * sizeof(Datum));
+
+               /* 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;
+                       }
+               }
+
+               /* setup for 1-D array of the given length */
+               dims[0] = nelems;
+               lbs[0] = 1;
+
+               result = construct_md_array(dvalues, ndims, dims, lbs,
+                                                                       element_type,
+                                                                       astate->elemlength,
+                                                                       astate->elembyval,
+                                                                       astate->elemalign);
+       }
+       else
+       {
+               char       *dat = NULL;
+               Size            ndatabytes = 0;
+               int                     nbytes;
+               int                     outer_nelems = length(astate->elements);
+               int                     elem_ndims = 0;
+               int                *elem_dims = NULL;
+               int                *elem_lbs = NULL;
+               bool            firstone = true;
+               int                     i;
+
+               if (ndims <= 0 || ndims > MAXDIM)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                        errmsg("number of array dimensions exceeds the maximum allowed, %d",
+                                                       MAXDIM)));
+
+               /* loop through and get data area from each element */
+               foreach(element, astate->elements)
+               {
+                       ExprState   *e = (ExprState *) lfirst(element);
+                       bool            eisnull;
+                       Datum           arraydatum;
+                       ArrayType  *array;
+                       int                     elem_ndatabytes;
+
+                       arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL);
+                       if (eisnull)
+                       {
+                               *isNull = true;
+                               return (Datum) 0;
+                       }
+
+                       array = DatumGetArrayTypeP(arraydatum);
+
+                       if (firstone)
+                       {
+                               /* Get sub-array details from first member */
+                               elem_ndims = ARR_NDIM(array);
+                               elem_dims = (int *) palloc(elem_ndims * sizeof(int));
+                               memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int));
+                               elem_lbs = (int *) palloc(elem_ndims * sizeof(int));
+                               memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int));
+                               firstone = false;
+                       }
+                       else
+                       {
+                               /* Check other sub-arrays are compatible */
+                               if (elem_ndims != ARR_NDIM(array) ||
+                                       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")));
+                       }
+
+                       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);
+               }
+
+               /* 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);
+
+               result->size = nbytes;
+               result->ndim = ndims;
+               result->flags = 0;
+               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);
+       }
+
+       return PointerGetDatum(result);
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalCoalesce
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
+                                bool *isNull)
+{
+       List *arg;
+
+       /* Simply loop through until something NOT NULL is found */
+       foreach(arg, coalesceExpr->args)
+       {
+               ExprState *e = (ExprState *) lfirst(arg);
+               Datum value;
+
+               value = ExecEvalExpr(e, econtext, isNull, NULL);
+               if (!*isNull)
+                       return value;
+       }
+
+       /* Else return NULL */
+       *isNull = true;
+       return (Datum) 0;
+}
+       
+/* ----------------------------------------------------------------
+ *             ExecEvalNullIf
+ *
+ * Note that this is *always* derived from the equals operator,
+ * but since we need special processing of the arguments
+ * we can not simply reuse ExecEvalOper() or ExecEvalFunc().
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalNullIf(FuncExprState *fcache, ExprContext *econtext,
+                          bool *isNull)
+{
+       Datum           result;
+       FunctionCallInfoData fcinfo;
+       ExprDoneCond argDone;
+       List       *argList;
+
+       /*
+        * Initialize function cache if first time through
+        */
+       if (fcache->func.fn_oid == InvalidOid)
+       {
+               NullIfExpr *op = (NullIfExpr *) fcache->xprstate.expr;
+
+               init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory);
+               Assert(!fcache->func.fn_retset);
+       }
+
+       /*
+        * extract info from fcache
+        */
+       argList = fcache->args;
+
+       /* Need to prep callinfo structure */
+       MemSet(&fcinfo, 0, sizeof(fcinfo));
+       fcinfo.flinfo = &(fcache->func);
+       argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
+       if (argDone != ExprSingleResult)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("NULLIF does not support set arguments")));
+       Assert(fcinfo.nargs == 2);
+
+       /* if either argument is NULL they can't be equal */
+       if (!fcinfo.argnull[0] && !fcinfo.argnull[1])
+       {
+               fcinfo.isnull = false;
+               result = FunctionCallInvoke(&fcinfo);
+               /* if the arguments are equal return null */
+               if (!fcinfo.isnull && DatumGetBool(result))
+               {
+                       *isNull = true;
+                       return (Datum) 0;
+               }
+       }
+
+       /* else return first argument */
+       *isNull = fcinfo.argnull[0];
+       return fcinfo.arg[0];
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalNullTest
+ *
+ *             Evaluate a NullTest node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalNullTest(GenericExprState *nstate,
+                                ExprContext *econtext,
+                                bool *isNull,
+                                ExprDoneCond *isDone)
+{
+       NullTest   *ntest = (NullTest *) nstate->xprstate.expr;
+       Datum           result;
+
+       result = ExecEvalExpr(nstate->arg, econtext, isNull, isDone);
+
+       if (isDone && *isDone == ExprEndResult)
+               return result;                  /* nothing to check */
+
+       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 */
+       }
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalBooleanTest
+ *
+ *             Evaluate a BooleanTest node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalBooleanTest(GenericExprState *bstate,
+                                       ExprContext *econtext,
+                                       bool *isNull,
+                                       ExprDoneCond *isDone)
+{
+       BooleanTest *btest = (BooleanTest *) bstate->xprstate.expr;
+       Datum           result;
+
+       result = ExecEvalExpr(bstate->arg, econtext, isNull, isDone);
+
+       if (isDone && *isDone == ExprEndResult)
+               return result;                  /* nothing to check */
+
+       switch (btest->booltesttype)
+       {
+               case IS_TRUE:
+                       if (*isNull)
+                       {
+                               *isNull = false;
+                               return BoolGetDatum(false);
+                       }
+                       else if (DatumGetBool(result))
+                               return BoolGetDatum(true);
+                       else
+                               return BoolGetDatum(false);
+               case IS_NOT_TRUE:
+                       if (*isNull)
+                       {
+                               *isNull = false;
+                               return BoolGetDatum(true);
+                       }
+                       else if (DatumGetBool(result))
+                               return BoolGetDatum(false);
+                       else
+                               return BoolGetDatum(true);
+               case IS_FALSE:
+                       if (*isNull)
+                       {
+                               *isNull = false;
+                               return BoolGetDatum(false);
+                       }
+                       else if (DatumGetBool(result))
+                               return BoolGetDatum(false);
+                       else
+                               return BoolGetDatum(true);
+               case IS_NOT_FALSE:
+                       if (*isNull)
+                       {
+                               *isNull = false;
+                               return BoolGetDatum(true);
+                       }
+                       else if (DatumGetBool(result))
+                               return BoolGetDatum(true);
+                       else
+                               return BoolGetDatum(false);
+               case IS_UNKNOWN:
+                       if (*isNull)
+                       {
+                               *isNull = false;
+                               return BoolGetDatum(true);
+                       }
+                       else
+                               return BoolGetDatum(false);
+               case IS_NOT_UNKNOWN:
+                       if (*isNull)
+                       {
+                               *isNull = false;
+                               return BoolGetDatum(false);
+                       }
+                       else
+                               return BoolGetDatum(true);
+               default:
+                       elog(ERROR, "unrecognized booltesttype: %d",
+                                (int) btest->booltesttype);
+                       return (Datum) 0;       /* keep compiler quiet */
+       }
+}
+
+/*
+ * ExecEvalCoerceToDomain
+ *
+ * Test the provided data against the domain constraint(s).  If the data
+ * passes the constraint specifications, pass it through (return the
+ * datum) otherwise throw an error.
+ */
+static Datum
+ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
+                                          bool *isNull, ExprDoneCond *isDone)
+{
+       CoerceToDomain *ctest = (CoerceToDomain *) cstate->xprstate.expr;
+       Datum           result;
+       List       *l;
+
+       result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
+
+       if (isDone && *isDone == ExprEndResult)
+               return result;                  /* nothing to check */
+
+       foreach(l, cstate->constraints)
+       {
+               DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+
+               switch (con->constrainttype)
+               {
+                       case DOM_CONSTRAINT_NOTNULL:
+                               if (*isNull)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_NOT_NULL_VIOLATION),
+                                                        errmsg("domain %s does not allow NULL values",
+                                                                       format_type_be(ctest->resulttype))));
+                               break;
+                       case DOM_CONSTRAINT_CHECK:
+                       {
+                               Datum   conResult;
+                               bool    conIsNull;
+                               Datum   save_datum;
+                               bool    save_isNull;
+
+                               /*
+                                * 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.
+                                */
+                               save_datum = econtext->domainValue_datum;
+                               save_isNull = econtext->domainValue_isNull;
+
+                               econtext->domainValue_datum = result;
+                               econtext->domainValue_isNull = *isNull;
+
+                               conResult = ExecEvalExpr(con->check_expr,
+                                                                                econtext, &conIsNull, NULL);
+
+                               if (!conIsNull &&
+                                       !DatumGetBool(conResult))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_CHECK_VIOLATION),
+                                                        errmsg("value for domain %s violates CHECK constraint \"%s\"",
+                                                                       format_type_be(ctest->resulttype),
+                                                                       con->name)));
+                               econtext->domainValue_datum = save_datum;
+                               econtext->domainValue_isNull = save_isNull;
+
+                               break;
+                       }
+                       default:
+                               elog(ERROR, "unrecognized constraint type: %d",
+                                        (int) con->constrainttype);
+                               break;
+               }
+       }
+
+       /* If all has gone well (constraints did not fail) return the datum */
+       return result;
+}
+
+/*
+ * ExecEvalCoerceToDomainValue
+ *
+ * Return the value stored by CoerceToDomain.
+ */
+static Datum
+ExecEvalCoerceToDomainValue(CoerceToDomainValue *conVal,
+                                                       ExprContext *econtext, bool *isNull)
+{
+       *isNull = econtext->domainValue_isNull;
+       return econtext->domainValue_datum;
+}
+
 /* ----------------------------------------------------------------
  *             ExecEvalFieldSelect
  *
@@ -1168,175 +2077,651 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalFieldSelect(FieldSelect *fselect,
+ExecEvalFieldSelect(GenericExprState *fstate,
                                        ExprContext *econtext,
                                        bool *isNull,
-                                       bool *isDone)
+                                       ExprDoneCond *isDone)
 {
-       Datum                   result;
+       FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
+       Datum           result;
        TupleTableSlot *resSlot;
 
-       result = ExecEvalExpr(fselect->arg, econtext, isNull, isDone);
-       if (*isNull)
-               return result;
-       /* XXX what about isDone? */
-       resSlot = (TupleTableSlot *) DatumGetPointer(result);
-       Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot));
-       result = heap_getattr(resSlot->val,
-                                                 fselect->fieldnum,
-                                                 resSlot->ttc_tupleDescriptor,
-                                                 isNull);
-       return result;
-}
+       result = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
+
+       /* this test covers the isDone exception too: */
+       if (*isNull)
+               return result;
+
+       resSlot = (TupleTableSlot *) DatumGetPointer(result);
+       Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot));
+       result = heap_getattr(resSlot->val,
+                                                 fselect->fieldnum,
+                                                 resSlot->ttc_tupleDescriptor,
+                                                 isNull);
+       return result;
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEvalExpr
+ *
+ *             Recursively evaluate a targetlist or qualification expression.
+ *
+ * Inputs:
+ *             expression: the expression state tree to evaluate
+ *             econtext: evaluation context information
+ *
+ * Outputs:
+ *             return value: Datum value of result
+ *             *isNull: set to TRUE if result is NULL (actual return value is
+ *                              meaningless if so); set to FALSE if non-null result
+ *             *isDone: set to indicator of set-result status
+ *
+ * A caller that can only accept a singleton (non-set) result should pass
+ * NULL for isDone; if the expression computes a set result then an error
+ * will be reported via ereport.  If the caller does pass an isDone pointer
+ * then *isDone is set to one of these three states:
+ *             ExprSingleResult                singleton result (not a set)
+ *             ExprMultipleResult              return value is one element of a set
+ *             ExprEndResult                   there are no more elements in the set
+ * When ExprMultipleResult is returned, the caller should invoke
+ * ExecEvalExpr() repeatedly until ExprEndResult is returned.  ExprEndResult
+ * is returned after the last real set element.  For convenience isNull will
+ * always be set TRUE when ExprEndResult is returned, but this should not be
+ * taken as indicating a NULL element of the set.  Note that these return
+ * conventions allow us to distinguish among a singleton NULL, a NULL element
+ * of a set, and an empty set.
+ *
+ * The caller should already have switched into the temporary memory
+ * context econtext->ecxt_per_tuple_memory.  The convenience entry point
+ * ExecEvalExprSwitchContext() is provided for callers who don't prefer to
+ * do the switch in an outer loop.     We do not do the switch here because
+ * it'd be a waste of cycles during recursive entries to ExecEvalExpr().
+ *
+ * This routine is an inner loop routine and must be as fast as possible.
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalExpr(ExprState *expression,
+                        ExprContext *econtext,
+                        bool *isNull,
+                        ExprDoneCond *isDone)
+{
+       Datum           retDatum;
+       Expr       *expr;
+
+       /* Set default values for result flags: non-null, not a set result */
+       *isNull = false;
+       if (isDone)
+               *isDone = ExprSingleResult;
+
+       /* Is this still necessary?  Doubtful... */
+       if (expression == NULL)
+       {
+               *isNull = true;
+               return (Datum) 0;
+       }
+
+       /*
+        * here we dispatch the work to the appropriate type of function given
+        * the type of our expression.
+        */
+       expr = expression->expr;
+       switch (nodeTag(expr))
+       {
+               case T_Var:
+                       retDatum = ExecEvalVar((Var *) expr, econtext, isNull);
+                       break;
+               case T_Const:
+                       {
+                               Const      *con = (Const *) expr;
+
+                               retDatum = con->constvalue;
+                               *isNull = con->constisnull;
+                               break;
+                       }
+               case T_Param:
+                       retDatum = ExecEvalParam((Param *) expr, econtext, isNull);
+                       break;
+               case T_Aggref:
+                       retDatum = ExecEvalAggref((AggrefExprState *) expression,
+                                                                         econtext,
+                                                                         isNull);
+                       break;
+               case T_ArrayRef:
+                       retDatum = ExecEvalArrayRef((ArrayRefExprState *) expression,
+                                                                               econtext,
+                                                                               isNull,
+                                                                               isDone);
+                       break;
+               case T_FuncExpr:
+                       retDatum = ExecEvalFunc((FuncExprState *) expression, econtext,
+                                                                       isNull, isDone);
+                       break;
+               case T_OpExpr:
+                       retDatum = ExecEvalOper((FuncExprState *) expression, econtext,
+                                                                       isNull, isDone);
+                       break;
+               case T_DistinctExpr:
+                       retDatum = ExecEvalDistinct((FuncExprState *) expression, econtext,
+                                                                               isNull);
+                       break;
+               case T_ScalarArrayOpExpr:
+                       retDatum = ExecEvalScalarArrayOp((ScalarArrayOpExprState *) expression,
+                                                                                        econtext, isNull);
+                       break;
+               case T_BoolExpr:
+                       {
+                               BoolExprState *state = (BoolExprState *) expression;
+
+                               switch (((BoolExpr *) expr)->boolop)
+                               {
+                                       case AND_EXPR:
+                                               retDatum = ExecEvalAnd(state, econtext, isNull);
+                                               break;
+                                       case OR_EXPR:
+                                               retDatum = ExecEvalOr(state, econtext, isNull);
+                                               break;
+                                       case NOT_EXPR:
+                                               retDatum = ExecEvalNot(state, econtext, isNull);
+                                               break;
+                                       default:
+                                               elog(ERROR, "unrecognized boolop: %d",
+                                                        (int) ((BoolExpr *) expr)->boolop);
+                                               retDatum = 0;   /* keep compiler quiet */
+                                               break;
+                               }
+                               break;
+                       }
+               case T_SubPlan:
+                       retDatum = ExecSubPlan((SubPlanState *) expression,
+                                                                  econtext,
+                                                                  isNull);
+                       break;
+               case T_FieldSelect:
+                       retDatum = ExecEvalFieldSelect((GenericExprState *) expression,
+                                                                                  econtext,
+                                                                                  isNull,
+                                                                                  isDone);
+                       break;
+               case T_RelabelType:
+                       retDatum = ExecEvalExpr(((GenericExprState *) expression)->arg,
+                                                                       econtext,
+                                                                       isNull,
+                                                                       isDone);
+                       break;
+               case T_CaseExpr:
+                       retDatum = ExecEvalCase((CaseExprState *) expression,
+                                                                       econtext,
+                                                                       isNull,
+                                                                       isDone);
+                       break;
+               case T_ArrayExpr:
+                       retDatum = ExecEvalArray((ArrayExprState *) expression,
+                                                                        econtext,
+                                                                        isNull);
+                       break;
+               case T_CoalesceExpr:
+                       retDatum = ExecEvalCoalesce((CoalesceExprState *) expression,
+                                                                               econtext,
+                                                                               isNull);
+                       break;
+               case T_NullIfExpr:
+                       retDatum = ExecEvalNullIf((FuncExprState *) expression,
+                                                                         econtext,
+                                                                         isNull);
+                       break;
+               case T_NullTest:
+                       retDatum = ExecEvalNullTest((GenericExprState *) expression,
+                                                                               econtext,
+                                                                               isNull,
+                                                                               isDone);
+                       break;
+               case T_BooleanTest:
+                       retDatum = ExecEvalBooleanTest((GenericExprState *) expression,
+                                                                                  econtext,
+                                                                                  isNull,
+                                                                                  isDone);
+                       break;
+               case T_CoerceToDomain:
+                       retDatum = ExecEvalCoerceToDomain((CoerceToDomainState *) expression,
+                                                                                         econtext,
+                                                                                         isNull,
+                                                                                         isDone);
+                       break;
+               case T_CoerceToDomainValue:
+                       retDatum = ExecEvalCoerceToDomainValue((CoerceToDomainValue *) expr,
+                                                                                                  econtext,
+                                                                                                  isNull);
+                       break;
+               default:
+                       elog(ERROR, "unrecognized node type: %d",
+                                (int) nodeTag(expression));
+                       retDatum = 0;           /* keep compiler quiet */
+                       break;
+       }
+
+       return retDatum;
+}      /* ExecEvalExpr() */
+
+
+/*
+ * Same as above, but get into the right allocation context explicitly.
+ */
+Datum
+ExecEvalExprSwitchContext(ExprState *expression,
+                                                 ExprContext *econtext,
+                                                 bool *isNull,
+                                                 ExprDoneCond *isDone)
+{
+       Datum           retDatum;
+       MemoryContext oldContext;
+
+       oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+       retDatum = ExecEvalExpr(expression, econtext, isNull, isDone);
+       MemoryContextSwitchTo(oldContext);
+       return retDatum;
+}
+
+
+/*
+ * ExecInitExpr: prepare an expression tree for execution
+ *
+ * This function builds and returns an ExprState tree paralleling the given
+ * Expr node tree.  The ExprState tree can then be handed to ExecEvalExpr
+ * for execution.  Because the Expr tree itself is read-only as far as
+ * ExecInitExpr and ExecEvalExpr are concerned, several different executions
+ * of the same plan tree can occur concurrently.
+ *
+ * This must be called in a memory context that will last as long as repeated
+ * executions of the expression are needed.  Typically the context will be
+ * the same as the per-query context of the associated ExprContext.
+ *
+ * Any Aggref and SubPlan nodes found in the tree are added to the lists
+ * of such nodes held by the parent PlanState.  Otherwise, we do very little
+ * initialization here other than building the state-node tree.  Any nontrivial
+ * work associated with initializing runtime info for a node should happen
+ * during the first actual evaluation of that node.  (This policy lets us
+ * avoid work if the node is never actually evaluated.)
+ *
+ * Note: there is no ExecEndExpr function; we assume that any resource
+ * cleanup needed will be handled by just releasing the memory context
+ * in which the state tree is built.  Functions that require additional
+ * cleanup work can register a shutdown callback in the ExprContext.
+ *
+ *     'node' is the root of the expression tree to examine
+ *     'parent' is the PlanState node that owns the expression.
+ *
+ * 'parent' may be NULL if we are preparing an expression that is not
+ * associated with a plan tree.  (If so, it can't have aggs or subplans.)
+ * This case should usually come through ExecPrepareExpr, not directly here.
+ */
+ExprState *
+ExecInitExpr(Expr *node, PlanState *parent)
+{
+       ExprState  *state;
+
+       if (node == NULL)
+               return NULL;
+       switch (nodeTag(node))
+       {
+               case T_Var:
+               case T_Const:
+               case T_Param:
+               case T_CoerceToDomainValue:
+                       /* No special setup needed for these node types */
+                       state = (ExprState *) makeNode(ExprState);
+                       break;
+               case T_Aggref:
+                       {
+                               Aggref   *aggref = (Aggref *) node;
+                               AggrefExprState *astate = makeNode(AggrefExprState);
+
+                               if (parent && IsA(parent, AggState))
+                               {
+                                       AggState   *aggstate = (AggState *) parent;
+                                       int                     naggs;
+
+                                       aggstate->aggs = lcons(astate, aggstate->aggs);
+                                       naggs = ++aggstate->numaggs;
+
+                                       astate->target = ExecInitExpr(aggref->target, parent);
+
+                                       /*
+                                        * Complain if the aggregate's argument contains any
+                                        * aggregates; nested agg functions are semantically
+                                        * 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")));
+                               }
+                               else
+                               {
+                                       /* planner messed up */
+                                       elog(ERROR, "aggref found in non-Agg plan node");
+                               }
+                               state = (ExprState *) astate;
+                       }
+                       break;
+               case T_ArrayRef:
+                       {
+                               ArrayRef   *aref = (ArrayRef *) node;
+                               ArrayRefExprState *astate = makeNode(ArrayRefExprState);
+
+                               astate->refupperindexpr = (List *)
+                                       ExecInitExpr((Expr *) aref->refupperindexpr, parent);
+                               astate->reflowerindexpr = (List *)
+                                       ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
+                               astate->refexpr = ExecInitExpr(aref->refexpr, parent);
+                               astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
+                                                                                                       parent);
+                               /* do one-time catalog lookups for type info */
+                               astate->refattrlength = get_typlen(aref->refarraytype);
+                               get_typlenbyvalalign(aref->refelemtype,
+                                                                        &astate->refelemlength,
+                                                                        &astate->refelembyval,
+                                                                        &astate->refelemalign);
+                               state = (ExprState *) astate;
+                       }
+                       break;
+               case T_FuncExpr:
+                       {
+                               FuncExpr   *funcexpr = (FuncExpr *) node;
+                               FuncExprState *fstate = makeNode(FuncExprState);
+
+                               fstate->args = (List *)
+                                       ExecInitExpr((Expr *) funcexpr->args, parent);
+                               fstate->func.fn_oid = InvalidOid; /* not initialized */
+                               state = (ExprState *) fstate;
+                       }
+                       break;
+               case T_OpExpr:
+                       {
+                               OpExpr   *opexpr = (OpExpr *) node;
+                               FuncExprState *fstate = makeNode(FuncExprState);
+
+                               fstate->args = (List *)
+                                       ExecInitExpr((Expr *) opexpr->args, parent);
+                               fstate->func.fn_oid = InvalidOid; /* not initialized */
+                               state = (ExprState *) fstate;
+                       }
+                       break;
+               case T_DistinctExpr:
+                       {
+                               DistinctExpr   *distinctexpr = (DistinctExpr *) node;
+                               FuncExprState *fstate = makeNode(FuncExprState);
 
-/* ----------------------------------------------------------------
- *             ExecEvalExpr
- *
- *             Recursively evaluate a targetlist or qualification expression.
- *
- *             The caller should already have switched into the temporary
- *             memory context econtext->ecxt_per_tuple_memory.  The convenience
- *             entry point ExecEvalExprSwitchContext() is provided for callers
- *             who don't prefer to do the switch in an outer loop.  We do not
- *             do the switch here because it'd be a waste of cycles during
- *             recursive entries to ExecEvalExpr().
- *
- *             This routine is an inner loop routine and must be as fast
- *             as possible.
- * ----------------------------------------------------------------
- */
-Datum
-ExecEvalExpr(Node *expression,
-                        ExprContext *econtext,
-                        bool *isNull,
-                        bool *isDone)
-{
-       Datum           retDatum;
+                               fstate->args = (List *)
+                                       ExecInitExpr((Expr *) distinctexpr->args, parent);
+                               fstate->func.fn_oid = InvalidOid; /* not initialized */
+                               state = (ExprState *) fstate;
+                       }
+                       break;
+               case T_ScalarArrayOpExpr:
+                       {
+                               ScalarArrayOpExpr   *opexpr = (ScalarArrayOpExpr *) node;
+                               ScalarArrayOpExprState *sstate = makeNode(ScalarArrayOpExprState);
+
+                               sstate->fxprstate.args = (List *)
+                                       ExecInitExpr((Expr *) opexpr->args, parent);
+                               sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */
+                               sstate->element_type = InvalidOid; /* ditto */
+                               state = (ExprState *) sstate;
+                       }
+                       break;
+               case T_BoolExpr:
+                       {
+                               BoolExpr   *boolexpr = (BoolExpr *) node;
+                               BoolExprState *bstate = makeNode(BoolExprState);
 
-       /* Set default values for result flags: non-null, not a set result */
-       *isNull = false;
-       *isDone = true;
+                               bstate->args = (List *)
+                                       ExecInitExpr((Expr *) boolexpr->args, parent);
+                               state = (ExprState *) bstate;
+                       }
+                       break;
+               case T_SubPlan:
+                       {
+                               /* Keep this in sync with ExecInitExprInitPlan, below */
+                               SubPlan *subplan = (SubPlan *) node;
+                               SubPlanState *sstate = makeNode(SubPlanState);
 
-       /* Is this still necessary?  Doubtful... */
-       if (expression == NULL)
-       {
-               *isNull = true;
-               return (Datum) 0;
-       }
+                               if (!parent)
+                                       elog(ERROR, "SubPlan found with no parent plan");
 
-       /*
-        * here we dispatch the work to the appropriate type of function given
-        * the type of our expression.
-        */
-       switch (nodeTag(expression))
-       {
-               case T_Var:
-                       retDatum = ExecEvalVar((Var *) expression, econtext, isNull);
+                               /*
+                                * 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->exprs = (List *)
+                                       ExecInitExpr((Expr *) subplan->exprs, parent);
+                               sstate->args = (List *)
+                                       ExecInitExpr((Expr *) subplan->args, parent);
+
+                               state = (ExprState *) sstate;
+                       }
                        break;
-               case T_Const:
+               case T_FieldSelect:
                        {
-                               Const      *con = (Const *) expression;
+                               FieldSelect   *fselect = (FieldSelect *) node;
+                               GenericExprState *gstate = makeNode(GenericExprState);
 
-                               retDatum = con->constvalue;
-                               *isNull = con->constisnull;
-                               break;
+                               gstate->arg = ExecInitExpr(fselect->arg, parent);
+                               state = (ExprState *) gstate;
                        }
-               case T_Param:
-                       retDatum = ExecEvalParam((Param *) expression, econtext, isNull);
                        break;
-               case T_Iter:
-                       retDatum = ExecEvalIter((Iter *) expression,
-                                                                       econtext,
-                                                                       isNull,
-                                                                       isDone);
+               case T_RelabelType:
+                       {
+                               RelabelType   *relabel = (RelabelType *) node;
+                               GenericExprState *gstate = makeNode(GenericExprState);
+
+                               gstate->arg = ExecInitExpr(relabel->arg, parent);
+                               state = (ExprState *) gstate;
+                       }
                        break;
-               case T_Aggref:
-                       retDatum = ExecEvalAggref((Aggref *) expression, econtext, isNull);
+               case T_CaseExpr:
+                       {
+                               CaseExpr   *caseexpr = (CaseExpr *) node;
+                               CaseExprState *cstate = makeNode(CaseExprState);
+                               FastList        outlist;
+                               List       *inlist;
+
+                               FastListInit(&outlist);
+                               foreach(inlist, caseexpr->args)
+                               {
+                                       CaseWhen   *when = (CaseWhen *) lfirst(inlist);
+                                       CaseWhenState *wstate = makeNode(CaseWhenState);
+
+                                       Assert(IsA(when, CaseWhen));
+                                       wstate->xprstate.expr = (Expr *) when;
+                                       wstate->expr = ExecInitExpr(when->expr, parent);
+                                       wstate->result = ExecInitExpr(when->result, parent);
+                                       FastAppend(&outlist, wstate);
+                               }
+                               cstate->args = FastListValue(&outlist);
+                               /* caseexpr->arg should be null by now */
+                               Assert(caseexpr->arg == NULL);
+                               cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
+                               state = (ExprState *) cstate;
+                       }
                        break;
-               case T_ArrayRef:
-                       retDatum = ExecEvalArrayRef((ArrayRef *) expression,
-                                                                               econtext,
-                                                                               isNull,
-                                                                               isDone);
+               case T_ArrayExpr:
+                       {
+                               ArrayExpr          *arrayexpr = (ArrayExpr *) node;
+                               ArrayExprState *astate = makeNode(ArrayExprState);
+                               FastList                outlist;
+                               List               *inlist;
+
+                               FastListInit(&outlist);
+                               foreach(inlist, arrayexpr->elements)
+                               {
+                                       Expr       *e = (Expr *) lfirst(inlist);
+                                       ExprState  *estate;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       FastAppend(&outlist, estate);
+                               }
+                               astate->elements = FastListValue(&outlist);
+                               /* do one-time catalog lookup for type info */
+                               get_typlenbyvalalign(arrayexpr->element_typeid,
+                                                                        &astate->elemlength,
+                                                                        &astate->elembyval,
+                                                                        &astate->elemalign);
+                               state = (ExprState *) astate;
+                       }
                        break;
-               case T_Expr:
+               case T_CoalesceExpr:
                        {
-                               Expr       *expr = (Expr *) expression;
+                               CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
+                               CoalesceExprState *cstate = makeNode(CoalesceExprState);
+                               FastList        outlist;
+                               List       *inlist;
 
-                               switch (expr->opType)
+                               FastListInit(&outlist);
+                               foreach(inlist, coalesceexpr->args)
                                {
-                                       case OP_EXPR:
-                                               retDatum = ExecEvalOper(expr, econtext, isNull);
-                                               break;
-                                       case FUNC_EXPR:
-                                               retDatum = ExecEvalFunc(expr, econtext,
-                                                                                               isNull, isDone);
-                                               break;
-                                       case OR_EXPR:
-                                               retDatum = ExecEvalOr(expr, econtext, isNull);
-                                               break;
-                                       case AND_EXPR:
-                                               retDatum = ExecEvalAnd(expr, econtext, isNull);
-                                               break;
-                                       case NOT_EXPR:
-                                               retDatum = ExecEvalNot(expr, econtext, isNull);
-                                               break;
-                                       case SUBPLAN_EXPR:
-                                               retDatum = ExecSubPlan((SubPlan *) expr->oper,
-                                                                                          expr->args, econtext,
-                                                                                          isNull);
-                                               break;
-                                       default:
-                                               elog(ERROR, "ExecEvalExpr: unknown expression type %d",
-                                                        expr->opType);
-                                               retDatum = 0;   /* keep compiler quiet */
-                                               break;
+                                       Expr *e = (Expr *) lfirst(inlist);
+                                       ExprState *estate;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       FastAppend(&outlist, estate);
                                }
-                               break;
+                               cstate->args = FastListValue(&outlist);
+                               state = (ExprState *) cstate;
                        }
-               case T_FieldSelect:
-                       retDatum = ExecEvalFieldSelect((FieldSelect *) expression,
-                                                                                  econtext,
-                                                                                  isNull,
-                                                                                  isDone);
                        break;
-               case T_RelabelType:
-                       retDatum = ExecEvalExpr(((RelabelType *) expression)->arg,
-                                                                       econtext,
-                                                                       isNull,
-                                                                       isDone);
+               case T_NullIfExpr:
+                       {
+                               NullIfExpr *nullifexpr = (NullIfExpr *) node;
+                               FuncExprState *fstate = makeNode(FuncExprState);
+
+                               fstate->args = (List *)
+                                       ExecInitExpr((Expr *) nullifexpr->args, parent);
+                               fstate->func.fn_oid = InvalidOid; /* not initialized */
+                               state = (ExprState *) fstate;
+                       }
                        break;
-               case T_CaseExpr:
-                       retDatum = ExecEvalCase((CaseExpr *) expression, econtext, isNull);
+               case T_NullTest:
+                       {
+                               NullTest   *ntest = (NullTest *) node;
+                               GenericExprState *gstate = makeNode(GenericExprState);
+
+                               gstate->arg = ExecInitExpr(ntest->arg, parent);
+                               state = (ExprState *) gstate;
+                       }
+                       break;
+               case T_BooleanTest:
+                       {
+                               BooleanTest   *btest = (BooleanTest *) node;
+                               GenericExprState *gstate = makeNode(GenericExprState);
+
+                               gstate->arg = ExecInitExpr(btest->arg, parent);
+                               state = (ExprState *) gstate;
+                       }
+                       break;
+               case T_CoerceToDomain:
+                       {
+                               CoerceToDomain   *ctest = (CoerceToDomain *) node;
+                               CoerceToDomainState *cstate = makeNode(CoerceToDomainState);
+
+                               cstate->arg = ExecInitExpr(ctest->arg, parent);
+                               cstate->constraints = GetDomainConstraints(ctest->resulttype);
+                               state = (ExprState *) cstate;
+                       }
+                       break;
+               case T_TargetEntry:
+                       {
+                               TargetEntry   *tle = (TargetEntry *) node;
+                               GenericExprState *gstate = makeNode(GenericExprState);
+
+                               gstate->arg = ExecInitExpr(tle->expr, parent);
+                               state = (ExprState *) gstate;
+                       }
                        break;
+               case T_List:
+                       {
+                               FastList        outlist;
+                               List       *inlist;
 
+                               FastListInit(&outlist);
+                               foreach(inlist, (List *) node)
+                               {
+                                       FastAppend(&outlist,
+                                                          ExecInitExpr((Expr *) lfirst(inlist),
+                                                                                       parent));
+                               }
+                               /* Don't fall through to the "common" code below */
+                               return (ExprState *) FastListValue(&outlist);
+                       }
                default:
-                       elog(ERROR, "ExecEvalExpr: unknown expression type %d",
-                                nodeTag(expression));
-                       retDatum = 0;           /* keep compiler quiet */
+                       elog(ERROR, "unrecognized node type: %d",
+                                (int) nodeTag(node));
+                       state = NULL;           /* keep compiler quiet */
                        break;
        }
 
-       return retDatum;
-}      /* ExecEvalExpr() */
+       /* Common code for all state-node types */
+       state->expr = node;
 
+       return state;
+}
 
 /*
- * Same as above, but get into the right allocation context explicitly.
+ * 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.
  */
-Datum
-ExecEvalExprSwitchContext(Node *expression,
-                                                 ExprContext *econtext,
-                                                 bool *isNull,
-                                                 bool *isDone)
+SubPlanState *
+ExecInitExprInitPlan(SubPlan *node, PlanState *parent)
 {
-       Datum           retDatum;
-       MemoryContext oldContext;
+       SubPlanState *sstate = makeNode(SubPlanState);
 
-       oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-       retDatum = ExecEvalExpr(expression, econtext, isNull, isDone);
-       MemoryContextSwitchTo(oldContext);
-       return retDatum;
+       if (!parent)
+               elog(ERROR, "SubPlan found with no parent plan");
+
+       /* The subplan's state will be initialized later */
+       sstate->sub_estate = NULL;
+       sstate->planstate = NULL;
+
+       sstate->exprs = (List *) ExecInitExpr((Expr *) node->exprs, parent);
+       sstate->args = (List *) ExecInitExpr((Expr *) node->args, parent);
+
+       sstate->xprstate.expr = (Expr *) node;
+
+       return sstate;
+}
+
+/*
+ * ExecPrepareExpr --- initialize for expression execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we apply
+ * fix_opfuncids() to the passed expression tree to be sure it is ready
+ * to run.  (In ordinary Plan trees the planner will have fixed opfuncids,
+ * but callers outside the executor will not have done this.)
+ */
+ExprState *
+ExecPrepareExpr(Expr *node, EState *estate)
+{
+       ExprState  *result;
+       MemoryContext oldcontext;
+
+       fix_opfuncids((Node *) node);
+
+       oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+       result = ExecInitExpr(node, NULL);
+
+       MemoryContextSwitchTo(oldcontext);
+
+       return result;
 }
 
 
@@ -1410,30 +2795,25 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
 
        foreach(qlist, qual)
        {
-               Node       *clause = (Node *) lfirst(qlist);
+               ExprState  *clause = (ExprState *) lfirst(qlist);
                Datum           expr_value;
                bool            isNull;
-               bool            isDone;
 
-               /*
-                * pass isDone, but ignore it.  We don't iterate over multiple
-                * returns in the qualifications.
-                */
-               expr_value = ExecEvalExpr(clause, econtext, &isNull, &isDone);
+               expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
 
                if (isNull)
                {
                        if (resultForNull == false)
                        {
-                               result = false; /* treat NULL as FALSE */
+                               result = false; /* treat NULL as FALSE */
                                break;
                        }
                }
                else
                {
-                       if (! DatumGetBool(expr_value))
+                       if (!DatumGetBool(expr_value))
                        {
-                               result = false; /* definitely FALSE */
+                               result = false; /* definitely FALSE */
                                break;
                        }
                }
@@ -1450,19 +2830,8 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
 int
 ExecTargetListLength(List *targetlist)
 {
-       int                     len = 0;
-       List       *tl;
-
-       foreach(tl, targetlist)
-       {
-               TargetEntry        *curTle = (TargetEntry *) lfirst(tl);
-
-               if (curTle->resdom != NULL)
-                       len++;
-               else
-                       len += curTle->fjoin->fj_nNodes;
-       }
-       return len;
+       /* This used to be more complex, but fjoins are dead */
+       return length(targetlist);
 }
 
 /*
@@ -1476,17 +2845,11 @@ ExecCleanTargetListLength(List *targetlist)
 
        foreach(tl, targetlist)
        {
-               TargetEntry        *curTle = (TargetEntry *) lfirst(tl);
+               TargetEntry *curTle = (TargetEntry *) lfirst(tl);
 
-               if (curTle->resdom != NULL)
-               {
-                       if (! curTle->resdom->resjunk)
-                               len++;
-               }
-               else
-               {
-                       len += curTle->fjoin->fj_nNodes;
-               }
+               Assert(IsA(curTle, TargetEntry));
+               if (!curTle->resdom->resjunk)
+                       len++;
        }
        return len;
 }
@@ -1494,34 +2857,33 @@ ExecCleanTargetListLength(List *targetlist)
 /* ----------------------------------------------------------------
  *             ExecTargetList
  *
- *             Evaluates a targetlist with respect to the current
- *             expression context and return a tuple.
+ *             Evaluates a targetlist with respect to the given
+ *             expression context and returns a tuple.
+ *
+ * The caller must pass workspace for the values and nulls arrays
+ * as well as the itemIsDone array.  This convention saves palloc'ing
+ * workspace on each call, and some callers may find it useful to examine
+ * the values array directly.
+ *
+ * As with ExecEvalExpr, the caller should pass isDone = NULL if not
+ * prepared to deal with sets of result tuples.  Otherwise, a return
+ * of *isDone = ExprMultipleResult signifies a set element, and a return
+ * of *isDone = ExprEndResult signifies end of the set of tuple.
  * ----------------------------------------------------------------
  */
 static HeapTuple
 ExecTargetList(List *targetlist,
-                          int nodomains,
                           TupleDesc targettype,
-                          Datum *values,
                           ExprContext *econtext,
-                          bool *isDone)
+                          Datum *values,
+                          char *nulls,
+                          ExprDoneCond *itemIsDone,
+                          ExprDoneCond *isDone)
 {
        MemoryContext oldContext;
-       char            nulls_array[64];
-       bool            fjNullArray[64];
-       bool            itemIsDoneArray[64];
-       char       *null_head;
-       bool       *fjIsNull;
-       bool       *itemIsDone;
        List       *tl;
-       TargetEntry *tle;
-       Node       *expr;
-       Resdom     *resdom;
-       AttrNumber      resind;
-       Datum           constvalue;
-       HeapTuple       newTuple;
        bool            isNull;
-       bool            haveDoneIters;
+       bool            haveDoneSets;
        static struct tupleDesc NullTupleDesc;          /* we assume this inits to
                                                                                                 * zeroes */
 
@@ -1548,230 +2910,135 @@ ExecTargetList(List *targetlist,
        if (targettype == NULL)
                targettype = &NullTupleDesc;
 
-       /*
-        * allocate an array of char's to hold the "null" information only if
-        * we have a really large targetlist.  otherwise we use the stack.
-        *
-        * We also allocate a bool array that is used to hold fjoin result state,
-        * and another that holds the isDone status for each targetlist item.
-        */
-       if (nodomains > 64)
-       {
-               null_head = (char *) palloc(nodomains + 1);
-               fjIsNull = (bool *) palloc(nodomains + 1);
-               itemIsDone = (bool *) palloc(nodomains + 1);
-       }
-       else
-       {
-               null_head = &nulls_array[0];
-               fjIsNull = &fjNullArray[0];
-               itemIsDone = &itemIsDoneArray[0];
-       }
-
        /*
         * evaluate all the expressions in the target list
         */
+       if (isDone)
+               *isDone = ExprSingleResult;             /* until proven otherwise */
 
-       *isDone = true;                         /* until proven otherwise */
-       haveDoneIters = false;          /* any isDone Iter exprs in tlist? */
+       haveDoneSets = false;           /* any exhausted set exprs in tlist? */
 
        foreach(tl, targetlist)
        {
+               GenericExprState *gstate = (GenericExprState *) lfirst(tl);
+               TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
+               AttrNumber      resind = tle->resdom->resno - 1;
 
-               /*
-                * remember, a target list is a list of lists:
-                *
-                * ((<resdom | fjoin> expr) (<resdom | fjoin> expr) ...)
-                *
-                * tl is a pointer to successive cdr's of the targetlist tle is a
-                * pointer to the target list entry in tl
-                */
-               tle = lfirst(tl);
-
-               if (tle->resdom != NULL)
-               {
-                       expr = tle->expr;
-                       resdom = tle->resdom;
-                       resind = resdom->resno - 1;
-
-                       constvalue = ExecEvalExpr(expr,
+               values[resind] = ExecEvalExpr(gstate->arg,
                                                                          econtext,
                                                                          &isNull,
                                                                          &itemIsDone[resind]);
+               nulls[resind] = isNull ? 'n' : ' ';
 
-                       values[resind] = constvalue;
-
-                       if (!isNull)
-                               null_head[resind] = ' ';
-                       else
-                               null_head[resind] = 'n';
-
-                       if (IsA(expr, Iter))
-                       {
-                               if (itemIsDone[resind])
-                                       haveDoneIters = true;
-                               else
-                                       *isDone = false;        /* we have undone Iters in the
-                                                                                * list */
-                       }
-               }
-               else
+               if (itemIsDone[resind] != ExprSingleResult)
                {
-                       int                     curNode;
-                       Resdom     *fjRes;
-                       List       *fjTlist = (List *) tle->expr;
-                       Fjoin      *fjNode = tle->fjoin;
-                       int                     nNodes = fjNode->fj_nNodes;
-                       DatumPtr        results = fjNode->fj_results;
-
-                       ExecEvalFjoin(tle, econtext, fjIsNull, isDone);
-
-                       /* this is probably wrong: */
-                       if (*isDone)
+                       /* We have a set-valued expression in the tlist */
+                       if (isDone == NULL)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("set-valued function called in context that cannot accept a set")));
+                       if (itemIsDone[resind] == ExprMultipleResult)
                        {
-                               newTuple = NULL;
-                               goto exit;
+                               /* we have undone sets in the tlist, set flag */
+                               *isDone = ExprMultipleResult;
                        }
-
-                       /*
-                        * get the result from the inner node
-                        */
-                       fjRes = (Resdom *) fjNode->fj_innerNode;
-                       resind = fjRes->resno - 1;
-                       if (fjIsNull[0])
-                               null_head[resind] = 'n';
                        else
                        {
-                               null_head[resind] = ' ';
-                               values[resind] = results[0];
-                       }
-
-                       /*
-                        * Get results from all of the outer nodes
-                        */
-                       for (curNode = 1;
-                                curNode < nNodes;
-                                curNode++, fjTlist = lnext(fjTlist))
-                       {
-#ifdef NOT_USED                                        /* what is this?? */
-                               Node       *outernode = lfirst(fjTlist);
-
-                               fjRes = (Resdom *) outernode->iterexpr;
-#endif
-                               resind = fjRes->resno - 1;
-                               if (fjIsNull[curNode])
-                                       null_head[resind] = 'n';
-                               else
-                               {
-                                       null_head[resind] = ' ';
-                                       values[resind] = results[curNode];
-                               }
+                               /* we have done sets in the tlist, set flag for that */
+                               haveDoneSets = true;
                        }
                }
        }
 
-       if (haveDoneIters)
+       if (haveDoneSets)
        {
-               if (*isDone)
+               /*
+                * note: can't get here unless we verified isDone != NULL
+                */
+               if (*isDone == ExprSingleResult)
                {
-
                        /*
-                        * all Iters are done, so return a null indicating tlist set
-                        * expansion is complete.
+                        * all sets are done, so report that tlist expansion is
+                        * complete.
                         */
-                       newTuple = NULL;
-                       goto exit;
+                       *isDone = ExprEndResult;
+                       MemoryContextSwitchTo(oldContext);
+                       return NULL;
                }
                else
                {
-
                        /*
-                        * We have some done and some undone Iters.  Restart the done
+                        * We have some done and some undone sets.      Restart the done
                         * ones so that we can deliver a tuple (if possible).
-                        *
-                        * XXX this code is a crock, because it only works for Iters at
-                        * the top level of tlist expressions, and doesn't even work
-                        * right for them: you should get all possible combinations of
-                        * Iter results, but you won't unless the numbers of values
-                        * returned by each are relatively prime.  Should have a
-                        * mechanism more like aggregate functions, where we make a
-                        * list of all Iters contained in the tlist and cycle through
-                        * their values in a methodical fashion.  To do someday; can't
-                        * get excited about fixing a Berkeley feature that's not in
-                        * SQL92.  (The only reason we're doing this much is that we
-                        * have to be sure all the Iters are run to completion, or
-                        * their subplan executors will have unreleased resources,
-                        * e.g. pinned buffers...)
                         */
                        foreach(tl, targetlist)
                        {
-                               tle = lfirst(tl);
+                               GenericExprState *gstate = (GenericExprState *) lfirst(tl);
+                               TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
+                               AttrNumber      resind = tle->resdom->resno - 1;
 
-                               if (tle->resdom != NULL)
+                               if (itemIsDone[resind] == ExprEndResult)
                                {
-                                       expr = tle->expr;
-                                       resdom = tle->resdom;
-                                       resind = resdom->resno - 1;
-
-                                       if (IsA(expr, Iter) &&itemIsDone[resind])
-                                       {
-                                               constvalue = ExecEvalExpr(expr,
+                                       values[resind] = ExecEvalExpr(gstate->arg,
                                                                                                  econtext,
                                                                                                  &isNull,
                                                                                                  &itemIsDone[resind]);
-                                               if (itemIsDone[resind])
-                                               {
-
-                                                       /*
-                                                        * Oh dear, this Iter is returning an empty
-                                                        * set. Guess we can't make a tuple after all.
-                                                        */
-                                                       *isDone = true;
-                                                       newTuple = NULL;
-                                                       goto exit;
-                                               }
-
-                                               values[resind] = constvalue;
-
-                                               if (!isNull)
-                                                       null_head[resind] = ' ';
-                                               else
-                                                       null_head[resind] = 'n';
+                                       nulls[resind] = isNull ? 'n' : ' ';
+
+                                       if (itemIsDone[resind] == ExprEndResult)
+                                       {
+                                               /*
+                                                * Oh dear, this item is returning an empty
+                                                * set. Guess we can't make a tuple after all.
+                                                */
+                                               *isDone = ExprEndResult;
+                                               break;
                                        }
                                }
                        }
-               }
-       }
 
-       /*
-        * form the new result tuple (in the caller's memory context!)
-        */
-       MemoryContextSwitchTo(oldContext);
+                       /*
+                        * 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?
+                        */
+                       if (*isDone == ExprEndResult)
+                       {
+                               foreach(tl, targetlist)
+                               {
+                                       GenericExprState *gstate = (GenericExprState *) lfirst(tl);
+                                       TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
+                                       AttrNumber      resind = tle->resdom->resno - 1;
 
-       newTuple = (HeapTuple) heap_formtuple(targettype, values, null_head);
+                                       while (itemIsDone[resind] == ExprMultipleResult)
+                                       {
+                                               (void) ExecEvalExpr(gstate->arg,
+                                                                                       econtext,
+                                                                                       &isNull,
+                                                                                       &itemIsDone[resind]);
+                                       }
+                               }
 
-exit:
+                               MemoryContextSwitchTo(oldContext);
+                               return NULL;
+                       }
+               }
+       }
 
        /*
-        * free the status arrays if we palloc'd them
+        * form the new result tuple (in the caller's memory context!)
         */
-       if (nodomains > 64)
-       {
-               pfree(null_head);
-               pfree(fjIsNull);
-               pfree(itemIsDone);
-       }
-
-       /* make sure we are in the right context if we did "goto exit" */
        MemoryContextSwitchTo(oldContext);
 
-       return newTuple;
+       return heap_formtuple(targettype, values, nulls);
 }
 
 /* ----------------------------------------------------------------
  *             ExecProject
  *
- *             projects a tuple based in projection info and stores
+ *             projects a tuple based on projection info and stores
  *             it in the specified tuple table slot.
  *
  *             Note: someday soon the executor can be extended to eliminate
@@ -1782,14 +3049,10 @@ exit:
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, bool *isDone)
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
 {
        TupleTableSlot *slot;
-       List       *targetlist;
-       int                     len;
        TupleDesc       tupType;
-       Datum      *tupValue;
-       ExprContext *econtext;
        HeapTuple       newTuple;
 
        /*
@@ -1802,29 +3065,24 @@ ExecProject(ProjectionInfo *projInfo, bool *isDone)
         * get the projection info we want
         */
        slot = projInfo->pi_slot;
-       targetlist = projInfo->pi_targetlist;
-       len = projInfo->pi_len;
        tupType = slot->ttc_tupleDescriptor;
 
-       tupValue = projInfo->pi_tupValue;
-       econtext = projInfo->pi_exprContext;
-
        /*
-        * form a new (result) tuple
+        * form a new result tuple (if possible --- result can be NULL)
         */
-       newTuple = ExecTargetList(targetlist,
-                                                         len,
+       newTuple = ExecTargetList(projInfo->pi_targetlist,
                                                          tupType,
-                                                         tupValue,
-                                                         econtext,
+                                                         projInfo->pi_exprContext,
+                                                         projInfo->pi_tupValues,
+                                                         projInfo->pi_tupNulls,
+                                                         projInfo->pi_itemIsDone,
                                                          isDone);
 
        /*
         * store the tuple in the projection slot and return the slot.
         */
-       return (TupleTableSlot *)
-               ExecStoreTuple(newTuple,/* tuple to store */
-                                          slot,        /* slot to store in */
-                                          InvalidBuffer,       /* tuple has no buffer */
-                                          true);
+       return ExecStoreTuple(newTuple,         /* tuple to store */
+                                                 slot, /* slot to store in */
+                                                 InvalidBuffer,                /* tuple has no buffer */
+                                                 true);
 }