]> 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 63e5bab6ee911e369fcaa8d3d531b1d9831aab79..d2efab0e36fa459b5991387cf0d2fda832604d3a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.99 2002/07/18 17:14:19 momjian 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_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/fcache.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, ExprDoneCond *isDone);
+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 ExecEvalOper(Expr *opClause, ExprContext *econtext,
+static Datum ExecEvalParam(Param *expression, ExprContext *econtext,
+                                                  bool *isNull);
+static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
                         bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalDistinct(Expr *opClause, ExprContext *econtext,
-                        bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
+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(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 ExecEvalCase(CaseExpr *caseExpr, 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 ExecEvalNullTest(NullTest *ntest, ExprContext *econtext,
-                                bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
-                                       bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalConstraint(Constraint *constraint, 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);
 
 
 /*----------
@@ -96,11 +127,12 @@ static Datum ExecEvalConstraint(Constraint *constraint, ExprContext *econtext,
  *----------
  */
 static Datum
-ExecEvalArrayRef(ArrayRef *arrayRef,
+ExecEvalArrayRef(ArrayRefExprState *astate,
                                 ExprContext *econtext,
                                 bool *isNull,
                                 ExprDoneCond *isDone)
 {
+       ArrayRef   *arrayRef = (ArrayRef *) astate->xprstate.expr;
        ArrayType  *array_source;
        ArrayType  *resultArray;
        bool            isAssignment = (arrayRef->refassgnexpr != NULL);
@@ -114,7 +146,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
        if (arrayRef->refexpr != NULL)
        {
                array_source = (ArrayType *)
-                       DatumGetPointer(ExecEvalExpr(arrayRef->refexpr,
+                       DatumGetPointer(ExecEvalExpr(astate->refexpr,
                                                                                 econtext,
                                                                                 isNull,
                                                                                 isDone));
@@ -140,13 +172,15 @@ 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,
                                                                                                         NULL));
@@ -160,15 +194,17 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
                }
        }
 
-       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,
                                                                                                                 NULL));
@@ -185,9 +221,9 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
                                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
@@ -195,7 +231,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 
        if (isAssignment)
        {
-               Datum           sourceData = ExecEvalExpr(arrayRef->refassgnexpr,
+               Datum           sourceData = ExecEvalExpr(astate->refassgnexpr,
                                                                                          econtext,
                                                                                          isNull,
                                                                                          NULL);
@@ -219,35 +255,38 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
                        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,
+                                                                                 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);
        }
@@ -262,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];
@@ -344,7 +383,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
         * 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
-        * TransactionCommandContext which should be safe enough.  This
+        * 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.
@@ -358,7 +397,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
                TupleTableSlot *tempSlot;
                HeapTuple       tup;
 
-               oldContext = MemoryContextSwitchTo(TransactionCommandContext);
+               oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
                tempSlot = MakeTupleTableSlot();
                tup = heap_copytuple(heapTuple);
                ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
@@ -382,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);
@@ -423,82 +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:
+                               }
+                               if (!matchFound)
+                                       paramList++;
+                       } /* while */
+               } /* if */
 
-                                       /*
-                                        * oops! this is not supposed to happen!
-                                        */
-                                       elog(ERROR, "ExecEvalParam: invalid paramkind %d",
-                                                thisParameterKind);
-                       }
-                       if (!matchFound)
-                               paramList++;
-               }                                               /* while */
-       }                                                       /* if */
+               if (!matchFound)
+               {
+                       if (thisParamKind == PARAM_NAMED)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                                errmsg("no value found for parameter \"%s\"",
+                                                               thisParamName)));
+                       else
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                                errmsg("no value found for parameter %d",
+                                                               thisParamId)));
+               }
 
-       if (!matchFound)
-       {
-               /*
-                * 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;
 }
 
 
@@ -524,13 +533,10 @@ 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))
        {
@@ -558,10 +564,10 @@ 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))
        {
@@ -583,7 +589,7 @@ 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,
@@ -595,6 +601,31 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
        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));
+
+       /* 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.
  */
@@ -614,7 +645,7 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
        {
                ExprDoneCond thisArgIsDone;
 
-               fcinfo->arg[i] = ExecEvalExpr((Node *) lfirst(arg),
+               fcinfo->arg[i] = ExecEvalExpr((ExprState *) lfirst(arg),
                                                                          econtext,
                                                                          &fcinfo->argnull[i],
                                                                          &thisArgIsDone);
@@ -628,7 +659,9 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
                         * it.
                         */
                        if (argIsDone != ExprSingleResult)
-                               elog(ERROR, "Functions and operators can take only one set argument");
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("functions and operators can take at most one set argument")));
                        argIsDone = thisArgIsDone;
                }
                i++;
@@ -643,17 +676,14 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
  *             ExecMakeFunctionResult
  *
  * Evaluate the arguments to a function and then the function itself.
- *
- * NOTE: econtext is used only for evaluating the argument expressions;
- * it is not passed to the function itself.
  */
 Datum
-ExecMakeFunctionResult(FunctionCachePtr fcache,
-                                          List *arguments,
+ExecMakeFunctionResult(FuncExprState *fcache,
                                           ExprContext *econtext,
                                           bool *isNull,
                                           ExprDoneCond *isDone)
 {
+       List       *arguments = fcache->args;
        Datum           result;
        FunctionCallInfoData fcinfo;
        ReturnSetInfo rsinfo;           /* for functions returning sets */
@@ -681,7 +711,9 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
                        if (isDone)
                                *isDone = ExprEndResult;
                        else
-                               elog(ERROR, "Set-valued function called in context that cannot accept a set");
+                               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);
@@ -704,6 +736,12 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
                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;
        }
 
        /*
@@ -717,7 +755,9 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
                 * to accept one.
                 */
                if (isDone == NULL)
-                       elog(ERROR, "Set-valued function called in context that cannot accept a set");
+                       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
@@ -834,10 +874,278 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
 }
 
 
+/*
+ *             ExecMakeTableFunctionResult
+ *
+ * Evaluate a table function, producing a materialized result in a Tuplestore
+ * object.     (If function returns an empty set, we just return NULL instead.)
+ */
+Tuplestorestate *
+ExecMakeTableFunctionResult(ExprState *funcexpr,
+                                                       ExprContext *econtext,
+                                                       TupleDesc expectedDesc,
+                                                       TupleDesc *returnDesc)
+{
+       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;
+
+       /*
+        * Normally the passed expression tree will be a FuncExprState, since the
+        * grammar only allows a function call at the top level of a table
+        * function reference.  However, if the function doesn't return set then
+        * the planner might have replaced the function call via constant-folding
+        * or inlining.  So if we see any other kind of expression node, execute
+        * it via the general ExecEvalExpr() code; the only difference is that
+        * we don't get a chance to pass a special ReturnSetInfo to any functions
+        * buried in the expression.
+        */
+       if (funcexpr && IsA(funcexpr, FuncExprState) &&
+               IsA(funcexpr->expr, FuncExpr))
+       {
+               FuncExprState *fcache = (FuncExprState *) funcexpr;
+               ExprDoneCond argDone;
+
+               /*
+                * This path is similar to ExecMakeFunctionResult.
+                */
+               direct_function_call = true;
+
+               /*
+                * 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);
+               }
+
+               /*
+                * 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")));
+
+               /*
+                * 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
+       {
+               /* Treat funcexpr as a generic expression */
+               direct_function_call = false;
+       }
+
+       funcrettype = exprType((Node *) funcexpr->expr);
+
+       /*
+        * Prepare a resultinfo node for communication.  We always do this
+        * even if not expecting a set result, so that we can pass
+        * expectedDesc.  In the generic-expression case, the expression
+        * doesn't actually get to see the resultinfo, but set it up anyway
+        * because we use some of the fields as our own state variables.
+        */
+       fcinfo.resultinfo = (Node *) &rsinfo;
+       rsinfo.type = T_ReturnSetInfo;
+       rsinfo.econtext = econtext;
+       rsinfo.expectedDesc = expectedDesc;
+       rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
+       rsinfo.returnMode = SFRM_ValuePerCall;
+       /* isDone is filled below */
+       rsinfo.setResult = NULL;
+       rsinfo.setDesc = NULL;
+
+       /*
+        * Switch to short-lived context for calling the function or expression.
+        */
+       callerContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+       /*
+        * Loop to handle the ValuePerCall protocol (which is also the same
+        * behavior needed in the generic ExecEvalExpr path).
+        */
+       for (;;)
+       {
+               Datum           result;
+               HeapTuple       tuple;
+
+               /*
+                * reset per-tuple memory context before each call of the
+                * function or expression. This cleans up any local memory the
+                * function may leak when called.
+                */
+               ResetExprContext(econtext);
+
+               /* Call the function or expression one time */
+               if (direct_function_call)
+               {
+                       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;
+}
+
+
 /* ----------------------------------------------------------------
- *             ExecEvalOper
- *             ExecEvalDistinct
  *             ExecEvalFunc
+ *             ExecEvalOper
  *
  *             Evaluate the functional result of a list of arguments by calling the
  *             function manager.
@@ -845,42 +1153,49 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
  */
 
 /* ----------------------------------------------------------------
- *             ExecEvalOper
+ *             ExecEvalFunc
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalOper(Expr *opClause,
+ExecEvalFunc(FuncExprState *fcache,
                         ExprContext *econtext,
                         bool *isNull,
                         ExprDoneCond *isDone)
 {
-       Oper       *op;
-       List       *argList;
-       FunctionCachePtr fcache;
-
        /*
-        * 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.
+        * Initialize function cache if first time through
         */
-       op = (Oper *) opClause->oper;
-       argList = opClause->args;
+       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)
+{
        /*
-        * get the fcache from the Oper node. If it is NULL, then initialize
-        * it
+        * Initialize function cache if first time through
         */
-       fcache = op->op_fcache;
-       if (fcache == NULL)
+       if (fcache->func.fn_oid == InvalidOid)
        {
-               fcache = init_fcache(op->opid, length(argList),
-                                                        econtext->ecxt_per_query_memory);
-               op->op_fcache = fcache;
+               OpExpr *op = (OpExpr *) fcache->xprstate.expr;
+
+               init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory);
        }
 
-       return ExecMakeFunctionResult(fcache, argList, econtext,
-                                                                 isNull, isDone);
+       return ExecMakeFunctionResult(fcache, econtext, isNull, isDone);
 }
 
 /* ----------------------------------------------------------------
@@ -890,113 +1205,211 @@ ExecEvalOper(Expr *opClause,
  * 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've already evaluated the arguments
+ * operator, but since we need special processing of the arguments
  * we can not simply reuse ExecEvalOper() or ExecEvalFunc().
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalDistinct(Expr *opClause,
+ExecEvalDistinct(FuncExprState *fcache,
                                 ExprContext *econtext,
-                                bool *isNull,
-                                ExprDoneCond *isDone)
+                                bool *isNull)
 {
-       bool result;
-       FunctionCachePtr fcache;
+       Datum           result;
        FunctionCallInfoData fcinfo;
        ExprDoneCond argDone;
-       Oper       *op;
        List       *argList;
 
        /*
-        * 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.
+        * Initialize function cache if first time through
         */
-       op = (Oper *) opClause->oper;
-       argList = opClause->args;
+       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);
+       }
 
        /*
-        * get the fcache from the Oper node. If it is NULL, then initialize
-        * it
+        * extract info from fcache
         */
-       fcache = op->op_fcache;
-       if (fcache == NULL)
-       {
-               fcache = init_fcache(op->opid, length(argList),
-                                                        econtext->ecxt_per_query_memory);
-               op->op_fcache = fcache;
-       }
-       Assert(fcache->func.fn_retset == FALSE);
+       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 = FALSE;
+               result = BoolGetDatum(FALSE);
        }
        else if (fcinfo.argnull[0] || fcinfo.argnull[1])
        {
-               /* One is NULL? Then is distinct... */
-               result = TRUE;
+               /* Only one is NULL? Then is distinct... */
+               result = BoolGetDatum(TRUE);
        }
        else
        {
                fcinfo.isnull = false;
                result = FunctionCallInvoke(&fcinfo);
                *isNull = fcinfo.isnull;
-
-               result = (!DatumGetBool(result));
+               /* Must invert result of "=" */
+               result = BoolGetDatum(!DatumGetBool(result));
        }
 
-       return BoolGetDatum(result);
+       return result;
 }
 
-/* ----------------------------------------------------------------
- *             ExecEvalFunc
- * ----------------------------------------------------------------
+/*
+ * 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
-ExecEvalFunc(Expr *funcClause,
-                        ExprContext *econtext,
-                        bool *isNull,
-                        ExprDoneCond *isDone)
+ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
+                                         ExprContext *econtext, bool *isNull)
 {
-       Func       *func;
-       List       *argList;
-       FunctionCachePtr fcache;
+       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;
 
        /*
-        * 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.
+        * 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.)
         */
-       func = (Func *) funcClause->oper;
-       argList = funcClause->args;
+       if (fcinfo.argnull[0] && sstate->fxprstate.func.fn_strict)
+       {
+               *isNull = true;
+               return (Datum) 0;
+       }
 
        /*
-        * get the fcache from the Func node. If it is NULL, then initialize
-        * it
+        * 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.
         */
-       fcache = func->func_fcache;
-       if (fcache == NULL)
+       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++)
        {
-               fcache = init_fcache(func->funcid, length(argList),
-                                                        econtext->ecxt_per_query_memory);
-               func->func_fcache = fcache;
+               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 */
+                       }
+               }
        }
 
-       return ExecMakeFunctionResult(fcache, argList, econtext,
-                                                                 isNull, isDone);
+       *isNull = resultnull;
+       return result;
 }
 
 /* ----------------------------------------------------------------
@@ -1004,8 +1417,7 @@ ExecEvalFunc(Expr *funcClause,
  *             ExecEvalOr
  *             ExecEvalAnd
  *
- *             Evaluate boolean expressions.  Evaluation of 'or' is
- *             short-circuited when the first true (or null) value is found.
+ *             Evaluate boolean expressions, with appropriate short-circuiting.
  *
  *             The query planner reformulates clause expressions in the
  *             qualification to conjunctive normal form.  If we ever get
@@ -1016,9 +1428,9 @@ ExecEvalFunc(Expr *funcClause,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
+ExecEvalNot(BoolExprState *notclause, ExprContext *econtext, bool *isNull)
 {
-       Node       *clause;
+       ExprState  *clause;
        Datum           expr_value;
 
        clause = lfirst(notclause->args);
@@ -1044,7 +1456,7 @@ ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
+ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, bool *isNull)
 {
        List       *clauses;
        List       *clause;
@@ -1070,7 +1482,7 @@ ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
         */
        foreach(clause, clauses)
        {
-               clause_value = ExecEvalExpr((Node *) lfirst(clause),
+               clause_value = ExecEvalExpr((ExprState *) lfirst(clause),
                                                                        econtext, isNull, NULL);
 
                /*
@@ -1092,7 +1504,7 @@ 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;
@@ -1112,7 +1524,7 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
         */
        foreach(clause, clauses)
        {
-               clause_value = ExecEvalExpr((Node *) lfirst(clause),
+               clause_value = ExecEvalExpr((ExprState *) lfirst(clause),
                                                                        econtext, isNull, NULL);
 
                /*
@@ -1129,6 +1541,7 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
        return BoolGetDatum(!AnyNull);
 }
 
+
 /* ----------------------------------------------------------------
  *             ExecEvalCase
  *
@@ -1139,7 +1552,7 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
+ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
                         bool *isNull, ExprDoneCond *isDone)
 {
        List       *clauses;
@@ -1155,7 +1568,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
         */
        foreach(clause, clauses)
        {
-               CaseWhen   *wclause = lfirst(clause);
+               CaseWhenState *wclause = lfirst(clause);
 
                clause_value = ExecEvalExpr(wclause->expr,
                                                                        econtext,
@@ -1189,80 +1602,296 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
 }
 
 /* ----------------------------------------------------------------
- *             ExecEvalNullTest
+ *             ExecEvalArray - ARRAY[] expressions
  *
- *             Evaluate a NullTest node.
+ * 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
-ExecEvalNullTest(NullTest *ntest,
-                                ExprContext *econtext,
-                                bool *isNull,
-                                ExprDoneCond *isDone)
+ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
+                         bool *isNull)
 {
-       Datum           result;
-
-       result = ExecEvalExpr(ntest->arg, econtext, isNull, isDone);
-       switch (ntest->nulltesttype)
+       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)
        {
-               case IS_NULL:
-                       if (*isNull)
-                       {
-                               *isNull = false;
-                               return BoolGetDatum(true);
+               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
-                               return BoolGetDatum(false);
-               case IS_NOT_NULL:
-                       if (*isNull)
                        {
-                               *isNull = false;
-                               return BoolGetDatum(false);
+                               /* 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
-                               return BoolGetDatum(true);
-               default:
-                       elog(ERROR, "ExecEvalNullTest: unexpected nulltesttype %d",
-                                (int) ntest->nulltesttype);
-                       return (Datum) 0;       /* keep compiler quiet */
+                               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);
 }
 
-/*
- * ExecEvalConstraint
+/* ----------------------------------------------------------------
+ *             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
  *
- * Test the constraint against the data provided.  If the data fits
- * within the constraint specifications, pass it through (return the
- * datum) otherwise throw an error.
+ * 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
-ExecEvalConstraint(Constraint *constraint, ExprContext *econtext,
-                                  bool *isNull, ExprDoneCond *isDone)
+ExecEvalNullTest(GenericExprState *nstate,
+                                ExprContext *econtext,
+                                bool *isNull,
+                                ExprDoneCond *isDone)
 {
+       NullTest   *ntest = (NullTest *) nstate->xprstate.expr;
        Datum           result;
 
-       result = ExecEvalExpr(constraint->raw_expr, econtext, isNull, isDone);
+       result = ExecEvalExpr(nstate->arg, econtext, isNull, isDone);
+
+       if (isDone && *isDone == ExprEndResult)
+               return result;                  /* nothing to check */
 
-       /* Test for the constraint type */
-       switch(constraint->contype)
+       switch (ntest->nulltesttype)
        {
-               case CONSTR_NOTNULL:
+               case IS_NULL:
                        if (*isNull)
                        {
-                               elog(ERROR, "Domain %s does not allow NULL values", constraint->name);
+                               *isNull = false;
+                               return BoolGetDatum(true);
                        }
-                       break;
-               case CONSTR_CHECK:
-
-                               elog(ERROR, "ExecEvalConstraint: Domain CHECK Constraints not yet implemented");
-                       break;
+                       else
+                               return BoolGetDatum(false);
+               case IS_NOT_NULL:
+                       if (*isNull)
+                       {
+                               *isNull = false;
+                               return BoolGetDatum(false);
+                       }
+                       else
+                               return BoolGetDatum(true);
                default:
-                       elog(ERROR, "ExecEvalConstraint: Constraint type unknown");
-                       break;
+                       elog(ERROR, "unrecognized nulltesttype: %d",
+                                (int) ntest->nulltesttype);
+                       return (Datum) 0;       /* keep compiler quiet */
        }
-
-       /* If all has gone well (constraint did not fail) return the datum */
-       return result;
 }
 
 /* ----------------------------------------------------------------
@@ -1272,14 +1901,19 @@ ExecEvalConstraint(Constraint *constraint, ExprContext *econtext,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalBooleanTest(BooleanTest *btest,
+ExecEvalBooleanTest(GenericExprState *bstate,
                                        ExprContext *econtext,
                                        bool *isNull,
                                        ExprDoneCond *isDone)
 {
+       BooleanTest *btest = (BooleanTest *) bstate->xprstate.expr;
        Datum           result;
 
-       result = ExecEvalExpr(btest->arg, econtext, isNull, isDone);
+       result = ExecEvalExpr(bstate->arg, econtext, isNull, isDone);
+
+       if (isDone && *isDone == ExprEndResult)
+               return result;                  /* nothing to check */
+
        switch (btest->booltesttype)
        {
                case IS_TRUE:
@@ -1339,12 +1973,103 @@ ExecEvalBooleanTest(BooleanTest *btest,
                        else
                                return BoolGetDatum(true);
                default:
-                       elog(ERROR, "ExecEvalBooleanTest: unexpected booltesttype %d",
+                       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
  *
@@ -1352,17 +2077,21 @@ ExecEvalBooleanTest(BooleanTest *btest,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalFieldSelect(FieldSelect *fselect,
+ExecEvalFieldSelect(GenericExprState *fstate,
                                        ExprContext *econtext,
                                        bool *isNull,
                                        ExprDoneCond *isDone)
 {
+       FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
        Datum           result;
        TupleTableSlot *resSlot;
 
-       result = ExecEvalExpr(fselect->arg, econtext, isNull, isDone);
+       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,
@@ -1378,7 +2107,7 @@ ExecEvalFieldSelect(FieldSelect *fselect,
  *             Recursively evaluate a targetlist or qualification expression.
  *
  * Inputs:
- *             expression: the expression tree to evaluate
+ *             expression: the expression state tree to evaluate
  *             econtext: evaluation context information
  *
  * Outputs:
@@ -1388,9 +2117,9 @@ ExecEvalFieldSelect(FieldSelect *fselect,
  *             *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 elog()
- * error will be reported.     If the caller does pass an isDone pointer then
- * *isDone is set to one of these three states:
+ * 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
@@ -1412,12 +2141,13 @@ ExecEvalFieldSelect(FieldSelect *fselect,
  * ----------------------------------------------------------------
  */
 Datum
-ExecEvalExpr(Node *expression,
+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;
@@ -1435,111 +2165,137 @@ ExecEvalExpr(Node *expression,
         * here we dispatch the work to the appropriate type of function given
         * the type of our expression.
         */
-       switch (nodeTag(expression))
+       expr = expression->expr;
+       switch (nodeTag(expr))
        {
                case T_Var:
-                       retDatum = ExecEvalVar((Var *) expression, econtext, isNull);
+                       retDatum = ExecEvalVar((Var *) expr, econtext, isNull);
                        break;
                case T_Const:
                        {
-                               Const      *con = (Const *) expression;
+                               Const      *con = (Const *) expr;
 
                                retDatum = con->constvalue;
                                *isNull = con->constisnull;
                                break;
                        }
                case T_Param:
-                       retDatum = ExecEvalParam((Param *) expression, econtext, isNull);
+                       retDatum = ExecEvalParam((Param *) expr, econtext, isNull);
                        break;
                case T_Aggref:
-                       retDatum = ExecEvalAggref((Aggref *) expression, econtext, isNull);
+                       retDatum = ExecEvalAggref((AggrefExprState *) expression,
+                                                                         econtext,
+                                                                         isNull);
                        break;
                case T_ArrayRef:
-                       retDatum = ExecEvalArrayRef((ArrayRef *) expression,
+                       retDatum = ExecEvalArrayRef((ArrayRefExprState *) expression,
                                                                                econtext,
                                                                                isNull,
                                                                                isDone);
                        break;
-               case T_Expr:
+               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:
                        {
-                               Expr       *expr = (Expr *) expression;
+                               BoolExprState *state = (BoolExprState *) expression;
 
-                               switch (expr->opType)
+                               switch (((BoolExpr *) expr)->boolop)
                                {
-                                       case OP_EXPR:
-                                               retDatum = ExecEvalOper(expr, econtext,
-                                                                                               isNull, isDone);
-                                               break;
-                                       case FUNC_EXPR:
-                                               retDatum = ExecEvalFunc(expr, econtext,
-                                                                                               isNull, isDone);
+                                       case AND_EXPR:
+                                               retDatum = ExecEvalAnd(state, econtext, isNull);
                                                break;
                                        case OR_EXPR:
-                                               retDatum = ExecEvalOr(expr, econtext, isNull);
-                                               break;
-                                       case AND_EXPR:
-                                               retDatum = ExecEvalAnd(expr, econtext, isNull);
+                                               retDatum = ExecEvalOr(state, econtext, isNull);
                                                break;
                                        case NOT_EXPR:
-                                               retDatum = ExecEvalNot(expr, econtext, isNull);
-                                               break;
-                                       case DISTINCT_EXPR:
-                                               retDatum = ExecEvalDistinct(expr, econtext,
-                                                                                                       isNull, isDone);
-                                               break;
-                                       case SUBPLAN_EXPR:
-                                               retDatum = ExecSubPlan((SubPlan *) expr->oper,
-                                                                                          expr->args, econtext,
-                                                                                          isNull);
+                                               retDatum = ExecEvalNot(state, econtext, isNull);
                                                break;
                                        default:
-                                               elog(ERROR, "ExecEvalExpr: unknown expression type %d",
-                                                        expr->opType);
+                                               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((FieldSelect *) expression,
+                       retDatum = ExecEvalFieldSelect((GenericExprState *) expression,
                                                                                   econtext,
                                                                                   isNull,
                                                                                   isDone);
                        break;
                case T_RelabelType:
-                       retDatum = ExecEvalExpr(((RelabelType *) expression)->arg,
+                       retDatum = ExecEvalExpr(((GenericExprState *) expression)->arg,
                                                                        econtext,
                                                                        isNull,
                                                                        isDone);
                        break;
-               case T_Constraint:
-                       retDatum = ExecEvalConstraint((Constraint *) expression,
-                                                                                econtext,
-                                                                                isNull,
-                                                                                isDone);
-                       break;
                case T_CaseExpr:
-                       retDatum = ExecEvalCase((CaseExpr *) expression,
+                       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((NullTest *) expression,
+                       retDatum = ExecEvalNullTest((GenericExprState *) expression,
                                                                                econtext,
                                                                                isNull,
                                                                                isDone);
                        break;
                case T_BooleanTest:
-                       retDatum = ExecEvalBooleanTest((BooleanTest *) expression,
+                       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, "ExecEvalExpr: unknown expression type %d",
-                                nodeTag(expression));
+                       elog(ERROR, "unrecognized node type: %d",
+                                (int) nodeTag(expression));
                        retDatum = 0;           /* keep compiler quiet */
                        break;
        }
@@ -1552,7 +2308,7 @@ ExecEvalExpr(Node *expression,
  * Same as above, but get into the right allocation context explicitly.
  */
 Datum
-ExecEvalExprSwitchContext(Node *expression,
+ExecEvalExprSwitchContext(ExprState *expression,
                                                  ExprContext *econtext,
                                                  bool *isNull,
                                                  ExprDoneCond *isDone)
@@ -1567,6 +2323,408 @@ ExecEvalExprSwitchContext(Node *expression,
 }
 
 
+/*
+ * 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);
+
+                               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);
+
+                               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);
+
+                               if (!parent)
+                                       elog(ERROR, "SubPlan found with no parent plan");
+
+                               /*
+                                * Here we just add the SubPlanState nodes to
+                                * parent->subPlan.  The subplans will be initialized later.
+                                */
+                               parent->subPlan = lcons(sstate, parent->subPlan);
+                               sstate->sub_estate = NULL;
+                               sstate->planstate = NULL;
+
+                               sstate->exprs = (List *)
+                                       ExecInitExpr((Expr *) subplan->exprs, parent);
+                               sstate->args = (List *)
+                                       ExecInitExpr((Expr *) subplan->args, parent);
+
+                               state = (ExprState *) sstate;
+                       }
+                       break;
+               case T_FieldSelect:
+                       {
+                               FieldSelect   *fselect = (FieldSelect *) node;
+                               GenericExprState *gstate = makeNode(GenericExprState);
+
+                               gstate->arg = ExecInitExpr(fselect->arg, parent);
+                               state = (ExprState *) gstate;
+                       }
+                       break;
+               case T_RelabelType:
+                       {
+                               RelabelType   *relabel = (RelabelType *) node;
+                               GenericExprState *gstate = makeNode(GenericExprState);
+
+                               gstate->arg = ExecInitExpr(relabel->arg, parent);
+                               state = (ExprState *) gstate;
+                       }
+                       break;
+               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_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_CoalesceExpr:
+                       {
+                               CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
+                               CoalesceExprState *cstate = makeNode(CoalesceExprState);
+                               FastList        outlist;
+                               List       *inlist;
+
+                               FastListInit(&outlist);
+                               foreach(inlist, coalesceexpr->args)
+                               {
+                                       Expr *e = (Expr *) lfirst(inlist);
+                                       ExprState *estate;
+
+                                       estate = ExecInitExpr(e, parent);
+                                       FastAppend(&outlist, estate);
+                               }
+                               cstate->args = FastListValue(&outlist);
+                               state = (ExprState *) cstate;
+                       }
+                       break;
+               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_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, "unrecognized node type: %d",
+                                (int) nodeTag(node));
+                       state = NULL;           /* keep compiler quiet */
+                       break;
+       }
+
+       /* Common code for all state-node types */
+       state->expr = node;
+
+       return state;
+}
+
+/*
+ * ExecInitExprInitPlan --- initialize a subplan expr that's being handled
+ * as an InitPlan.  This is identical to ExecInitExpr's handling of a regular
+ * subplan expr, except we do NOT want to add the node to the parent's
+ * subplan list.
+ */
+SubPlanState *
+ExecInitExprInitPlan(SubPlan *node, PlanState *parent)
+{
+       SubPlanState *sstate = makeNode(SubPlanState);
+
+       if (!parent)
+               elog(ERROR, "SubPlan found with no parent plan");
+
+       /* The subplan's state will be initialized later */
+       sstate->sub_estate = NULL;
+       sstate->planstate = NULL;
+
+       sstate->exprs = (List *) ExecInitExpr((Expr *) node->exprs, parent);
+       sstate->args = (List *) ExecInitExpr((Expr *) node->args, parent);
+
+       sstate->xprstate.expr = (Expr *) node;
+
+       return sstate;
+}
+
+/*
+ * ExecPrepareExpr --- initialize for expression execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we apply
+ * fix_opfuncids() to the passed expression tree to be sure it is ready
+ * to run.  (In ordinary Plan trees the planner will have fixed opfuncids,
+ * but callers outside the executor will not have done this.)
+ */
+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;
+}
+
+
 /* ----------------------------------------------------------------
  *                                      ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
@@ -1637,7 +2795,7 @@ 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;
 
@@ -1672,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);
 }
 
 /*
@@ -1700,13 +2847,9 @@ ExecCleanTargetListLength(List *targetlist)
        {
                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;
 }
@@ -1714,8 +2857,13 @@ 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
@@ -1725,25 +2873,15 @@ ExecCleanTargetListLength(List *targetlist)
  */
 static HeapTuple
 ExecTargetList(List *targetlist,
-                          int nodomains,
                           TupleDesc targettype,
-                          Datum *values,
                           ExprContext *econtext,
+                          Datum *values,
+                          char *nulls,
+                          ExprDoneCond *itemIsDone,
                           ExprDoneCond *isDone)
 {
        MemoryContext oldContext;
-
-#define NPREALLOCDOMAINS 64
-       char            nullsArray[NPREALLOCDOMAINS];
-       bool            fjIsNullArray[NPREALLOCDOMAINS];
-       ExprDoneCond itemIsDoneArray[NPREALLOCDOMAINS];
-       char       *nulls;
-       bool       *fjIsNull;
-       ExprDoneCond *itemIsDone;
        List       *tl;
-       TargetEntry *tle;
-       AttrNumber      resind;
-       HeapTuple       newTuple;
        bool            isNull;
        bool            haveDoneSets;
        static struct tupleDesc NullTupleDesc;          /* we assume this inits to
@@ -1772,35 +2910,9 @@ 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 array that holds the isDone status for each targetlist
-        * item. The isDone status is needed so that we can iterate,
-        * generating multiple tuples, when one or more tlist items return
-        * sets.  (We expect the caller to call us again if we return:
-        *
-        * isDone = ExprMultipleResult.)
-        */
-       if (nodomains > NPREALLOCDOMAINS)
-       {
-               nulls = (char *) palloc(nodomains * sizeof(char));
-               fjIsNull = (bool *) palloc(nodomains * sizeof(bool));
-               itemIsDone = (ExprDoneCond *) palloc(nodomains * sizeof(ExprDoneCond));
-       }
-       else
-       {
-               nulls = nullsArray;
-               fjIsNull = fjIsNullArray;
-               itemIsDone = itemIsDoneArray;
-       }
-
        /*
         * evaluate all the expressions in the target list
         */
-
        if (isDone)
                *isDone = ExprSingleResult;             /* until proven otherwise */
 
@@ -1808,84 +2920,33 @@ ExecTargetList(List *targetlist,
 
        foreach(tl, targetlist)
        {
-               tle = lfirst(tl);
-
-               if (tle->resdom != NULL)
-               {
-                       resind = tle->resdom->resno - 1;
+               GenericExprState *gstate = (GenericExprState *) lfirst(tl);
+               TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
+               AttrNumber      resind = tle->resdom->resno - 1;
 
-                       values[resind] = ExecEvalExpr(tle->expr,
-                                                                                 econtext,
-                                                                                 &isNull,
-                                                                                 &itemIsDone[resind]);
-                       nulls[resind] = isNull ? 'n' : ' ';
+               values[resind] = ExecEvalExpr(gstate->arg,
+                                                                         econtext,
+                                                                         &isNull,
+                                                                         &itemIsDone[resind]);
+               nulls[resind] = isNull ? 'n' : ' ';
 
-                       if (itemIsDone[resind] != ExprSingleResult)
-                       {
-                               /* We have a set-valued expression in the tlist */
-                               if (isDone == NULL)
-                                       elog(ERROR, "Set-valued function called in context that cannot accept a set");
-                               if (itemIsDone[resind] == ExprMultipleResult)
-                               {
-                                       /* we have undone sets in the tlist, set flag */
-                                       *isDone = ExprMultipleResult;
-                               }
-                               else
-                               {
-                                       /* we have done sets in the tlist, set flag for that */
-                                       haveDoneSets = true;
-                               }
-                       }
-               }
-               else
+               if (itemIsDone[resind] != ExprSingleResult)
                {
-#ifdef SETS_FIXED
-                       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);
-
-                       /*
-                        * XXX this is wrong, but since fjoin code is completely
-                        * broken anyway, I'm not going to worry about it now --- tgl
-                        * 8/23/00
-                        */
-                       if (isDone && *isDone == ExprEndResult)
+                       /* 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)
                        {
-                               MemoryContextSwitchTo(oldContext);
-                               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;
-                       values[resind] = results[0];
-                       nulls[resind] = fjIsNull[0] ? 'n' : ' ';
-
-                       /*
-                        * Get results from all of the outer nodes
-                        */
-                       for (curNode = 1;
-                                curNode < nNodes;
-                                curNode++, fjTlist = lnext(fjTlist))
+                       else
                        {
-                               Node       *outernode = lfirst(fjTlist);
-
-                               fjRes = (Resdom *) outernode->iterexpr;
-                               resind = fjRes->resno - 1;
-                               values[resind] = results[curNode];
-                               nulls[resind] = fjIsNull[curNode] ? 'n' : ' ';
+                               /* we have done sets in the tlist, set flag for that */
+                               haveDoneSets = true;
                        }
-#else
-                       elog(ERROR, "ExecTargetList: fjoin nodes not currently supported");
-#endif
                }
        }
 
@@ -1902,8 +2963,7 @@ ExecTargetList(List *targetlist,
                         */
                        *isDone = ExprEndResult;
                        MemoryContextSwitchTo(oldContext);
-                       newTuple = NULL;
-                       goto exit;
+                       return NULL;
                }
                else
                {
@@ -1913,29 +2973,26 @@ ExecTargetList(List *targetlist,
                         */
                        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)
                                {
-                                       resind = tle->resdom->resno - 1;
+                                       values[resind] = ExecEvalExpr(gstate->arg,
+                                                                                                 econtext,
+                                                                                                 &isNull,
+                                                                                                 &itemIsDone[resind]);
+                                       nulls[resind] = isNull ? 'n' : ' ';
 
                                        if (itemIsDone[resind] == ExprEndResult)
                                        {
-                                               values[resind] = ExecEvalExpr(tle->expr,
-                                                                                                         econtext,
-                                                                                                         &isNull,
-                                                                                                       &itemIsDone[resind]);
-                                               nulls[resind] = isNull ? 'n' : ' ';
-
-                                               if (itemIsDone[resind] == ExprEndResult)
-                                               {
-                                                       /*
-                                                        * Oh dear, this item is returning an empty
-                                                        * set. Guess we can't make a tuple after all.
-                                                        */
-                                                       *isDone = ExprEndResult;
-                                                       break;
-                                               }
+                                               /*
+                                                * Oh dear, this item is returning an empty
+                                                * set. Guess we can't make a tuple after all.
+                                                */
+                                               *isDone = ExprEndResult;
+                                               break;
                                        }
                                }
                        }
@@ -1944,30 +3001,28 @@ ExecTargetList(List *targetlist,
                         * If we cannot make a tuple because some sets are empty, we
                         * still have to cycle the nonempty sets to completion, else
                         * resources will not be released from subplans etc.
+                        *
+                        * XXX is that still necessary?
                         */
                        if (*isDone == ExprEndResult)
                        {
                                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)
+                                       while (itemIsDone[resind] == ExprMultipleResult)
                                        {
-                                               resind = tle->resdom->resno - 1;
-
-                                               while (itemIsDone[resind] == ExprMultipleResult)
-                                               {
-                                                       (void) ExecEvalExpr(tle->expr,
-                                                                                               econtext,
-                                                                                               &isNull,
-                                                                                               &itemIsDone[resind]);
-                                               }
+                                               (void) ExecEvalExpr(gstate->arg,
+                                                                                       econtext,
+                                                                                       &isNull,
+                                                                                       &itemIsDone[resind]);
                                        }
                                }
 
                                MemoryContextSwitchTo(oldContext);
-                               newTuple = NULL;
-                               goto exit;
+                               return NULL;
                        }
                }
        }
@@ -1977,21 +3032,7 @@ ExecTargetList(List *targetlist,
         */
        MemoryContextSwitchTo(oldContext);
 
-       newTuple = (HeapTuple) heap_formtuple(targettype, values, nulls);
-
-exit:
-
-       /*
-        * free the status arrays if we palloc'd them
-        */
-       if (nodomains > NPREALLOCDOMAINS)
-       {
-               pfree(nulls);
-               pfree(fjIsNull);
-               pfree(itemIsDone);
-       }
-
-       return newTuple;
+       return heap_formtuple(targettype, values, nulls);
 }
 
 /* ----------------------------------------------------------------
@@ -2011,11 +3052,7 @@ TupleTableSlot *
 ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
 {
        TupleTableSlot *slot;
-       List       *targetlist;
-       int                     len;
        TupleDesc       tupType;
-       Datum      *tupValue;
-       ExprContext *econtext;
        HeapTuple       newTuple;
 
        /*
@@ -2028,21 +3065,17 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *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 (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);
 
        /*