Faster expression evaluation and targetlist projection.
authorAndres Freund <andres@anarazel.de>
Tue, 14 Mar 2017 03:22:10 +0000 (20:22 -0700)
committerAndres Freund <andres@anarazel.de>
Tue, 14 Mar 2017 06:33:48 +0000 (23:33 -0700)
This replaces the recursive tree-walk based expression evaluation
method with a non-recursive opcode dispatched one.

This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.

The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
  function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
  out operation metadata sequentially; including the avoidance of
  nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
  constant re-checks at evaluation time

Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less critical code-paths can easily be
shared between interpreted and compiled evaluation.

While not implemented here, this also provides some basis to later
reduce the per executor-startup overhead of expression evaluation.

Author: Andres Freund
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de

63 files changed:
contrib/postgres_fdw/postgres_fdw.c
src/backend/bootstrap/bootstrap.c
src/backend/catalog/index.c
src/backend/catalog/partition.c
src/backend/catalog/toasting.c
src/backend/commands/analyze.c
src/backend/commands/copy.c
src/backend/commands/explain.c
src/backend/commands/indexcmds.c
src/backend/commands/prepare.c
src/backend/commands/tablecmds.c
src/backend/commands/trigger.c
src/backend/executor/Makefile
src/backend/executor/execExpr.c [new file with mode: 0644]
src/backend/executor/execIndexing.c
src/backend/executor/execInterpExpr.c [new file with mode: 0644]
src/backend/executor/execMain.c
src/backend/executor/execQual.c
src/backend/executor/execScan.c
src/backend/executor/execUtils.c
src/backend/executor/nodeAgg.c
src/backend/executor/nodeBitmapHeapscan.c
src/backend/executor/nodeCtescan.c
src/backend/executor/nodeCustom.c
src/backend/executor/nodeForeignscan.c
src/backend/executor/nodeFunctionscan.c
src/backend/executor/nodeGather.c
src/backend/executor/nodeGatherMerge.c
src/backend/executor/nodeGroup.c
src/backend/executor/nodeHash.c
src/backend/executor/nodeHashjoin.c
src/backend/executor/nodeIndexonlyscan.c
src/backend/executor/nodeIndexscan.c
src/backend/executor/nodeMergejoin.c
src/backend/executor/nodeModifyTable.c
src/backend/executor/nodeNestloop.c
src/backend/executor/nodeProjectSet.c
src/backend/executor/nodeResult.c
src/backend/executor/nodeSamplescan.c
src/backend/executor/nodeSeqscan.c
src/backend/executor/nodeSubplan.c
src/backend/executor/nodeSubqueryscan.c
src/backend/executor/nodeTableFuncscan.c
src/backend/executor/nodeTidscan.c
src/backend/executor/nodeValuesscan.c
src/backend/executor/nodeWindowAgg.c
src/backend/executor/nodeWorktablescan.c
src/backend/optimizer/util/clauses.c
src/backend/utils/adt/domains.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/xml.c
src/backend/utils/cache/typcache.c
src/include/executor/execExpr.h [new file with mode: 0644]
src/include/executor/execdebug.h
src/include/executor/executor.h
src/include/executor/nodeSubplan.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/utils/xml.h
src/pl/plpgsql/src/pl_exec.c
src/test/regress/expected/case.out
src/test/regress/sql/case.sql
src/tools/pgindent/typedefs.list

index 990313a597713048c70a61a7c2fd7ac704d6f52b..975aa88c5c6d30eacd410c4ea72e71a2ce771d48 100644 (file)
@@ -3414,7 +3414,7 @@ prepare_query_params(PlanState *node,
     * benefit, and it'd require postgres_fdw to know more than is desirable
     * about Param evaluation.)
     */
-   *param_exprs = (List *) ExecInitExpr((Expr *) fdw_exprs, node);
+   *param_exprs = ExecInitExprList(fdw_exprs, node);
 
    /* Allocate buffer for text form of query parameters. */
    *param_values = (const char **) palloc0(numParams * sizeof(char *));
index 6511c6064b8f6df3d5e2b21fc3c66a3a4f5bdd13..6cfce4f8ddb0f897e1b211f9f95e2525cc55a282 100644 (file)
@@ -1084,7 +1084,7 @@ index_register(Oid heap,
    /* predicate will likely be null, but may as well copy it */
    newind->il_info->ii_Predicate = (List *)
        copyObject(indexInfo->ii_Predicate);
-   newind->il_info->ii_PredicateState = NIL;
+   newind->il_info->ii_PredicateState = NULL;
    /* no exclusion constraints at bootstrap time, so no need to copy */
    Assert(indexInfo->ii_ExclusionOps == NULL);
    Assert(indexInfo->ii_ExclusionProcs == NULL);
index 8d42a347ea555b50e6c234b2c5b9eb65e217ebf4..d6be0915a7f89935a5af1afcbb9b7971bca97f37 100644 (file)
@@ -1658,7 +1658,7 @@ BuildIndexInfo(Relation index)
 
    /* fetch index predicate if any */
    ii->ii_Predicate = RelationGetIndexPredicate(index);
-   ii->ii_PredicateState = NIL;
+   ii->ii_PredicateState = NULL;
 
    /* fetch exclusion constraint info if any */
    if (indexStruct->indisexclusion)
@@ -1774,9 +1774,8 @@ FormIndexDatum(IndexInfo *indexInfo,
        indexInfo->ii_ExpressionsState == NIL)
    {
        /* First time through, set up expression evaluation state */
-       indexInfo->ii_ExpressionsState = (List *)
-           ExecPrepareExpr((Expr *) indexInfo->ii_Expressions,
-                           estate);
+       indexInfo->ii_ExpressionsState =
+           ExecPrepareExprList(indexInfo->ii_Expressions, estate);
        /* Check caller has set up context correctly */
        Assert(GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
    }
@@ -2208,7 +2207,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
    Datum       values[INDEX_MAX_KEYS];
    bool        isnull[INDEX_MAX_KEYS];
    double      reltuples;
-   List       *predicate;
+   ExprState  *predicate;
    TupleTableSlot *slot;
    EState     *estate;
    ExprContext *econtext;
@@ -2247,9 +2246,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
    econtext->ecxt_scantuple = slot;
 
    /* Set up execution state for predicate, if any. */
-   predicate = (List *)
-       ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-                       estate);
+   predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
    /*
     * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2552,9 +2549,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
         * In a partial index, discard tuples that don't satisfy the
         * predicate.
         */
-       if (predicate != NIL)
+       if (predicate != NULL)
        {
-           if (!ExecQual(predicate, econtext, false))
+           if (!ExecQual(predicate, econtext))
                continue;
        }
 
@@ -2619,7 +2616,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
    /* These may have been pointing to the now-gone estate */
    indexInfo->ii_ExpressionsState = NIL;
-   indexInfo->ii_PredicateState = NIL;
+   indexInfo->ii_PredicateState = NULL;
 
    return reltuples;
 }
@@ -2646,7 +2643,7 @@ IndexCheckExclusion(Relation heapRelation,
    HeapTuple   heapTuple;
    Datum       values[INDEX_MAX_KEYS];
    bool        isnull[INDEX_MAX_KEYS];
-   List       *predicate;
+   ExprState  *predicate;
    TupleTableSlot *slot;
    EState     *estate;
    ExprContext *econtext;
@@ -2672,9 +2669,7 @@ IndexCheckExclusion(Relation heapRelation,
    econtext->ecxt_scantuple = slot;
 
    /* Set up execution state for predicate, if any. */
-   predicate = (List *)
-       ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-                       estate);
+   predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
    /*
     * Scan all live tuples in the base relation.
@@ -2699,9 +2694,9 @@ IndexCheckExclusion(Relation heapRelation,
        /*
         * In a partial index, ignore tuples that don't satisfy the predicate.
         */
-       if (predicate != NIL)
+       if (predicate != NULL)
        {
-           if (!ExecQual(predicate, econtext, false))
+           if (!ExecQual(predicate, econtext))
                continue;
        }
 
@@ -2732,7 +2727,7 @@ IndexCheckExclusion(Relation heapRelation,
 
    /* These may have been pointing to the now-gone estate */
    indexInfo->ii_ExpressionsState = NIL;
-   indexInfo->ii_PredicateState = NIL;
+   indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2962,7 +2957,7 @@ validate_index_heapscan(Relation heapRelation,
    HeapTuple   heapTuple;
    Datum       values[INDEX_MAX_KEYS];
    bool        isnull[INDEX_MAX_KEYS];
-   List       *predicate;
+   ExprState  *predicate;
    TupleTableSlot *slot;
    EState     *estate;
    ExprContext *econtext;
@@ -2992,9 +2987,7 @@ validate_index_heapscan(Relation heapRelation,
    econtext->ecxt_scantuple = slot;
 
    /* Set up execution state for predicate, if any. */
-   predicate = (List *)
-       ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-                       estate);
+   predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
    /*
     * Prepare for scan of the base relation.  We need just those tuples
@@ -3121,9 +3114,9 @@ validate_index_heapscan(Relation heapRelation,
             * In a partial index, discard tuples that don't satisfy the
             * predicate.
             */
-           if (predicate != NIL)
+           if (predicate != NULL)
            {
-               if (!ExecQual(predicate, econtext, false))
+               if (!ExecQual(predicate, econtext))
                    continue;
            }
 
@@ -3177,7 +3170,7 @@ validate_index_heapscan(Relation heapRelation,
 
    /* These may have been pointing to the now-gone estate */
    indexInfo->ii_ExpressionsState = NIL;
-   indexInfo->ii_PredicateState = NIL;
+   indexInfo->ii_PredicateState = NULL;
 }
 
 
index e01ef864f00f5f02258752d060ac8b077bc5c623..1e75777acb88ffc463d83a4b608e1c93db0691a6 100644 (file)
@@ -1618,8 +1618,8 @@ FormPartitionKeyDatum(PartitionDispatch pd,
               GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
 
        /* First time through, set up expression evaluation state */
-       pd->keystate = (List *) ExecPrepareExpr((Expr *) pd->key->partexprs,
-                                               estate);
+       pd->keystate = ExecPrepareExprList(pd->key->partexprs,
+                                                   estate);
    }
 
    partexpr_item = list_head(pd->keystate);
index 0e4231668d4e69a9d5b119b3e95b7c162b4aa449..29756eb14eb43f2a4f15ad8a7eda00bbdbd3158d 100644 (file)
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
    indexInfo->ii_Expressions = NIL;
    indexInfo->ii_ExpressionsState = NIL;
    indexInfo->ii_Predicate = NIL;
-   indexInfo->ii_PredicateState = NIL;
+   indexInfo->ii_PredicateState = NULL;
    indexInfo->ii_ExclusionOps = NULL;
    indexInfo->ii_ExclusionProcs = NULL;
    indexInfo->ii_ExclusionStrats = NULL;
index b91df986c54e821e30c56ecf667c9fd05d3704ab..d124366db2901f0f58422d848dfe46e6c84c0265 100644 (file)
@@ -703,7 +703,7 @@ compute_index_stats(Relation onerel, double totalrows,
        TupleTableSlot *slot;
        EState     *estate;
        ExprContext *econtext;
-       List       *predicate;
+       ExprState  *predicate;
        Datum      *exprvals;
        bool       *exprnulls;
        int         numindexrows,
@@ -729,9 +729,7 @@ compute_index_stats(Relation onerel, double totalrows,
        econtext->ecxt_scantuple = slot;
 
        /* Set up execution state for predicate. */
-       predicate = castNode(List,
-                            ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-                                            estate));
+       predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
        /* Compute and save index expression values */
        exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -754,9 +752,9 @@ compute_index_stats(Relation onerel, double totalrows,
            ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
            /* If index is partial, check predicate */
-           if (predicate != NIL)
+           if (predicate != NULL)
            {
-               if (!ExecQual(predicate, econtext, false))
+               if (!ExecQual(predicate, econtext))
                    continue;
            }
            numindexrows++;
index 3102ab18c57a45af4756c76d1e949f971ba6b3a0..7295853e15628abd6ac3c5796beb9414123d2ec5 100644 (file)
@@ -3406,7 +3406,7 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext,
        Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory);
 
        values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext,
-                                        &nulls[defmap[i]]);
+                                         &nulls[defmap[i]]);
    }
 
    return true;
index c9b55ead3dc35d8a4113b4a416a20e66be795193..55d09c0e299bc46ed565af6bda7df980d8fd8a6a 100644 (file)
@@ -2890,7 +2890,7 @@ ExplainSubPlans(List *plans, List *ancestors,
    foreach(lst, plans)
    {
        SubPlanState *sps = (SubPlanState *) lfirst(lst);
-       SubPlan    *sp = (SubPlan *) sps->xprstate.expr;
+       SubPlan    *sp = sps->subplan;
 
        /*
         * There can be multiple SubPlan nodes referencing the same physical
index 72bb06c7602b97f6480ea92e44047ac48d83d1eb..fd0aeb06b9251184cd9fe3e26490b67f43c4096c 100644 (file)
@@ -179,7 +179,7 @@ CheckIndexCompatible(Oid oldId,
    indexInfo = makeNode(IndexInfo);
    indexInfo->ii_Expressions = NIL;
    indexInfo->ii_ExpressionsState = NIL;
-   indexInfo->ii_PredicateState = NIL;
+   indexInfo->ii_PredicateState = NULL;
    indexInfo->ii_ExclusionOps = NULL;
    indexInfo->ii_ExclusionProcs = NULL;
    indexInfo->ii_ExclusionStrats = NULL;
@@ -556,7 +556,7 @@ DefineIndex(Oid relationId,
    indexInfo->ii_Expressions = NIL;    /* for now */
    indexInfo->ii_ExpressionsState = NIL;
    indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-   indexInfo->ii_PredicateState = NIL;
+   indexInfo->ii_PredicateState = NULL;
    indexInfo->ii_ExclusionOps = NULL;
    indexInfo->ii_ExclusionProcs = NULL;
    indexInfo->ii_ExclusionStrats = NULL;
index 1cf0d2b971a370f97d4959dcc417e49f5a03cac9..46a362132c921de5b392b3b663319ec6f833846a 100644 (file)
@@ -391,7 +391,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
    }
 
    /* Prepare the expressions for execution */
-   exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
+   exprstates = ExecPrepareExprList(params, estate);
 
    paramLI = (ParamListInfo)
        palloc(offsetof(ParamListInfoData, params) +
@@ -407,7 +407,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
    i = 0;
    foreach(l, exprstates)
    {
-       ExprState  *n = lfirst(l);
+       ExprState  *n = (ExprState *) lfirst(l);
        ParamExternData *prm = &paramLI->params[i];
 
        prm->ptype = param_types[i];
index 1ddb72d16472376e72087b80b19767f7925e465e..07d0bb7af083ef3e3d6336296bf9691d101aa7d1 100644 (file)
@@ -185,7 +185,7 @@ typedef struct NewConstraint
    Oid         refindid;       /* OID of PK's index, if FOREIGN */
    Oid         conid;          /* OID of pg_constraint entry, if FOREIGN */
    Node       *qual;           /* Check expr or CONSTR_FOREIGN Constraint */
-   List       *qualstate;      /* Execution state for CHECK */
+   ExprState  *qualstate;      /* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4262,7 +4262,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
    CommandId   mycid;
    BulkInsertState bistate;
    int         hi_options;
-   List       *partqualstate = NIL;
+   ExprState  *partqualstate = NULL;
 
    /*
     * Open the relation(s).  We have surely already locked the existing
@@ -4315,8 +4315,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
        {
            case CONSTR_CHECK:
                needscan = true;
-               con->qualstate = (List *)
-                   ExecPrepareExpr((Expr *) con->qual, estate);
+               con->qualstate = ExecPrepareQual((List *) con->qual, estate);
                break;
            case CONSTR_FOREIGN:
                /* Nothing to do here */
@@ -4331,9 +4330,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
    if (tab->partition_constraint)
    {
        needscan = true;
-       partqualstate = (List *)
-           ExecPrepareExpr((Expr *) tab->partition_constraint,
-                           estate);
+       partqualstate = ExecPrepareCheck(tab->partition_constraint, estate);
    }
 
    foreach(l, tab->newvals)
@@ -4508,7 +4505,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
                switch (con->contype)
                {
                    case CONSTR_CHECK:
-                       if (!ExecQual(con->qualstate, econtext, true))
+                       if (!ExecQual(con->qualstate, econtext))
                            ereport(ERROR,
                                    (errcode(ERRCODE_CHECK_VIOLATION),
                                     errmsg("check constraint \"%s\" is violated by some row",
@@ -4524,7 +4521,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
                }
            }
 
-           if (partqualstate && !ExecQual(partqualstate, econtext, true))
+           if (partqualstate && !ExecCheck(partqualstate, econtext))
                ereport(ERROR,
                        (errcode(ERRCODE_CHECK_VIOLATION),
                    errmsg("partition constraint is violated by some row")));
@@ -7786,7 +7783,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
    Datum       val;
    char       *conbin;
    Expr       *origexpr;
-   List       *exprstate;
+   ExprState  *exprstate;
    TupleDesc   tupdesc;
    HeapScanDesc scan;
    HeapTuple   tuple;
@@ -7817,8 +7814,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
             HeapTupleGetOid(constrtup));
    conbin = TextDatumGetCString(val);
    origexpr = (Expr *) stringToNode(conbin);
-   exprstate = (List *)
-       ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+   exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
    econtext = GetPerTupleExprContext(estate);
    tupdesc = RelationGetDescr(rel);
@@ -7838,7 +7834,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
    {
        ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-       if (!ExecQual(exprstate, econtext, true))
+       if (!ExecQual(exprstate, econtext))
            ereport(ERROR,
                    (errcode(ERRCODE_CHECK_VIOLATION),
                     errmsg("check constraint \"%s\" is violated by some row",
index a1bb3e958c768e8e0d30a3f82b395cf0bf1671df..53fb9f557efee4568be567cf88774061559d1fc5 100644 (file)
@@ -3057,7 +3057,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
    if (trigger->tgqual)
    {
        TupleDesc   tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-       List      **predicate;
+       ExprState **predicate;
        ExprContext *econtext;
        TupleTableSlot *oldslot = NULL;
        TupleTableSlot *newslot = NULL;
@@ -3078,7 +3078,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
         * nodetrees for it.  Keep them in the per-query memory context so
         * they'll survive throughout the query.
         */
-       if (*predicate == NIL)
+       if (*predicate == NULL)
        {
            Node       *tgqual;
 
@@ -3089,7 +3089,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
            ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
            /* ExecQual wants implicit-AND form */
            tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-           *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+           *predicate = ExecPrepareQual((List *) tgqual, estate);
            MemoryContextSwitchTo(oldContext);
        }
 
@@ -3137,7 +3137,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
         */
        econtext->ecxt_innertuple = oldslot;
        econtext->ecxt_outertuple = newslot;
-       if (!ExecQual(*predicate, econtext, false))
+       if (!ExecQual(*predicate, econtext))
            return false;
    }
 
index d281906cd5cf496449f61cd358a1566c8ad9b5ef..2c68e18a5b3680c375519330482e436a9f1232ae 100644 (file)
@@ -12,9 +12,9 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
-       execReplication.o execScan.o execTuples.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execInterpExpr.o execGrouping.o \
+       execIndexing.o execJunk.o execMain.o execParallel.o execProcnode.o \
+       execQual.o execReplication.o execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
        nodeBitmapHeapscan.o nodeBitmapIndexscan.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644 (file)
index 0000000..4cf0883
--- /dev/null
@@ -0,0 +1,2295 @@
+/*-------------------------------------------------------------------------
+ *
+ * execExpr.c
+ *   Expression evaluation infrastructure.
+ *
+ * Expression evaluation now works by first converting expression trees
+ * (which went through planning first) into an ExprState using ExecInitExpr()
+ * et al. This converts the tree into a opcode based program (ExprEvalStep
+ * representing individual instructions); allocated as a flat array of
+ * steps.
+ *
+ * This flat representation has the big advantage that it can be implemented
+ * non-recursively, within a single function.  This allows to interpret
+ * larger expressions from largely within a single function, keeping state
+ * between them.  In contrast to that, tree-walk based approaches in contrast
+ * often run into performance issues due to function call / stack
+ * manipulation overhead.
+ *
+ * The ExprEvalStep representation is designed to be usable for interpreting
+ * the expression, as well as compiling into native code. Thus, if possible,
+ * as much complexity as possible should be handed by ExecInitExpr() (and
+ * helpers), instead of handled at execution time where both interpreted and
+ * compiled versions would need to deal with the complexity. Additionally
+ * checks for initialization at run time have a small but noticeable cost at
+ * every execution.
+ *
+ * The next step is preparing the ExprState for execution, using
+ * ExecInstantiateExpr(). This is internally done by ExecInitExpr() and other
+ * functions that prepare for expression evaluation.  ExecInstantiateExpr()
+ * initializes the expression for the relevant method chosen to evaluate the
+ * expression.
+ *
+ * Note that a lot of the more complex expression evaluation steps, which are
+ * less performance critical than some of the simpler and more common ones,
+ * are implemented as separate functions outside the fast-path of interpreted
+ * expression, like e.g. ExecEvalRow(), so that the implementation can be
+ * shared between interpreted and compiled expression evaluation.  That means
+ * that ExecInstantiateExpr() always has to initialize the expression for
+ * evaluation by execInterpExpr.c.  It also means that these helper functions
+ * are not "allowed" to perform dispatch themselves, as the method of
+ * dispatch will vary based on the caller.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *   src/backend/executor/execExpr.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execExpr.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/timestamp.h"
+#include "utils/typcache.h"
+
+
+/*
+ * Support for building execution state.
+ */
+static void ExecInstantiateExpr(ExprState *state);
+static void ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
+               Datum *resv, bool *resnull);
+static void ExprEvalPushStep(ExprState *es, ExprEvalStep *s);
+static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args,
+            Oid funcid, Oid inputcollid, PlanState *parent,
+            ExprState *state, Datum *resv, bool *resnull);
+static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable,
+                   PlanState *parent, ExprState *state,
+                   Datum *resv, bool *resnull);
+static void ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref,
+                PlanState *parent, ExprState *state,
+                Datum *resv, bool *resnull);
+static void ExecInitExprSlots(ExprState *state, Node *node);
+
+/* support functions */
+static bool isAssignmentIndirectionExpr(Expr *expr);
+
+
+/*
+ * ExecInitExpr: prepare an expression tree for execution
+ *
+ * This function builds and returns an ExprState implementing the given
+ * Expr node tree.  The return ExprState 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, WindowFunc, or SubPlan nodes found in the tree are added to the
+ * lists of such nodes held by the parent PlanState.
+ *
+ * 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 = makeNode(ExprState);
+   ExprEvalStep scratch;
+
+   if (node == NULL)
+       return NULL;
+
+   state->expr = node;
+   ExecInitExprSlots(state, (Node *) node);
+
+   ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
+
+   scratch.resvalue = &state->resvalue;
+   scratch.resnull = &state->resnull;
+   scratch.opcode = EEO_DONE;
+   ExprEvalPushStep(state, &scratch);
+
+   ExecInstantiateExpr(state);
+
+   return state;
+}
+
+/*
+ * ExecInitQual: prepare a qual for execution
+ *
+ * Prepares for the evaluation of a conjunctive boolean expression (qual
+ * list) that returns true iff none of the subexpressions are false.  (We
+ * also return true if the list is empty.)
+ *
+ * If some of the subexpressions yield NULL, then the result of the
+ * conjunction is false.  This makes this routine primarily useful for
+ * evaluating WHERE clauses, since SQL specifies that tuples with null WHERE
+ * results do not get selected.
+ */
+ExprState *
+ExecInitQual(List *qual, PlanState *parent)
+{
+   ExprState  *state = makeNode(ExprState);
+   ExprEvalStep scratch;
+   ListCell   *lc;
+   List       *adjust_bailout = NIL;
+
+   /* short-circuit (here and in ExecQual) for empty restriction list */
+   if (qual == NULL)
+       return NULL;
+
+   Assert(IsA(qual, List));
+
+   /*
+    * ExecQual() needs to return false for expression returning NULL. That
+    * allows to short-circuit the evaluation the first time a NULL is
+    * encountered.  As qual evaluation is a hot-path this warrants using a
+    * special opcode for qual evaluation that's simpler than BOOL_AND (which
+    * has more complex NULL handling).
+    */
+   state->expr = (Expr *) qual;
+   ExecInitExprSlots(state, (Node *) qual);
+
+   scratch.opcode = EEO_QUAL;
+   scratch.resvalue = &state->resvalue;
+   scratch.resnull = &state->resnull;
+
+   foreach(lc, qual)
+   {
+       Expr       *node = (Expr *) lfirst(lc);
+
+       /* first evaluate expression */
+       ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
+
+       /* then check whether it's false or NULL */
+       scratch.d.qualexpr.jumpdone = -1;
+       ExprEvalPushStep(state, &scratch);
+       adjust_bailout = lappend_int(adjust_bailout,
+                                    state->steps_len - 1);
+   }
+
+   /* adjust early bail out jump target */
+   foreach(lc, adjust_bailout)
+   {
+       ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+       Assert(as->d.qualexpr.jumpdone == -1);
+       as->d.qualexpr.jumpdone = state->steps_len;
+   }
+
+   scratch.opcode = EEO_DONE;
+   ExprEvalPushStep(state, &scratch);
+
+   ExecInstantiateExpr(state);
+
+   return state;
+}
+
+/*
+ * ExecInitCheck: prepare a check constraint for execution
+ *
+ * Prepares for the evaluation of a conjunctive boolean expression (qual
+ * list) that returns true iff none of the subexpressions are false.  (We
+ * also return true if the list is empty.)
+ *
+ * If some of the subexpressions yield NULL, then the result of the
+ * conjunction is true, since SQL specifies that NULL constraint conditions
+ * are not failures.
+ */
+ExprState *
+ExecInitCheck(List *qual, PlanState *parent)
+{
+   Expr       *expr;
+
+   if (qual == NULL)
+       return NULL;
+
+   Assert(IsA(qual, List));
+
+   if (list_length(qual) == 1)
+       expr = linitial(qual);
+   else
+   {
+       /*
+        * Just whip-up a boolean AND expression, that behaves just as needed.
+        * It'd be valid to implement short-circuiting behaviour on NULLs, but
+        * that doesn't seem warranted.
+        */
+       expr = makeBoolExpr(AND_EXPR, qual, -1);
+   }
+
+   return ExecInitExpr(expr, parent);
+}
+
+/* ----------------
+ *     ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ *
+ * This is implemented by internally building an ExprState that performs the
+ * projection. That way faster implementations of expression evaluation, e.g
+ * compiled to native code, can evaluate the whole projection in one go.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+                       ExprContext *econtext,
+                       TupleTableSlot *slot,
+                       PlanState *parent,
+                       TupleDesc inputDesc)
+{
+   ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+   ExprEvalStep scratch;
+   ListCell   *lc;
+   ExprState  *state;
+
+   projInfo->pi_exprContext = econtext;
+   projInfo->pi_state.tag.type = T_ExprState;
+   state = &projInfo->pi_state;
+   state->expr = (Expr *) targetList;
+   state->resultslot = slot;
+   ExecInitExprSlots(state, (Node *) targetList);
+
+   foreach(lc, targetList)
+   {
+       TargetEntry *tle;
+       Var        *variable = NULL;
+       AttrNumber  attnum;
+       bool        isSimpleVar = false;
+
+       Assert(IsA(lfirst(lc), TargetEntry));
+
+       tle = (TargetEntry *) lfirst(lc);
+
+       if (tle->expr != NULL &&
+           IsA(tle->expr, Var) &&
+           ((Var *) tle->expr)->varattno > 0)
+       {
+           variable = (Var *) tle->expr;
+           attnum = variable->varattno;
+
+           if (!inputDesc)
+               isSimpleVar = true;     /* can't check type, assume OK */
+           else if (variable->varattno <= inputDesc->natts)
+           {
+               Form_pg_attribute attr;
+
+               attr = inputDesc->attrs[variable->varattno - 1];
+               if (!attr->attisdropped && variable->vartype == attr->atttypid)
+                   isSimpleVar = true;
+           }
+       }
+
+       if (isSimpleVar)
+       {
+           switch (variable->varno)
+           {
+               case INNER_VAR: /* get the tuple from the inner node */
+                   scratch.opcode = EEO_ASSIGN_INNER_VAR;
+                   break;
+
+               case OUTER_VAR: /* get the tuple from the outer node */
+                   scratch.opcode = EEO_ASSIGN_OUTER_VAR;
+                   break;
+
+                   /* INDEX_VAR is handled by default case */
+               default:        /* get the tuple from the relation being
+                                * scanned */
+                   scratch.opcode = EEO_ASSIGN_SCAN_VAR;
+                   break;
+           }
+
+           scratch.d.assign_var.attnum = attnum - 1;
+           scratch.d.assign_var.resultnum = tle->resno - 1;
+           ExprEvalPushStep(state, &scratch);
+       }
+       else
+       {
+           /*
+            * We can't directly point into the result slot for the contained
+            * expression, as the result slot (and the exprstate for that
+            * matter) can change below us. So we instead evaluate into a
+            * temporary value and then move.
+            */
+           ExecInitExprRec(tle->expr, parent, state, &state->resvalue, &state->resnull);
+           if (get_typlen(exprType((Node *) tle->expr)) == -1)
+               scratch.opcode = EEO_ASSIGN_TMP_UNEXPAND;
+           else
+               scratch.opcode = EEO_ASSIGN_TMP;
+           scratch.d.assign_tmp.resultnum = tle->resno - 1;
+           ExprEvalPushStep(state, &scratch);
+       }
+   }
+
+   scratch.resvalue = &state->resvalue;
+   scratch.resnull = &state->resnull;
+   scratch.opcode = EEO_DONE;
+   ExprEvalPushStep(state, &scratch);
+
+   ExecInstantiateExpr(state);
+
+   return projInfo;
+}
+
+/*
+ * Call ExecInitExpr() on a list of expressions, return a list of ExprStates.
+ */
+List *
+ExecInitExprList(List *nodes, PlanState *parent)
+{
+   List       *result = NIL;
+   ListCell   *lc;
+
+   foreach(lc, nodes)
+   {
+       Expr       *e = lfirst(lc);
+
+       result = lappend(result, ExecInitExpr(e, parent));
+   }
+
+   return result;
+}
+
+/*
+ * 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 run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState *
+ExecPrepareExpr(Expr *node, EState *estate)
+{
+   ExprState  *result;
+   MemoryContext oldcontext;
+
+   oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+   node = expression_planner(node);
+
+   result = ExecInitExpr(node, NULL);
+
+   MemoryContextSwitchTo(oldcontext);
+
+   return result;
+}
+
+/*
+ * ExecPrepareQual --- initialize for qual 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 run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState *
+ExecPrepareQual(List *qual, EState *estate)
+{
+   ExprState  *result;
+   MemoryContext oldcontext;
+
+   oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+   qual = (List *) expression_planner((Expr *) qual);
+
+   result = ExecInitQual(qual, NULL);
+
+   MemoryContextSwitchTo(oldcontext);
+
+   return result;
+}
+
+/*
+ * ExecPrepareCheck -- initialize qual for execution outside a normal Plan
+ * tree context.
+ *
+ * See ExecPrepareExpr() and ExecInitQual() for details.
+ */
+ExprState *
+ExecPrepareCheck(List *qual, EState *estate)
+{
+   ExprState  *result;
+   MemoryContext oldcontext;
+
+   oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+   qual = (List *) expression_planner((Expr *) qual);
+
+   result = ExecInitCheck(qual, NULL);
+
+   MemoryContextSwitchTo(oldcontext);
+
+   return result;
+}
+
+/*
+ * Call ExecPrepareExpr() on each member of nodes, and return list of
+ * ExprStates.
+ *
+ * See ExecPrepareExpr() for details.
+ */
+List *
+ExecPrepareExprList(List *nodes, EState *estate)
+{
+   List       *result = NIL;
+   ListCell   *lc;
+
+   foreach(lc, nodes)
+   {
+       Expr       *e = lfirst(lc);
+
+       result = lappend(result, ExecPrepareExpr(e, estate));
+   }
+
+   return result;
+}
+
+/*
+ * ExecCheck - evaluate a check constraint prepared with ExecInitCheck
+ * (possibly via ExecPrepareCheck).
+ */
+bool
+ExecCheck(ExprState *state, ExprContext *econtext)
+{
+   bool        isnull;
+   Datum       ret;
+
+   ret = ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+   if (isnull)
+       return true;
+   return DatumGetBool(ret);
+}
+
+/*
+ * Prepare an expression for execution.  This has to be called for every
+ * ExprState before it can be executed.
+ *
+ * NB: While this currently only calls ExecInstantiateInterpretedExpr(), this
+ * will likely get extended to further expression evaluation methods.
+ * Therefore this should be used instead of ExecInstantiateInterpretedExpr().
+ */
+static void
+ExecInstantiateExpr(ExprState *state)
+{
+   ExecInstantiateInterpretedExpr(state);
+}
+
+/*
+ * Append evaluation of node to ExprState, possibly recursing into
+ * sub-expressions of node.
+ */
+static void
+ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state, Datum *resv, bool *resnull)
+{
+   ExprEvalStep scratch;
+
+   /*
+    * Guard against stack overflow due to overly complex expressions. Because
+    * expression evaluation is not recursive, but expression planning is,
+    * we'll hit stack limits due to recursion either here, or when recursing
+    * into a separate expression evaluation inside a function call.  This
+    * lets us avoid repeatedly doing the quite expensive stack depth checks
+    * during expression evaluation.
+    */
+   check_stack_depth();
+
+   Assert(resv != NULL && resnull != NULL);
+   scratch.resvalue = resv;
+   scratch.resnull = resnull;
+
+   /* cases should be ordered as they are in enum NodeTag */
+   switch (nodeTag(node))
+   {
+       case T_Var:
+           {
+               Var        *variable = (Var *) node;
+
+               /* varattno == InvalidAttrNumber means it's a whole-row Var */
+               if (variable->varattno == InvalidAttrNumber)
+               {
+                   ExecInitWholeRowVar(&scratch, variable, parent, state, resv, resnull);
+
+                   scratch.opcode = EEO_WHOLEROW;
+               }
+               else if (variable->varattno <= 0)
+               {
+                   scratch.d.var.attnum = variable->varattno;
+                   switch (variable->varno)
+                   {
+                       case INNER_VAR:
+                           scratch.opcode = EEO_INNER_SYSVAR;
+                           break;
+                       case OUTER_VAR:
+                           scratch.opcode = EEO_OUTER_SYSVAR;
+                           break;
+                       default:
+                           scratch.opcode = EEO_SCAN_SYSVAR;
+                           break;
+                   }
+               }
+               else
+               {
+                   switch (variable->varno)
+                   {
+                       case INNER_VAR:
+                           scratch.opcode = EEO_INNER_VAR;
+                           break;
+                       case OUTER_VAR:
+                           scratch.opcode = EEO_OUTER_VAR;
+                           break;
+                       default:
+                           scratch.opcode = EEO_SCAN_VAR;
+                           break;
+                   }
+                   scratch.d.var.attnum = variable->varattno - 1;
+               }
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_Const:
+           {
+               Const      *con = (Const *) node;
+
+               scratch.opcode = EEO_CONST;
+               scratch.d.constval.value = con->constvalue;
+               scratch.d.constval.isnull = con->constisnull;
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_Param:
+           {
+               Param      *param = (Param *) node;
+
+               switch (param->paramkind)
+               {
+                   case PARAM_EXEC:
+                       {
+                           scratch.opcode = EEO_PARAM_EXEC;
+                           scratch.d.param.paramid = param->paramid;
+                           scratch.d.param.paramtype = InvalidOid;
+                           break;
+                       }
+                   case PARAM_EXTERN:
+                       {
+                           scratch.opcode = EEO_PARAM_EXTERN;
+                           scratch.d.param.paramid = param->paramid;
+                           scratch.d.param.paramtype = param->paramtype;
+                           break;
+                       }
+                   default:
+                       elog(ERROR, "unrecognized paramkind: %d",
+                            (int) ((Param *) node)->paramkind);
+                       break;
+               }
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_Aggref:
+           {
+               Aggref     *aggref = (Aggref *) node;
+               AggrefExprState *astate = makeNode(AggrefExprState);
+
+               scratch.opcode = EEO_AGGREF;
+               scratch.d.aggref.astate = astate;
+               astate->aggref = aggref;
+               if (parent && IsA(parent, AggState))
+               {
+                   AggState   *aggstate = (AggState *) parent;
+
+                   aggstate->aggs = lcons(astate, aggstate->aggs);
+                   aggstate->numaggs++;
+               }
+               else
+               {
+                   /* planner messed up */
+                   elog(ERROR, "Aggref found in non-Agg plan node");
+               }
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_GroupingFunc:
+           {
+               GroupingFunc *grp_node = (GroupingFunc *) node;
+               Agg        *agg = NULL;
+
+               if (!parent || !IsA(parent, AggState) ||!IsA(parent->plan, Agg))
+                   elog(ERROR, "parent of GROUPING is not Agg node");
+
+               scratch.opcode = EEO_GROUPING_FUNC;
+               scratch.d.grouping_func.parent = (AggState *) parent;
+
+               agg = (Agg *) (parent->plan);
+
+               if (agg->groupingSets)
+                   scratch.d.grouping_func.clauses = grp_node->cols;
+               else
+                   scratch.d.grouping_func.clauses = NIL;
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_WindowFunc:
+           {
+               WindowFunc *wfunc = (WindowFunc *) node;
+               WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
+
+               wfstate->wfunc = wfunc;
+
+               if (parent && IsA(parent, WindowAggState))
+               {
+                   WindowAggState *winstate = (WindowAggState *) parent;
+                   int         nfuncs;
+
+                   winstate->funcs = lcons(wfstate, winstate->funcs);
+                   nfuncs = ++winstate->numfuncs;
+                   if (wfunc->winagg)
+                       winstate->numaggs++;
+
+                   /* for now intialize agg using old style expressions */
+                   wfstate->args = NIL;
+                   wfstate->args = ExecInitExprList(wfunc->args, parent);
+                   wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter,
+                                                     parent);
+
+                   /*
+                    * Complain if the windowfunc's arguments contain any
+                    * windowfuncs; nested window functions are semantically
+                    * nonsensical.  (This should have been caught earlier,
+                    * but we defend against it here anyway.)
+                    */
+                   if (nfuncs != winstate->numfuncs)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_WINDOWING_ERROR),
+                         errmsg("window function calls cannot be nested")));
+               }
+               else
+               {
+                   /* planner messed up */
+                   elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
+               }
+
+               scratch.opcode = EEO_WINDOW_FUNC;
+               scratch.d.window_func.wfstate = wfstate;
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_ArrayRef:
+           {
+               ArrayRef   *aref = (ArrayRef *) node;
+
+               ExecInitArrayRef(&scratch, aref, parent, state, resv, resnull);
+               break;
+           }
+
+       case T_FuncExpr:
+           {
+               FuncExpr   *func = (FuncExpr *) node;
+
+               ExecInitFunc(&scratch, node, func->args, func->funcid, func->inputcollid,
+                            parent, state, resv, resnull);
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_OpExpr:
+           {
+               OpExpr     *op = (OpExpr *) node;
+
+               ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+                            parent, state, resv, resnull);
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_DistinctExpr:
+           {
+               DistinctExpr *op = (DistinctExpr *) node;
+
+               ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+                            parent, state, resv, resnull);
+
+               /*
+                * Can't use normal function call, override opcode for
+                * DISTINCT
+                */
+
+               /*
+                * XXX: historically we've not called the function usage
+                * pgstat infrastructure - that seems inconsistent given that
+                * we do so for normal function *and* operator evaluation
+                */
+               scratch.opcode = EEO_DISTINCT;
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_NullIfExpr:
+           {
+               NullIfExpr *op = (NullIfExpr *) node;
+
+               ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+                            parent, state, resv, resnull);
+
+               /* Can't use normal function call, override opcode for NULL() */
+
+               /*
+                * XXX: historically we've not called the function usage
+                * pgstat infrastructure - that seems inconsistent given that
+                * we do so for normal function *and* operator evaluation
+                */
+               scratch.opcode = EEO_NULLIF;
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_ScalarArrayOpExpr:
+           {
+               ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
+               FmgrInfo   *finfo;
+               FunctionCallInfo fcinfo;
+
+               finfo = palloc0(sizeof(FmgrInfo));
+               fcinfo = palloc0(sizeof(FunctionCallInfoData));
+               fmgr_info(opexpr->opfuncid, finfo);
+               fmgr_info_set_expr((Node *) node, finfo);
+               InitFunctionCallInfoData(*fcinfo, finfo, 2,
+                                        opexpr->inputcollid, NULL, NULL);
+
+               scratch.opcode = EEO_SCALARARRAYOP;
+               scratch.d.scalararrayop.opexpr = opexpr;
+               scratch.d.scalararrayop.finfo = finfo;
+               scratch.d.scalararrayop.fcinfo_data = fcinfo;
+               scratch.d.scalararrayop.fn_addr = fcinfo->flinfo->fn_addr;
+
+               Assert(fcinfo->nargs == 2);
+               Assert(list_length(opexpr->args) == 2);
+
+               /* evaluate scalar directly into function argument */
+               ExecInitExprRec((Expr *) linitial(opexpr->args), parent, state,
+                               &fcinfo->arg[0], &fcinfo->argnull[0]);
+
+               /*
+                * Evaluate array argument into our return value, overwrite
+                * with comparison results afterwards.
+                */
+               ExecInitExprRec((Expr *) lsecond(opexpr->args), parent, state,
+                               resv, resnull);
+
+               scratch.d.scalararrayop.element_type = InvalidOid;
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_BoolExpr:
+           {
+               BoolExpr   *boolexpr = (BoolExpr *) node;
+               ListCell   *lc;
+               List       *adjust_bailout = NIL;
+               int         nargs = list_length(boolexpr->args);
+               int         off = 0;
+
+               /* allocate scratch memory used by all steps */
+               scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+               scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+               scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+               /*
+                * For each argument evaluate the argument itself, then
+                * perform the bool operation's appropriate handling.
+                */
+               foreach(lc, boolexpr->args)
+               {
+                   Expr       *arg = (Expr *) lfirst(lc);
+
+                   switch (boolexpr->boolop)
+                   {
+                       case AND_EXPR:
+                           Assert(list_length(boolexpr->args) >= 2);
+
+                           if (off == 0)
+                               scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+                           else if (off + 1 == nargs)
+                               scratch.opcode = EEO_BOOL_AND_STEP_LAST;
+                           else
+                               scratch.opcode = EEO_BOOL_AND_STEP;
+                           break;
+                       case OR_EXPR:
+                           Assert(list_length(boolexpr->args) >= 2);
+
+                           if (off == 0)
+                               scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+                           else if (off + 1 == nargs)
+                               scratch.opcode = EEO_BOOL_OR_STEP_LAST;
+                           else
+                               scratch.opcode = EEO_BOOL_OR_STEP;
+                           break;
+                       case NOT_EXPR:
+                           Assert(list_length(boolexpr->args) == 1);
+
+                           scratch.opcode = EEO_BOOL_NOT_STEP;
+                           break;
+                       default:
+                           elog(ERROR, "unrecognized boolop: %d",
+                                (int) boolexpr->boolop);
+                           break;
+                   }
+
+                   ExecInitExprRec(arg, parent, state,
+                                   scratch.d.boolexpr.value,
+                                   scratch.d.boolexpr.isnull);
+                   scratch.d.boolexpr.jumpdone = -1;
+                   ExprEvalPushStep(state, &scratch);
+                   adjust_bailout = lappend_int(adjust_bailout,
+                                                state->steps_len - 1);
+                   off++;
+               }
+
+               /* adjust early bail out jump target */
+               foreach(lc, adjust_bailout)
+               {
+                   ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+                   Assert(as->d.boolexpr.jumpdone == -1);
+                   as->d.boolexpr.jumpdone = state->steps_len;
+               }
+
+               break;
+           }
+
+       case T_SubPlan:
+           {
+               SubPlan    *subplan = (SubPlan *) node;
+               SubPlanState *sstate;
+
+               if (!parent)
+                   elog(ERROR, "SubPlan found with no parent plan");
+
+               sstate = ExecInitSubPlan(subplan, parent);
+
+               /* Add SubPlanState nodes to parent->subPlan */
+               parent->subPlan = lappend(parent->subPlan, sstate);
+
+               scratch.opcode = EEO_SUBPLAN;
+               scratch.d.subplan.sstate = sstate;
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_AlternativeSubPlan:
+           {
+               AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
+               AlternativeSubPlanState *asstate;
+
+               if (!parent)
+                   elog(ERROR, "AlternativeSubPlan found with no parent plan");
+
+               asstate = ExecInitAlternativeSubPlan(asplan, parent);
+
+               scratch.opcode = EEO_ALTERNATIVE_SUBPLAN;
+               scratch.d.alternative_subplan.asstate = asstate;
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_FieldSelect:
+           {
+               FieldSelect *fselect = (FieldSelect *) node;
+
+
+               /* evaluate argument */
+               ExecInitExprRec(fselect->arg, parent, state, resv, resnull);
+
+               scratch.opcode = EEO_FIELDSELECT;
+               scratch.d.fieldselect.fieldnum = fselect->fieldnum;
+               scratch.d.fieldselect.resulttype = fselect->resulttype;
+               scratch.d.fieldselect.argdesc = NULL;
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_FieldStore:
+           {
+               FieldStore *fstore = (FieldStore *) node;
+               ListCell   *l1,
+                          *l2;
+               Datum      *values;
+               bool       *nulls;
+               TupleDesc  *descp;
+
+               /* FIXME: properly size workspace */
+               values = (Datum *) palloc(sizeof(Datum) * MaxTupleAttributeNumber);
+               nulls = (bool *) palloc(sizeof(bool) * MaxTupleAttributeNumber);
+               descp = (TupleDesc *) palloc(sizeof(TupleDesc));
+               *descp = NULL;
+
+               /* prepare argument evaluation */
+               ExecInitExprRec(fstore->arg, parent, state, resv, resnull);
+
+               /* first deform the input tuple */
+               scratch.opcode = EEO_FIELDSTORE_DEFORM;
+               scratch.d.fieldstore.argdesc = descp;
+               scratch.d.fieldstore.fstore = fstore;
+               scratch.d.fieldstore.values = values;
+               scratch.d.fieldstore.nulls = nulls;
+               ExprEvalPushStep(state, &scratch);
+
+               /* evaluate new values, one step for each arg */
+               forboth(l1, fstore->newvals, l2, fstore->fieldnums)
+               {
+                   Expr       *e = (Expr *) lfirst(l1);
+                   AttrNumber  fieldnum = lfirst_int(l2);
+                   Datum      *save_innermost_caseval = NULL;
+                   bool       *save_innermost_casenull = NULL;
+
+                   /*
+                    * Use the CaseTestExpr mechanism to pass down the old
+                    * value of the field being replaced; this is needed in
+                    * case the newval is itself a FieldStore or ArrayRef that
+                    * has to obtain and modify the old value.  It's safe to
+                    * reuse the CASE mechanism because there cannot be a CASE
+                    * between here and where the value would be needed, and a
+                    * field assignment can't be within a CASE either.  (So
+                    * saving and restoring the caseValue is just paranoia,
+                    * but let's do it anyway.)
+                    */
+                   save_innermost_caseval = state->innermost_caseval;
+                   save_innermost_casenull = state->innermost_casenull;
+                   state->innermost_caseval = &values[fieldnum - 1];
+                   state->innermost_casenull = &nulls[fieldnum - 1];
+
+                   ExecInitExprRec(e, parent, state,
+                                   &values[fieldnum - 1],
+                                   &nulls[fieldnum - 1]);
+
+                   state->innermost_caseval = save_innermost_caseval;
+                   state->innermost_casenull = save_innermost_casenull;
+               }
+
+               /* then form result tuple */
+               scratch.opcode = EEO_FIELDSTORE_FORM;
+               scratch.d.fieldstore.fstore = fstore;
+               scratch.d.fieldstore.argdesc = descp;
+               scratch.d.fieldstore.values = values;
+               scratch.d.fieldstore.nulls = nulls;
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_RelabelType:
+           {
+               /* at runtime relabel doesn't need to do anything */
+               RelabelType *relabel = (RelabelType *) node;
+
+               ExecInitExprRec(relabel->arg, parent, state, resv, resnull);
+               break;
+           }
+
+       case T_CoerceViaIO:
+           {
+               CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+               Oid         iofunc;
+               bool        typisvarlena;
+
+               /* evaluate argument */
+               ExecInitExprRec(iocoerce->arg, parent, state, resv, resnull);
+
+               /*
+                * Compute coercion by preparing both output / input calls, to
+                * be evaluated inside a single evaluation step for speed -
+                * this can be a very common operation.
+                */
+               scratch.opcode = EEO_IOCOERCE;
+
+               /* lookup the input type's output function */
+               scratch.d.iocoerce.finfo_out = palloc0(sizeof(*scratch.d.iocoerce.finfo_out));
+               scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(*scratch.d.iocoerce.fcinfo_data_out));
+
+               getTypeOutputInfo(exprType((Node *) iocoerce->arg),
+                                 &iofunc, &typisvarlena);
+               fmgr_info(iofunc, scratch.d.iocoerce.finfo_out);
+               fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_out);
+               InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_out,
+                                        scratch.d.iocoerce.finfo_out,
+                                        1, InvalidOid, NULL, NULL);
+
+               /* lookup the result type's input function */
+               scratch.d.iocoerce.finfo_in = palloc0(sizeof(*scratch.d.iocoerce.finfo_in));
+               scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(*scratch.d.iocoerce.fcinfo_data_in));
+
+               getTypeInputInfo(iocoerce->resulttype, &iofunc,
+                                &scratch.d.iocoerce.intypioparam);
+               fmgr_info(iofunc, scratch.d.iocoerce.finfo_in);
+               fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in);
+               InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in,
+                                        scratch.d.iocoerce.finfo_in,
+                                        3, InvalidOid, NULL, NULL);
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_ArrayCoerceExpr:
+           {
+               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+
+               /* evaluate argument */
+               ExecInitExprRec(acoerce->arg, parent, state, resv, resnull);
+
+               scratch.opcode = EEO_ARRAYCOERCE;
+               scratch.d.arraycoerce.coerceexpr = acoerce;
+               scratch.d.arraycoerce.resultelemtype =
+                   get_element_type(acoerce->resulttype);
+               if (scratch.d.arraycoerce.resultelemtype == InvalidOid)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("target type is not an array")));
+               /* Arrays over domains aren't supported yet */
+               Assert(getBaseType(scratch.d.arraycoerce.resultelemtype) ==
+                      scratch.d.arraycoerce.resultelemtype);
+               scratch.d.arraycoerce.elemfunc =
+                   (FmgrInfo *) palloc(sizeof(FmgrInfo));
+               scratch.d.arraycoerce.elemfunc->fn_oid =
+                   InvalidOid; /* not initialized */
+               scratch.d.arraycoerce.amstate =
+                   (ArrayMapState *) palloc0(sizeof(ArrayMapState));
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_ConvertRowtypeExpr:
+           {
+               ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
+
+               /* evaluate argument */
+               ExecInitExprRec(convert->arg, parent, state, resv, resnull);
+
+               /* and push conversion step */
+               scratch.opcode = EEO_CONVERT_ROWTYPE;
+               scratch.d.convert_rowtype.convert = convert;
+               scratch.d.convert_rowtype.indesc = NULL;
+               scratch.d.convert_rowtype.outdesc = NULL;
+               scratch.d.convert_rowtype.map = NULL;
+               scratch.d.convert_rowtype.initialized = false;
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+           /* note that CaseWhen expressions are handled within this block */
+       case T_CaseExpr:
+           {
+               CaseExpr   *caseExpr = (CaseExpr *) node;
+               ListCell   *clause;
+               List       *adjust_bailout = NIL;
+               ListCell   *lc;
+               Datum      *casevalue = palloc0(sizeof(Datum));
+               bool       *caseisnull = palloc0(sizeof(bool));
+               Datum      *save_innermost_caseval = NULL;
+               bool       *save_innermost_casenull = NULL;
+               Datum      *caseval = NULL;
+               bool       *casenull = NULL;
+
+               /* arg == NULL -> CASE WHEN foo */
+               /* arg != NULL -> CASE foo WHEN blarg */
+               if (caseExpr->arg != NULL)
+               {
+                   caseval = palloc0(sizeof(Datum));
+                   casenull = palloc0(sizeof(bool));
+
+                   ExecInitExprRec(caseExpr->arg, parent, state,
+                                   caseval, casenull);
+               }
+
+               /*
+                * If there's a test expression, we have to evaluate it and
+                * save the value where the CaseTestExpr placeholders can find
+                * it.  We must save and restore prior setting of caseValue
+                * fields, in case this node is itself within a larger CASE.
+                *
+                * If there's no test expression, we don't actually need to
+                * save and restore these fields; but it's less code to just
+                * do so unconditionally.
+                */
+
+
+               /*
+                * Prepare to evaluate each of the WHEN clauses in turn, as
+                * soon as one is true we return the corresponding result; and
+                * If none are true then we return the value of the default
+                * clause, or NULL if there is none.
+                */
+               foreach(clause, caseExpr->args)
+               {
+                   CaseWhen   *when = (CaseWhen *) lfirst(clause);
+                   int         whenstep;
+
+                   /* evaluate condition */
+                   save_innermost_caseval = state->innermost_caseval;
+                   save_innermost_casenull = state->innermost_casenull;
+                   state->innermost_caseval = caseval;
+                   state->innermost_casenull = casenull;
+
+                   ExecInitExprRec(when->expr, parent, state,
+                                   casevalue, caseisnull);
+
+                   state->innermost_caseval = save_innermost_caseval;
+                   state->innermost_casenull = save_innermost_casenull;
+
+                   scratch.opcode = EEO_CASE_WHEN_STEP;
+                   scratch.d.casewhen.value = casevalue;
+                   scratch.d.casewhen.isnull = caseisnull;
+                   scratch.d.casewhen.jumpfalse = -1;  /* computed later */
+                   ExprEvalPushStep(state, &scratch);
+                   whenstep = state->steps_len - 1;
+
+                   /* evaluate result */
+                   ExecInitExprRec(when->result, parent, state, resv, resnull);
+
+                   scratch.opcode = EEO_CASE_THEN_STEP;
+                   scratch.d.casewhen.value = casevalue;
+                   scratch.d.casewhen.isnull = caseisnull;
+                   scratch.d.casethen.jumpdone = -1;   /* computed later */
+                   ExprEvalPushStep(state, &scratch);
+
+                   /*
+                    * Don't know "address" of jump target yet, compute once
+                    * the whole case expression is built.
+                    */
+                   adjust_bailout = lappend_int(adjust_bailout,
+                                                state->steps_len - 1);
+
+                   /* adjust jump target for WHEN step, for the !match case */
+                   state->steps[whenstep].d.casewhen.jumpfalse = state->steps_len;
+               }
+
+               if (caseExpr->defresult)
+               {
+                   /* evaluate result, directly into result datum */
+                   ExecInitExprRec(caseExpr->defresult, parent, state,
+                                   resv, resnull);
+               }
+               else
+               {
+                   /* statically return NULL */
+                   scratch.opcode = EEO_CONST;
+                   scratch.d.constval.isnull = true;
+                   scratch.d.constval.value = 0;
+                   ExprEvalPushStep(state, &scratch);
+               }
+
+               /* adjust early bail out jump target */
+               foreach(lc, adjust_bailout)
+               {
+                   ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+                   Assert(as->d.casethen.jumpdone == -1);
+                   as->d.casethen.jumpdone = state->steps_len;
+               }
+
+               break;
+           }
+
+       case T_CaseTestExpr:
+           {
+               CaseTestExpr *casetestexpr = (CaseTestExpr *) node;
+
+               scratch.d.casetest.value = state->innermost_caseval;
+               scratch.d.casetest.isnull = state->innermost_casenull;
+
+               /* only check for extended datums if possible */
+               if (get_typlen(casetestexpr->typeId) == -1)
+                   scratch.opcode = EEO_CASE_TESTVAL_UNEXPAND;
+               else
+                   scratch.opcode = EEO_CASE_TESTVAL;
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_ArrayExpr:
+           {
+               ArrayExpr  *arrayexpr = (ArrayExpr *) node;
+               int         nelems = list_length(arrayexpr->elements);
+               ListCell   *lc;
+               int         elemoff;
+
+               /*
+                * Evaluate by computing each element, and then forming the
+                * array.
+                */
+               scratch.opcode = EEO_ARRAYEXPR;
+               scratch.d.arrayexpr.arrayexpr = arrayexpr;
+               scratch.d.arrayexpr.nelems = nelems;
+               scratch.d.arrayexpr.elemvalues =
+                   (Datum *) palloc(sizeof(Datum) * nelems);
+               scratch.d.arrayexpr.elemnulls =
+                   (bool *) palloc(sizeof(bool) * nelems);
+
+               /* do one-time catalog lookup for type info */
+               get_typlenbyvalalign(arrayexpr->element_typeid,
+                                    &scratch.d.arrayexpr.elemlength,
+                                    &scratch.d.arrayexpr.elembyval,
+                                    &scratch.d.arrayexpr.elemalign);
+
+               /* prepare to evaluate all arguments */
+               elemoff = 0;
+               foreach(lc, arrayexpr->elements)
+               {
+                   Expr       *e = (Expr *) lfirst(lc);
+
+                   ExecInitExprRec(e, parent, state,
+                                   &scratch.d.arrayexpr.elemvalues[elemoff],
+                                   &scratch.d.arrayexpr.elemnulls[elemoff]);
+                   elemoff++;
+               }
+
+               /* and then to collect collect all into an array */
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_RowExpr:
+           {
+               RowExpr    *rowexpr = (RowExpr *) node;
+               int         nelems = list_length(rowexpr->args);
+               TupleDesc   tupdesc;
+               Form_pg_attribute *attrs;
+               int         i;
+               ListCell   *l;
+
+               /*
+                * Evaluate by first building datums for each field, and then
+                * a final step forming the composite datum.
+                */
+               scratch.opcode = EEO_ROW;
+
+               /* space for the individual field datums */
+               scratch.d.row.elemvalues =
+                   (Datum *) palloc(sizeof(Datum) * nelems);
+               scratch.d.row.elemnulls =
+                   (bool *) palloc(sizeof(bool) * nelems);
+
+               /* Build tupdesc to describe result tuples */
+               if (rowexpr->row_typeid == RECORDOID)
+               {
+                   /* generic record, use types of given expressions */
+                   tupdesc = ExecTypeFromExprList(rowexpr->args);
+               }
+               else
+               {
+                   /* it's been cast to a named type, use that */
+                   tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
+               }
+
+               scratch.d.row.tupdesc = tupdesc;
+
+               /* In either case, adopt RowExpr's column aliases */
+               ExecTypeSetColNames(tupdesc, rowexpr->colnames);
+               /* Bless the tupdesc in case it's now of type RECORD */
+               BlessTupleDesc(tupdesc);
+
+               /* Set up evaluation, skipping any deleted columns */
+               attrs = tupdesc->attrs;
+               i = 0;
+               foreach(l, rowexpr->args)
+               {
+                   Expr       *e = (Expr *) lfirst(l);
+
+                   if (!attrs[i]->attisdropped)
+                   {
+                       /*
+                        * Guard against ALTER COLUMN TYPE on rowtype since
+                        * the RowExpr was created.  XXX should we check
+                        * typmod too?  Not sure we can be sure it'll be the
+                        * same.
+                        */
+                       if (exprType((Node *) e) != attrs[i]->atttypid)
+                           ereport(ERROR,
+                                   (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                    errmsg("ROW() column has type %s instead of type %s",
+                                       format_type_be(exprType((Node *) e)),
+                                      format_type_be(attrs[i]->atttypid))));
+                   }
+                   else
+                   {
+                       /*
+                        * Ignore original expression and insert a NULL. We
+                        * don't really care what type of NULL it is, so
+                        * always make an int4 NULL.
+                        */
+                       e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
+                   }
+
+                   ExecInitExprRec(e, parent, state,
+                                   &scratch.d.row.elemvalues[i],
+                                   &scratch.d.row.elemnulls[i]);
+                   i++;
+               }
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_RowCompareExpr:
+           {
+               RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+               int         nopers = list_length(rcexpr->opnos);
+               List       *adjust_bailout = NIL;
+               ListCell   *l_left_expr,
+                          *l_right_expr,
+                          *l_opno,
+                          *l_opfamily,
+                          *l_inputcollid;
+               ListCell   *lc;
+               int         off;
+
+               /*
+                * Iterate over each field, prepare comparisons. To handle
+                * NULL results, prepare jumps to after the expression. If
+                * expression yields a != 0 result, jump to the final step.
+                */
+               Assert(list_length(rcexpr->largs) == nopers);
+               Assert(list_length(rcexpr->rargs) == nopers);
+
+               off = 0;
+               for (off = 0,
+                    l_left_expr = list_head(rcexpr->largs),
+                    l_right_expr = list_head(rcexpr->rargs),
+                    l_opno = list_head(rcexpr->opnos),
+                    l_opfamily = list_head(rcexpr->opfamilies),
+                    l_inputcollid = list_head(rcexpr->inputcollids);
+                    off < nopers;
+                    off++,
+                    l_left_expr = lnext(l_left_expr),
+                    l_right_expr = lnext(l_right_expr),
+                    l_opno = lnext(l_opno),
+                    l_opfamily = lnext(l_opfamily),
+                    l_inputcollid = lnext(l_inputcollid))
+               {
+                   Expr       *left_expr = (Expr *) lfirst(l_left_expr);
+                   Expr       *right_expr = (Expr *) lfirst(l_right_expr);
+                   Oid         opno = lfirst_oid(l_opno);
+                   Oid         opfamily = lfirst_oid(l_opfamily);
+                   Oid         inputcollid = lfirst_oid(l_inputcollid);
+                   int         strategy;
+                   Oid         lefttype;
+                   Oid         righttype;
+                   Oid         proc;
+                   FmgrInfo   *finfo;
+                   FunctionCallInfo fcinfo;
+
+                   get_op_opfamily_properties(opno, opfamily, false,
+                                              &strategy,
+                                              &lefttype,
+                                              &righttype);
+                   proc = get_opfamily_proc(opfamily,
+                                            lefttype,
+                                            righttype,
+                                            BTORDER_PROC);
+
+                   /* Set up the primary fmgr lookup information */
+                   finfo = palloc0(sizeof(FmgrInfo));
+                   fcinfo = palloc0(sizeof(FunctionCallInfoData));
+                   fmgr_info(proc, finfo);
+                   fmgr_info_set_expr((Node *) node, finfo);
+                   InitFunctionCallInfoData(*fcinfo, finfo, 2, inputcollid, NULL, NULL);
+
+                   /*
+                    * If we enforced permissions checks on index support
+                    * functions, we'd need to make a check here.  But the
+                    * index support machinery doesn't do that, and thus
+                    * neither does this code.
+                    */
+
+                   /* evaluate left and right expression directly into fcinfo */
+                   ExecInitExprRec(left_expr, parent, state,
+                                   &fcinfo->arg[0], &fcinfo->argnull[0]);
+                   ExecInitExprRec(right_expr, parent, state,
+                                   &fcinfo->arg[1], &fcinfo->argnull[1]);
+
+                   scratch.opcode = EEO_ROWCOMPARE_STEP;
+
+                   /* jump targets computed later */
+                   scratch.d.rowcompare_step.jumpnull = -1;
+                   scratch.d.rowcompare_step.jumpdone = -1;
+
+                   scratch.d.rowcompare_step.finfo = finfo;
+                   scratch.d.rowcompare_step.fcinfo_data = fcinfo;
+                   scratch.d.rowcompare_step.fn_addr = fcinfo->flinfo->fn_addr;
+
+                   ExprEvalPushStep(state, &scratch);
+                   adjust_bailout = lappend_int(adjust_bailout,
+                                                state->steps_len - 1);
+               }
+
+               /* and then compare the last result */
+               scratch.opcode = EEO_ROWCOMPARE_FINAL;
+               scratch.d.rowcompare_final.rctype = rcexpr->rctype;
+               ExprEvalPushStep(state, &scratch);
+
+               /* adjust early bail out jump targets */
+               foreach(lc, adjust_bailout)
+               {
+                   ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+                   Assert(as->d.rowcompare_step.jumpdone == -1);
+                   Assert(as->d.rowcompare_step.jumpnull == -1);
+
+                   /* jump to comparison evaluation */
+                   as->d.rowcompare_step.jumpdone = state->steps_len - 1;
+                   /* jump to the following expression */
+                   as->d.rowcompare_step.jumpnull = state->steps_len;
+               }
+
+               break;
+           }
+
+       case T_CoalesceExpr:
+           {
+               CoalesceExpr *coalesce = (CoalesceExpr *) node;
+               List       *adjust_bailout = NIL;
+               ListCell   *lc;
+
+               Assert(list_length(coalesce->args) > 0);
+
+               /*
+                * Prepare evaluation of all coalesced arguments, after each
+                * push a step that short-circuits if not null.
+                */
+               foreach(lc, coalesce->args)
+               {
+                   Expr       *e = (Expr *) lfirst(lc);
+
+                   /* evaluate result, directly into result datum */
+                   ExecInitExprRec(e, parent, state, resv, resnull);
+
+                   /* then push step checking for NULLs */
+                   scratch.opcode = EEO_COALESCE;
+                   scratch.d.coalesce.jumpdone = -1;   /* adjust later */
+                   ExprEvalPushStep(state, &scratch);
+
+                   adjust_bailout = lappend_int(adjust_bailout,
+                                                state->steps_len - 1);
+               }
+
+               /*
+                * No need to add a constant NULL return - we only can get to
+                * the end of the expression if a NULL already is being
+                * returned.
+                */
+
+               /* adjust early bail out jump target */
+               foreach(lc, adjust_bailout)
+               {
+                   ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+                   Assert(as->d.coalesce.jumpdone == -1);
+                   as->d.coalesce.jumpdone = state->steps_len;
+               }
+
+               break;
+           }
+
+       case T_MinMaxExpr:
+           {
+               MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
+               int         nelems = list_length(minmaxexpr->args);
+               TypeCacheEntry *typentry;
+               FmgrInfo   *finfo;
+               FunctionCallInfo fcinfo;
+               ListCell   *lc;
+               int         off;
+
+               /* Look up the btree comparison function for the datatype */
+               typentry = lookup_type_cache(minmaxexpr->minmaxtype,
+                                            TYPECACHE_CMP_PROC);
+               if (!OidIsValid(typentry->cmp_proc))
+                   ereport(ERROR,
+                           (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                            errmsg("could not identify a comparison function for type %s",
+                                   format_type_be(minmaxexpr->minmaxtype))));
+
+               /*
+                * If we enforced permissions checks on index support
+                * functions, we'd need to make a check here.  But the index
+                * support machinery doesn't do that, and thus neither does
+                * this code.
+                */
+               finfo = palloc0(sizeof(FmgrInfo));
+               fcinfo = palloc0(sizeof(FunctionCallInfoData));
+               fmgr_info(typentry->cmp_proc, finfo);
+               fmgr_info_set_expr((Node *) node, finfo);
+               InitFunctionCallInfoData(*fcinfo, finfo, 2,
+                                        minmaxexpr->inputcollid, NULL, NULL);
+
+               scratch.opcode = EEO_MINMAX;
+               /* allocate space to store arguments */
+               scratch.d.minmax.values =
+                   (Datum *) palloc(sizeof(Datum) * nelems);
+               scratch.d.minmax.nulls =
+                   (bool *) palloc(sizeof(bool) * nelems);
+               scratch.d.minmax.nelems = nelems;
+               scratch.d.minmax.op = minmaxexpr->op;
+
+               scratch.d.minmax.finfo = finfo;
+               scratch.d.minmax.fcinfo_data = fcinfo;
+
+               /* evaluate expressions into minmax->values/nulls */
+               off = 0;
+               foreach(lc, minmaxexpr->args)
+               {
+                   Expr       *e = (Expr *) lfirst(lc);
+
+                   ExecInitExprRec(e, parent, state,
+                                   &scratch.d.minmax.values[off],
+                                   &scratch.d.minmax.nulls[off]);
+                   off++;
+               }
+
+               /* and push the final comparison */
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_SQLValueFunction:
+           {
+               SQLValueFunction *svf = (SQLValueFunction *) node;
+
+               scratch.opcode = EEO_SQLVALUEFUNCTION;
+               scratch.d.sqlvaluefunction.svf = svf;
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_XmlExpr:
+           {
+               XmlExpr    *xexpr = (XmlExpr *) node;
+               ListCell   *arg;
+               int         nnamed = list_length(xexpr->named_args);
+               int         nargs = list_length(xexpr->args);
+               int         off;
+
+               scratch.opcode = EEO_XMLEXPR;
+               scratch.d.xmlexpr.xexpr = xexpr;
+
+               /* allocate space for storing all the arguments */
+               if (nnamed)
+               {
+                   scratch.d.xmlexpr.named_argvalue =
+                       (Datum *) palloc(sizeof(Datum) * nnamed);
+                   scratch.d.xmlexpr.named_argnull =
+                       (bool *) palloc(sizeof(bool) * nnamed);
+               }
+               else
+               {
+                   scratch.d.xmlexpr.named_argvalue = NULL;
+                   scratch.d.xmlexpr.named_argnull = NULL;
+               }
+
+               if (nargs)
+               {
+                   scratch.d.xmlexpr.argvalue =
+                       (Datum *) palloc(sizeof(Datum) * nargs);
+                   scratch.d.xmlexpr.argnull =
+                       (bool *) palloc(sizeof(bool) * nargs);
+               }
+               else
+               {
+                   scratch.d.xmlexpr.argvalue = NULL;
+                   scratch.d.xmlexpr.argnull = NULL;
+               }
+
+               /* prepare argument execution */
+               off = 0;
+               foreach(arg, xexpr->named_args)
+               {
+                   Expr       *e = (Expr *) lfirst(arg);
+
+                   ExecInitExprRec(e, parent, state,
+                                   &scratch.d.xmlexpr.named_argvalue[off],
+                                   &scratch.d.xmlexpr.named_argnull[off]);
+                   off++;
+               }
+
+               off = 0;
+               foreach(arg, xexpr->args)
+               {
+                   Expr       *e = (Expr *) lfirst(arg);
+
+                   ExecInitExprRec(e, parent, state,
+                                   &scratch.d.xmlexpr.argvalue[off],
+                                   &scratch.d.xmlexpr.argnull[off]);
+                   off++;
+               }
+
+               /* and evaluate the actual XML expression */
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_NullTest:
+           {
+               NullTest   *ntest = (NullTest *) node;
+
+               if (ntest->nulltesttype == IS_NULL)
+               {
+                   if (ntest->argisrow)
+                       scratch.opcode = EEO_NULLTEST_ROWISNULL;
+                   else
+                       scratch.opcode = EEO_NULLTEST_ISNULL;
+               }
+               else if (ntest->nulltesttype == IS_NOT_NULL)
+               {
+                   if (ntest->argisrow)
+                       scratch.opcode = EEO_NULLTEST_ROWISNOTNULL;
+                   else
+                       scratch.opcode = EEO_NULLTEST_ISNOTNULL;
+               }
+               else
+               {
+                   elog(ERROR, "unrecognized nulltesttype: %d",
+                        (int) ntest->nulltesttype);
+               }
+
+               /* first evaluate argument */
+               ExecInitExprRec(ntest->arg, parent, state,
+                               resv, resnull);
+
+               /* then push the test of that argument */
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_BooleanTest:
+           {
+               BooleanTest *btest = (BooleanTest *) node;
+
+               /*
+                * Evaluate argument, directly into result datum. That's a bit
+                * debatable, because the types will be different, but it's
+                * efficient...  The evaluation step will then store an actual
+                * boolean.
+                */
+               ExecInitExprRec(btest->arg, parent, state, resv, resnull);
+
+               switch (btest->booltesttype)
+               {
+                   case IS_TRUE:
+                       scratch.opcode = EEO_BOOLTEST_IS_TRUE;
+                       break;
+                   case IS_NOT_TRUE:
+                       scratch.opcode = EEO_BOOLTEST_IS_NOT_TRUE;
+                       break;
+                   case IS_FALSE:
+                       scratch.opcode = EEO_BOOLTEST_IS_FALSE;
+                       break;
+                   case IS_NOT_FALSE:
+                       scratch.opcode = EEO_BOOLTEST_IS_NOT_FALSE;
+                       break;
+                   case IS_UNKNOWN:
+                       scratch.opcode = EEO_BOOLTEST_IS_UNKNOWN;
+                       break;
+                   case IS_NOT_UNKNOWN:
+                       scratch.opcode = EEO_BOOLTEST_IS_NOT_UNKNOWN;
+                       break;
+                   default:
+                       elog(ERROR, "unrecognized booltesttype: %d",
+                            (int) btest->booltesttype);
+               }
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_CoerceToDomain:
+           {
+               CoerceToDomain *ctest = (CoerceToDomain *) node;
+               Datum      *save_innermost_domainval = NULL;
+               bool       *save_innermost_domainnull = NULL;
+               DomainConstraintRef *constraint_ref =
+               palloc(sizeof(DomainConstraintRef));
+               ListCell   *l;
+
+               scratch.d.domaincheck.resulttype = ctest->resulttype;
+               scratch.d.domaincheck.checkvalue = (Datum *) palloc(sizeof(Datum));
+               scratch.d.domaincheck.checknull = (bool *) palloc(sizeof(bool));
+
+               /* evaluate argument */
+               ExecInitExprRec(ctest->arg, parent, state, resv, resnull);
+
+               /*
+                * XXX: In contrast to the old implementation we're evaluating
+                * the set of to-be-checked constraints at query start - that
+                * seems perfectly sensible to me.  But perhaps there's a
+                * reason the previous implementation did what it did? ISTM
+                * that was just a side-effect of using the typecache (which
+                * is longer lived than a single query).
+                */
+
+               /* Make sure we have up-to-date constraints */
+               InitDomainConstraintRef(ctest->resulttype,
+                                       constraint_ref,
+                                       CurrentMemoryContext);
+               UpdateDomainConstraintRef(constraint_ref);
+
+               /*
+                * Set up value to be returned by CoerceToDomainValue nodes.
+                * We must save and restore innermost_domainval/null fields,
+                * in case this node is itself within a check expression for
+                * another domain.
+                */
+               save_innermost_domainval = state->innermost_domainval;
+               save_innermost_domainnull = state->innermost_domainnull;
+               state->innermost_domainval = resv;
+               state->innermost_domainnull = resnull;
+
+               foreach(l, constraint_ref->constraints)
+               {
+                   DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+
+                   scratch.d.domaincheck.constraintname = con->name;
+
+                   switch (con->constrainttype)
+                   {
+                       case DOM_CONSTRAINT_NOTNULL:
+                           scratch.opcode = EEO_DOMAIN_NOTNULL;
+                           ExprEvalPushStep(state, &scratch);
+                           break;
+                       case DOM_CONSTRAINT_CHECK:
+                           /* evaluate check expression value */
+                           ExecInitExprRec(con->check_expr, parent, state,
+                                           scratch.d.domaincheck.checkvalue,
+                                           scratch.d.domaincheck.checknull);
+
+                           /* and then check result value */
+                           scratch.opcode = EEO_DOMAIN_CHECK;
+                           ExprEvalPushStep(state, &scratch);
+                           break;
+                       default:
+                           elog(ERROR, "unrecognized constraint type: %d",
+                                (int) con->constrainttype);
+                           break;
+                   }
+               }
+
+               state->innermost_domainval = save_innermost_domainval;
+               state->innermost_domainnull = save_innermost_domainnull;
+
+               break;
+           }
+
+       case T_CoerceToDomainValue:
+           {
+               CoerceToDomainValue *domainval = (CoerceToDomainValue *) node;
+
+               /* share datastructure with case testval */
+               scratch.d.casetest.value = state->innermost_domainval;
+               scratch.d.casetest.isnull = state->innermost_domainnull;
+
+               if (get_typlen(domainval->typeId) == -1)
+                   scratch.opcode = EEO_DOMAIN_TESTVAL_UNEXPAND;
+               else
+                   scratch.opcode = EEO_DOMAIN_TESTVAL;
+
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       case T_CurrentOfExpr:
+           {
+               scratch.opcode = EEO_CURRENTOFEXPR;
+               ExprEvalPushStep(state, &scratch);
+               break;
+           }
+
+       default:
+           elog(ERROR, "unrecognized node type: %d",
+                (int) nodeTag(node));
+           break;
+   }
+}
+
+static void
+ExprEvalPushStep(ExprState *es, ExprEvalStep *s)
+{
+   if (es->steps_alloc == 0)
+   {
+       es->steps_alloc = 16;
+       es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+   }
+   else if (es->steps_alloc == es->steps_len)
+   {
+       es->steps_alloc *= 2;
+       es->steps = repalloc(es->steps,
+                            sizeof(ExprEvalStep) * es->steps_alloc);
+   }
+
+   memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static void
+ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
+            Oid inputcollid, PlanState *parent, ExprState *state,
+            Datum *resv, bool *resnull)
+{
+   ListCell   *lc;
+   AclResult   aclresult;
+   int         nargs = list_length(args);
+   FunctionCallInfo fcinfo;
+   int         argno;
+
+   /* Check permission to call function */
+   aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+   if (aclresult != ACLCHECK_OK)
+       aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+   InvokeFunctionExecuteHook(funcid);
+
+   /*
+    * Safety check on nargs.  Under normal circumstances this should never
+    * fail, as parser should check sooner.  But possibly it might fail if
+    * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+    * declared in pg_proc?
+    */
+   if (nargs > FUNC_MAX_ARGS)
+       ereport(ERROR,
+               (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+            errmsg_plural("cannot pass more than %d argument to a function",
+                          "cannot pass more than %d arguments to a function",
+                          FUNC_MAX_ARGS,
+                          FUNC_MAX_ARGS)));
+
+   /* Set up the primary fmgr lookup information */
+   scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+   scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+   fcinfo = scratch->d.func.fcinfo_data;
+   fmgr_info(funcid, scratch->d.func.finfo);
+   fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+   InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+                            nargs, inputcollid, NULL, NULL);
+   scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+   if (scratch->d.func.finfo->fn_retset)
+   {
+       ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                errmsg("set-valued function called in context that cannot accept a set")));
+   }
+
+   argno = 0;
+   foreach(lc, args)
+   {
+       Expr       *arg = (Expr *) lfirst(lc);
+
+       if (IsA(arg, Const))
+       {
+           /*
+            * Don't evaluate const arguments every round; especially
+            * interesting for constants in comparisons.
+            */
+           Const      *con = (Const *) arg;
+
+           fcinfo->arg[argno] = con->constvalue;
+           fcinfo->argnull[argno] = con->constisnull;
+       }
+       else
+       {
+           ExecInitExprRec(arg, parent, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]);
+       }
+       argno++;
+   }
+
+   scratch->d.func.nargs = nargs;
+
+   if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+   {
+       if (scratch->d.func.finfo->fn_strict && nargs > 0)
+           scratch->opcode = EEO_FUNCEXPR_STRICT;
+       else
+           scratch->opcode = EEO_FUNCEXPR;
+   }
+   else
+   {
+       if (scratch->d.func.finfo->fn_strict && nargs > 0)
+           scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+       else
+           scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+   }
+}
+
+static void
+ExecInitExprSlots(ExprState *state, Node *node)
+{
+   ExprEvalStep scratch;
+   int         last_outer = -1;
+   int         last_inner = -1;
+   int         last_scan = -1;
+
+   /*
+    * Figure out which attributes we're going to need.
+    */
+   ExecGetLastAttnums((Node *) node,
+                      &last_outer,
+                      &last_inner,
+                      &last_scan);
+   if (last_inner > 0)
+   {
+       scratch.opcode = EEO_INNER_FETCHSOME;
+       scratch.d.fetch.last_var = last_inner;
+       ExprEvalPushStep(state, &scratch);
+   }
+   if (last_outer > 0)
+   {
+       scratch.opcode = EEO_OUTER_FETCHSOME;
+       scratch.d.fetch.last_var = last_outer;
+       ExprEvalPushStep(state, &scratch);
+   }
+   if (last_scan > 0)
+   {
+       scratch.opcode = EEO_SCAN_FETCHSOME;
+       scratch.d.fetch.last_var = last_scan;
+       ExprEvalPushStep(state, &scratch);
+   }
+}
+
+static void
+ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, PlanState *parent, ExprState *state, Datum *resv, bool *resnull)
+{
+   scratch->d.wholerow.tupdesc = NULL;
+   scratch->d.wholerow.junkFilter = NULL;
+   scratch->d.wholerow.var = variable;
+   scratch->d.wholerow.first = true;
+
+   /*
+    * If the input tuple came from a subquery, it might contain "resjunk"
+    * columns (such as GROUP BY or ORDER BY columns), which we don't want to
+    * keep in the whole-row result.  We can get rid of such columns by
+    * passing the tuple through a JunkFilter --- but to make one, we have to
+    * lay our hands on the subquery's targetlist.  Fortunately, there are not
+    * very many cases where this can happen, and we can identify all of them
+    * by examining our parent PlanState.  We assume this is not an issue in
+    * standalone expressions that don't have parent plans.  (Whole-row Vars
+    * can occur in such expressions, but they will always be referencing
+    * table rows.)
+    */
+
+   if (parent)
+   {
+       PlanState  *subplan = NULL;
+
+       switch (nodeTag(parent))
+       {
+           case T_SubqueryScanState:
+               subplan = ((SubqueryScanState *) parent)->subplan;
+               break;
+           case T_CteScanState:
+               subplan = ((CteScanState *) parent)->cteplanstate;
+               break;
+           default:
+               break;
+       }
+
+       if (subplan)
+       {
+           bool        junk_filter_needed = false;
+           ListCell   *tlist;
+
+           /* Detect whether subplan tlist actually has any junk columns */
+           foreach(tlist, subplan->plan->targetlist)
+           {
+               TargetEntry *tle = (TargetEntry *) lfirst(tlist);
+
+               if (tle->resjunk)
+               {
+                   junk_filter_needed = true;
+                   break;
+               }
+           }
+
+           if (junk_filter_needed)
+           {
+               /* If so, build the junkfilter in the query memory context */
+               scratch->d.wholerow.junkFilter =
+                   ExecInitJunkFilter(subplan->plan->targetlist,
+                                      ExecGetResultType(subplan)->tdhasoid,
+                                      ExecInitExtraTupleSlot(parent->state));
+           }
+       }
+   }
+}
+
+static void
+ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
+                ExprState *state, Datum *resv, bool *resnull)
+{
+   List       *adjust_bailout = NIL;
+   ListCell   *lc;
+   bool        isAssignment = (aref->refassgnexpr != NULL);
+   ArrayRefState *arefstate = palloc(sizeof(ArrayRefState));
+   int         i;
+
+   if (list_length(aref->refupperindexpr) >= MAXDIM)
+       ereport(ERROR,
+               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+                       list_length(aref->refupperindexpr), MAXDIM)));
+
+   if (list_length(aref->reflowerindexpr) >= MAXDIM)
+       ereport(ERROR,
+               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+                       list_length(aref->refupperindexpr), MAXDIM)));
+
+   if (list_length(aref->reflowerindexpr) > 0 &&
+   list_length(aref->refupperindexpr) != list_length(aref->reflowerindexpr))
+       elog(ERROR, "upper and lower index lists are not same length");
+
+   arefstate->isassignment = isAssignment;
+   arefstate->refattrlength = get_typlen(aref->refarraytype);
+   arefstate->refelemtype = aref->refelemtype;
+   get_typlenbyvalalign(aref->refelemtype,
+                        &arefstate->refelemlength,
+                        &arefstate->refelembyval,
+                        &arefstate->refelemalign);
+
+   /* evaluate input */
+   ExecInitExprRec(aref->refexpr, parent, state,
+                   resv, resnull);
+
+   /*
+    * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
+    * assignment case, we'll create an empty input.
+    */
+   if (!isAssignment)
+   {
+       scratch->opcode = EEO_ARRAYREF_CHECKINPUT;
+       scratch->d.arrayref.state = arefstate;
+       scratch->d.arrayref.jumpdone = -1;      /* adjust later */
+       ExprEvalPushStep(state, scratch);
+       adjust_bailout = lappend_int(adjust_bailout,
+                                    state->steps_len - 1);
+   }
+
+   /* evaluate upper subscripts */
+   i = 0;
+   foreach(lc, aref->refupperindexpr)
+   {
+       Expr       *e = (Expr *) lfirst(lc);
+
+       if (!e)
+       {
+           arefstate->upperprovided[i] = false;
+           i++;
+           continue;
+       }
+
+       arefstate->upperprovided[i] = true;
+
+       ExecInitExprRec(e, parent, state,
+                       &arefstate->upper[i], &arefstate->uppernull[i]);
+
+       scratch->opcode = EEO_ARRAYREF_CHECKSUBSCRIPT;
+       scratch->d.arrayref_checksubscript.state = arefstate;
+       scratch->d.arrayref_checksubscript.off = i;
+       scratch->d.arrayref_checksubscript.isupper = true;
+       scratch->d.arrayref_checksubscript.jumpdone = -1;       /* adjust later */
+       ExprEvalPushStep(state, scratch);
+       adjust_bailout = lappend_int(adjust_bailout,
+                                    state->steps_len - 1);
+       i++;
+   }
+   arefstate->numupper = i;
+
+   /* evaluate lower subscripts */
+   i = 0;
+   foreach(lc, aref->reflowerindexpr)
+   {
+       Expr       *e = (Expr *) lfirst(lc);
+
+       if (!e)
+       {
+           arefstate->lowerprovided[i] = false;
+           i++;
+           continue;
+       }
+
+       arefstate->lowerprovided[i] = true;
+
+       ExecInitExprRec(e, parent, state,
+                       &arefstate->lower[i], &arefstate->lowernull[i]);
+
+       scratch->opcode = EEO_ARRAYREF_CHECKSUBSCRIPT;
+       scratch->d.arrayref_checksubscript.state = arefstate;
+       scratch->d.arrayref_checksubscript.off = i;
+       scratch->d.arrayref_checksubscript.isupper = false;
+       scratch->d.arrayref_checksubscript.jumpdone = -1;       /* adjust later */
+       ExprEvalPushStep(state, scratch);
+       adjust_bailout = lappend_int(adjust_bailout,
+                                    state->steps_len - 1);
+       i++;
+   }
+   arefstate->numlower = i;
+
+   if (isAssignment)
+   {
+       Datum      *save_innermost_caseval = NULL;
+       bool       *save_innermost_casenull = NULL;
+
+       /*
+        * We might have a nested-assignment situation, in which the
+        * refassgnexpr is itself a FieldStore or ArrayRef that needs to
+        * obtain and modify the previous value of the array element or slice
+        * being replaced.  If so, we have to extract that value from the
+        * array and pass it down via the econtext's caseValue. It's safe to
+        * reuse the CASE mechanism because there cannot be a CASE between
+        * here and where the value would be needed, and an array assignment
+        * can't be within a CASE either.  (So saving and restoring the
+        * caseValue is just paranoia, but let's do it anyway.)
+        *
+        * Since fetching the old element might be a nontrivial expense, do it
+        * only if the argument appears to actually need it.
+        */
+       if (isAssignmentIndirectionExpr(aref->refassgnexpr))
+       {
+           scratch->opcode = EEO_ARRAYREF_OLD;
+           scratch->d.arrayref.state = arefstate;
+           ExprEvalPushStep(state, scratch);
+       }
+
+       save_innermost_caseval = state->innermost_caseval;
+       save_innermost_casenull = state->innermost_casenull;
+       state->innermost_caseval = &arefstate->prevvalue;
+       state->innermost_casenull = &arefstate->prevnull;
+
+       /* evaluate replacement value */
+       ExecInitExprRec(aref->refassgnexpr, parent, state,
+                       &arefstate->replacevalue, &arefstate->replacenull);
+
+       state->innermost_caseval = save_innermost_caseval;
+       state->innermost_casenull = save_innermost_casenull;
+
+       scratch->opcode = EEO_ARRAYREF_ASSIGN;
+       scratch->d.arrayref.state = arefstate;
+       ExprEvalPushStep(state, scratch);
+   }
+   else
+   {
+       scratch->opcode = EEO_ARRAYREF_FETCH;
+       scratch->d.arrayref.state = arefstate;
+       ExprEvalPushStep(state, scratch);
+   }
+
+   /* adjust early bail out jump target */
+   foreach(lc, adjust_bailout)
+   {
+       ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+       if (as->opcode == EEO_ARRAYREF_CHECKSUBSCRIPT)
+       {
+           Assert(as->d.arrayref_checksubscript.jumpdone == -1);
+           as->d.arrayref_checksubscript.jumpdone = state->steps_len;
+       }
+       else
+       {
+           Assert(as->d.arrayref.jumpdone == -1);
+           as->d.arrayref.jumpdone = state->steps_len;
+       }
+   }
+}
+
+/*
+ * Helper for preparing ArrayRef expressions for evaluation: is expr a nested
+ * FieldStore or ArrayRef that might need the old element value passed down?
+ *
+ * (We could use this in FieldStore too, but in that case passing the old
+ * value is so cheap there's no need.)
+ */
+static bool
+isAssignmentIndirectionExpr(Expr *expr)
+{
+   if (expr == NULL)
+       return false;           /* just paranoia */
+   if (IsA(expr, FieldStore))
+   {
+       FieldStore *fstore = (FieldStore *) expr;
+
+       if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
+           return true;
+   }
+   else if (IsA(expr, ArrayRef))
+   {
+       ArrayRef   *arrayRef = (ArrayRef *) expr;
+
+       if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
+           return true;
+   }
+   return false;
+}
index 5242dee00644725ba4eb45727f1d6ad11ec84269..108060ac0f17c810a7c33113ccd7a35626fdde4f 100644 (file)
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
        /* Check for partial index */
        if (indexInfo->ii_Predicate != NIL)
        {
-           List       *predicate;
+           ExprState  *predicate;
 
            /*
             * If predicate state not set up yet, create it (in the estate's
             * per-query context)
             */
            predicate = indexInfo->ii_PredicateState;
-           if (predicate == NIL)
+           if (predicate == NULL)
            {
-               predicate = (List *)
-                   ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-                                   estate);
+               predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
                indexInfo->ii_PredicateState = predicate;
            }
 
            /* Skip this index-update if the predicate isn't satisfied */
-           if (!ExecQual(predicate, econtext, false))
+           if (!ExecQual(predicate, econtext))
                continue;
        }
 
@@ -551,23 +549,21 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
        /* Check for partial index */
        if (indexInfo->ii_Predicate != NIL)
        {
-           List       *predicate;
+           ExprState  *predicate;
 
            /*
             * If predicate state not set up yet, create it (in the estate's
             * per-query context)
             */
            predicate = indexInfo->ii_PredicateState;
-           if (predicate == NIL)
+           if (predicate == NULL)
            {
-               predicate = (List *)
-                   ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-                                   estate);
+               predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
                indexInfo->ii_PredicateState = predicate;
            }
 
            /* Skip this index-update if the predicate isn't satisfied */
-           if (!ExecQual(predicate, econtext, false))
+           if (!ExecQual(predicate, econtext))
                continue;
        }
 
diff --git a/src/backend/executor/execInterpExpr.c b/src/backend/executor/execInterpExpr.c
new file mode 100644 (file)
index 0000000..21388f4
--- /dev/null
@@ -0,0 +1,3300 @@
+/*-------------------------------------------------------------------------
+ *
+ * execInterpExpr.c
+ *   Opcode based expression evaluation.
+ *
+ * This file provides a "switch threaded" (all compilers) and "direct
+ * threaded" (gcc, clang and compatible) implementation of expression
+ * evaluation.  The former is among st the fastest known methods of
+ * interpreting programs without resorting to assembly level work, or
+ * just-in-time compilation, but requires support for computed gotos.  The
+ * latter is amongst the fastest approaches doable in standard C.
+ *
+ * Both work by using ExprEvalStep->opcode to dispatch into code blocks
+ * implementing the specific method.  Switch based uses a plain switch()
+ * statement to perform the dispatch. This has the advantages of being plain C
+ * and allowing to warn if implementation of a specific opcode has been
+ * forgotten.  The disadvantage is that dispatches will, as commonly
+ * implemented by compilers, happen from a single location, causing bad branch
+ * prediction.  Direct dispatch uses the label-as-values gcc extension - also
+ * adopted by some other compilers - to replace ExprEvalStep-> opcode with the
+ * address of the block implementing the instruction. This allows for better
+ * branch prediction (the jumps are happening from different locations) and
+ * fewer jumps (no jumps to common dispatch location needed).
+ *
+ * If computed gotos are available, ExecInstantiateInterpretedExpr will
+ * replace ->opcode with the address of relevant code-block and
+ * ExprState->flags will contain EEO_FLAG_JUMP_THREADED to "remember" that
+ * fact.
+ *
+ * For very simple instructions the overhead of the full interpreter
+ * "startup", as minimal as it is, is noticeable.  Therefore
+ * ExecInstantiateInterpretedExpr() will choose to implement simple scalar Var
+ * and Const expressions using special cased routines (ExecJust*).
+ * Benchmarking shows anything more complex than those will benefit from the
+ * "full interpreter".
+ *
+ * Complex or uncommon instructions are not implemented in ExecInterpExpr. For
+ * one, there'd not be a noticeable performance benefit, but more importantly
+ * those complex routines are intended to be shared between different
+ * expression evaluation approaches.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *   src/backend/executor/execInterprExpr.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "executor/execExpr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/lsyscache.h"
+#include "utils/timestamp.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+
+/*
+ * Use computed goto based opcode dispatch when computed gotos are
+ * available. But allow to disable locally in this file for development.
+ */
+#ifdef HAVE__COMPUTED_GOTO
+#define EEO_USE_COMPUTED_GOTO
+#endif   /* HAVE__COMPUTED_GOTO */
+
+/*
+ * Macros for opcode dispatch.
+ */
+#if defined(EEO_USE_COMPUTED_GOTO)
+static void **dispatch_table = NULL;
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH_DIRECT(op) goto *((void *) op->opcode)
+#define EEO_CASE(name) CASE_##name:
+#else
+#define EEO_SWITCH(d) switch ((ExprEvalOp) d)
+#define EEO_DISPATCH_DIRECT(op) goto starteval
+#define EEO_CASE(name) case name:
+#endif   /* EEO_USE_COMPUTED_GOTO */
+
+#define EEO_DISPATCH(op) \
+   do \
+   { \
+       op++; \
+       EEO_DISPATCH_DIRECT(op); \
+   } \
+   while (0)
+
+
+static Datum ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull);
+static void ExecPrepareInterp(void);
+
+/* support functions */
+static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, bool checkisnull);
+static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
+                  TupleDesc *cache_field, ExprContext *econtext);
+static void ShutdownTupleDescRef(Datum arg);
+
+/* fast-path evaluation functions */
+static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
+
+
+/*
+ * Prepare ExprState for interpreted execution.
+ */
+void
+ExecInstantiateInterpretedExpr(ExprState *state)
+{
+   ExecPrepareInterp();
+
+   Assert(state->steps_len >= 1);
+   Assert(ExecEvalStepOp(state, &state->steps[state->steps_len - 1]) == EEO_DONE);
+
+   /*
+    * Fast-path for very simple expressions. "starting up" the full
+    * interpreter is a measurable overhead for these.  Plain Vars and Const
+    * seem to be the only ones where the intrinsic cost is small enough that
+    * the overhead of ExecInterpExpr matters.  For more complex expressions
+    * it's cheaper to use ExecInterpExpr anyways.
+    */
+   if (state->steps_len == 3)
+   {
+       ExprEvalOp  step0 = ExecEvalStepOp(state, &state->steps[0]);
+       ExprEvalOp  step1 = ExecEvalStepOp(state, &state->steps[1]);
+
+       if (step0 == EEO_INNER_FETCHSOME && step1 == EEO_INNER_VAR)
+       {
+           state->evalfunc = ExecJustInnerVar;
+           return;
+       }
+       else if (step0 == EEO_OUTER_FETCHSOME && step1 == EEO_OUTER_VAR)
+       {
+           state->evalfunc = ExecJustOuterVar;
+           return;
+       }
+       else if (step0 == EEO_SCAN_FETCHSOME && step1 == EEO_SCAN_VAR)
+       {
+           state->evalfunc = ExecJustScanVar;
+           return;
+       }
+       else if (step0 == EEO_INNER_FETCHSOME && step1 == EEO_ASSIGN_INNER_VAR)
+       {
+           state->evalfunc = ExecJustAssignInnerVar;
+           return;
+       }
+       else if (step0 == EEO_OUTER_FETCHSOME && step1 == EEO_ASSIGN_OUTER_VAR)
+       {
+           state->evalfunc = ExecJustAssignOuterVar;
+           return;
+       }
+       else if (step0 == EEO_SCAN_FETCHSOME && step1 == EEO_ASSIGN_SCAN_VAR)
+       {
+           state->evalfunc = ExecJustAssignScanVar;
+           return;
+       }
+   }
+   else if (state->steps_len == 2 &&
+            ExecEvalStepOp(state, &state->steps[0]) == EEO_CONST)
+   {
+       state->evalfunc = ExecJustConst;
+       return;
+   }
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+
+   /*
+    * In the jump-threaded implementation, replace opcode with the address to
+    * jump to. To reverse, use ExecEvalStepOp().
+    */
+   {
+       int         off;
+
+       for (off = 0; off < state->steps_len; off++)
+       {
+           ExprEvalStep *op = &state->steps[off];
+
+           op->opcode = (size_t) dispatch_table[op->opcode];
+       }
+
+       state->flags |= EEO_FLAG_JUMP_THREADED;
+   }
+#endif   /* defined(EEO_USE_COMPUTED_GOTO) */
+
+   state->evalfunc = ExecInterpExpr;
+}
+
+
+static Datum
+ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+   ExprEvalStep *op;
+   const TupleTableSlot *resultslot;
+   TupleTableSlot *innerslot;
+   TupleTableSlot *outerslot;
+   TupleTableSlot *scanslot;
+
+   /*
+    * This array has to be in the same order as ExprEvalOp.
+    */
+#if defined(EEO_USE_COMPUTED_GOTO)
+   static const void **dispatch_table[] = {
+       &&CASE_EEO_DONE,
+       &&CASE_EEO_INNER_FETCHSOME,
+       &&CASE_EEO_OUTER_FETCHSOME,
+       &&CASE_EEO_SCAN_FETCHSOME,
+       &&CASE_EEO_INNER_VAR,
+       &&CASE_EEO_OUTER_VAR,
+       &&CASE_EEO_SCAN_VAR,
+       &&CASE_EEO_ASSIGN_INNER_VAR,
+       &&CASE_EEO_ASSIGN_OUTER_VAR,
+       &&CASE_EEO_ASSIGN_SCAN_VAR,
+       &&CASE_EEO_ASSIGN_TMP,
+       &&CASE_EEO_ASSIGN_TMP_UNEXPAND,
+       &&CASE_EEO_INNER_SYSVAR,
+       &&CASE_EEO_OUTER_SYSVAR,
+       &&CASE_EEO_SCAN_SYSVAR,
+       &&CASE_EEO_CONST,
+       &&CASE_EEO_FUNCEXPR,
+       &&CASE_EEO_FUNCEXPR_STRICT,
+       &&CASE_EEO_FUNCEXPR_FUSAGE,
+       &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+       &&CASE_EEO_BOOL_AND_STEP_FIRST,
+       &&CASE_EEO_BOOL_AND_STEP,
+       &&CASE_EEO_BOOL_AND_STEP_LAST,
+       &&CASE_EEO_BOOL_OR_STEP_FIRST,
+       &&CASE_EEO_BOOL_OR_STEP,
+       &&CASE_EEO_BOOL_OR_STEP_LAST,
+       &&CASE_EEO_BOOL_NOT_STEP,
+       &&CASE_EEO_QUAL,
+       &&CASE_EEO_NULLTEST_ISNULL,
+       &&CASE_EEO_NULLTEST_ISNOTNULL,
+       &&CASE_EEO_NULLTEST_ROWISNULL,
+       &&CASE_EEO_NULLTEST_ROWISNOTNULL,
+       &&CASE_EEO_PARAM_EXEC,
+       &&CASE_EEO_PARAM_EXTERN,
+       &&CASE_EEO_CASE_WHEN_STEP,
+       &&CASE_EEO_CASE_THEN_STEP,
+       &&CASE_EEO_CASE_TESTVAL,
+       &&CASE_EEO_CASE_TESTVAL_UNEXPAND,
+       &&CASE_EEO_COALESCE,
+       &&CASE_EEO_BOOLTEST_IS_TRUE,
+       &&CASE_EEO_BOOLTEST_IS_NOT_TRUE,
+       &&CASE_EEO_BOOLTEST_IS_FALSE,
+       &&CASE_EEO_BOOLTEST_IS_NOT_FALSE,
+       &&CASE_EEO_BOOLTEST_IS_UNKNOWN,
+       &&CASE_EEO_BOOLTEST_IS_NOT_UNKNOWN,
+       &&CASE_EEO_WHOLEROW,
+       &&CASE_EEO_IOCOERCE,
+       &&CASE_EEO_DISTINCT,
+       &&CASE_EEO_NULLIF,
+       &&CASE_EEO_SQLVALUEFUNCTION,
+       &&CASE_EEO_CURRENTOFEXPR,
+       &&CASE_EEO_ARRAYEXPR,
+       &&CASE_EEO_ARRAYCOERCE,
+       &&CASE_EEO_ROW,
+       &&CASE_EEO_ROWCOMPARE_STEP,
+       &&CASE_EEO_ROWCOMPARE_FINAL,
+       &&CASE_EEO_MINMAX,
+       &&CASE_EEO_FIELDSELECT,
+       &&CASE_EEO_FIELDSTORE_DEFORM,
+       &&CASE_EEO_FIELDSTORE_FORM,
+       &&CASE_EEO_ARRAYREF_CHECKINPUT,
+       &&CASE_EEO_ARRAYREF_CHECKSUBSCRIPT,
+       &&CASE_EEO_ARRAYREF_OLD,
+       &&CASE_EEO_ARRAYREF_ASSIGN,
+       &&CASE_EEO_ARRAYREF_FETCH,
+       &&CASE_EEO_CONVERT_ROWTYPE,
+       &&CASE_EEO_SCALARARRAYOP,
+       &&CASE_EEO_DOMAIN_TESTVAL,
+       &&CASE_EEO_DOMAIN_TESTVAL_UNEXPAND,
+       &&CASE_EEO_DOMAIN_NOTNULL,
+       &&CASE_EEO_DOMAIN_CHECK,
+       &&CASE_EEO_XMLEXPR,
+       &&CASE_EEO_AGGREF,
+       &&CASE_EEO_GROUPING_FUNC,
+       &&CASE_EEO_WINDOW_FUNC,
+       &&CASE_EEO_SUBPLAN,
+       &&CASE_EEO_ALTERNATIVE_SUBPLAN,
+       &&CASE_EEO_LAST
+   };
+
+   StaticAssertStmt(EEO_LAST + 1 == lengthof(dispatch_table),
+                    "dispatch_table out of whack with ExprEvalOp");
+#endif
+
+#ifdef EEO_USE_COMPUTED_GOTO
+   if (unlikely(state == NULL))
+   {
+       return PointerGetDatum(dispatch_table);
+   }
+#endif
+
+   /* setup state */
+   op = state->steps;
+   resultslot = state->resultslot;
+   innerslot = econtext->ecxt_innertuple;
+   outerslot = econtext->ecxt_outertuple;
+   scanslot = econtext->ecxt_scantuple;
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+   EEO_DISPATCH_DIRECT(op);
+#else
+starteval:
+#endif
+   EEO_SWITCH(op->opcode)
+   {
+       EEO_CASE(EEO_DONE)
+       {
+           goto out;
+       }
+
+       EEO_CASE(EEO_INNER_FETCHSOME)
+       {
+           /* XXX: worthwhile to check tts_nvalid inline first? */
+           slot_getsomeattrs(innerslot, op->d.fetch.last_var);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_OUTER_FETCHSOME)
+       {
+           slot_getsomeattrs(outerslot, op->d.fetch.last_var);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_SCAN_FETCHSOME)
+       {
+           slot_getsomeattrs(scanslot, op->d.fetch.last_var);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_INNER_VAR)
+       {
+           int         attnum = op->d.var.attnum;
+
+           /*
+            * Can't assert tts_nvalid, as wholerow var evaluation or such
+            * could have materialized the slot - but the contents are still
+            * valid :/
+            */
+           Assert(op->d.var.attnum >= 0);
+           *op->resnull = innerslot->tts_isnull[attnum];
+           *op->resvalue = innerslot->tts_values[attnum];
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_OUTER_VAR)
+       {
+           int         attnum = op->d.var.attnum;
+
+           /* See EEO_INNER_VAR comments */
+           Assert(op->d.var.attnum >= 0);
+           *op->resnull = outerslot->tts_isnull[attnum];
+           *op->resvalue = outerslot->tts_values[attnum];
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_SCAN_VAR)
+       {
+           int         attnum = op->d.var.attnum;
+
+           /* See EEO_INNER_VAR comments */
+           Assert(op->d.var.attnum >= 0);
+           *op->resnull = scanslot->tts_isnull[attnum];
+           *op->resvalue = scanslot->tts_values[attnum];
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_ASSIGN_INNER_VAR)
+       {
+           size_t      resultnum = op->d.assign_var.resultnum;
+           size_t      attnum = op->d.assign_var.attnum;
+
+           resultslot->tts_values[resultnum] = innerslot->tts_values[attnum];
+           resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum];
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_ASSIGN_OUTER_VAR)
+       {
+           size_t      resultnum = op->d.assign_var.resultnum;
+           size_t      attnum = op->d.assign_var.attnum;
+
+           resultslot->tts_values[resultnum] = outerslot->tts_values[attnum];
+           resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum];
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_ASSIGN_SCAN_VAR)
+       {
+           size_t      resultnum = op->d.assign_var.resultnum;
+           size_t      attnum = op->d.assign_var.attnum;
+
+           resultslot->tts_values[resultnum] = scanslot->tts_values[attnum];
+           resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum];
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_ASSIGN_TMP)
+       {
+           size_t      resultnum = op->d.assign_tmp.resultnum;
+
+           resultslot->tts_values[resultnum] = state->resvalue;
+           resultslot->tts_isnull[resultnum] = state->resnull;
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_ASSIGN_TMP_UNEXPAND)
+       {
+           size_t      resultnum = op->d.assign_tmp.resultnum;
+
+           resultslot->tts_isnull[resultnum] = state->resnull;
+
+           if (!resultslot->tts_isnull[resultnum])
+               resultslot->tts_values[resultnum] =
+                   MakeExpandedObjectReadOnlyInternal(state->resvalue);
+           else
+               resultslot->tts_values[resultnum] = state->resvalue;
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_INNER_SYSVAR)
+       {
+           int         attnum = op->d.var.attnum;
+
+           Assert(op->d.var.attnum < 0);
+           *op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
+                                           innerslot->tts_tupleDescriptor,
+                                           op->resnull);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_OUTER_SYSVAR)
+       {
+           int         attnum = op->d.var.attnum;
+
+           Assert(op->d.var.attnum < 0);
+           *op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
+                                           scanslot->tts_tupleDescriptor,
+                                           op->resnull);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_SCAN_SYSVAR)
+       {
+           int         attnum = op->d.var.attnum;
+
+           Assert(op->d.var.attnum < 0);
+           *op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
+                                           scanslot->tts_tupleDescriptor,
+                                           op->resnull);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_CONST)
+       {
+           *op->resnull = op->d.constval.isnull;
+           *op->resvalue = op->d.constval.value;
+
+           EEO_DISPATCH(op);
+       }
+
+       /*
+        * Function-call implementations. Arguments have previously been
+        * evaluated directly into fcinfo->args.
+        *
+        * As both STRICT checks and function-usage are noticeable performance
+        * wise, and function calls are a very hot-path (they also back
+        * operators!), separate all of them into separate opcodes.
+        */
+       EEO_CASE(EEO_FUNCEXPR)
+       {
+           FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+           fcinfo->isnull = false;
+           *op->resvalue = (op->d.func.fn_addr) (fcinfo);
+           *op->resnull = fcinfo->isnull;
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_FUNCEXPR_STRICT)
+       {
+           FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+           int         argno;
+           bool       *argnull = fcinfo->argnull;
+
+           /* strict function, check for NULL args */
+           for (argno = 0; argno < op->d.func.nargs; argno++)
+           {
+               if (argnull[argno])
+               {
+                   *op->resnull = true;
+                   goto strictfail;
+               }
+           }
+           fcinfo->isnull = false;
+           *op->resvalue = (op->d.func.fn_addr) (fcinfo);
+           *op->resnull = fcinfo->isnull;
+
+   strictfail:
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_FUNCEXPR_FUSAGE)
+       {
+           PgStat_FunctionCallUsage fcusage;
+           FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+           pgstat_init_function_usage(fcinfo, &fcusage);
+
+           fcinfo->isnull = false;
+           *op->resvalue = (op->d.func.fn_addr) (fcinfo);
+           *op->resnull = fcinfo->isnull;
+
+           pgstat_end_function_usage(&fcusage, true);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE)
+       {
+           PgStat_FunctionCallUsage fcusage;
+           FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+           int         argno;
+           bool       *argnull = fcinfo->argnull;
+
+           /* strict function, check for NULL args */
+           for (argno = 0; argno < op->d.func.nargs; argno++)
+           {
+               if (argnull[argno])
+               {
+                   *op->resnull = true;
+                   goto strictfail_fusage;
+               }
+           }
+
+           pgstat_init_function_usage(fcinfo, &fcusage);
+
+           fcinfo->isnull = false;
+           *op->resvalue = (op->d.func.fn_addr) (fcinfo);
+           *op->resnull = fcinfo->isnull;
+   strictfail_fusage:
+           pgstat_end_function_usage(&fcusage, true);
+
+           EEO_DISPATCH(op);
+       }
+
+       /*
+        * If any of the clauses is FALSE, the AND result is FALSE regardless
+        * of the states of the rest of the clauses, so we can stop evaluating
+        * and return FALSE immediately.  If none are FALSE and one or more is
+        * NULL, we return NULL; otherwise we return TRUE.  This makes sense
+        * when you interpret NULL as "don't know", using the same sort of
+        * reasoning as for OR, above.
+        */
+       EEO_CASE(EEO_BOOL_AND_STEP_FIRST)
+       {
+           *op->d.boolexpr.anynull = false;
+
+           /*
+            * Fallthrough (can't be last - ANDs have two arguments at least).
+            */
+       }
+
+       EEO_CASE(EEO_BOOL_AND_STEP)
+       {
+           if (*op->d.boolexpr.isnull)
+           {
+               *op->d.boolexpr.anynull = true;
+           }
+           else if (!DatumGetBool(*op->d.boolexpr.value))
+           {
+               *op->resnull = false;
+               *op->resvalue = BoolGetDatum(false);
+               /* bail out early */
+               op = &state->steps[op->d.boolexpr.jumpdone];
+
+               EEO_DISPATCH_DIRECT(op);
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_BOOL_AND_STEP_LAST)
+       {
+           if (*op->d.boolexpr.isnull)
+           {
+               *op->resnull = true;
+               *op->resvalue = 0;
+           }
+           else if (!DatumGetBool(*op->d.boolexpr.value))
+           {
+               *op->resnull = false;
+               *op->resvalue = BoolGetDatum(false);
+
+               /*
+                * No point jumping early to jumpdone - would be same target
+                * (as this is the last argument to the AND expression),
+                * except more expensive.
+                */
+           }
+           else if (*op->d.boolexpr.anynull)
+           {
+               *op->resnull = true;
+               *op->resvalue = 0;
+           }
+           else
+           {
+               *op->resnull = false;
+               *op->resvalue = BoolGetDatum(true);
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       /*
+        * If any of the clauses is TRUE, the OR result is TRUE regardless of
+        * the states of the rest of the clauses, so we can stop evaluating
+        * and return TRUE immediately.  If none are TRUE and one or more is
+        * NULL, we return NULL; otherwise we return FALSE.  This makes sense
+        * when you interpret NULL as "don't know": if we have a TRUE then the
+        * OR is TRUE even if we aren't sure about some of the other inputs.
+        * If all the known inputs are FALSE, but we have one or more "don't
+        * knows", then we have to report that we "don't know" what the OR's
+        * result should be --- perhaps one of the "don't knows" would have
+        * been TRUE if we'd known its value.  Only when all the inputs are
+        * known to be FALSE can we state confidently that the OR's result is
+        * FALSE.
+        */
+       EEO_CASE(EEO_BOOL_OR_STEP_FIRST)
+       {
+           *op->d.boolexpr.anynull = false;
+
+           /*
+            * Fallthrough (can't be last - ORs have two arguments at least).
+            */
+       }
+
+       EEO_CASE(EEO_BOOL_OR_STEP)
+       {
+           if (*op->d.boolexpr.isnull)
+           {
+               *op->d.boolexpr.anynull = true;
+           }
+           else if (DatumGetBool(*op->d.boolexpr.value))
+           {
+               *op->resnull = false;
+               *op->resvalue = BoolGetDatum(true);
+
+               /* bail out early */
+               op = &state->steps[op->d.boolexpr.jumpdone];
+
+               EEO_DISPATCH_DIRECT(op);
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_BOOL_OR_STEP_LAST)
+       {
+           if (*op->d.boolexpr.isnull)
+           {
+               *op->resnull = true;
+               *op->resvalue = 0;
+           }
+           else if (DatumGetBool(*op->d.boolexpr.value))
+           {
+               *op->resnull = false;
+               *op->resvalue = BoolGetDatum(true);
+
+               /*
+                * No point jumping to jumpdone - would be same target (as
+                * this is the last argument to the AND expression), except
+                * more expensive.
+                */
+           }
+           else if (*op->d.boolexpr.anynull)
+           {
+               *op->resnull = true;
+               *op->resvalue = 0;
+           }
+           else
+           {
+               *op->resnull = false;
+               *op->resvalue = BoolGetDatum(false);
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_BOOL_NOT_STEP)
+       {
+           /*
+            * If the expression evaluates to null, then just return null
+            * back.
+            */
+           *op->resnull = *op->d.boolexpr.isnull;
+
+           /*
+            * evaluation of 'not' is simple.. expr is false, then return
+            * 'true' and vice versa.
+            */
+           *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_QUAL)
+       {
+           /* special case for ExecQual() */
+
+           if (*op->resnull ||
+               !DatumGetBool(*op->resvalue))
+           {
+               /* bail out early */
+               *op->resnull = false;
+               *op->resvalue = BoolGetDatum(false);
+               op = &state->steps[op->d.qualexpr.jumpdone];
+
+               EEO_DISPATCH_DIRECT(op);
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_NULLTEST_ISNULL)
+       {
+           *op->resvalue = BoolGetDatum(*op->resnull);
+           *op->resnull = false;
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_NULLTEST_ISNOTNULL)
+       {
+           *op->resvalue = BoolGetDatum(!*op->resnull);
+           *op->resnull = false;
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_NULLTEST_ROWISNULL)
+       {
+           /* out of line implementation: too large */
+           ExecEvalRowNull(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_NULLTEST_ROWISNOTNULL)
+       {
+           /* out of line implementation: too large */
+           ExecEvalRowNotNull(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_PARAM_EXEC)
+       {
+           /* out of line implementation: too large */
+           ExecEvalParamExec(state, op, econtext);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_PARAM_EXTERN)
+       {
+           /* out of line implementation: too large */
+           ExecEvalParamExtern(state, op, econtext);
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_CASE_WHEN_STEP)
+       {
+           if (!*op->d.casewhen.isnull
+               && DatumGetBool(*op->d.casewhen.value))
+           {
+               EEO_DISPATCH(op);
+           }
+           else
+           {
+               op = &state->steps[op->d.casewhen.jumpfalse];
+
+               EEO_DISPATCH_DIRECT(op);
+           }
+       }
+
+       EEO_CASE(EEO_CASE_THEN_STEP)
+       {
+           /* results already placed in correct place during preceding steps */
+           op = &state->steps[op->d.casethen.jumpdone];
+
+           EEO_DISPATCH_DIRECT(op);
+       }
+
+       EEO_CASE(EEO_CASE_TESTVAL)
+       {
+           /*
+            * Normally upper parts in the expression tree have setup the
+            * values to be returned here, but some parts of the system
+            * currently misuse {caseValue,domainValue}_{datum,isNull} to set
+            * run-time data.  So if no values have been set-up, use
+            * ExprContext's.  This isn't pretty, but also not *that* ugly,
+            * and this is unlikely to be performance sensitive enoiugh to
+            * worry about a branch.
+            */
+           if (op->d.casetest.value)
+           {
+               *op->resvalue = *op->d.casetest.value;
+               *op->resnull = *op->d.casetest.isnull;
+           }
+           else
+           {
+               *op->resvalue = econtext->caseValue_datum;
+               *op->resnull = econtext->caseValue_isNull;
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_CASE_TESTVAL_UNEXPAND)
+       {
+           /*
+            * See EEO_CASE_TESTVAL comment.
+            */
+           if (op->d.casetest.value)
+           {
+               *op->resvalue = *op->d.casetest.value;
+               *op->resnull = *op->d.casetest.isnull;
+           }
+           else
+           {
+               *op->resvalue = econtext->caseValue_datum;
+               *op->resnull = econtext->caseValue_isNull;
+           }
+
+           /* Since caseValue_datum may be read multiple times, force to R/O */
+           if (!*op->resnull)
+           {
+               *op->resvalue =
+                   MakeExpandedObjectReadOnlyInternal(*op->resvalue);
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_DOMAIN_TESTVAL)
+       {
+           /*
+            * See EEO_CASE_TESTVAL comment.
+            */
+           if (op->d.casetest.value)
+           {
+               *op->resvalue = *op->d.casetest.value;
+               *op->resnull = *op->d.casetest.isnull;
+           }
+           else
+           {
+               *op->resvalue = econtext->domainValue_datum;
+               *op->resnull = econtext->domainValue_isNull;
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_DOMAIN_TESTVAL_UNEXPAND)
+       {
+           /*
+            * See EEO_CASE_TESTVAL comment.
+            */
+           if (op->d.casetest.value)
+           {
+               *op->resvalue = *op->d.casetest.value;
+               *op->resnull = *op->d.casetest.isnull;
+           }
+           else
+           {
+               *op->resvalue = econtext->domainValue_datum;
+               *op->resnull = econtext->domainValue_isNull;
+           }
+
+           /*
+            * Since domainValue_datum may be read multiple times, force to
+            * R/O
+            */
+           if (!*op->resnull)
+           {
+               *op->resvalue =
+                   MakeExpandedObjectReadOnlyInternal(*op->resvalue);
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       /*
+        * Evaluate a single argument to COALESCE and jump-out if a NOT NULL
+        * one has been found.
+        */
+       EEO_CASE(EEO_COALESCE)
+       {
+           if (!*op->resnull)
+           {
+               op = &state->steps[op->d.coalesce.jumpdone];
+
+               EEO_DISPATCH_DIRECT(op);
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       /* BooleanTest implementations for all booltesttypes */
+       EEO_CASE(EEO_BOOLTEST_IS_TRUE)
+       {
+           if (*op->resnull)
+               *op->resvalue = false;
+           else
+               *op->resvalue = *op->resvalue;
+           *op->resnull = false;
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_BOOLTEST_IS_NOT_FALSE)
+       {
+           if (*op->resnull)
+               *op->resvalue = true;
+           else
+               *op->resvalue = *op->resvalue;
+           *op->resnull = false;
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_BOOLTEST_IS_FALSE)
+       {
+           if (*op->resnull)
+               *op->resvalue = false;
+           else
+               *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+           *op->resnull = false;
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_BOOLTEST_IS_NOT_TRUE)
+       {
+           if (*op->resnull)
+               *op->resvalue = true;
+           else
+               *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+           *op->resnull = false;
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_BOOLTEST_IS_UNKNOWN)
+       {
+           *op->resvalue = BoolGetDatum(*op->resnull);
+           *op->resnull = false;
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_BOOLTEST_IS_NOT_UNKNOWN)
+       {
+           *op->resvalue = BoolGetDatum(!*op->resnull);
+           *op->resnull = false;
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_WHOLEROW)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalWholeRowVar(state, op, econtext);
+
+           EEO_DISPATCH(op);
+       }
+
+       /*
+        * Evaluate a CoerceViaIO node.  This can be quite a hot path, so
+        * inline as much work as possible.
+        */
+       EEO_CASE(EEO_IOCOERCE)
+       {
+           char       *str;
+
+           /* call output function (similar to OutputFunctionCall) */
+           if (*op->resnull)
+           {
+               /* output functions are not called on nulls */
+               str = NULL;
+           }
+           else
+           {
+               FunctionCallInfo fcinfo_out;
+
+               fcinfo_out = op->d.iocoerce.fcinfo_data_out;
+               fcinfo_out->arg[0] = *op->resvalue;
+               fcinfo_out->argnull[0] = false;
+
+               str = DatumGetPointer(FunctionCallInvoke(fcinfo_out));
+               Assert(!fcinfo_out->isnull);
+           }
+
+           /* call input function (similar to InputFunctionCall) */
+           if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL)
+           {
+               FunctionCallInfo fcinfo_in;
+
+               fcinfo_in = op->d.iocoerce.fcinfo_data_in;
+               fcinfo_in->arg[0] = PointerGetDatum(str);
+               fcinfo_in->argnull[0] = BoolGetDatum(*op->resnull);
+               fcinfo_in->arg[1] = op->d.iocoerce.intypioparam;
+               fcinfo_in->argnull[1] = false;
+               fcinfo_in->arg[2] = -1;
+               fcinfo_in->argnull[2] = false;
+
+               fcinfo_in->isnull = false;
+               *op->resvalue = FunctionCallInvoke(fcinfo_in);
+
+               /* Should get null result if and only if str is NULL */
+               if (str == NULL)
+               {
+                   Assert(*op->resnull);
+                   Assert(fcinfo_in->isnull);
+               }
+               else
+               {
+                   Assert(!*op->resnull);
+                   Assert(!fcinfo_in->isnull);
+               }
+
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       /*
+        * IS DISTINCT FROM must evaluate arguments (already done into
+        * fcinfo->arg/argnull) to determine whether they are NULL; if either
+        * is NULL then the result is already known. If neither is NULL, then
+        * proceed to evaluate the comparison function. Note that this is
+        * *always* derived from the equals operator, but since we need
+        * special processing of the arguments we can not simply reuse
+        * ExecEvalOper() or ExecEvalFunc().
+        */
+       EEO_CASE(EEO_DISTINCT)
+       {
+           FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+           /* check function arguments for NULLness */
+           if (fcinfo->argnull[0] && fcinfo->argnull[1])
+           {
+               /* Both NULL? Then is not distinct... */
+               *op->resnull = false;
+               *op->resvalue = BoolGetDatum(false);
+           }
+           else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+           {
+               /* Only one is NULL? Then is distinct... */
+               *op->resnull = false;
+               *op->resvalue = BoolGetDatum(true);
+           }
+           else
+           {
+               fcinfo->isnull = false;
+               *op->resvalue = (op->d.func.fn_addr) (fcinfo);
+               /* Must invert result of "=" */
+               *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+               *op->resnull = fcinfo->isnull;
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_NULLIF)
+       {
+           FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+           /* if either argument is NULL they can't be equal */
+           if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
+           {
+               bool        result;
+
+               fcinfo->isnull = false;
+               result = (op->d.func.fn_addr) (fcinfo);
+
+               /* if the arguments are equal return null */
+               if (!fcinfo->isnull && DatumGetBool(result))
+               {
+                   *op->resnull = true;
+                   *op->resvalue = true;
+
+                   EEO_DISPATCH(op);
+               }
+           }
+           *op->resnull = fcinfo->argnull[0];
+           *op->resvalue = fcinfo->arg[0];
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_SQLVALUEFUNCTION)
+       {
+           /*
+            * Doesn't seem worthwhile to have an inline implementation
+            * efficency-wise.
+            */
+           ExecEvalSQLValueFunction(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_CURRENTOFEXPR)
+       {
+           /* error invocation uses space, and shouldn't ever occur */
+           ExecEvalCurrentOfExpr(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_ARRAYEXPR)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalArrayExpr(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_ARRAYCOERCE)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalArrayCoerce(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_ROW)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalRow(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_ROWCOMPARE_STEP)
+       {
+           FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data;
+
+           /* force NULL result */
+           if (op->d.rowcompare_step.finfo->fn_strict &&
+               (fcinfo->argnull[0] || fcinfo->argnull[1]))
+           {
+               *op->resnull = true;
+               op = &state->steps[op->d.rowcompare_step.jumpnull];
+
+               EEO_DISPATCH_DIRECT(op);
+           }
+
+           fcinfo->isnull = false;
+           *op->resvalue = (op->d.rowcompare_step.fn_addr) (fcinfo);
+
+           /* force NULL result */
+           if (fcinfo->isnull)
+           {
+               *op->resnull = true;
+               op = &state->steps[op->d.rowcompare_step.jumpnull];
+
+               EEO_DISPATCH_DIRECT(op);
+           }
+
+           /* no need to compare remaining columns */
+           if (DatumGetInt32(*op->resvalue) != 0)
+           {
+               op = &state->steps[op->d.rowcompare_step.jumpdone];
+
+               EEO_DISPATCH_DIRECT(op);
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_ROWCOMPARE_FINAL)
+       {
+           int32       cmpresult = DatumGetInt32(*op->resvalue);
+           RowCompareType rctype = op->d.rowcompare_final.rctype;
+
+           *op->resnull = false;
+           switch (rctype)
+           {
+                   /* EQ and NE cases aren't allowed here */
+               case ROWCOMPARE_LT:
+                   *op->resvalue = BoolGetDatum(cmpresult < 0);
+                   break;
+               case ROWCOMPARE_LE:
+                   *op->resvalue = BoolGetDatum(cmpresult <= 0);
+                   break;
+               case ROWCOMPARE_GE:
+                   *op->resvalue = BoolGetDatum(cmpresult >= 0);
+                   break;
+               case ROWCOMPARE_GT:
+                   *op->resvalue = BoolGetDatum(cmpresult > 0);
+                   break;
+               default:
+                   Assert(false);
+                   break;
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_MINMAX)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalMinMax(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_FIELDSELECT)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalFieldSelect(state, op, econtext);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_FIELDSTORE_DEFORM)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalFieldStoreDeForm(state, op, econtext);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_FIELDSTORE_FORM)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalFieldStoreForm(state, op, econtext);
+
+           EEO_DISPATCH(op);
+       }
+
+       /*
+        * Verify input for an ArrayRef reference (not assignment).  If the
+        * array is null, return the entire result is NULL.
+        */
+       EEO_CASE(EEO_ARRAYREF_CHECKINPUT)
+       {
+           Assert(!op->d.arrayref.state->isassignment);
+
+           /*
+            * If refexpr yields NULL, and it's a fetch, then result is NULL.
+            */
+           if (*op->resnull &&
+               !op->d.arrayref.state->isassignment)
+           {
+               op = &state->steps[op->d.arrayref.jumpdone];
+
+               EEO_DISPATCH_DIRECT(op);
+           }
+
+           EEO_DISPATCH(op);
+       }
+
+       /*
+        * Check whether a subscript is NULL and handle that.
+        */
+       EEO_CASE(EEO_ARRAYREF_CHECKSUBSCRIPT)
+       {
+           /* too complex for an inline implementation */
+           if (ExecEvalArrayRefCheckSubscript(state, op))
+           {
+               op++;
+           }
+           else
+           {
+               op = &state->steps[op->d.arrayref_checksubscript.jumpdone];
+           }
+
+           EEO_DISPATCH_DIRECT(op);
+       }
+
+       /*
+        * Fetch the old value in an arrayref, if referenced (via a
+        * CaseTestExpr) inside the assignment expression.
+        */
+       EEO_CASE(EEO_ARRAYREF_OLD)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalArrayRefOld(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       /*
+        * Perform ArrayRef assignment
+        */
+       EEO_CASE(EEO_ARRAYREF_ASSIGN)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalArrayRefAssign(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       /*
+        * Fetch subset of an array.
+        */
+       EEO_CASE(EEO_ARRAYREF_FETCH)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalArrayRefFetch(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_CONVERT_ROWTYPE)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalConvertRowtype(state, op, econtext);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_SCALARARRAYOP)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalScalarArrayOp(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_DOMAIN_NOTNULL)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalConstraintNotNull(state, op);
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_DOMAIN_CHECK)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalConstraintCheck(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_XMLEXPR)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalXmlExpr(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       /*
+        * Returns a Datum whose value is the value of the precomputed
+        * aggregate found in the given expression context.
+        */
+       EEO_CASE(EEO_AGGREF)
+       {
+           AggrefExprState *aggref = op->d.aggref.astate;
+
+           Assert(econtext->ecxt_aggvalues != NULL);
+
+           *op->resnull = econtext->ecxt_aggnulls[aggref->aggno];
+           *op->resvalue = econtext->ecxt_aggvalues[aggref->aggno];
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_GROUPING_FUNC)
+       {
+           /* too complex/uncommon for an inline implementation */
+           ExecEvalGroupingFunc(state, op);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_WINDOW_FUNC)
+       {
+           WindowFuncExprState *wfunc = op->d.window_func.wfstate;
+
+           Assert(econtext->ecxt_aggvalues != NULL);
+
+           *op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno];
+           *op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno];
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_SUBPLAN)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalSubPlan(state, op, econtext);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_ALTERNATIVE_SUBPLAN)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalAlternativeSubPlan(state, op, econtext);
+
+           EEO_DISPATCH(op);
+       }
+
+       EEO_CASE(EEO_LAST)
+       {
+           /* unreachable */
+           Assert(false);
+           goto out;
+       }
+   }
+out:
+   *isnull = state->resnull;
+   return state->resvalue;
+}
+
+static Datum
+ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+   ExprEvalStep *op = &state->steps[1];
+   int         attnum = op->d.var.attnum + 1;
+   TupleTableSlot *slot = econtext->ecxt_outertuple;
+
+   return slot_getattr(slot, attnum, isnull);
+}
+
+static Datum
+ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+   ExprEvalStep *op = &state->steps[1];
+   int         attnum = op->d.var.attnum + 1;
+   TupleTableSlot *slot = econtext->ecxt_innertuple;
+
+   return slot_getattr(slot, attnum, isnull);
+}
+
+static Datum
+ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+   ExprEvalStep *op = &state->steps[1];
+   int         attnum = op->d.var.attnum + 1;
+   TupleTableSlot *slot = econtext->ecxt_scantuple;
+
+   return slot_getattr(slot, attnum, isnull);
+}
+
+static Datum
+ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+   ExprEvalStep *op = &state->steps[0];
+
+   *isnull = op->d.constval.isnull;
+   return op->d.constval.value;
+}
+
+static Datum
+ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+   ExprEvalStep *op = &state->steps[1];
+   int         attnum = op->d.assign_var.attnum + 1;
+   size_t      resultnum = op->d.assign_var.resultnum;
+   TupleTableSlot *inslot = econtext->ecxt_outertuple;
+   TupleTableSlot *outslot = state->resultslot;
+
+   outslot->tts_values[resultnum] =
+       slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+   return 0;
+}
+
+static Datum
+ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+   ExprEvalStep *op = &state->steps[1];
+   int         attnum = op->d.assign_var.attnum + 1;
+   size_t      resultnum = op->d.assign_var.resultnum;
+   TupleTableSlot *inslot = econtext->ecxt_innertuple;
+   TupleTableSlot *outslot = state->resultslot;
+
+   outslot->tts_values[resultnum] =
+       slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+   return 0;
+}
+
+static Datum
+ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+   ExprEvalStep *op = &state->steps[1];
+   int         attnum = op->d.assign_var.attnum + 1;
+   size_t      resultnum = op->d.assign_var.resultnum;
+   TupleTableSlot *inslot = econtext->ecxt_scantuple;
+   TupleTableSlot *outslot = state->resultslot;
+
+   outslot->tts_values[resultnum] =
+       slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+   return 0;
+}
+
+static void
+ExecPrepareInterp(void)
+{
+   static bool prepared = false;
+
+   if (prepared)
+       return;
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+   if (dispatch_table == NULL)
+       dispatch_table = (void **) DatumGetPointer(ExecInterpExpr(NULL, NULL, NULL));
+#endif
+
+   prepared = true;
+}
+
+ExprEvalOp
+ExecEvalStepOp(ExprState *state, ExprEvalStep *op)
+{
+#if defined(EEO_USE_COMPUTED_GOTO)
+   if (state->flags & EEO_FLAG_JUMP_THREADED)
+   {
+       int         i;
+
+       for (i = 0; i < EEO_LAST; i++)
+       {
+           if ((void *) op->opcode == dispatch_table[i])
+           {
+               return (ExprEvalOp) i;
+           }
+       }
+       elog(ERROR, "unknown opcode");
+   }
+#endif
+   return (ExprEvalOp) op->opcode;
+}
+
+/*
+ * Computes the value for a PARAM_EXEC parameter.
+ */
+void
+ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+   ParamExecData *prm;
+
+   prm = &(econtext->ecxt_param_exec_vals[op->d.param.paramid]);
+   if (unlikely(prm->execPlan != NULL))
+   {
+       /*
+        * XXX: Worthwhile to extract this into a separate opcode?
+        */
+       ExecSetParamPlan(prm->execPlan, econtext);
+       /* ExecSetParamPlan should have processed this param... */
+       Assert(prm->execPlan == NULL);
+   }
+   *op->resvalue = prm->value;
+   *op->resnull = prm->isnull;
+}
+
+/*
+ * Computes the value for a PARAM_EXTERN parameter.
+ */
+void
+ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+   ParamListInfo paramInfo = econtext->ecxt_param_list_info;
+   int         paramId = op->d.param.paramid;
+
+   if (likely(paramInfo &&
+              paramId > 0 && paramId <= paramInfo->numParams))
+   {
+       ParamExternData *prm = &paramInfo->params[paramId - 1];
+
+       /* give hook a chance in case parameter is dynamic */
+       /* XXX: separate opcode */
+       if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
+           (*paramInfo->paramFetch) (paramInfo, paramId);
+
+       if (likely(OidIsValid(prm->ptype)))
+       {
+           /* safety check in case hook did something unexpected */
+           /* XXX: assert instead? */
+           if (unlikely(prm->ptype != op->d.param.paramtype))
+               ereport(ERROR,
+                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                        errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
+                               paramId,
+                               format_type_be(prm->ptype),
+                               format_type_be(op->d.param.paramtype))));
+           *op->resvalue = prm->value;
+           *op->resnull = prm->isnull;
+           return;
+       }
+   }
+
+   ereport(ERROR,
+           (errcode(ERRCODE_UNDEFINED_OBJECT),
+            errmsg("no value found for parameter %d", paramId)));
+}
+
+/*
+ * Evaluate SQLValueFunction expressions.
+ */
+void
+ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
+{
+   FunctionCallInfoData fcinfo;
+   SQLValueFunction *svf = op->d.sqlvaluefunction.svf;
+
+   *op->resnull = false;
+
+   /*
+    * Note: current_schema() can return NULL.  current_user() etc currently
+    * cannot, but might as well code those cases the same way for safety.
+    */
+   switch (svf->op)
+   {
+       case SVFOP_CURRENT_DATE:
+           *op->resvalue = DateADTGetDatum(GetSQLCurrentDate());
+           break;
+       case SVFOP_CURRENT_TIME:
+       case SVFOP_CURRENT_TIME_N:
+           *op->resvalue = TimeTzADTPGetDatum(GetSQLCurrentTime(svf->typmod));
+           break;
+       case SVFOP_CURRENT_TIMESTAMP:
+       case SVFOP_CURRENT_TIMESTAMP_N:
+           *op->resvalue = TimestampTzGetDatum(GetSQLCurrentTimestamp(svf->typmod));
+           break;
+       case SVFOP_LOCALTIME:
+       case SVFOP_LOCALTIME_N:
+           *op->resvalue = TimeADTGetDatum(GetSQLLocalTime(svf->typmod));
+           break;
+       case SVFOP_LOCALTIMESTAMP:
+       case SVFOP_LOCALTIMESTAMP_N:
+           *op->resvalue = TimestampGetDatum(GetSQLLocalTimestamp(svf->typmod));
+           break;
+       case SVFOP_CURRENT_ROLE:
+       case SVFOP_CURRENT_USER:
+       case SVFOP_USER:
+           InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+           *op->resvalue = current_user(&fcinfo);
+           *op->resnull = fcinfo.isnull;
+           break;
+       case SVFOP_SESSION_USER:
+           InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+           *op->resvalue = session_user(&fcinfo);
+           *op->resnull = fcinfo.isnull;
+           break;
+       case SVFOP_CURRENT_CATALOG:
+           InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+           *op->resvalue = current_database(&fcinfo);
+           *op->resnull = fcinfo.isnull;
+           break;
+       case SVFOP_CURRENT_SCHEMA:
+           InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+           *op->resvalue = current_schema(&fcinfo);
+           *op->resnull = fcinfo.isnull;
+           break;
+   }
+}
+
+void
+ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op)
+{
+   /*
+    * The planner should convert CURRENT OF into a TidScan qualification, or
+    * some other special handling in a ForeignScan node.  So we have to be
+    * able to do ExecInitExpr on a CurrentOfExpr, but we shouldn't ever
+    * actually execute it.  If we get here, we suppose we must be dealing
+    * with CURRENT OF on a foreign table whose FDW doesn't handle it, and
+    * complain accordingly.
+    */
+   ereport(ERROR,
+           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+          errmsg("WHERE CURRENT OF is not supported for this table type")));
+}
+
+/*
+ * Evaluate NullTest / IS NULL for rows.
+ */
+void
+ExecEvalRowNull(ExprState *state, ExprEvalStep *op)
+{
+   ExecEvalRowNullInt(state, op, true);
+}
+
+/*
+ * Evaluate NullTest / IS NOT NULL for rows.
+ */
+void
+ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op)
+{
+   ExecEvalRowNullInt(state, op, false);
+}
+
+static void
+ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, bool checkisnull)
+{
+   Datum       value = *op->resvalue;
+   bool        isnull = *op->resnull;
+   HeapTupleHeader tuple;
+   Oid         tupType;
+   int32       tupTypmod;
+   TupleDesc   tupDesc;
+   HeapTupleData tmptup;
+   int         att;
+
+   *op->resnull = false;
+
+   /* NULL row variables are treated just as NULL scalar columns */
+   if (isnull && checkisnull)
+   {
+       *op->resvalue = BoolGetDatum(true);
+       return;
+   }
+   else if (isnull)
+   {
+       *op->resvalue = BoolGetDatum(false);
+       return;
+   }
+
+   /*
+    * The SQL standard defines IS [NOT] NULL for a non-null rowtype argument
+    * as:
+    *
+    * "R IS NULL" is true if every field is the null value.
+    *
+    * "R IS NOT NULL" is true if no field is the null value.
+    *
+    * This definition is (apparently intentionally) not recursive; so our
+    * tests on the fields are primitive attisnull tests, not recursive checks
+    * to see if they are all-nulls or no-nulls rowtypes.
+    *
+    * The standard does not consider the possibility of zero-field rows, but
+    * here we consider them to vacuously satisfy both predicates.
+    */
+
+   tuple = DatumGetHeapTupleHeader(value);
+
+   tupType = HeapTupleHeaderGetTypeId(tuple);
+   tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+
+   /* Lookup tupdesc if first time through or if type changes */
+   tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+
+   /*
+    * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
+    */
+   tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+   tmptup.t_data = tuple;
+
+   for (att = 1; att <= tupDesc->natts; att++)
+   {
+       /* ignore dropped columns */
+       if (tupDesc->attrs[att - 1]->attisdropped)
+           continue;
+       if (heap_attisnull(&tmptup, att))
+       {
+           /* null field disproves IS NOT NULL */
+           if (!checkisnull)
+           {
+               ReleaseTupleDesc(tupDesc);
+               *op->resvalue = BoolGetDatum(false);
+               return;
+           }
+       }
+       else
+       {
+           /* non-null field disproves IS NULL */
+           if (checkisnull)
+           {
+               ReleaseTupleDesc(tupDesc);
+               *op->resvalue = BoolGetDatum(false);
+               return;
+           }
+       }
+   }
+
+   ReleaseTupleDesc(tupDesc);
+   *op->resvalue = BoolGetDatum(true);
+}
+
+
+/*
+ * Evaluate ARRAY[] expressions.
+ */
+void
+ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
+{
+   ArrayType  *result;
+   ArrayExpr  *arrayExpr = op->d.arrayexpr.arrayexpr;
+   Oid         element_type = arrayExpr->element_typeid;
+   int         ndims = 0;
+   int         dims[MAXDIM];
+   int         lbs[MAXDIM];
+   int         nelems = op->d.arrayexpr.nelems;
+
+   /* Set non-null as default */
+   *op->resnull = false;
+
+   if (!arrayExpr->multidims)
+   {
+       /* Elements are presumably of scalar type */
+       Datum      *dvalues = op->d.arrayexpr.elemvalues;
+       bool       *dnulls = op->d.arrayexpr.elemnulls;
+
+       ndims = 1;
+
+       /* Shouldn't happen here, but if length is 0, return empty array */
+       if (nelems == 0)
+       {
+           *op->resvalue = PointerGetDatum(construct_empty_array(element_type));
+           return;
+       }
+
+       /* setup for 1-D array of the given length */
+       dims[0] = nelems;
+       lbs[0] = 1;
+
+       result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
+                                   element_type,
+                                   op->d.arrayexpr.elemlength,
+                                   op->d.arrayexpr.elembyval,
+                                   op->d.arrayexpr.elemalign);
+   }
+   else
+   {
+       /* Must be nested array expressions */
+       int         nbytes = 0;
+       int         nitems = 0;
+       int         outer_nelems = 0;
+       int         elem_ndims = 0;
+       int        *elem_dims = NULL;
+       int        *elem_lbs = NULL;
+       bool        firstone = true;
+       bool        havenulls = false;
+       bool        haveempty = false;
+       char      **subdata;
+       bits8     **subbitmaps;
+       int        *subbytes;
+       int        *subnitems;
+       int32       dataoffset;
+       char       *dat;
+       int         iitem;
+       int         elemoff;
+       int         i;
+
+       subdata = (char **) palloc(nelems * sizeof(char *));
+       subbitmaps = (bits8 **) palloc(nelems * sizeof(bits8 *));
+       subbytes = (int *) palloc(nelems * sizeof(int));
+       subnitems = (int *) palloc(nelems * sizeof(int));
+
+       /* loop through and get data area from each element */
+       for (elemoff = 0; elemoff < nelems; elemoff++)
+       {
+           bool        eisnull;
+           Datum       arraydatum;
+           ArrayType  *array;
+           int         this_ndims;
+
+           arraydatum = op->d.arrayexpr.elemvalues[elemoff];
+           eisnull = op->d.arrayexpr.elemnulls[elemoff];
+
+           /* temporarily ignore null subarrays */
+           if (eisnull)
+           {
+               haveempty = true;
+               continue;
+           }
+
+           array = DatumGetArrayTypeP(arraydatum);
+
+           /* run-time double-check on element type */
+           if (element_type != ARR_ELEMTYPE(array))
+               ereport(ERROR,
+                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                        errmsg("cannot merge incompatible arrays"),
+                        errdetail("Array with element type %s cannot be "
+                        "included in ARRAY construct with element type %s.",
+                                  format_type_be(ARR_ELEMTYPE(array)),
+                                  format_type_be(element_type))));
+
+           this_ndims = ARR_NDIM(array);
+           /* temporarily ignore zero-dimensional subarrays */
+           if (this_ndims <= 0)
+           {
+               haveempty = true;
+               continue;
+           }
+
+           if (firstone)
+           {
+               /* Get sub-array details from first member */
+               elem_ndims = this_ndims;
+               ndims = elem_ndims + 1;
+               if (ndims <= 0 || ndims > MAXDIM)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                         errmsg("number of array dimensions (%d) exceeds " \
+                                "the maximum allowed (%d)", ndims, MAXDIM)));
+
+               elem_dims = (int *) palloc(elem_ndims * sizeof(int));
+               memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int));
+               elem_lbs = (int *) palloc(elem_ndims * sizeof(int));
+               memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int));
+
+               firstone = false;
+           }
+           else
+           {
+               /* Check other sub-arrays are compatible */
+               if (elem_ndims != this_ndims ||
+                   memcmp(elem_dims, ARR_DIMS(array),
+                          elem_ndims * sizeof(int)) != 0 ||
+                   memcmp(elem_lbs, ARR_LBOUND(array),
+                          elem_ndims * sizeof(int)) != 0)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+                            errmsg("multidimensional arrays must have array "
+                                   "expressions with matching dimensions")));
+           }
+
+           subdata[outer_nelems] = ARR_DATA_PTR(array);
+           subbitmaps[outer_nelems] = ARR_NULLBITMAP(array);
+           subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array);
+           nbytes += subbytes[outer_nelems];
+           subnitems[outer_nelems] = ArrayGetNItems(this_ndims,
+                                                    ARR_DIMS(array));
+           nitems += subnitems[outer_nelems];
+           havenulls |= ARR_HASNULL(array);
+           outer_nelems++;
+       }
+
+       /*
+        * If all items were null or empty arrays, return an empty array;
+        * otherwise, if some were and some weren't, raise error.  (Note: we
+        * must special-case this somehow to avoid trying to generate a 1-D
+        * array formed from empty arrays.  It's not ideal...)
+        */
+       if (haveempty)
+       {
+           if (ndims == 0)     /* didn't find any nonempty array */
+           {
+               *op->resvalue = PointerGetDatum(construct_empty_array(element_type));
+               return;
+           }
+           ereport(ERROR,
+                   (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+                    errmsg("multidimensional arrays must have array "
+                           "expressions with matching dimensions")));
+       }
+
+       /* setup for multi-D array */
+       dims[0] = outer_nelems;
+       lbs[0] = 1;
+       for (i = 1; i < ndims; i++)
+       {
+           dims[i] = elem_dims[i - 1];
+           lbs[i] = elem_lbs[i - 1];
+       }
+
+       if (havenulls)
+       {
+           dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
+           nbytes += dataoffset;
+       }
+       else
+       {
+           dataoffset = 0;     /* marker for no null bitmap */
+           nbytes += ARR_OVERHEAD_NONULLS(ndims);
+       }
+
+       result = (ArrayType *) palloc(nbytes);
+       SET_VARSIZE(result, nbytes);
+       result->ndim = ndims;
+       result->dataoffset = dataoffset;
+       result->elemtype = element_type;
+       memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
+       memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
+
+       dat = ARR_DATA_PTR(result);
+       iitem = 0;
+       for (i = 0; i < outer_nelems; i++)
+       {
+           memcpy(dat, subdata[i], subbytes[i]);
+           dat += subbytes[i];
+           if (havenulls)
+               array_bitmap_copy(ARR_NULLBITMAP(result), iitem,
+                                 subbitmaps[i], 0,
+                                 subnitems[i]);
+           iitem += subnitems[i];
+       }
+   }
+
+   *op->resvalue = PointerGetDatum(result);
+}
+
+
+
+/*
+ * Evaluate an ArrayCoerceExpr expression.
+ */
+void
+ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
+{
+   ArrayCoerceExpr *acoerce = op->d.arraycoerce.coerceexpr;
+   Datum       result;
+   FunctionCallInfoData locfcinfo;
+
+   /* input is in *step->resvalue/resnull */
+   result = *op->resvalue;
+   if (*op->resnull)
+   {
+       return;                 /* nothing to do */
+   }
+
+   /*
+    * If it's binary-compatible, modify the element type in the array header,
+    * but otherwise leave the array as we received it.
+    */
+   if (!OidIsValid(acoerce->elemfuncid))
+   {
+       /* Detoast input array if necessary, and copy in any case */
+       ArrayType  *array = DatumGetArrayTypePCopy(result);
+
+       ARR_ELEMTYPE(array) = op->d.arraycoerce.resultelemtype;
+       *op->resvalue = PointerGetDatum(array);
+       return;
+   }
+
+   /* Initialize function cache if first time through */
+   if (op->d.arraycoerce.elemfunc->fn_oid == InvalidOid)
+   {
+       AclResult   aclresult;
+
+       /* Check permission to call function */
+       aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(),
+                                    ACL_EXECUTE);
+       if (aclresult != ACLCHECK_OK)
+           aclcheck_error(aclresult, ACL_KIND_PROC,
+                          get_func_name(acoerce->elemfuncid));
+       InvokeFunctionExecuteHook(acoerce->elemfuncid);
+
+       /* Set up the primary fmgr lookup information */
+       fmgr_info(acoerce->elemfuncid, op->d.arraycoerce.elemfunc);
+       fmgr_info_set_expr((Node *) acoerce, op->d.arraycoerce.elemfunc);
+   }
+
+   /*
+    * Use array_map to apply the function to each array element.
+    *
+    * We pass on the desttypmod and isExplicit flags whether or not the
+    * function wants them.
+    *
+    * Note: coercion functions are assumed to not use collation.
+    */
+   InitFunctionCallInfoData(locfcinfo, op->d.arraycoerce.elemfunc, 3,
+                            InvalidOid, NULL, NULL);
+   locfcinfo.arg[0] = result;
+   locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
+   locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
+   locfcinfo.argnull[0] = false;
+   locfcinfo.argnull[1] = false;
+   locfcinfo.argnull[2] = false;
+
+   *op->resvalue = array_map(&locfcinfo, op->d.arraycoerce.resultelemtype,
+                             op->d.arraycoerce.amstate);
+}
+
+/*
+ * Evaluate ROW() expressions.
+ *
+ * The individual columns have already been evaluated into
+ * op->d.row.elemvalues/nulls.
+ */
+void
+ExecEvalRow(ExprState *state, ExprEvalStep *op)
+{
+   HeapTuple   tuple;
+
+   /* Set non-null as default */
+   *op->resnull = false;
+
+   /* build tuple from evaluated field values */
+   tuple = heap_form_tuple(op->d.row.tupdesc,
+                           op->d.row.elemvalues,
+                           op->d.row.elemnulls);
+
+   *op->resvalue = HeapTupleGetDatum(tuple);
+}
+
+/*
+ * Evaluate GREATEST()/LEAST() type expression (note this is *not* MIN()/
+ * MAX()).
+ *
+ * All of the to-be-compared expressions have already been evaluated into
+ * ->op->d.minmax.values/nulls.
+ */
+void
+ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
+{
+   int         off;
+   Datum      *values = op->d.minmax.values;
+   bool       *nulls = op->d.minmax.nulls;
+   FunctionCallInfo fcinfo = op->d.minmax.fcinfo_data;
+   MinMaxOp    operator = op->d.minmax.op;
+
+   /* set at initialization */
+   Assert(fcinfo->argnull[0] == false);
+   Assert(fcinfo->argnull[1] == false);
+
+   *op->resnull = true;
+
+   for (off = 0; off < op->d.minmax.nelems; off++)
+   {
+       /* ignore NULL inputs */
+       if (nulls[off])
+           continue;
+
+       if (*op->resnull)
+       {
+           /* first nonnull input, adopt value */
+           *op->resvalue = values[off];
+           *op->resnull = false;
+       }
+       else
+       {
+           int         cmpresult;
+
+           /* apply comparison function */
+           fcinfo->isnull = false;
+           fcinfo->arg[0] = *op->resvalue;
+           fcinfo->arg[1] = values[off];
+           Assert(!fcinfo->isnull);
+
+           cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo));
+
+           if (cmpresult > 0 && operator == IS_LEAST)
+               *op->resvalue = values[off];
+           else if (cmpresult < 0 && operator == IS_GREATEST)
+               *op->resvalue = values[off];
+       }
+   }
+}
+
+/*
+ * Evaluate a FieldSelect node.
+ */
+void
+ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+   AttrNumber  fieldnum = op->d.fieldselect.fieldnum;
+   Datum       tupDatum;
+   HeapTupleHeader tuple;
+   Oid         tupType;
+   int32       tupTypmod;
+   TupleDesc   tupDesc;
+   Form_pg_attribute attr;
+   HeapTupleData tmptup;
+
+   tupDatum = *op->resvalue;
+
+   /* this test covers the isDone exception too: */
+   if (*op->resnull)
+       return;
+
+   tuple = DatumGetHeapTupleHeader(tupDatum);
+
+   tupType = HeapTupleHeaderGetTypeId(tuple);
+   tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+
+   /* Lookup tupdesc if first time through or if type changes */
+   tupDesc = get_cached_rowtype(tupType, tupTypmod,
+                                &op->d.fieldselect.argdesc,
+                                econtext);
+
+   /*
+    * Find field's attr record.  Note we don't support system columns here: a
+    * datum tuple doesn't have valid values for most of the interesting
+    * system columns anyway.
+    */
+   if (fieldnum <= 0)          /* should never happen */
+       elog(ERROR, "unsupported reference to system column %d in FieldSelect",
+            fieldnum);
+   if (fieldnum > tupDesc->natts)      /* should never happen */
+       elog(ERROR, "attribute number %d exceeds number of columns %d",
+            fieldnum, tupDesc->natts);
+   attr = tupDesc->attrs[fieldnum - 1];
+
+   /* Check for dropped column, and force a NULL result if so */
+   if (attr->attisdropped)
+   {
+       *op->resnull = true;
+       return;
+   }
+
+   /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
+   /* As in ExecEvalScalarVar, we should but can't check typmod */
+   if (op->d.fieldselect.resulttype != attr->atttypid)
+       ereport(ERROR,
+               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                errmsg("attribute %d has wrong type", fieldnum),
+                errdetail("Table has type %s, but query expects %s.",
+                          format_type_be(attr->atttypid),
+                          format_type_be(op->d.fieldselect.resulttype))));
+
+   /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
+   tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+   tmptup.t_data = tuple;
+
+   *op->resvalue = heap_getattr(&tmptup,
+                                fieldnum,
+                                tupDesc,
+                                op->resnull);
+}
+
+/*
+ * Deform tuple before evaluating the individual new values as part of a
+ * FieldStore expression.
+ */
+void
+ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+   /* Lookup tupdesc if first time through or after rescan */
+   get_cached_rowtype(op->d.fieldstore.fstore->resulttype, -1,
+                      op->d.fieldstore.argdesc, econtext);
+
+   if (*op->resnull)
+   {
+       /* Convert null input tuple into an all-nulls row */
+       memset(op->d.fieldstore.nulls, true,
+              MaxTupleAttributeNumber * sizeof(bool));
+   }
+   else
+   {
+       /*
+        * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
+        * set all the fields in the struct just in case.
+        */
+       Datum       tupDatum = *op->resvalue;
+       HeapTupleHeader tuphdr;
+       HeapTupleData tmptup;
+
+       tuphdr = DatumGetHeapTupleHeader(tupDatum);
+       tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
+       ItemPointerSetInvalid(&(tmptup.t_self));
+       tmptup.t_tableOid = InvalidOid;
+       tmptup.t_data = tuphdr;
+
+       heap_deform_tuple(&tmptup, *op->d.fieldstore.argdesc,
+                         op->d.fieldstore.values,
+                         op->d.fieldstore.nulls);
+   }
+}
+
+/*
+ * Compute the new wholerow datum after each individual row part of a
+ * FieldStore expression has been evaluated.
+ */
+void
+ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+   HeapTuple   tuple;
+
+   /* Result is never null */
+   *op->resnull = false;
+
+   tuple = heap_form_tuple(*op->d.fieldstore.argdesc,
+                           op->d.fieldstore.values,
+                           op->d.fieldstore.nulls);
+
+   *op->resvalue = HeapTupleGetDatum(tuple);
+}
+
+void
+ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op)
+{
+   ArrayRefState *arefstate = op->d.arrayref.state;
+
+   if (*op->resnull)
+   {
+       arefstate->prevnull = true;
+       arefstate->prevvalue = (Datum) 0;
+   }
+   else if (arefstate->numlower == 0)
+   {
+       arefstate->prevvalue =
+           array_get_element(*op->resvalue,
+                             arefstate->numupper,
+                             arefstate->upperindex,
+                             arefstate->refattrlength,
+                             arefstate->refelemlength,
+                             arefstate->refelembyval,
+                             arefstate->refelemalign,
+                             &arefstate->prevnull);
+   }
+   else
+   {
+       /* this is currently unreachable */
+       arefstate->prevvalue =
+           array_get_slice(*op->resvalue,
+                           arefstate->numupper,
+                           arefstate->upperindex,
+                           arefstate->lowerindex,
+                           arefstate->upperprovided,
+                           arefstate->lowerprovided,
+                           arefstate->refattrlength,
+                           arefstate->refelemlength,
+                           arefstate->refelembyval,
+                           arefstate->refelemalign);
+   }
+}
+
+void
+ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op)
+{
+   ArrayRefState *arefstate = op->d.arrayref.state;
+
+   /*
+    * For an assignment to a fixed-length array type, both the original array
+    * and the value to be assigned into it must be non-NULL, else we punt and
+    * return the original array.
+    */
+   if (arefstate->refattrlength > 0 &&
+       (*op->resnull || arefstate->replacenull))
+   {
+       return;
+   }
+
+   /*
+    * For assignment to varlena arrays, we handle a NULL original array by
+    * substituting an empty (zero-dimensional) array; insertion of the new
+    * element will result in a singleton array value.  It does not matter
+    * whether the new element is NULL.
+    */
+   if (*op->resnull)
+   {
+       *op->resvalue = PointerGetDatum(construct_empty_array(arefstate->refelemtype));
+       *op->resnull = false;
+   }
+
+   if (arefstate->numlower == 0)
+   {
+       *op->resvalue =
+           array_set_element(*op->resvalue, arefstate->numupper,
+                             arefstate->upperindex,
+                             arefstate->replacevalue,
+                             arefstate->replacenull,
+                             arefstate->refattrlength,
+                             arefstate->refelemlength,
+                             arefstate->refelembyval,
+                             arefstate->refelemalign);
+   }
+   else
+   {
+       *op->resvalue =
+           array_set_slice(*op->resvalue, arefstate->numupper,
+                           arefstate->upperindex,
+                           arefstate->lowerindex,
+                           arefstate->upperprovided,
+                           arefstate->lowerprovided,
+                           arefstate->replacevalue,
+                           arefstate->replacenull,
+                           arefstate->refattrlength,
+                           arefstate->refelemlength,
+                           arefstate->refelembyval,
+                           arefstate->refelemalign);
+   }
+}
+
+void
+ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op)
+{
+   ArrayRefState *arefstate = op->d.arrayref.state;
+
+   if (arefstate->numlower == 0)
+   {
+
+       *op->resvalue =
+           array_get_element(*op->resvalue,
+                             arefstate->numupper,
+                             arefstate->upperindex,
+                             arefstate->refattrlength,
+                             arefstate->refelemlength,
+                             arefstate->refelembyval,
+                             arefstate->refelemalign,
+                             op->resnull);
+   }
+   else
+   {
+       *op->resvalue =
+           array_get_slice(*op->resvalue,
+                           arefstate->numupper,
+                           arefstate->upperindex,
+                           arefstate->lowerindex,
+                           arefstate->upperprovided,
+                           arefstate->lowerprovided,
+                           arefstate->refattrlength,
+                           arefstate->refelemlength,
+                           arefstate->refelembyval,
+                           arefstate->refelemalign);
+   }
+}
+
+bool
+ExecEvalArrayRefCheckSubscript(ExprState *state, ExprEvalStep *op)
+{
+   ArrayRefState *arefstate = op->d.arrayref_checksubscript.state;
+   int         off = op->d.arrayref_checksubscript.off;
+   bool       *nulls;
+   Datum      *values;
+   int        *indexes;
+
+   if (op->d.arrayref_checksubscript.isupper)
+   {
+       nulls = arefstate->uppernull;
+       values = arefstate->upper;
+       indexes = arefstate->upperindex;
+   }
+   else
+   {
+       nulls = arefstate->lowernull;
+       values = arefstate->lower;
+       indexes = arefstate->lowerindex;
+   }
+
+   /* If any index expr yields NULL, result is NULL or error */
+   if (nulls[off])
+   {
+       if (arefstate->isassignment)
+           ereport(ERROR,
+                   (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                 errmsg("array subscript in assignment must not be null")));
+       *op->resnull = true;
+       return false;
+   }
+
+   /* convert datum to int */
+   indexes[off] = values[off];
+
+   return true;
+}
+
+/*
+ * Evaluate a rowtype coercion operation.  This may require rearranging field
+ * positions.
+ */
+void
+ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+   ConvertRowtypeExpr *convert = op->d.convert_rowtype.convert;
+   HeapTuple   result;
+   Datum       tupDatum;
+   HeapTupleHeader tuple;
+   HeapTupleData tmptup;
+   TupleDesc   indesc,
+               outdesc;
+
+   tupDatum = *op->resvalue;
+
+   /* this test covers the isDone exception too: */
+   if (*op->resnull)
+       return;
+
+   tuple = DatumGetHeapTupleHeader(tupDatum);
+
+   /* Lookup tupdescs if first time through or after rescan */
+   if (op->d.convert_rowtype.indesc == NULL)
+   {
+       get_cached_rowtype(exprType((Node *) convert->arg), -1,
+                          &op->d.convert_rowtype.indesc,
+                          econtext);
+       op->d.convert_rowtype.initialized = false;
+   }
+   if (op->d.convert_rowtype.outdesc == NULL)
+   {
+       get_cached_rowtype(convert->resulttype, -1,
+                          &op->d.convert_rowtype.outdesc,
+                          econtext);
+       op->d.convert_rowtype.initialized = false;
+   }
+
+   indesc = op->d.convert_rowtype.indesc;
+   outdesc = op->d.convert_rowtype.outdesc;
+
+   /*
+    * We used to be able to assert that incoming tuples are marked with
+    * exactly the rowtype of cstate->indesc.  However, now that
+    * ExecEvalWholeRowVar might change the tuples' marking to plain RECORD
+    * due to inserting aliases, we can only make this weak test:
+    */
+   Assert(HeapTupleHeaderGetTypeId(tuple) == indesc->tdtypeid ||
+          HeapTupleHeaderGetTypeId(tuple) == RECORDOID);
+
+   /* if first time through, initialize conversion map */
+   if (!op->d.convert_rowtype.initialized)
+   {
+       MemoryContext old_cxt;
+
+       /* allocate map in long-lived memory context */
+       old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+       /* prepare map from old to new attribute numbers */
+       op->d.convert_rowtype.map =
+           convert_tuples_by_name(indesc, outdesc,
+                                gettext_noop("could not convert row type"));
+       op->d.convert_rowtype.initialized = true;
+
+       MemoryContextSwitchTo(old_cxt);
+   }
+
+   /*
+    * No-op if no conversion needed (not clear this can happen here).
+    */
+   if (op->d.convert_rowtype.map == NULL)
+       return;
+
+   /*
+    * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader.
+    */
+   tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+   tmptup.t_data = tuple;
+
+   result = do_convert_tuple(&tmptup, op->d.convert_rowtype.map);
+
+   *op->resvalue = HeapTupleGetDatum(result);
+
+}
+
+/*
+ * ExecEvalScalarArrayOp
+ *
+ * Evaluate "scalar op ANY/ALL (array)".  The operator always yields boolean,
+ * and we combine the results across all array elements using OR and AND
+ * (for ANY and ALL respectively).  Of course we short-circuit as soon as
+ * the result is known.
+ */
+void
+ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
+{
+   ScalarArrayOpExpr *opexpr = op->d.scalararrayop.opexpr;
+   bool        useOr = opexpr->useOr;
+   FunctionCallInfo fcinfo = op->d.scalararrayop.fcinfo_data;
+   bool        strictfunc = op->d.scalararrayop.finfo->fn_strict;
+   ArrayType  *arr;
+   int         nitems;
+   Datum       result;
+   bool        resultnull;
+   int         i;
+   int16       typlen;
+   bool        typbyval;
+   char        typalign;
+   char       *s;
+   bits8      *bitmap;
+   int         bitmask;
+
+   /*
+    * 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 (*op->resnull)
+   {
+       return;
+   }
+
+   /* no "bad" nulls, okay to fetch and detoast the array */
+   arr = DatumGetArrayTypeP(*op->resvalue);
+
+   /*
+    * 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)
+   {
+       *op->resnull = false;
+       *op->resvalue = BoolGetDatum(!useOr);
+       return;
+   }
+
+   /*
+    * If the scalar is NULL, and the function is strict, return NULL; no
+    * point in iterating the loop.
+    */
+   if (fcinfo->argnull[0] && strictfunc)
+   {
+       *op->resnull = true;
+       return;
+   }
+
+   /*
+    * We arrange to look up info about the element type only once per series
+    * of calls, assuming the element type doesn't change underneath us.
+    */
+   if (op->d.scalararrayop.element_type != ARR_ELEMTYPE(arr))
+   {
+       get_typlenbyvalalign(ARR_ELEMTYPE(arr),
+                            &op->d.scalararrayop.typlen,
+                            &op->d.scalararrayop.typbyval,
+                            &op->d.scalararrayop.typalign);
+       op->d.scalararrayop.element_type = ARR_ELEMTYPE(arr);
+   }
+
+   typlen = op->d.scalararrayop.typlen;
+   typbyval = op->d.scalararrayop.typbyval;
+   typalign = op->d.scalararrayop.typalign;
+
+   result = BoolGetDatum(!useOr);
+   resultnull = false;
+
+   /* Loop over the array elements */
+   s = (char *) ARR_DATA_PTR(arr);
+   bitmap = ARR_NULLBITMAP(arr);
+   bitmask = 1;
+
+   for (i = 0; i < nitems; i++)
+   {
+       Datum       elt;
+       Datum       thisresult;
+
+       /* Get array element, checking for NULL */
+       if (bitmap && (*bitmap & bitmask) == 0)
+       {
+           fcinfo->arg[1] = (Datum) 0;
+           fcinfo->argnull[1] = true;
+       }
+       else
+       {
+           elt = fetch_att(s, typbyval, typlen);
+           s = att_addlength_pointer(s, typlen, s);
+           s = (char *) att_align_nominal(s, typalign);
+           fcinfo->arg[1] = elt;
+           fcinfo->argnull[1] = false;
+       }
+
+       /* Call comparison function */
+       if (fcinfo->argnull[1] && strictfunc)
+       {
+           fcinfo->isnull = true;
+           thisresult = (Datum) 0;
+       }
+       else
+       {
+           fcinfo->isnull = false;
+           thisresult = (op->d.scalararrayop.fn_addr) (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 */
+           }
+       }
+
+       /* advance bitmap pointer if any */
+       if (bitmap)
+       {
+           bitmask <<= 1;
+           if (bitmask == 0x100)
+           {
+               bitmap++;
+               bitmask = 1;
+           }
+       }
+   }
+
+   *op->resnull = resultnull;
+   *op->resvalue = result;
+}
+
+/*
+ * Evaluate a NOT NULL constraint.
+ */
+void
+ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op)
+{
+   if (*op->resnull)
+       ereport(ERROR,
+               (errcode(ERRCODE_NOT_NULL_VIOLATION),
+                errmsg("domain %s does not allow null values",
+                       format_type_be(op->d.domaincheck.resulttype)),
+                errdatatype(op->d.domaincheck.resulttype)));
+}
+
+/*
+ * Evaluate a CHECK constraint.
+ */
+void
+ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op)
+{
+   if (!*op->d.domaincheck.checknull &&
+       !DatumGetBool(*op->d.domaincheck.checkvalue))
+       ereport(ERROR,
+               (errcode(ERRCODE_CHECK_VIOLATION),
+              errmsg("value for domain %s violates check constraint \"%s\"",
+                     format_type_be(op->d.domaincheck.resulttype),
+                     op->d.domaincheck.constraintname),
+                errdomainconstraint(op->d.domaincheck.resulttype,
+                                    op->d.domaincheck.constraintname)));
+}
+
+/*
+ * Evaluate the various forms of XmlExpr.
+ */
+void
+ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
+{
+   XmlExpr    *xexpr = op->d.xmlexpr.xexpr;
+   Datum       value;
+   ListCell   *arg;
+   ListCell   *narg;
+   int         i;
+
+   *op->resnull = true;        /* until we get a result */
+   *op->resvalue = (Datum) 0;
+
+   switch (xexpr->op)
+   {
+       case IS_XMLCONCAT:
+           {
+               Datum      *argvalue = op->d.xmlexpr.argvalue;
+               bool       *argnull = op->d.xmlexpr.argnull;
+               List       *values = NIL;
+
+               for (i = 0; i < list_length(xexpr->args); i++)
+               {
+                   if (!argnull[i])
+                       values = lappend(values, DatumGetPointer(argvalue[i]));
+               }
+
+               if (list_length(values) > 0)
+               {
+                   *op->resnull = false;
+                   *op->resvalue = PointerGetDatum(xmlconcat(values));
+               }
+           }
+           break;
+
+       case IS_XMLFOREST:
+           {
+               Datum      *argvalue = op->d.xmlexpr.named_argvalue;
+               bool       *argnull = op->d.xmlexpr.named_argnull;
+               StringInfoData buf;
+
+               initStringInfo(&buf);
+
+               i = 0;
+               forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
+               {
+                   char       *argname = strVal(lfirst(narg));
+                   Expr       *e = (Expr *) lfirst(arg);
+
+                   if (!argnull[i])
+                   {
+                       value = argvalue[i];
+                       appendStringInfo(&buf, "<%s>%s</%s>",
+                                        argname,
+                                        map_sql_value_to_xml_value(value, exprType((Node *) e), true),
+                                        argname);
+                       *op->resnull = false;
+                   }
+                   i++;
+               }
+
+               if (*op->resnull)
+               {
+                   pfree(buf.data);
+               }
+               else
+               {
+                   text       *result;
+
+                   result = cstring_to_text_with_len(buf.data, buf.len);
+                   pfree(buf.data);
+
+                   *op->resvalue = PointerGetDatum(result);
+               }
+           }
+           break;
+
+       case IS_XMLELEMENT:
+           *op->resnull = false;
+           *op->resvalue = PointerGetDatum(
+                                           xmlelement(xexpr,
+                                                op->d.xmlexpr.named_argnull,
+                                               op->d.xmlexpr.named_argvalue,
+                                                      op->d.xmlexpr.argnull,
+                                                   op->d.xmlexpr.argvalue));
+           break;
+       case IS_XMLPARSE:
+           {
+               Datum      *argvalue = op->d.xmlexpr.argvalue;
+               bool       *argnull = op->d.xmlexpr.argnull;
+               text       *data;
+               bool        preserve_whitespace;
+
+               /* arguments are known to be text, bool */
+               Assert(list_length(xexpr->args) == 2);
+
+               if (argnull[0])
+                   return;
+               value = argvalue[0];
+               data = DatumGetTextPP(value);
+
+               if (argnull[1]) /* probably can't happen */
+                   return;
+               value = argvalue[1];
+               preserve_whitespace = DatumGetBool(value);
+
+               *op->resnull = false;
+               *op->resvalue = PointerGetDatum(
+                     xmlparse(data, xexpr->xmloption, preserve_whitespace));
+           }
+           break;
+
+       case IS_XMLPI:
+           {
+               text       *arg;
+               bool        isnull;
+
+               /* optional argument is known to be text */
+               Assert(list_length(xexpr->args) <= 1);
+
+               if (xexpr->args)
+               {
+                   isnull = op->d.xmlexpr.argnull[0];
+                   if (isnull)
+                       arg = NULL;
+                   else
+                       arg = DatumGetTextPP(op->d.xmlexpr.argvalue[0]);
+
+               }
+               else
+               {
+                   arg = NULL;
+                   isnull = false;
+               }
+
+               *op->resvalue = PointerGetDatum(xmlpi(xexpr->name, arg,
+                                                     isnull, op->resnull));
+           }
+           break;
+
+       case IS_XMLROOT:
+           {
+               Datum      *argvalue = op->d.xmlexpr.argvalue;
+               bool       *argnull = op->d.xmlexpr.argnull;
+               xmltype    *data;
+               text       *version;
+               int         standalone;
+
+               /* arguments are known to be xml, text, int */
+               Assert(list_length(xexpr->args) == 3);
+
+               if (argnull[0])
+                   return;
+               data = DatumGetXmlP(argvalue[0]);
+
+               if (argnull[1])
+                   version = NULL;
+               else
+                   version = DatumGetTextPP(argvalue[1]);
+
+               Assert(!argnull[2]);    /* always present */
+               standalone = DatumGetInt32(argvalue[2]);
+
+               *op->resnull = false;
+               *op->resvalue = PointerGetDatum(xmlroot(data,
+                                                       version,
+                                                       standalone));
+           }
+           break;
+
+       case IS_XMLSERIALIZE:
+           {
+               Datum      *argvalue = op->d.xmlexpr.argvalue;
+               bool       *argnull = op->d.xmlexpr.argnull;
+
+               /* argument type is known to be xml */
+               Assert(list_length(xexpr->args) == 1);
+
+               if (argnull[0])
+                   return;
+
+               value = argvalue[0];
+               *op->resnull = false;
+               *op->resvalue = PointerGetDatum(
+                               xmltotext_with_xmloption(DatumGetXmlP(value),
+                                                        xexpr->xmloption));
+           }
+           break;
+
+       case IS_DOCUMENT:
+           {
+               Datum      *argvalue = op->d.xmlexpr.argvalue;
+               bool       *argnull = op->d.xmlexpr.argnull;
+
+               /* optional argument is known to be xml */
+               Assert(list_length(xexpr->args) == 1);
+
+               if (argnull[0])
+                   break;
+
+               value = argvalue[0];
+               *op->resnull = false;
+               *op->resvalue =
+                   BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
+           }
+           break;
+       default:
+           elog(ERROR, "unrecognized XML operation");
+   }
+}
+
+/*
+ * ExecEvalGroupingFunc
+ *
+ * Computes a bitmask with a bit for each (unevaluated) argument expression
+ * (rightmost arg is least significant bit).
+ *
+ * A bit is set if the corresponding expression is NOT part of the set of
+ * grouping expressions in the current grouping set.
+ */
+void
+ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op)
+{
+   int         result = 0;
+   int         attnum = 0;
+   Bitmapset  *grouped_cols = op->d.grouping_func.parent->grouped_cols;
+   ListCell   *lc;
+
+   *op->resnull = false;
+
+   foreach(lc, op->d.grouping_func.clauses)
+   {
+       attnum = lfirst_int(lc);
+
+       result = result << 1;
+
+       if (!bms_is_member(attnum, grouped_cols))
+           result = result | 1;
+   }
+
+   *op->resvalue = Int32GetDatum(result);
+}
+
+/*
+ * Hand off evaluation of a subplan to nodeSubplan.c
+ */
+void
+ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+   SubPlanState *sstate = op->d.subplan.sstate;
+
+   /* could potentially be nested, so make sure there's enough stack */
+   check_stack_depth();
+
+   *op->resvalue = ExecSubPlan(sstate, econtext, op->resnull);
+}
+
+/*
+ * Hand off evaluation of an alternative subplan to nodeSubplan.c
+ */
+void
+ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+   AlternativeSubPlanState *asstate = op->d.alternative_subplan.asstate;
+
+   /* could potentially be nested, so make sure there's enough stack */
+   check_stack_depth();
+
+   *op->resvalue = ExecAlternativeSubPlan(asstate, econtext, op->resnull);
+}
+
+/*
+ * Evaluate a wholerow Var expression.
+ *
+ * Returns a Datum whose value is the value of a whole-row range variable with
+ * respect to given expression context.
+ */
+void
+ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+   Var        *variable = op->d.wholerow.var;
+   TupleTableSlot *slot;
+   TupleDesc   output_tupdesc;
+   MemoryContext oldcontext;
+   bool        needslow = false;
+   HeapTupleHeader dtuple;
+   HeapTuple   tuple;
+
+   /* This was checked by ExecInitExpr */
+   Assert(variable->varattno == InvalidAttrNumber);
+
+   /* Get the input slot we want */
+   switch (variable->varno)
+   {
+       case INNER_VAR: /* get the tuple from the inner node */
+           slot = econtext->ecxt_innertuple;
+           break;
+
+       case OUTER_VAR: /* get the tuple from the outer node */
+           slot = econtext->ecxt_outertuple;
+           break;
+
+           /* INDEX_VAR is handled by default case */
+
+       default:                /* get the tuple from the relation being
+                                * scanned */
+           slot = econtext->ecxt_scantuple;
+           break;
+   }
+
+
+   /* Apply the junkfilter if any */
+   if (op->d.wholerow.junkFilter != NULL)
+       slot = ExecFilterJunk(op->d.wholerow.junkFilter, slot);
+
+   /*
+    * First time through, perform initialization.
+    *
+    * XXX: It'd be great if this could be moved to the the expression
+    * initialization phase, but due to using slots that's currently not
+    * feasible.
+    */
+   if (op->d.wholerow.first)
+   {
+       /*
+        * If the Var identifies a named composite type, we must check that
+        * the actual tuple type is compatible with it.
+        */
+       if (variable->vartype != RECORDOID)
+       {
+           TupleDesc   var_tupdesc;
+           TupleDesc   slot_tupdesc;
+           int         i;
+
+           /*
+            * We really only care about numbers of attributes and data types.
+            * Also, we can ignore type mismatch on columns that are dropped
+            * in the destination type, so long as (1) the physical storage
+            * matches or (2) the actual column value is NULL.  Case (1) is
+            * helpful in some cases involving out-of-date cached plans, while
+            * case (2) is expected behavior in situations such as an INSERT
+            * into a table with dropped columns (the planner typically
+            * generates an INT4 NULL regardless of the dropped column type).
+            * If we find a dropped column and cannot verify that case (1)
+            * holds, we have to use ExecEvalWholeRowSlow to check (2) for
+            * each row.
+            */
+           var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
+
+           slot_tupdesc = slot->tts_tupleDescriptor;
+
+           if (var_tupdesc->natts != slot_tupdesc->natts)
+               ereport(ERROR,
+                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                        errmsg("table row type and query-specified row type do not match"),
+                        errdetail_plural("Table row contains %d attribute, but query expects %d.",
+                  "Table row contains %d attributes, but query expects %d.",
+                                         slot_tupdesc->natts,
+                                         slot_tupdesc->natts,
+                                         var_tupdesc->natts)));
+
+           for (i = 0; i < var_tupdesc->natts; i++)
+           {
+               Form_pg_attribute vattr = var_tupdesc->attrs[i];
+               Form_pg_attribute sattr = slot_tupdesc->attrs[i];
+
+               if (vattr->atttypid == sattr->atttypid)
+                   continue;   /* no worries */
+               if (!vattr->attisdropped)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_DATATYPE_MISMATCH),
+                            errmsg("table row type and query-specified row type do not match"),
+                            errdetail("Table has type %s at ordinal position %d, but query expects %s.",
+                                      format_type_be(sattr->atttypid),
+                                      i + 1,
+                                      format_type_be(vattr->atttypid))));
+
+               if (vattr->attlen != sattr->attlen ||
+                   vattr->attalign != sattr->attalign)
+                   needslow = true;    /* need runtime check for null */
+           }
+
+           /*
+            * Use the variable's declared rowtype as the descriptor for the
+            * output values, modulo possibly assigning new column names
+            * below. In particular, we *must* absorb any attisdropped
+            * markings.
+            */
+           oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+           output_tupdesc = CreateTupleDescCopy(var_tupdesc);
+           MemoryContextSwitchTo(oldcontext);
+
+           ReleaseTupleDesc(var_tupdesc);
+       }
+       else
+       {
+           /*
+            * In the RECORD case, we use the input slot's rowtype as the
+            * descriptor for the output values, modulo possibly assigning new
+            * column names below.
+            */
+           oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+           output_tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
+           MemoryContextSwitchTo(oldcontext);
+       }
+
+       /*
+        * Construct a tuple descriptor for the composite values we'll
+        * produce, and make sure its record type is "blessed".  The main
+        * reason to do this is to be sure that operations such as
+        * row_to_json() will see the desired column names when they look up
+        * the descriptor from the type information embedded in the composite
+        * values.
+        *
+        * We already got the correct physical datatype info above, but now we
+        * should try to find the source RTE and adopt its column aliases, in
+        * case they are different from the original rowtype's names.  For
+        * example, in "SELECT foo(t) FROM tab t(x,y)", the first two columns
+        * in the composite output should be named "x" and "y" regardless of
+        * tab's column names.
+        *
+        * If we can't locate the RTE, assume the column names we've got are
+        * OK. (As of this writing, the only cases where we can't locate the
+        * RTE are in execution of trigger WHEN clauses, and then the Var will
+        * have the trigger's relation's rowtype, so its names are fine.)
+        * Also, if the creator of the RTE didn't bother to fill in an eref
+        * field, assume our column names are OK.  (This happens in COPY, and
+        * perhaps other places.)
+        */
+       if (econtext->ecxt_estate &&
+       variable->varno <= list_length(econtext->ecxt_estate->es_range_table))
+       {
+           RangeTblEntry *rte = rt_fetch(variable->varno,
+                                     econtext->ecxt_estate->es_range_table);
+
+           if (rte->eref)
+               ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
+       }
+
+       /* Bless the tupdesc if needed, and save it in the execution state */
+       op->d.wholerow.tupdesc = BlessTupleDesc(output_tupdesc);
+
+       op->d.wholerow.first = false;
+   }
+
+   if (needslow)
+   {
+       TupleDesc   tupleDesc = slot->tts_tupleDescriptor;
+       TupleDesc   var_tupdesc = op->d.wholerow.tupdesc;
+       int         i;
+
+       tuple = ExecFetchSlotTuple(slot);
+
+
+       /* Check to see if any dropped attributes are non-null */
+       for (i = 0; i < var_tupdesc->natts; i++)
+       {
+           Form_pg_attribute vattr = var_tupdesc->attrs[i];
+           Form_pg_attribute sattr = tupleDesc->attrs[i];
+
+           if (!vattr->attisdropped)
+               continue;       /* already checked non-dropped cols */
+           if (heap_attisnull(tuple, i + 1))
+               continue;       /* null is always okay */
+           if (vattr->attlen != sattr->attlen ||
+               vattr->attalign != sattr->attalign)
+               ereport(ERROR,
+                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                        errmsg("table row type and query-specified row type do not match"),
+                        errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
+                                  i + 1)));
+       }
+   }
+
+   /*
+    * Copy the slot tuple and make sure any toasted fields get detoasted.
+    */
+   dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
+
+   /*
+    * Label the datum with the composite type info we identified before.
+    */
+   HeapTupleHeaderSetTypeId(dtuple, op->d.wholerow.tupdesc->tdtypeid);
+   HeapTupleHeaderSetTypMod(dtuple, op->d.wholerow.tupdesc->tdtypmod);
+
+   *op->resnull = false;
+   *op->resvalue = PointerGetDatum(dtuple);
+}
+
+/*
+ * Callback function to release a tupdesc refcount at expression tree shutdown
+ */
+static void
+ShutdownTupleDescRef(Datum arg)
+{
+   TupleDesc  *cache_field = (TupleDesc *) DatumGetPointer(arg);
+
+   if (*cache_field)
+       ReleaseTupleDesc(*cache_field);
+   *cache_field = NULL;
+}
+
+/*
+ * get_cached_rowtype: utility function to lookup a rowtype tupdesc
+ *
+ * type_id, typmod: identity of the rowtype
+ * cache_field: where to cache the TupleDesc pointer in expression state node
+ *     (field must be initialized to NULL)
+ * econtext: expression context we are executing in
+ *
+ * NOTE: because the shutdown callback will be called during plan rescan,
+ * must be prepared to re-do this during any node execution; cannot call
+ * just once during expression initialization
+ */
+static TupleDesc
+get_cached_rowtype(Oid type_id, int32 typmod,
+                  TupleDesc *cache_field, ExprContext *econtext)
+{
+   TupleDesc   tupDesc = *cache_field;
+
+   /* Do lookup if no cached value or if requested type changed */
+   if (tupDesc == NULL ||
+       type_id != tupDesc->tdtypeid ||
+       typmod != tupDesc->tdtypmod)
+   {
+       tupDesc = lookup_rowtype_tupdesc(type_id, typmod);
+
+       if (*cache_field)
+       {
+           /* Release old tupdesc; but callback is already registered */
+           ReleaseTupleDesc(*cache_field);
+       }
+       else
+       {
+           /* Need to register shutdown callback to release tupdesc */
+           RegisterExprContextCallback(econtext,
+                                       ShutdownTupleDescRef,
+                                       PointerGetDatum(cache_field));
+       }
+       *cache_field = tupDesc;
+   }
+   return tupDesc;
+}
index f5cd65d8a0d17ab1242e363bf9bdcb0198b1f995..ede675fa61b2b2e1ca879bf7f2d7557d61068351 100644 (file)
@@ -1246,7 +1246,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
        resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
            palloc0(n * sizeof(FmgrInfo));
-       resultRelInfo->ri_TrigWhenExprs = (List **)
+       resultRelInfo->ri_TrigWhenExprs = (ExprState **)
            palloc0(n * sizeof(List *));
        if (instrument_options)
            resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1689,7 +1689,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
    ConstrCheck *check = rel->rd_att->constr->check;
    ExprContext *econtext;
    MemoryContext oldContext;
-   List       *qual;
    int         i;
 
    /*
@@ -1701,13 +1700,15 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
    {
        oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
        resultRelInfo->ri_ConstraintExprs =
-           (List **) palloc(ncheck * sizeof(List *));
+           (ExprState **) palloc(ncheck * sizeof(ExprState *));
        for (i = 0; i < ncheck; i++)
        {
-           /* ExecQual wants implicit-AND form */
+           List       *qual;
+
+           /* ExecPrepareQual wants implicit-AND form */
            qual = make_ands_implicit(stringToNode(check[i].ccbin));
-           resultRelInfo->ri_ConstraintExprs[i] = (List *)
-               ExecPrepareExpr((Expr *) qual, estate);
+           resultRelInfo->ri_ConstraintExprs[i] =
+               ExecPrepareCheck(qual, estate);
        }
        MemoryContextSwitchTo(oldContext);
    }
@@ -1724,14 +1725,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
    /* And evaluate the constraints */
    for (i = 0; i < ncheck; i++)
    {
-       qual = resultRelInfo->ri_ConstraintExprs[i];
+       ExprState  *qual = resultRelInfo->ri_ConstraintExprs[i];
 
        /*
         * NOTE: SQL specifies that a NULL result from a constraint expression
-        * is not to be treated as a failure.  Therefore, tell ExecQual to
-        * return TRUE for NULL.
+        * is not to be treated as a failure.  Therefore, use ExecCheck not
+        * ExecQual.
         */
-       if (!ExecQual(qual, econtext, true))
+       if (!ExecCheck(qual, econtext))
            return check[i].ccname;
    }
 
@@ -1759,8 +1760,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
    {
        List       *qual = resultRelInfo->ri_PartitionCheck;
 
-       resultRelInfo->ri_PartitionCheckExpr = (List *)
-           ExecPrepareExpr((Expr *) qual, estate);
+       resultRelInfo->ri_PartitionCheckExpr = ExecPrepareCheck(qual, estate);
    }
 
    /*
@@ -1776,7 +1776,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
     * As in case of the catalogued constraints, we treat a NULL result as
     * success here, not a failure.
     */
-   return ExecQual(resultRelInfo->ri_PartitionCheckExpr, econtext, true);
+   return ExecCheck(resultRelInfo->ri_PartitionCheckExpr, econtext);
 }
 
 /*
@@ -1956,11 +1956,9 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
         * is visible (in the case of a view) or that it passes the
         * 'with-check' policy (in the case of row security). If the qual
         * evaluates to NULL or FALSE, then the new tuple won't be included in
-        * the view or doesn't pass the 'with-check' policy for the table.  We
-        * need ExecQual to return FALSE for NULL to handle the view case (the
-        * opposite of what we do above for CHECK constraints).
+        * the view or doesn't pass the 'with-check' policy for the table.
         */
-       if (!ExecQual((List *) wcoExpr, econtext, false))
+       if (!ExecQual(wcoExpr, econtext))
        {
            char       *val_desc;
            Bitmapset  *modifiedCols;
index 90bef6f01f04af338fdc8fe9518fd079c23385dd..d250627bbe92e4ff18ff1ff4515c94ee8df99667 100644 (file)
@@ -1,7 +1,7 @@
 /*-------------------------------------------------------------------------
  *
  * execQual.c
- *   Routines to evaluate qualification and targetlist expressions
+ *   Former Routines to evaluate qualification and targetlist expressions
  *
  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *-------------------------------------------------------------------------
  */
-/*
- *  INTERFACE ROUTINES
- *     ExecEvalExpr    - (now a macro) evaluate an expression, return a datum
- *     ExecEvalExprSwitchContext - same, but switch into eval memory context
- *     ExecQual        - return true/false if qualification is satisfied
- *     ExecProject     - form a new tuple by projecting the given tuple
- *
- *  NOTES
- *     The more heavily used ExecEvalExpr routines, such as ExecEvalScalarVar,
- *     are hotspots. Making these faster will speed up the entire system.
- *
- *     ExecProject() is used to make tuple projections.  Rather then
- *     trying to speed it up, the execution plan should be pre-processed
- *     to facilitate attribute sharing between nodes wherever possible,
- *     instead of doing needless copying.  -cim 5/31/91
- *
- *     During expression evaluation, we check_stack_depth only in
- *     ExecMakeFunctionResultSet/ExecMakeFunctionResultNoSets rather than at
- *     every single node.  This is a compromise that trades off precision of
- *     the stack limit setting to gain speed.
- */
-
 #include "postgres.h"
 
 #include "access/htup_details.h"
 
 
 /* static function decls */
-static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
-                ExprContext *econtext,
-                bool *isNull);
-static bool isAssignmentIndirectionExpr(ExprState *exprstate);
-static Datum ExecEvalAggref(AggrefExprState *aggref,
-              ExprContext *econtext,
-              bool *isNull);
-static Datum ExecEvalWindowFunc(WindowFuncExprState *wfunc,
-                  ExprContext *econtext,
-                  bool *isNull);
-static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
-                 bool *isNull);
-static Datum ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
-                     bool *isNull);
-static Datum ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate,
-                   ExprContext *econtext,
-                   bool *isNull);
-static Datum ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate,
-                    ExprContext *econtext,
-                    bool *isNull);
-static Datum ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate,
-                    ExprContext *econtext,
-                    bool *isNull);
-static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
-             bool *isNull);
-static Datum ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
-                 bool *isNull);
-static Datum ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
-                   bool *isNull);
-static void init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
-           MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF);
-static void ShutdownFuncExpr(Datum arg);
-static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
-                  TupleDesc *cache_field, ExprContext *econtext);
-static void ShutdownTupleDescRef(Datum arg);
+static void init_sexpr(Oid foid, Oid input_collation, SetExprState *sexpr,
+           MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF);
+static void ShutdownSetExpr(Datum arg);
 static void ExecEvalFuncArgs(FunctionCallInfo fcinfo,
                 List *argList, ExprContext *econtext);
-static void ExecPrepareTuplestoreResult(FuncExprState *fcache,
+static void ExecPrepareTuplestoreResult(SetExprState *sexpr,
                            ExprContext *econtext,
                            Tuplestorestate *resultStore,
                            TupleDesc resultDesc);
 static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
-static Datum ExecMakeFunctionResultNoSets(FuncExprState *fcache,
-                            ExprContext *econtext,
-                            bool *isNull);
-static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
-            bool *isNull);
-static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
-            bool *isNull);
-static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
-                bool *isNull);
-static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
-                     ExprContext *econtext,
-                     bool *isNull);
-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 ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
-                      ExprContext *econtext,
-                      bool *isNull);
-static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
-            bool *isNull);
-static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
-                    ExprContext *econtext,
-                    bool *isNull);
-static Datum ExecEvalArray(ArrayExprState *astate,
-             ExprContext *econtext,
-             bool *isNull);
-static Datum ExecEvalRow(RowExprState *rstate,
-           ExprContext *econtext,
-           bool *isNull);
-static Datum ExecEvalRowCompare(RowCompareExprState *rstate,
-                  ExprContext *econtext,
-                  bool *isNull);
-static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
-                ExprContext *econtext,
-                bool *isNull);
-static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr,
-              ExprContext *econtext,
-              bool *isNull);
-static Datum ExecEvalSQLValueFunction(ExprState *svfExpr,
-                        ExprContext *econtext,
-                        bool *isNull);
-static Datum ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
-           bool *isNull);
-static Datum ExecEvalNullIf(FuncExprState *nullIfExpr,
-              ExprContext *econtext,
-              bool *isNull);
-static Datum ExecEvalNullTest(NullTestState *nstate,
-                ExprContext *econtext,
-                bool *isNull);
-static Datum ExecEvalBooleanTest(GenericExprState *bstate,
-                   ExprContext *econtext,
-                   bool *isNull);
-static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate,
-                      ExprContext *econtext,
-                      bool *isNull);
-static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
-                           ExprContext *econtext,
-                           bool *isNull);
-static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
-                   ExprContext *econtext,
-                   bool *isNull);
-static Datum ExecEvalFieldStore(FieldStoreState *fstate,
-                  ExprContext *econtext,
-                  bool *isNull);
-static Datum ExecEvalRelabelType(GenericExprState *exprstate,
-                   ExprContext *econtext,
-                   bool *isNull);
-static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
-                   ExprContext *econtext,
-                   bool *isNull);
-static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
-                       ExprContext *econtext,
-                       bool *isNull);
-static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
-                     bool *isNull);
-static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
-                        ExprContext *econtext,
-                        bool *isNull);
-
-
-/* ----------------------------------------------------------------
- *     ExecEvalExpr routines
- *
- *     Recursively evaluate a targetlist or qualification expression.
- *
- * Each of the following routines having the signature
- *     Datum ExecEvalFoo(ExprState *expression,
- *                       ExprContext *econtext,
- *                       bool *isNull);
- * is responsible for evaluating one type or subtype of ExprState node.
- * They are normally called via the ExecEvalExpr macro, which makes use of
- * the function pointer set up when the ExprState node was built by
- * ExecInitExpr.  (In some cases, we change this pointer later to avoid
- * re-executing one-time overhead.)
- *
- * Note: for notational simplicity we declare these functions as taking the
- * specific type of ExprState that they work on.  This requires casting when
- * assigning the function pointer in ExecInitExpr.  Be careful that the
- * function signature is declared correctly, because the cast suppresses
- * automatic checking!
- *
- *
- * All these functions share this calling convention:
- *
- * Inputs:
- *     expression: the expression state tree to evaluate
- *     econtext: evaluation context information
- *
- * Outputs:
- *     return value: Datum value of result
- *     *isNull: set to TRUE if result is NULL (actual return value is
- *              meaningless if so); set to FALSE if non-null result
- *
- * The caller should already have switched into the temporary memory
- * context econtext->ecxt_per_tuple_memory.  The convenience entry point
- * ExecEvalExprSwitchContext() is provided for callers who don't prefer to
- * do the switch in an outer loop.  We do not do the switch in these routines
- * because it'd be a waste of cycles during nested expression evaluation.
- * ----------------------------------------------------------------
- */
 
 
-/*----------
- *   ExecEvalArrayRef
- *
- *    This function takes an ArrayRef and returns the extracted Datum
- *    if it's a simple reference, or the modified array value if it's
- *    an array assignment (i.e., array element or slice insertion).
+/*
+ *     GetAttributeByName
+ *     GetAttributeByNum
  *
- * NOTE: if we get a NULL result from a subscript expression, we return NULL
- * when it's an array reference, or raise an error when it's an assignment.
- *----------
+ *     These functions return the value of the requested attribute
+ *     out of the given tuple Datum.
+ *     C functions which take a tuple as an argument are expected
+ *     to use these.  Ex: overpaid(EMP) might call GetAttributeByNum().
+ *     Note: these are actually rather slow because they do a typcache
+ *     lookup on each call.
  */
-static Datum
-ExecEvalArrayRef(ArrayRefExprState *astate,
-                ExprContext *econtext,
-                bool *isNull)
+Datum
+GetAttributeByNum(HeapTupleHeader tuple,
+                 AttrNumber attrno,
+                 bool *isNull)
 {
-   ArrayRef   *arrayRef = (ArrayRef *) astate->xprstate.expr;
-   Datum       array_source;
-   bool        isAssignment = (arrayRef->refassgnexpr != NULL);
-   bool        eisnull;
-   ListCell   *l;
-   int         i = 0,
-               j = 0;
-   IntArray    upper,
-               lower;
-   bool        upperProvided[MAXDIM],
-               lowerProvided[MAXDIM];
-   int        *lIndex;
+   Datum       result;
+   Oid         tupType;
+   int32       tupTypmod;
+   TupleDesc   tupDesc;
+   HeapTupleData tmptup;
 
-   array_source = ExecEvalExpr(astate->refexpr,
-                               econtext,
-                               isNull);
+   if (!AttributeNumberIsValid(attrno))
+       elog(ERROR, "invalid attribute number %d", attrno);
 
-   /*
-    * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
-    * assignment case, we'll cons up something below.
-    */
-   if (*isNull)
-   {
-       if (!isAssignment)
-           return (Datum) NULL;
-   }
+   if (isNull == NULL)
+       elog(ERROR, "a NULL isNull pointer was passed");
 
-   foreach(l, astate->refupperindexpr)
+   if (tuple == NULL)
    {
-       ExprState  *eltstate = (ExprState *) lfirst(l);
-
-       if (i >= MAXDIM)
-           ereport(ERROR,
-                   (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-                    errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
-                           i + 1, MAXDIM)));
-
-       if (eltstate == NULL)
-       {
-           /* Slice bound is omitted, so use array's upper bound */
-           Assert(astate->reflowerindexpr != NIL);
-           upperProvided[i++] = false;
-           continue;
-       }
-       upperProvided[i] = true;
-
-       upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
-                                                    econtext,
-                                                    &eisnull));
-       /* If any index expr yields NULL, result is NULL or error */
-       if (eisnull)
-       {
-           if (isAssignment)
-               ereport(ERROR,
-                       (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-                 errmsg("array subscript in assignment must not be null")));
-           *isNull = true;
-           return (Datum) NULL;
-       }
+       /* Kinda bogus but compatible with old behavior... */
+       *isNull = true;
+       return (Datum) 0;
    }
 
-   if (astate->reflowerindexpr != NIL)
-   {
-       foreach(l, astate->reflowerindexpr)
-       {
-           ExprState  *eltstate = (ExprState *) lfirst(l);
-
-           if (j >= MAXDIM)
-               ereport(ERROR,
-                       (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-                        errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
-                               j + 1, MAXDIM)));
-
-           if (eltstate == NULL)
-           {
-               /* Slice bound is omitted, so use array's lower bound */
-               lowerProvided[j++] = false;
-               continue;
-           }
-           lowerProvided[j] = true;
-
-           lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
-                                                        econtext,
-                                                        &eisnull));
-           /* If any index expr yields NULL, result is NULL or error */
-           if (eisnull)
-           {
-               if (isAssignment)
-                   ereport(ERROR,
-                           (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-                            errmsg("array subscript in assignment must not be null")));
-               *isNull = true;
-               return (Datum) NULL;
-           }
-       }
-       /* this can't happen unless parser messed up */
-       if (i != j)
-           elog(ERROR, "upper and lower index lists are not same length");
-       lIndex = lower.indx;
-   }
-   else
-       lIndex = NULL;
+   tupType = HeapTupleHeaderGetTypeId(tuple);
+   tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+   tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 
-   if (isAssignment)
-   {
-       Datum       sourceData;
-       Datum       save_datum;
-       bool        save_isNull;
+   /*
+    * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set all
+    * the fields in the struct just in case user tries to inspect system
+    * columns.
+    */
+   tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+   ItemPointerSetInvalid(&(tmptup.t_self));
+   tmptup.t_tableOid = InvalidOid;
+   tmptup.t_data = tuple;
 
-       /*
-        * We might have a nested-assignment situation, in which the
-        * refassgnexpr is itself a FieldStore or ArrayRef that needs to
-        * obtain and modify the previous value of the array element or slice
-        * being replaced.  If so, we have to extract that value from the
-        * array and pass it down via the econtext's caseValue.  It's safe to
-        * reuse the CASE mechanism because there cannot be a CASE between
-        * here and where the value would be needed, and an array assignment
-        * can't be within a CASE either.  (So saving and restoring the
-        * caseValue is just paranoia, but let's do it anyway.)
-        *
-        * Since fetching the old element might be a nontrivial expense, do it
-        * only if the argument appears to actually need it.
-        */
-       save_datum = econtext->caseValue_datum;
-       save_isNull = econtext->caseValue_isNull;
+   result = heap_getattr(&tmptup,
+                         attrno,
+                         tupDesc,
+                         isNull);
 
-       if (isAssignmentIndirectionExpr(astate->refassgnexpr))
-       {
-           if (*isNull)
-           {
-               /* whole array is null, so any element or slice is too */
-               econtext->caseValue_datum = (Datum) 0;
-               econtext->caseValue_isNull = true;
-           }
-           else if (lIndex == NULL)
-           {
-               econtext->caseValue_datum =
-                   array_get_element(array_source, i,
-                                     upper.indx,
-                                     astate->refattrlength,
-                                     astate->refelemlength,
-                                     astate->refelembyval,
-                                     astate->refelemalign,
-                                     &econtext->caseValue_isNull);
-           }
-           else
-           {
-               /* this is currently unreachable */
-               econtext->caseValue_datum =
-                   array_get_slice(array_source, i,
-                                   upper.indx, lower.indx,
-                                   upperProvided, lowerProvided,
-                                   astate->refattrlength,
-                                   astate->refelemlength,
-                                   astate->refelembyval,
-                                   astate->refelemalign);
-               econtext->caseValue_isNull = false;
-           }
-       }
-       else
-       {
-           /* argument shouldn't need caseValue, but for safety set it null */
-           econtext->caseValue_datum = (Datum) 0;
-           econtext->caseValue_isNull = true;
-       }
+   ReleaseTupleDesc(tupDesc);
 
-       /*
-        * Evaluate the value to be assigned into the array.
-        */
-       sourceData = ExecEvalExpr(astate->refassgnexpr,
-                                 econtext,
-                                 &eisnull);
+   return result;
+}
 
-       econtext->caseValue_datum = save_datum;
-       econtext->caseValue_isNull = save_isNull;
+Datum
+GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
+{
+   AttrNumber  attrno;
+   Datum       result;
+   Oid         tupType;
+   int32       tupTypmod;
+   TupleDesc   tupDesc;
+   HeapTupleData tmptup;
+   int         i;
 
-       /*
-        * For an assignment to a fixed-length array type, both the original
-        * array and the value to be assigned into it must be non-NULL, else
-        * we punt and return the original array.
-        */
-       if (astate->refattrlength > 0)  /* fixed-length array? */
-           if (eisnull || *isNull)
-               return array_source;
+   if (attname == NULL)
+       elog(ERROR, "invalid attribute name");
 
-       /*
-        * For assignment to varlena arrays, we handle a NULL original array
-        * by substituting an empty (zero-dimensional) array; insertion of the
-        * new element will result in a singleton array value.  It does not
-        * matter whether the new element is NULL.
-        */
-       if (*isNull)
-       {
-           array_source = PointerGetDatum(construct_empty_array(arrayRef->refelemtype));
-           *isNull = false;
-       }
+   if (isNull == NULL)
+       elog(ERROR, "a NULL isNull pointer was passed");
 
-       if (lIndex == NULL)
-           return array_set_element(array_source, i,
-                                    upper.indx,
-                                    sourceData,
-                                    eisnull,
-                                    astate->refattrlength,
-                                    astate->refelemlength,
-                                    astate->refelembyval,
-                                    astate->refelemalign);
-       else
-           return array_set_slice(array_source, i,
-                                  upper.indx, lower.indx,
-                                  upperProvided, lowerProvided,
-                                  sourceData,
-                                  eisnull,
-                                  astate->refattrlength,
-                                  astate->refelemlength,
-                                  astate->refelembyval,
-                                  astate->refelemalign);
+   if (tuple == NULL)
+   {
+       /* Kinda bogus but compatible with old behavior... */
+       *isNull = true;
+       return (Datum) 0;
    }
 
-   if (lIndex == NULL)
-       return array_get_element(array_source, i,
-                                upper.indx,
-                                astate->refattrlength,
-                                astate->refelemlength,
-                                astate->refelembyval,
-                                astate->refelemalign,
-                                isNull);
-   else
-       return array_get_slice(array_source, i,
-                              upper.indx, lower.indx,
-                              upperProvided, lowerProvided,
-                              astate->refattrlength,
-                              astate->refelemlength,
-                              astate->refelembyval,
-                              astate->refelemalign);
-}
+   tupType = HeapTupleHeaderGetTypeId(tuple);
+   tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+   tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 
-/*
- * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
- * that might need the old element value passed down?
- *
- * (We could use this in ExecEvalFieldStore too, but in that case passing
- * the old value is so cheap there's no need.)
- */
-static bool
-isAssignmentIndirectionExpr(ExprState *exprstate)
-{
-   if (exprstate == NULL)
-       return false;           /* just paranoia */
-   if (IsA(exprstate, FieldStoreState))
+   attrno = InvalidAttrNumber;
+   for (i = 0; i < tupDesc->natts; i++)
    {
-       FieldStore *fstore = (FieldStore *) exprstate->expr;
-
-       if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
-           return true;
+       if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0)
+       {
+           attrno = tupDesc->attrs[i]->attnum;
+           break;
+       }
    }
-   else if (IsA(exprstate, ArrayRefExprState))
-   {
-       ArrayRef   *arrayRef = (ArrayRef *) exprstate->expr;
 
-       if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
-           return true;
-   }
-   return false;
-}
+   if (attrno == InvalidAttrNumber)
+       elog(ERROR, "attribute \"%s\" does not exist", attname);
 
-/* ----------------------------------------------------------------
- *     ExecEvalAggref
- *
- *     Returns a Datum whose value is the value of the precomputed
- *     aggregate found in the given expression context.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext,
-              bool *isNull)
-{
-   if (econtext->ecxt_aggvalues == NULL)       /* safety check */
-       elog(ERROR, "no aggregates in this expression context");
+   /*
+    * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set all
+    * the fields in the struct just in case user tries to inspect system
+    * columns.
+    */
+   tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+   ItemPointerSetInvalid(&(tmptup.t_self));
+   tmptup.t_tableOid = InvalidOid;
+   tmptup.t_data = tuple;
 
-   *isNull = econtext->ecxt_aggnulls[aggref->aggno];
-   return econtext->ecxt_aggvalues[aggref->aggno];
-}
+   result = heap_getattr(&tmptup,
+                         attrno,
+                         tupDesc,
+                         isNull);
 
-/* ----------------------------------------------------------------
- *     ExecEvalWindowFunc
- *
- *     Returns a Datum whose value is the value of the precomputed
- *     window function found in the given expression context.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWindowFunc(WindowFuncExprState *wfunc, ExprContext *econtext,
-                  bool *isNull)
-{
-   if (econtext->ecxt_aggvalues == NULL)       /* safety check */
-       elog(ERROR, "no window functions in this expression context");
+   ReleaseTupleDesc(tupDesc);
 
-   *isNull = econtext->ecxt_aggnulls[wfunc->wfuncno];
-   return econtext->ecxt_aggvalues[wfunc->wfuncno];
+   return result;
 }
 
-/* ----------------------------------------------------------------
- *     ExecEvalScalarVar
- *
- *     Returns a Datum whose value is the value of a scalar (not whole-row)
- *     range variable with respect to given expression context.
- *
- * Note: ExecEvalScalarVar is executed only the first time through in a given
- * plan; it changes the ExprState's function pointer to pass control directly
- * to ExecEvalScalarVarFast after making one-time checks.
- * ----------------------------------------------------------------
+/*
+ * init_sexpr - initialize a SetExprState node during first use
  */
-static Datum
-ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
-                 bool *isNull)
+static void
+init_sexpr(Oid foid, Oid input_collation, SetExprState *sexpr,
+          MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF)
 {
-   Var        *variable = (Var *) exprstate->expr;
-   TupleTableSlot *slot;
-   AttrNumber  attnum;
+   AclResult   aclresult;
 
-   /* Get the input slot and attribute number we want */
-   switch (variable->varno)
-   {
-       case INNER_VAR: /* get the tuple from the inner node */
-           slot = econtext->ecxt_innertuple;
-           break;
+   /* Check permission to call function */
+   aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
+   if (aclresult != ACLCHECK_OK)
+       aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid));
+   InvokeFunctionExecuteHook(foid);
 
-       case OUTER_VAR: /* get the tuple from the outer node */
-           slot = econtext->ecxt_outertuple;
-           break;
+   /*
+    * Safety check on nargs.  Under normal circumstances this should never
+    * fail, as parser should check sooner.  But possibly it might fail if
+    * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+    * declared in pg_proc?
+    */
+   if (list_length(sexpr->args) > FUNC_MAX_ARGS)
+       ereport(ERROR,
+               (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+            errmsg_plural("cannot pass more than %d argument to a function",
+                          "cannot pass more than %d arguments to a function",
+                          FUNC_MAX_ARGS,
+                          FUNC_MAX_ARGS)));
 
-           /* INDEX_VAR is handled by default case */
+   /* Set up the primary fmgr lookup information */
+   fmgr_info_cxt(foid, &(sexpr->func), sexprCxt);
+   fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
 
-       default:                /* get the tuple from the relation being
-                                * scanned */
-           slot = econtext->ecxt_scantuple;
-           break;
-   }
+   /* Initialize the function call parameter struct as well */
+   InitFunctionCallInfoData(sexpr->fcinfo_data, &(sexpr->func),
+                            list_length(sexpr->args),
+                            input_collation, NULL, NULL);
 
-   attnum = variable->varattno;
+   /* If function returns set, check if that's allowed by caller */
+   if (sexpr->func.fn_retset && !allowSRF)
+       ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                errmsg("set-valued function called in context that cannot accept a set")));
 
-   /* This was checked by ExecInitExpr */
-   Assert(attnum != InvalidAttrNumber);
+   /* Otherwise, ExecInitExpr should have marked the sexpr correctly */
+   Assert(sexpr->func.fn_retset == sexpr->funcReturnsSet);
 
-   /*
-    * If it's a user attribute, check validity (bogus system attnums will be
-    * caught inside slot_getattr).  What we have to check for here is the
-    * possibility of an attribute having been changed in type since the plan
-    * tree was created.  Ideally the plan will get invalidated and not
-    * re-used, but just in case, we keep these defenses.  Fortunately it's
-    * sufficient to check once on the first time through.
-    *
-    * Note: we allow a reference to a dropped attribute.  slot_getattr will
-    * force a NULL result in such cases.
-    *
-    * Note: ideally we'd check typmod as well as typid, but that seems
-    * impractical at the moment: in many cases the tupdesc will have been
-    * generated by ExecTypeFromTL(), and that can't guarantee to generate an
-    * accurate typmod in all cases, because some expression node types don't
-    * carry typmod.
-    */
-   if (attnum > 0)
+   /* If function returns set, prepare expected tuple descriptor */
+   if (sexpr->func.fn_retset && needDescForSRF)
    {
-       TupleDesc   slot_tupdesc = slot->tts_tupleDescriptor;
-       Form_pg_attribute attr;
+       TypeFuncClass functypclass;
+       Oid         funcrettype;
+       TupleDesc   tupdesc;
+       MemoryContext oldcontext;
 
-       if (attnum > slot_tupdesc->natts)       /* should never happen */
-           elog(ERROR, "attribute number %d exceeds number of columns %d",
-                attnum, slot_tupdesc->natts);
+       functypclass = get_expr_result_type(sexpr->func.fn_expr,
+                                           &funcrettype,
+                                           &tupdesc);
 
-       attr = slot_tupdesc->attrs[attnum - 1];
+       /* Must save tupdesc in sexpr's context */
+       oldcontext = MemoryContextSwitchTo(sexprCxt);
 
-       /* can't check type if dropped, since atttypid is probably 0 */
-       if (!attr->attisdropped)
+       if (functypclass == TYPEFUNC_COMPOSITE)
        {
-           if (variable->vartype != attr->atttypid)
-               ereport(ERROR,
-                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                        errmsg("attribute %d has wrong type", attnum),
-                        errdetail("Table has type %s, but query expects %s.",
-                                  format_type_be(attr->atttypid),
-                                  format_type_be(variable->vartype))));
+           /* Composite data type, e.g. a table's row type */
+           Assert(tupdesc);
+           /* Must copy it out of typcache for safety */
+           sexpr->funcResultDesc = CreateTupleDescCopy(tupdesc);
+           sexpr->funcReturnsTuple = true;
+       }
+       else if (functypclass == TYPEFUNC_SCALAR)
+       {
+           /* Base data type, i.e. scalar */
+           tupdesc = CreateTemplateTupleDesc(1, false);
+           TupleDescInitEntry(tupdesc,
+                              (AttrNumber) 1,
+                              NULL,
+                              funcrettype,
+                              -1,
+                              0);
+           sexpr->funcResultDesc = tupdesc;
+           sexpr->funcReturnsTuple = false;
+       }
+       else if (functypclass == TYPEFUNC_RECORD)
+       {
+           /* This will work if function doesn't need an expectedDesc */
+           sexpr->funcResultDesc = NULL;
+           sexpr->funcReturnsTuple = true;
+       }
+       else
+       {
+           /* Else, we will fail if function needs an expectedDesc */
+           sexpr->funcResultDesc = NULL;
        }
-   }
 
-   /* Skip the checking on future executions of node */
-   exprstate->evalfunc = ExecEvalScalarVarFast;
+       MemoryContextSwitchTo(oldcontext);
+   }
+   else
+       sexpr->funcResultDesc = NULL;
 
-   /* Fetch the value from the slot */
-   return slot_getattr(slot, attnum, isNull);
+   /* Initialize additional state */
+   sexpr->funcResultStore = NULL;
+   sexpr->funcResultSlot = NULL;
+   sexpr->shutdown_reg = false;
 }
 
-/* ----------------------------------------------------------------
- *     ExecEvalScalarVarFast
- *
- *     Returns a Datum for a scalar variable.
- * ----------------------------------------------------------------
+/*
+ * callback function in case a SetExpr needs to be shut down before it has
+ * been run to completion
  */
-static Datum
-ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
-                     bool *isNull)
+static void
+ShutdownSetExpr(Datum arg)
 {
-   Var        *variable = (Var *) exprstate->expr;
-   TupleTableSlot *slot;
-   AttrNumber  attnum;
+   SetExprState *sexpr = (SetExprState *) DatumGetPointer(arg);
 
-   /* Get the input slot and attribute number we want */
-   switch (variable->varno)
-   {
-       case INNER_VAR: /* get the tuple from the inner node */
-           slot = econtext->ecxt_innertuple;
-           break;
-
-       case OUTER_VAR: /* get the tuple from the outer node */
-           slot = econtext->ecxt_outertuple;
-           break;
-
-           /* INDEX_VAR is handled by default case */
+   /* If we have a slot, make sure it's let go of any tuplestore pointer */
+   if (sexpr->funcResultSlot)
+       ExecClearTuple(sexpr->funcResultSlot);
 
-       default:                /* get the tuple from the relation being
-                                * scanned */
-           slot = econtext->ecxt_scantuple;
-           break;
-   }
+   /* Release any open tuplestore */
+   if (sexpr->funcResultStore)
+       tuplestore_end(sexpr->funcResultStore);
+   sexpr->funcResultStore = NULL;
 
-   attnum = variable->varattno;
+   /* Clear any active set-argument state */
+   sexpr->setArgsValid = false;
 
-   /* Fetch the value from the slot */
-   return slot_getattr(slot, attnum, isNull);
+   /* execUtils will deregister the callback... */
+   sexpr->shutdown_reg = false;
 }
 
-/* ----------------------------------------------------------------
- *     ExecEvalWholeRowVar
- *
- *     Returns a Datum whose value is the value of a whole-row range
- *     variable with respect to given expression context.
- *
- * Note: ExecEvalWholeRowVar is executed only the first time through in a
- * given plan; it changes the ExprState's function pointer to pass control
- * directly to ExecEvalWholeRowFast or ExecEvalWholeRowSlow after making
- * one-time checks.
- * ----------------------------------------------------------------
+/*
+ * Evaluate arguments for a function.
  */
-static Datum
-ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-                   bool *isNull)
+static void
+ExecEvalFuncArgs(FunctionCallInfo fcinfo,
+                List *argList,
+                ExprContext *econtext)
 {
-   Var        *variable = (Var *) wrvstate->xprstate.expr;
-   TupleTableSlot *slot;
-   TupleDesc   output_tupdesc;
-   MemoryContext oldcontext;
-   bool        needslow = false;
-
-   /* This was checked by ExecInitExpr */
-   Assert(variable->varattno == InvalidAttrNumber);
+   int         i;
+   ListCell   *arg;
 
-   /* Get the input slot we want */
-   switch (variable->varno)
+   i = 0;
+   foreach(arg, argList)
    {
-       case INNER_VAR: /* get the tuple from the inner node */
-           slot = econtext->ecxt_innertuple;
-           break;
-
-       case OUTER_VAR: /* get the tuple from the outer node */
-           slot = econtext->ecxt_outertuple;
-           break;
-
-           /* INDEX_VAR is handled by default case */
+       ExprState  *argstate = (ExprState *) lfirst(arg);
 
-       default:                /* get the tuple from the relation being
-                                * scanned */
-           slot = econtext->ecxt_scantuple;
-           break;
+       fcinfo->arg[i] = ExecEvalExpr(argstate,
+                                     econtext,
+                                     &fcinfo->argnull[i]);
+       i++;
    }
 
-   /*
-    * If the input tuple came from a subquery, it might contain "resjunk"
-    * columns (such as GROUP BY or ORDER BY columns), which we don't want to
-    * keep in the whole-row result.  We can get rid of such columns by
-    * passing the tuple through a JunkFilter --- but to make one, we have to
-    * lay our hands on the subquery's targetlist.  Fortunately, there are not
-    * very many cases where this can happen, and we can identify all of them
-    * by examining our parent PlanState.  We assume this is not an issue in
-    * standalone expressions that don't have parent plans.  (Whole-row Vars
-    * can occur in such expressions, but they will always be referencing
-    * table rows.)
-    */
-   if (wrvstate->parent)
-   {
-       PlanState  *subplan = NULL;
-
-       switch (nodeTag(wrvstate->parent))
-       {
-           case T_SubqueryScanState:
-               subplan = ((SubqueryScanState *) wrvstate->parent)->subplan;
-               break;
-           case T_CteScanState:
-               subplan = ((CteScanState *) wrvstate->parent)->cteplanstate;
-               break;
-           default:
-               break;
-       }
-
-       if (subplan)
-       {
-           bool        junk_filter_needed = false;
-           ListCell   *tlist;
-
-           /* Detect whether subplan tlist actually has any junk columns */
-           foreach(tlist, subplan->plan->targetlist)
-           {
-               TargetEntry *tle = (TargetEntry *) lfirst(tlist);
-
-               if (tle->resjunk)
-               {
-                   junk_filter_needed = true;
-                   break;
-               }
-           }
-
-           /* If so, build the junkfilter in the query memory context */
-           if (junk_filter_needed)
-           {
-               oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-               wrvstate->wrv_junkFilter =
-                   ExecInitJunkFilter(subplan->plan->targetlist,
-                                      ExecGetResultType(subplan)->tdhasoid,
-                           ExecInitExtraTupleSlot(wrvstate->parent->state));
-               MemoryContextSwitchTo(oldcontext);
-           }
-       }
-   }
+   Assert(i == fcinfo->nargs);
+}
 
-   /* Apply the junkfilter if any */
-   if (wrvstate->wrv_junkFilter != NULL)
-       slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
+/*
+ *     ExecPrepareTuplestoreResult
+ *
+ * Subroutine for ExecMakeFunctionResultSet: prepare to extract rows from a
+ * tuplestore function result.  We must set up a funcResultSlot (unless
+ * already done in a previous call cycle) and verify that the function
+ * returned the expected tuple descriptor.
+ */
+static void
+ExecPrepareTuplestoreResult(SetExprState *sexpr,
+                           ExprContext *econtext,
+                           Tuplestorestate *resultStore,
+                           TupleDesc resultDesc)
+{
+   sexpr->funcResultStore = resultStore;
 
-   /*
-    * If the Var identifies a named composite type, we must check that the
-    * actual tuple type is compatible with it.
-    */
-   if (variable->vartype != RECORDOID)
+   if (sexpr->funcResultSlot == NULL)
    {
-       TupleDesc   var_tupdesc;
-       TupleDesc   slot_tupdesc;
-       int         i;
+       /* Create a slot so we can read data out of the tuplestore */
+       TupleDesc   slotDesc;
+       MemoryContext oldcontext;
+
+       oldcontext = MemoryContextSwitchTo(sexpr->func.fn_mcxt);
 
        /*
-        * We really only care about numbers of attributes and data types.
-        * Also, we can ignore type mismatch on columns that are dropped in
-        * the destination type, so long as (1) the physical storage matches
-        * or (2) the actual column value is NULL.  Case (1) is helpful in
-        * some cases involving out-of-date cached plans, while case (2) is
-        * expected behavior in situations such as an INSERT into a table with
-        * dropped columns (the planner typically generates an INT4 NULL
-        * regardless of the dropped column type).  If we find a dropped
-        * column and cannot verify that case (1) holds, we have to use
-        * ExecEvalWholeRowSlow to check (2) for each row.
+        * If we were not able to determine the result rowtype from context,
+        * and the function didn't return a tupdesc, we have to fail.
         */
-       var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
-
-       slot_tupdesc = slot->tts_tupleDescriptor;
-
-       if (var_tupdesc->natts != slot_tupdesc->natts)
-           ereport(ERROR,
-                   (errcode(ERRCODE_DATATYPE_MISMATCH),
-                    errmsg("table row type and query-specified row type do not match"),
-                    errdetail_plural("Table row contains %d attribute, but query expects %d.",
-                  "Table row contains %d attributes, but query expects %d.",
-                                     slot_tupdesc->natts,
-                                     slot_tupdesc->natts,
-                                     var_tupdesc->natts)));
-
-       for (i = 0; i < var_tupdesc->natts; i++)
+       if (sexpr->funcResultDesc)
+           slotDesc = sexpr->funcResultDesc;
+       else if (resultDesc)
        {
-           Form_pg_attribute vattr = var_tupdesc->attrs[i];
-           Form_pg_attribute sattr = slot_tupdesc->attrs[i];
-
-           if (vattr->atttypid == sattr->atttypid)
-               continue;       /* no worries */
-           if (!vattr->attisdropped)
-               ereport(ERROR,
-                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                        errmsg("table row type and query-specified row type do not match"),
-                        errdetail("Table has type %s at ordinal position %d, but query expects %s.",
-                                  format_type_be(sattr->atttypid),
-                                  i + 1,
-                                  format_type_be(vattr->atttypid))));
-
-           if (vattr->attlen != sattr->attlen ||
-               vattr->attalign != sattr->attalign)
-               needslow = true;    /* need runtime check for null */
+           /* don't assume resultDesc is long-lived */
+           slotDesc = CreateTupleDescCopy(resultDesc);
+       }
+       else
+       {
+           ereport(ERROR,
+                   (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                    errmsg("function returning setof record called in "
+                           "context that cannot accept type record")));
+           slotDesc = NULL;    /* keep compiler quiet */
        }
 
-       /*
-        * Use the variable's declared rowtype as the descriptor for the
-        * output values, modulo possibly assigning new column names below. In
-        * particular, we *must* absorb any attisdropped markings.
-        */
-       oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-       output_tupdesc = CreateTupleDescCopy(var_tupdesc);
-       MemoryContextSwitchTo(oldcontext);
-
-       ReleaseTupleDesc(var_tupdesc);
-   }
-   else
-   {
-       /*
-        * In the RECORD case, we use the input slot's rowtype as the
-        * descriptor for the output values, modulo possibly assigning new
-        * column names below.
-        */
-       oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-       output_tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
+       sexpr->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
        MemoryContextSwitchTo(oldcontext);
    }
 
    /*
-    * Construct a tuple descriptor for the composite values we'll produce,
-    * and make sure its record type is "blessed".  The main reason to do this
-    * is to be sure that operations such as row_to_json() will see the
-    * desired column names when they look up the descriptor from the type
-    * information embedded in the composite values.
-    *
-    * We already got the correct physical datatype info above, but now we
-    * should try to find the source RTE and adopt its column aliases, in case
-    * they are different from the original rowtype's names.  For example, in
-    * "SELECT foo(t) FROM tab t(x,y)", the first two columns in the composite
-    * output should be named "x" and "y" regardless of tab's column names.
-    *
-    * If we can't locate the RTE, assume the column names we've got are OK.
-    * (As of this writing, the only cases where we can't locate the RTE are
-    * in execution of trigger WHEN clauses, and then the Var will have the
-    * trigger's relation's rowtype, so its names are fine.)  Also, if the
-    * creator of the RTE didn't bother to fill in an eref field, assume our
-    * column names are OK.  (This happens in COPY, and perhaps other places.)
+    * If function provided a tupdesc, cross-check it.  We only really need to
+    * do this for functions returning RECORD, but might as well do it always.
     */
-   if (econtext->ecxt_estate &&
-       variable->varno <= list_length(econtext->ecxt_estate->es_range_table))
+   if (resultDesc)
    {
-       RangeTblEntry *rte = rt_fetch(variable->varno,
-                                     econtext->ecxt_estate->es_range_table);
+       if (sexpr->funcResultDesc)
+           tupledesc_match(sexpr->funcResultDesc, resultDesc);
 
-       if (rte->eref)
-           ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
+       /*
+        * If it is a dynamically-allocated TupleDesc, free it: it is
+        * typically allocated in a per-query context, so we must avoid
+        * leaking it across multiple usages.
+        */
+       if (resultDesc->tdrefcount == -1)
+           FreeTupleDesc(resultDesc);
    }
 
-   /* Bless the tupdesc if needed, and save it in the execution state */
-   wrvstate->wrv_tupdesc = BlessTupleDesc(output_tupdesc);
-
-   /* Skip all the above on future executions of node */
-   if (needslow)
-       wrvstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowSlow;
-   else
-       wrvstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowFast;
-
-   /* Fetch the value */
-   return (*wrvstate->xprstate.evalfunc) ((ExprState *) wrvstate, econtext,
-                                          isNull);
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalWholeRowFast
- *
- *     Returns a Datum for a whole-row variable.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-                    bool *isNull)
-{
-   Var        *variable = (Var *) wrvstate->xprstate.expr;
-   TupleTableSlot *slot;
-   HeapTupleHeader dtuple;
-
-   *isNull = false;
-
-   /* Get the input slot we want */
-   switch (variable->varno)
+   /* Register cleanup callback if we didn't already */
+   if (!sexpr->shutdown_reg)
    {
-       case INNER_VAR: /* get the tuple from the inner node */
-           slot = econtext->ecxt_innertuple;
-           break;
-
-       case OUTER_VAR: /* get the tuple from the outer node */
-           slot = econtext->ecxt_outertuple;
-           break;
-
-           /* INDEX_VAR is handled by default case */
-
-       default:                /* get the tuple from the relation being
-                                * scanned */
-           slot = econtext->ecxt_scantuple;
-           break;
+       RegisterExprContextCallback(econtext,
+                                   ShutdownSetExpr,
+                                   PointerGetDatum(sexpr));
+       sexpr->shutdown_reg = true;
    }
-
-   /* Apply the junkfilter if any */
-   if (wrvstate->wrv_junkFilter != NULL)
-       slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
-
-   /*
-    * Copy the slot tuple and make sure any toasted fields get detoasted.
-    */
-   dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
-
-   /*
-    * Label the datum with the composite type info we identified before.
-    */
-   HeapTupleHeaderSetTypeId(dtuple, wrvstate->wrv_tupdesc->tdtypeid);
-   HeapTupleHeaderSetTypMod(dtuple, wrvstate->wrv_tupdesc->tdtypmod);
-
-   return PointerGetDatum(dtuple);
 }
 
-/* ----------------------------------------------------------------
- *     ExecEvalWholeRowSlow
+/*
+ * Check that function result tuple type (src_tupdesc) matches or can
+ * be considered to match what the query expects (dst_tupdesc). If
+ * they don't match, ereport.
  *
- *     Returns a Datum for a whole-row variable, in the "slow" case where
- *     we can't just copy the subplan's output.
- * ----------------------------------------------------------------
+ * We really only care about number of attributes and data type.
+ * Also, we can ignore type mismatch on columns that are dropped in the
+ * destination type, so long as the physical storage matches.  This is
+ * helpful in some cases involving out-of-date cached plans.
  */
-static Datum
-ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-                    bool *isNull)
+static void
+tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
 {
-   Var        *variable = (Var *) wrvstate->xprstate.expr;
-   TupleTableSlot *slot;
-   HeapTuple   tuple;
-   TupleDesc   tupleDesc;
-   TupleDesc   var_tupdesc;
-   HeapTupleHeader dtuple;
    int         i;
 
-   *isNull = false;
+   if (dst_tupdesc->natts != src_tupdesc->natts)
+       ereport(ERROR,
+               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                errmsg("function return row and query-specified return row do not match"),
+                errdetail_plural("Returned row contains %d attribute, but query expects %d.",
+               "Returned row contains %d attributes, but query expects %d.",
+                                 src_tupdesc->natts,
+                                 src_tupdesc->natts, dst_tupdesc->natts)));
 
-   /* Get the input slot we want */
-   switch (variable->varno)
+   for (i = 0; i < dst_tupdesc->natts; i++)
    {
-       case INNER_VAR: /* get the tuple from the inner node */
-           slot = econtext->ecxt_innertuple;
-           break;
-
-       case OUTER_VAR: /* get the tuple from the outer node */
-           slot = econtext->ecxt_outertuple;
-           break;
-
-           /* INDEX_VAR is handled by default case */
-
-       default:                /* get the tuple from the relation being
-                                * scanned */
-           slot = econtext->ecxt_scantuple;
-           break;
-   }
-
-   /* Apply the junkfilter if any */
-   if (wrvstate->wrv_junkFilter != NULL)
-       slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
-
-   tuple = ExecFetchSlotTuple(slot);
-   tupleDesc = slot->tts_tupleDescriptor;
-
-   /* wrv_tupdesc is a good enough representation of the Var's rowtype */
-   Assert(variable->vartype != RECORDOID);
-   var_tupdesc = wrvstate->wrv_tupdesc;
+       Form_pg_attribute dattr = dst_tupdesc->attrs[i];
+       Form_pg_attribute sattr = src_tupdesc->attrs[i];
 
-   /* Check to see if any dropped attributes are non-null */
-   for (i = 0; i < var_tupdesc->natts; i++)
-   {
-       Form_pg_attribute vattr = var_tupdesc->attrs[i];
-       Form_pg_attribute sattr = tupleDesc->attrs[i];
+       if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid))
+           continue;           /* no worries */
+       if (!dattr->attisdropped)
+           ereport(ERROR,
+                   (errcode(ERRCODE_DATATYPE_MISMATCH),
+                    errmsg("function return row and query-specified return row do not match"),
+                    errdetail("Returned type %s at ordinal position %d, but query expects %s.",
+                              format_type_be(sattr->atttypid),
+                              i + 1,
+                              format_type_be(dattr->atttypid))));
 
-       if (!vattr->attisdropped)
-           continue;           /* already checked non-dropped cols */
-       if (heap_attisnull(tuple, i + 1))
-           continue;           /* null is always okay */
-       if (vattr->attlen != sattr->attlen ||
-           vattr->attalign != sattr->attalign)
+       if (dattr->attlen != sattr->attlen ||
+           dattr->attalign != sattr->attalign)
            ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
-                    errmsg("table row type and query-specified row type do not match"),
+                    errmsg("function return row and query-specified return row do not match"),
                     errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
                               i + 1)));
    }
-
-   /*
-    * Copy the slot tuple and make sure any toasted fields get detoasted.
-    */
-   dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
-
-   /*
-    * Label the datum with the composite type info we identified before.
-    */
-   HeapTupleHeaderSetTypeId(dtuple, wrvstate->wrv_tupdesc->tdtypeid);
-   HeapTupleHeaderSetTypMod(dtuple, wrvstate->wrv_tupdesc->tdtypmod);
-
-   return PointerGetDatum(dtuple);
 }
 
-/* ----------------------------------------------------------------
- *     ExecEvalConst
- *
- *     Returns the value of a constant.
+/*
+ *     ExecMakeFunctionResultSet
  *
- *     Note that for pass-by-ref datatypes, we return a pointer to the
- *     actual constant node.  This is one of the reasons why functions
- *     must treat their input arguments as read-only.
- * ----------------------------------------------------------------
+ * Evaluate the arguments to a set-returning function and then call the
+ * function itself.  The argument expressions may not contain set-returning
+ * functions (the planner is supposed to have separated evaluation for those).
  */
-static Datum
-ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
-             bool *isNull)
+Datum
+ExecMakeFunctionResultSet(SetExprState *fcache,
+                         ExprContext *econtext,
+                         bool *isNull,
+                         ExprDoneCond *isDone)
 {
-   Const      *con = (Const *) exprstate->expr;
+   List       *arguments;
+   Datum       result;
+   FunctionCallInfo fcinfo;
+   PgStat_FunctionCallUsage fcusage;
+   ReturnSetInfo rsinfo;
+   bool        callit;
+   int         i;
 
-   *isNull = con->constisnull;
-   return con->constvalue;
-}
+restart:
 
-/* ----------------------------------------------------------------
- *     ExecEvalParamExec
- *
- *     Returns the value of a PARAM_EXEC parameter.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
-                 bool *isNull)
-{
-   Param      *expression = (Param *) exprstate->expr;
-   int         thisParamId = expression->paramid;
-   ParamExecData *prm;
+   /* Guard against stack overflow due to overly complex expressions */
+   check_stack_depth();
 
    /*
-    * PARAM_EXEC params (internal executor parameters) are stored in the
-    * ecxt_param_exec_vals array, and can be accessed by array index.
+    * If a previous call of the function returned a set result in the form of
+    * a tuplestore, continue reading rows from the tuplestore until it's
+    * empty.
     */
-   prm = &(econtext->ecxt_param_exec_vals[thisParamId]);
-   if (prm->execPlan != NULL)
+   if (fcache->funcResultStore)
    {
-       /* Parameter not evaluated yet, so go do it */
-       ExecSetParamPlan(prm->execPlan, econtext);
-       /* ExecSetParamPlan should have processed this param... */
-       Assert(prm->execPlan == NULL);
+       if (tuplestore_gettupleslot(fcache->funcResultStore, true, false,
+                                   fcache->funcResultSlot))
+       {
+           *isDone = ExprMultipleResult;
+           if (fcache->funcReturnsTuple)
+           {
+               /* We must return the whole tuple as a Datum. */
+               *isNull = false;
+               return ExecFetchSlotTupleDatum(fcache->funcResultSlot);
+           }
+           else
+           {
+               /* Extract the first column and return it as a scalar. */
+               return slot_getattr(fcache->funcResultSlot, 1, isNull);
+           }
+       }
+       /* Exhausted the tuplestore, so clean up */
+       tuplestore_end(fcache->funcResultStore);
+       fcache->funcResultStore = NULL;
+       *isDone = ExprEndResult;
+       *isNull = true;
+       return (Datum) 0;
    }
-   *isNull = prm->isnull;
-   return prm->value;
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalParamExtern
- *
- *     Returns the value of a PARAM_EXTERN parameter.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
-                   bool *isNull)
-{
-   Param      *expression = (Param *) exprstate->expr;
-   int         thisParamId = expression->paramid;
-   ParamListInfo paramInfo = econtext->ecxt_param_list_info;
 
    /*
-    * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
+    * arguments is a list of expressions to evaluate before passing to the
+    * function manager.  We skip the evaluation if it was already done in the
+    * previous call (ie, we are continuing the evaluation of a set-valued
+    * function).  Otherwise, collect the current argument values into fcinfo.
     */
-   if (paramInfo &&
-       thisParamId > 0 && thisParamId <= paramInfo->numParams)
+   fcinfo = &fcache->fcinfo_data;
+   arguments = fcache->args;
+   if (!fcache->setArgsValid)
+       ExecEvalFuncArgs(fcinfo, arguments, econtext);
+   else
    {
-       ParamExternData *prm = &paramInfo->params[thisParamId - 1];
+       /* Reset flag (we may set it again below) */
+       fcache->setArgsValid = false;
+   }
 
-       /* give hook a chance in case parameter is dynamic */
-       if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
-           (*paramInfo->paramFetch) (paramInfo, thisParamId);
+   /*
+    * Now call the function, passing the evaluated parameter values.
+    */
 
-       if (OidIsValid(prm->ptype))
-       {
-           /* safety check in case hook did something unexpected */
-           if (prm->ptype != expression->paramtype)
-               ereport(ERROR,
-                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                        errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
-                               thisParamId,
-                               format_type_be(prm->ptype),
-                               format_type_be(expression->paramtype))));
+   /* Prepare a resultinfo node for communication. */
+   fcinfo->resultinfo = (Node *) &rsinfo;
+   rsinfo.type = T_ReturnSetInfo;
+   rsinfo.econtext = econtext;
+   rsinfo.expectedDesc = fcache->funcResultDesc;
+   rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
+   /* note we do not set SFRM_Materialize_Random or _Preferred */
+   rsinfo.returnMode = SFRM_ValuePerCall;
+   /* isDone is filled below */
+   rsinfo.setResult = NULL;
+   rsinfo.setDesc = NULL;
 
-           *isNull = prm->isnull;
-           return prm->value;
+   /*
+    * If function is strict, and there are any NULL arguments, skip calling
+    * the function.
+    */
+   callit = true;
+   if (fcache->func.fn_strict)
+   {
+       for (i = 0; i < fcinfo->nargs; i++)
+       {
+           if (fcinfo->argnull[i])
+           {
+               callit = false;
+               break;
+           }
        }
    }
 
-   ereport(ERROR,
-           (errcode(ERRCODE_UNDEFINED_OBJECT),
-            errmsg("no value found for parameter %d", thisParamId)));
-   return (Datum) 0;           /* keep compiler quiet */
-}
-
-
-/* ----------------------------------------------------------------
- *     ExecEvalOper / ExecEvalFunc support routines
- * ----------------------------------------------------------------
- */
-
-/*
- *     GetAttributeByName
- *     GetAttributeByNum
- *
- *     These functions return the value of the requested attribute
- *     out of the given tuple Datum.
- *     C functions which take a tuple as an argument are expected
- *     to use these.  Ex: overpaid(EMP) might call GetAttributeByNum().
- *     Note: these are actually rather slow because they do a typcache
- *     lookup on each call.
- */
-Datum
-GetAttributeByNum(HeapTupleHeader tuple,
-                 AttrNumber attrno,
-                 bool *isNull)
-{
-   Datum       result;
-   Oid         tupType;
-   int32       tupTypmod;
-   TupleDesc   tupDesc;
-   HeapTupleData tmptup;
-
-   if (!AttributeNumberIsValid(attrno))
-       elog(ERROR, "invalid attribute number %d", attrno);
+   if (callit)
+   {
+       pgstat_init_function_usage(fcinfo, &fcusage);
 
-   if (isNull == NULL)
-       elog(ERROR, "a NULL isNull pointer was passed");
+       fcinfo->isnull = false;
+       rsinfo.isDone = ExprSingleResult;
+       result = FunctionCallInvoke(fcinfo);
+       *isNull = fcinfo->isnull;
+       *isDone = rsinfo.isDone;
 
-   if (tuple == NULL)
+       pgstat_end_function_usage(&fcusage,
+                                 rsinfo.isDone != ExprMultipleResult);
+   }
+   else
    {
-       /* Kinda bogus but compatible with old behavior... */
+       /* for a strict SRF, result for NULL is an empty set */
+       result = (Datum) 0;
        *isNull = true;
-       return (Datum) 0;
+       *isDone = ExprEndResult;
    }
 
-   tupType = HeapTupleHeaderGetTypeId(tuple);
-   tupTypmod = HeapTupleHeaderGetTypMod(tuple);
-   tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
-
-   /*
-    * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set all
-    * the fields in the struct just in case user tries to inspect system
-    * columns.
-    */
-   tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
-   ItemPointerSetInvalid(&(tmptup.t_self));
-   tmptup.t_tableOid = InvalidOid;
-   tmptup.t_data = tuple;
-
-   result = heap_getattr(&tmptup,
-                         attrno,
-                         tupDesc,
-                         isNull);
-
-   ReleaseTupleDesc(tupDesc);
+   /* Which protocol does function want to use? */
+   if (rsinfo.returnMode == SFRM_ValuePerCall)
+   {
+       if (*isDone != ExprEndResult)
+       {
+           /*
+            * Save the current argument values to re-use on the next call.
+            */
+           if (*isDone == ExprMultipleResult)
+           {
+               fcache->setArgsValid = true;
+               /* Register cleanup callback if we didn't already */
+               if (!fcache->shutdown_reg)
+               {
+                   RegisterExprContextCallback(econtext,
+                                               ShutdownSetExpr,
+                                               PointerGetDatum(fcache));
+                   fcache->shutdown_reg = true;
+               }
+           }
+       }
+   }
+   else if (rsinfo.returnMode == SFRM_Materialize)
+   {
+       /* check we're on the same page as the function author */
+       if (rsinfo.isDone != ExprSingleResult)
+           ereport(ERROR,
+                   (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
+                    errmsg("table-function protocol for materialize mode was not followed")));
+       if (rsinfo.setResult != NULL)
+       {
+           /* prepare to return values from the tuplestore */
+           ExecPrepareTuplestoreResult(fcache, econtext,
+                                       rsinfo.setResult,
+                                       rsinfo.setDesc);
+           /* loop back to top to start returning from tuplestore */
+           goto restart;
+       }
+       /* if setResult was left null, treat it as empty set */
+       *isDone = ExprEndResult;
+       *isNull = true;
+       result = (Datum) 0;
+   }
+   else
+       ereport(ERROR,
+               (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
+                errmsg("unrecognized table-function returnMode: %d",
+                       (int) rsinfo.returnMode)));
 
    return result;
 }
 
-Datum
-GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
+/*
+ * Prepare function call in nodeProjectSet.c (taretlist SRF) for execution.
+ */
+SetExprState *
+ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, PlanState *parent)
 {
-   AttrNumber  attrno;
-   Datum       result;
-   Oid         tupType;
-   int32       tupTypmod;
-   TupleDesc   tupDesc;
-   HeapTupleData tmptup;
-   int         i;
-
-   if (attname == NULL)
-       elog(ERROR, "invalid attribute name");
+   SetExprState *state = makeNode(SetExprState);
+   ListCell *lc;
 
-   if (isNull == NULL)
-       elog(ERROR, "a NULL isNull pointer was passed");
+   state->funcReturnsSet = true;
+   state->expr = expr;
+   state->func.fn_oid = InvalidOid;
 
-   if (tuple == NULL)
+   /*
+    * Initialize metadata.  The expression node could be either a FuncExpr or
+    * an OpExpr.
+    */
+   if (IsA(expr, FuncExpr))
    {
-       /* Kinda bogus but compatible with old behavior... */
-       *isNull = true;
-       return (Datum) 0;
-   }
-
-   tupType = HeapTupleHeaderGetTypeId(tuple);
-   tupTypmod = HeapTupleHeaderGetTypMod(tuple);
-   tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+       FuncExpr   *func = (FuncExpr *) expr;
 
-   attrno = InvalidAttrNumber;
-   for (i = 0; i < tupDesc->natts; i++)
-   {
-       if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0)
+       foreach(lc, func->args)
        {
-           attrno = tupDesc->attrs[i]->attnum;
-           break;
+           state->args = lappend(state->args, ExecInitExpr(lfirst(lc), parent));
        }
-   }
 
-   if (attrno == InvalidAttrNumber)
-       elog(ERROR, "attribute \"%s\" does not exist", attname);
+       init_sexpr(func->funcid, func->inputcollid, state,
+                  econtext->ecxt_per_query_memory, true, true);
+   }
+   else if (IsA(expr, OpExpr))
+   {
+       OpExpr     *op = (OpExpr *) expr;
 
-   /*
-    * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set all
-    * the fields in the struct just in case user tries to inspect system
-    * columns.
-    */
-   tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
-   ItemPointerSetInvalid(&(tmptup.t_self));
-   tmptup.t_tableOid = InvalidOid;
-   tmptup.t_data = tuple;
+       foreach(lc, op->args)
+       {
+           state->args = lappend(state->args, ExecInitExpr(lfirst(lc), parent));
+       }
 
-   result = heap_getattr(&tmptup,
-                         attrno,
-                         tupDesc,
-                         isNull);
+       init_sexpr(op->opfuncid, op->inputcollid, state,
+                  econtext->ecxt_per_query_memory, true, true);
+   }
+   else
+       elog(ERROR, "unrecognized node type: %d",
+            (int) nodeTag(expr));
 
-   ReleaseTupleDesc(tupDesc);
+   /* shouldn't get here otherwise */
+   Assert(state->func.fn_retset);
 
-   return result;
+   return state;
 }
 
 /*
- * init_fcache - initialize a FuncExprState node during first use
+ * Prepare function call in nodeFunctionscan.c (FROM function/ROWS FROM) for
+ * execution.
  */
-static void
-init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
-           MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF)
+SetExprState *
+ExecInitTableFunctionResult(Expr *expr, ExprContext *econtext, PlanState *parent)
 {
-   AclResult   aclresult;
+   SetExprState *state = makeNode(SetExprState);
+   ListCell *lc;
 
-   /* Check permission to call function */
-   aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
-   if (aclresult != ACLCHECK_OK)
-       aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid));
-   InvokeFunctionExecuteHook(foid);
+   state->funcReturnsSet = false;
+   state->expr = expr;
+   state->func.fn_oid = InvalidOid;
 
    /*
-    * Safety check on nargs.  Under normal circumstances this should never
-    * fail, as parser should check sooner.  But possibly it might fail if
-    * server has been compiled with FUNC_MAX_ARGS smaller than some functions
-    * declared in pg_proc?
+    * Normally the passed expression tree will be a FuncExpr, 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 (list_length(fcache->args) > FUNC_MAX_ARGS)
-       ereport(ERROR,
-               (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
-            errmsg_plural("cannot pass more than %d argument to a function",
-                          "cannot pass more than %d arguments to a function",
-                          FUNC_MAX_ARGS,
-                          FUNC_MAX_ARGS)));
-
-   /* Set up the primary fmgr lookup information */
-   fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
-   fmgr_info_set_expr((Node *) fcache->xprstate.expr, &(fcache->func));
-
-   /* Initialize the function call parameter struct as well */
-   InitFunctionCallInfoData(fcache->fcinfo_data, &(fcache->func),
-                            list_length(fcache->args),
-                            input_collation, NULL, NULL);
+   if (IsA(expr, FuncExpr))
+   {
+       FuncExpr   *func = (FuncExpr *) expr;
 
-   /* If function returns set, check if that's allowed by caller */
-   if (fcache->func.fn_retset && !allowSRF)
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("set-valued function called in context that cannot accept a set")));
+       state->funcReturnsSet = func->funcretset;
 
-   /* Otherwise, ExecInitExpr should have marked the fcache correctly */
-   Assert(fcache->func.fn_retset == fcache->funcReturnsSet);
+       foreach(lc, func->args)
+       {
+           state->args = lappend(state->args, ExecInitExpr(lfirst(lc), parent));
+       }
 
-   /* If function returns set, prepare expected tuple descriptor */
-   if (fcache->func.fn_retset && needDescForSRF)
+       init_sexpr(func->funcid, func->inputcollid, state,
+                  econtext->ecxt_per_query_memory, func->funcretset, false);
+   }
+   else
    {
-       TypeFuncClass functypclass;
-       Oid         funcrettype;
-       TupleDesc   tupdesc;
-       MemoryContext oldcontext;
+       state->elidedFuncState = ExecInitExpr(expr, parent);
+   }
 
-       functypclass = get_expr_result_type(fcache->func.fn_expr,
-                                           &funcrettype,
-                                           &tupdesc);
-
-       /* Must save tupdesc in fcache's context */
-       oldcontext = MemoryContextSwitchTo(fcacheCxt);
-
-       if (functypclass == TYPEFUNC_COMPOSITE)
-       {
-           /* Composite data type, e.g. a table's row type */
-           Assert(tupdesc);
-           /* Must copy it out of typcache for safety */
-           fcache->funcResultDesc = CreateTupleDescCopy(tupdesc);
-           fcache->funcReturnsTuple = true;
-       }
-       else if (functypclass == TYPEFUNC_SCALAR)
-       {
-           /* Base data type, i.e. scalar */
-           tupdesc = CreateTemplateTupleDesc(1, false);
-           TupleDescInitEntry(tupdesc,
-                              (AttrNumber) 1,
-                              NULL,
-                              funcrettype,
-                              -1,
-                              0);
-           fcache->funcResultDesc = tupdesc;
-           fcache->funcReturnsTuple = false;
-       }
-       else if (functypclass == TYPEFUNC_RECORD)
-       {
-           /* This will work if function doesn't need an expectedDesc */
-           fcache->funcResultDesc = NULL;
-           fcache->funcReturnsTuple = true;
-       }
-       else
-       {
-           /* Else, we will fail if function needs an expectedDesc */
-           fcache->funcResultDesc = NULL;
-       }
-
-       MemoryContextSwitchTo(oldcontext);
-   }
-   else
-       fcache->funcResultDesc = NULL;
-
-   /* Initialize additional state */
-   fcache->funcResultStore = NULL;
-   fcache->funcResultSlot = NULL;
-   fcache->shutdown_reg = false;
-}
-
-/*
- * callback function in case a FuncExpr returning a set needs to be shut down
- * before it has been run to completion
- */
-static void
-ShutdownFuncExpr(Datum arg)
-{
-   FuncExprState *fcache = (FuncExprState *) DatumGetPointer(arg);
-
-   /* If we have a slot, make sure it's let go of any tuplestore pointer */
-   if (fcache->funcResultSlot)
-       ExecClearTuple(fcache->funcResultSlot);
-
-   /* Release any open tuplestore */
-   if (fcache->funcResultStore)
-       tuplestore_end(fcache->funcResultStore);
-   fcache->funcResultStore = NULL;
-
-   /* Clear any active set-argument state */
-   fcache->setArgsValid = false;
-
-   /* execUtils will deregister the callback... */
-   fcache->shutdown_reg = false;
-}
-
-/*
- * get_cached_rowtype: utility function to lookup a rowtype tupdesc
- *
- * type_id, typmod: identity of the rowtype
- * cache_field: where to cache the TupleDesc pointer in expression state node
- *     (field must be initialized to NULL)
- * econtext: expression context we are executing in
- *
- * NOTE: because the shutdown callback will be called during plan rescan,
- * must be prepared to re-do this during any node execution; cannot call
- * just once during expression initialization
- */
-static TupleDesc
-get_cached_rowtype(Oid type_id, int32 typmod,
-                  TupleDesc *cache_field, ExprContext *econtext)
-{
-   TupleDesc   tupDesc = *cache_field;
-
-   /* Do lookup if no cached value or if requested type changed */
-   if (tupDesc == NULL ||
-       type_id != tupDesc->tdtypeid ||
-       typmod != tupDesc->tdtypmod)
-   {
-       tupDesc = lookup_rowtype_tupdesc(type_id, typmod);
-
-       if (*cache_field)
-       {
-           /* Release old tupdesc; but callback is already registered */
-           ReleaseTupleDesc(*cache_field);
-       }
-       else
-       {
-           /* Need to register shutdown callback to release tupdesc */
-           RegisterExprContextCallback(econtext,
-                                       ShutdownTupleDescRef,
-                                       PointerGetDatum(cache_field));
-       }
-       *cache_field = tupDesc;
-   }
-   return tupDesc;
-}
-
-/*
- * Callback function to release a tupdesc refcount at expression tree shutdown
- */
-static void
-ShutdownTupleDescRef(Datum arg)
-{
-   TupleDesc  *cache_field = (TupleDesc *) DatumGetPointer(arg);
-
-   if (*cache_field)
-       ReleaseTupleDesc(*cache_field);
-   *cache_field = NULL;
-}
-
-/*
- * Evaluate arguments for a function.
- */
-static void
-ExecEvalFuncArgs(FunctionCallInfo fcinfo,
-                List *argList,
-                ExprContext *econtext)
-{
-   int         i;
-   ListCell   *arg;
-
-   i = 0;
-   foreach(arg, argList)
-   {
-       ExprState  *argstate = (ExprState *) lfirst(arg);
-
-       fcinfo->arg[i] = ExecEvalExpr(argstate,
-                                     econtext,
-                                     &fcinfo->argnull[i]);
-       i++;
-   }
-
-   Assert(i == fcinfo->nargs);
-}
-
-/*
- *     ExecPrepareTuplestoreResult
- *
- * Subroutine for ExecMakeFunctionResultSet: prepare to extract rows from a
- * tuplestore function result.  We must set up a funcResultSlot (unless
- * already done in a previous call cycle) and verify that the function
- * returned the expected tuple descriptor.
- */
-static void
-ExecPrepareTuplestoreResult(FuncExprState *fcache,
-                           ExprContext *econtext,
-                           Tuplestorestate *resultStore,
-                           TupleDesc resultDesc)
-{
-   fcache->funcResultStore = resultStore;
-
-   if (fcache->funcResultSlot == NULL)
-   {
-       /* Create a slot so we can read data out of the tuplestore */
-       TupleDesc   slotDesc;
-       MemoryContext oldcontext;
-
-       oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt);
-
-       /*
-        * If we were not able to determine the result rowtype from context,
-        * and the function didn't return a tupdesc, we have to fail.
-        */
-       if (fcache->funcResultDesc)
-           slotDesc = fcache->funcResultDesc;
-       else if (resultDesc)
-       {
-           /* don't assume resultDesc is long-lived */
-           slotDesc = CreateTupleDescCopy(resultDesc);
-       }
-       else
-       {
-           ereport(ERROR,
-                   (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                    errmsg("function returning setof record called in "
-                           "context that cannot accept type record")));
-           slotDesc = NULL;    /* keep compiler quiet */
-       }
-
-       fcache->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
-       MemoryContextSwitchTo(oldcontext);
-   }
-
-   /*
-    * If function provided a tupdesc, cross-check it.  We only really need to
-    * do this for functions returning RECORD, but might as well do it always.
-    */
-   if (resultDesc)
-   {
-       if (fcache->funcResultDesc)
-           tupledesc_match(fcache->funcResultDesc, resultDesc);
-
-       /*
-        * If it is a dynamically-allocated TupleDesc, free it: it is
-        * typically allocated in a per-query context, so we must avoid
-        * leaking it across multiple usages.
-        */
-       if (resultDesc->tdrefcount == -1)
-           FreeTupleDesc(resultDesc);
-   }
-
-   /* Register cleanup callback if we didn't already */
-   if (!fcache->shutdown_reg)
-   {
-       RegisterExprContextCallback(econtext,
-                                   ShutdownFuncExpr,
-                                   PointerGetDatum(fcache));
-       fcache->shutdown_reg = true;
-   }
-}
-
-/*
- * Check that function result tuple type (src_tupdesc) matches or can
- * be considered to match what the query expects (dst_tupdesc). If
- * they don't match, ereport.
- *
- * We really only care about number of attributes and data type.
- * Also, we can ignore type mismatch on columns that are dropped in the
- * destination type, so long as the physical storage matches.  This is
- * helpful in some cases involving out-of-date cached plans.
- */
-static void
-tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
-{
-   int         i;
-
-   if (dst_tupdesc->natts != src_tupdesc->natts)
-       ereport(ERROR,
-               (errcode(ERRCODE_DATATYPE_MISMATCH),
-                errmsg("function return row and query-specified return row do not match"),
-                errdetail_plural("Returned row contains %d attribute, but query expects %d.",
-               "Returned row contains %d attributes, but query expects %d.",
-                                 src_tupdesc->natts,
-                                 src_tupdesc->natts, dst_tupdesc->natts)));
-
-   for (i = 0; i < dst_tupdesc->natts; i++)
-   {
-       Form_pg_attribute dattr = dst_tupdesc->attrs[i];
-       Form_pg_attribute sattr = src_tupdesc->attrs[i];
-
-       if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid))
-           continue;           /* no worries */
-       if (!dattr->attisdropped)
-           ereport(ERROR,
-                   (errcode(ERRCODE_DATATYPE_MISMATCH),
-                    errmsg("function return row and query-specified return row do not match"),
-                    errdetail("Returned type %s at ordinal position %d, but query expects %s.",
-                              format_type_be(sattr->atttypid),
-                              i + 1,
-                              format_type_be(dattr->atttypid))));
-
-       if (dattr->attlen != sattr->attlen ||
-           dattr->attalign != sattr->attalign)
-           ereport(ERROR,
-                   (errcode(ERRCODE_DATATYPE_MISMATCH),
-                    errmsg("function return row and query-specified return row do not match"),
-                    errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
-                              i + 1)));
-   }
-}
-
-/*
- *     ExecMakeFunctionResultSet
- *
- * Evaluate the arguments to a set-returning function and then call the
- * function itself.  The argument expressions may not contain set-returning
- * functions (the planner is supposed to have separated evaluation for those).
- */
-Datum
-ExecMakeFunctionResultSet(FuncExprState *fcache,
-                         ExprContext *econtext,
-                         bool *isNull,
-                         ExprDoneCond *isDone)
-{
-   List       *arguments;
-   Datum       result;
-   FunctionCallInfo fcinfo;
-   PgStat_FunctionCallUsage fcusage;
-   ReturnSetInfo rsinfo;
-   bool        callit;
-   int         i;
-
-restart:
-
-   /* Guard against stack overflow due to overly complex expressions */
-   check_stack_depth();
-
-   /*
-    * Initialize function cache if first time through.  The expression node
-    * could be either a FuncExpr or an OpExpr.
-    */
-   if (fcache->func.fn_oid == InvalidOid)
-   {
-       if (IsA(fcache->xprstate.expr, FuncExpr))
-       {
-           FuncExpr   *func = (FuncExpr *) fcache->xprstate.expr;
-
-           init_fcache(func->funcid, func->inputcollid, fcache,
-                       econtext->ecxt_per_query_memory, true, true);
-       }
-       else if (IsA(fcache->xprstate.expr, OpExpr))
-       {
-           OpExpr     *op = (OpExpr *) fcache->xprstate.expr;
-
-           init_fcache(op->opfuncid, op->inputcollid, fcache,
-                       econtext->ecxt_per_query_memory, true, true);
-       }
-       else
-           elog(ERROR, "unrecognized node type: %d",
-                (int) nodeTag(fcache->xprstate.expr));
-
-       /* shouldn't get here otherwise */
-       Assert(fcache->func.fn_retset);
-   }
-
-   /*
-    * If a previous call of the function returned a set result in the form of
-    * a tuplestore, continue reading rows from the tuplestore until it's
-    * empty.
-    */
-   if (fcache->funcResultStore)
-   {
-       if (tuplestore_gettupleslot(fcache->funcResultStore, true, false,
-                                   fcache->funcResultSlot))
-       {
-           *isDone = ExprMultipleResult;
-           if (fcache->funcReturnsTuple)
-           {
-               /* We must return the whole tuple as a Datum. */
-               *isNull = false;
-               return ExecFetchSlotTupleDatum(fcache->funcResultSlot);
-           }
-           else
-           {
-               /* Extract the first column and return it as a scalar. */
-               return slot_getattr(fcache->funcResultSlot, 1, isNull);
-           }
-       }
-       /* Exhausted the tuplestore, so clean up */
-       tuplestore_end(fcache->funcResultStore);
-       fcache->funcResultStore = NULL;
-       *isDone = ExprEndResult;
-       *isNull = true;
-       return (Datum) 0;
-   }
-
-   /*
-    * arguments is a list of expressions to evaluate before passing to the
-    * function manager.  We skip the evaluation if it was already done in the
-    * previous call (ie, we are continuing the evaluation of a set-valued
-    * function).  Otherwise, collect the current argument values into fcinfo.
-    */
-   fcinfo = &fcache->fcinfo_data;
-   arguments = fcache->args;
-   if (!fcache->setArgsValid)
-       ExecEvalFuncArgs(fcinfo, arguments, econtext);
-   else
-   {
-       /* Reset flag (we may set it again below) */
-       fcache->setArgsValid = false;
-   }
-
-   /*
-    * Now call the function, passing the evaluated parameter values.
-    */
-
-   /* Prepare a resultinfo node for communication. */
-   fcinfo->resultinfo = (Node *) &rsinfo;
-   rsinfo.type = T_ReturnSetInfo;
-   rsinfo.econtext = econtext;
-   rsinfo.expectedDesc = fcache->funcResultDesc;
-   rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
-   /* note we do not set SFRM_Materialize_Random or _Preferred */
-   rsinfo.returnMode = SFRM_ValuePerCall;
-   /* isDone is filled below */
-   rsinfo.setResult = NULL;
-   rsinfo.setDesc = NULL;
-
-   /*
-    * If function is strict, and there are any NULL arguments, skip calling
-    * the function.
-    */
-   callit = true;
-   if (fcache->func.fn_strict)
-   {
-       for (i = 0; i < fcinfo->nargs; i++)
-       {
-           if (fcinfo->argnull[i])
-           {
-               callit = false;
-               break;
-           }
-       }
-   }
-
-   if (callit)
-   {
-       pgstat_init_function_usage(fcinfo, &fcusage);
-
-       fcinfo->isnull = false;
-       rsinfo.isDone = ExprSingleResult;
-       result = FunctionCallInvoke(fcinfo);
-       *isNull = fcinfo->isnull;
-       *isDone = rsinfo.isDone;
-
-       pgstat_end_function_usage(&fcusage,
-                                 rsinfo.isDone != ExprMultipleResult);
-   }
-   else
-   {
-       /* for a strict SRF, result for NULL is an empty set */
-       result = (Datum) 0;
-       *isNull = true;
-       *isDone = ExprEndResult;
-   }
-
-   /* Which protocol does function want to use? */
-   if (rsinfo.returnMode == SFRM_ValuePerCall)
-   {
-       if (*isDone != ExprEndResult)
-       {
-           /*
-            * Save the current argument values to re-use on the next call.
-            */
-           if (*isDone == ExprMultipleResult)
-           {
-               fcache->setArgsValid = true;
-               /* Register cleanup callback if we didn't already */
-               if (!fcache->shutdown_reg)
-               {
-                   RegisterExprContextCallback(econtext,
-                                               ShutdownFuncExpr,
-                                               PointerGetDatum(fcache));
-                   fcache->shutdown_reg = true;
-               }
-           }
-       }
-   }
-   else if (rsinfo.returnMode == SFRM_Materialize)
-   {
-       /* check we're on the same page as the function author */
-       if (rsinfo.isDone != ExprSingleResult)
-           ereport(ERROR,
-                   (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
-                    errmsg("table-function protocol for materialize mode was not followed")));
-       if (rsinfo.setResult != NULL)
-       {
-           /* prepare to return values from the tuplestore */
-           ExecPrepareTuplestoreResult(fcache, econtext,
-                                       rsinfo.setResult,
-                                       rsinfo.setDesc);
-           /* loop back to top to start returning from tuplestore */
-           goto restart;
-       }
-       /* if setResult was left null, treat it as empty set */
-       *isDone = ExprEndResult;
-       *isNull = true;
-       result = (Datum) 0;
-   }
-   else
-       ereport(ERROR,
-               (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
-                errmsg("unrecognized table-function returnMode: %d",
-                       (int) rsinfo.returnMode)));
-
-   return result;
-}
-
-/*
- *     ExecMakeFunctionResultNoSets
- *
- * Evaluate a function or operator node with a non-set-returning function.
- * Assumes init_fcache() already done.  Hand-tuned for speed.
- */
-static Datum
-ExecMakeFunctionResultNoSets(FuncExprState *fcache,
-                            ExprContext *econtext,
-                            bool *isNull)
-{
-   ListCell   *arg;
-   Datum       result;
-   FunctionCallInfo fcinfo;
-   PgStat_FunctionCallUsage fcusage;
-   int         i;
-
-   /* Guard against stack overflow due to overly complex expressions */
-   check_stack_depth();
-
-   /* inlined, simplified version of ExecEvalFuncArgs */
-   fcinfo = &fcache->fcinfo_data;
-   i = 0;
-   foreach(arg, fcache->args)
-   {
-       ExprState  *argstate = (ExprState *) lfirst(arg);
-
-       fcinfo->arg[i] = ExecEvalExpr(argstate,
-                                     econtext,
-                                     &fcinfo->argnull[i]);
-       i++;
-   }
-
-   /*
-    * If function is strict, and there are any NULL arguments, skip calling
-    * the function and return NULL.
-    */
-   if (fcache->func.fn_strict)
-   {
-       while (--i >= 0)
-       {
-           if (fcinfo->argnull[i])
-           {
-               *isNull = true;
-               return (Datum) 0;
-           }
-       }
-   }
-
-   pgstat_init_function_usage(fcinfo, &fcusage);
-
-   fcinfo->isnull = false;
-   result = FunctionCallInvoke(fcinfo);
-   *isNull = fcinfo->isnull;
-
-   pgstat_end_function_usage(&fcusage, true);
-
-   return result;
-}
-
-
-/*
- *     ExecMakeTableFunctionResult
- *
- * Evaluate a table function, producing a materialized result in a Tuplestore
- * object.
- */
-Tuplestorestate *
-ExecMakeTableFunctionResult(ExprState *funcexpr,
-                           ExprContext *econtext,
-                           MemoryContext argContext,
-                           TupleDesc expectedDesc,
-                           bool randomAccess)
-{
-   Tuplestorestate *tupstore = NULL;
-   TupleDesc   tupdesc = NULL;
-   Oid         funcrettype;
-   bool        returnsTuple;
-   bool        returnsSet = false;
-   FunctionCallInfoData fcinfo;
-   PgStat_FunctionCallUsage fcusage;
-   ReturnSetInfo rsinfo;
-   HeapTupleData tmptup;
-   MemoryContext callerContext;
-   MemoryContext oldcontext;
-   bool        direct_function_call;
-   bool        first_time = true;
-
-   callerContext = CurrentMemoryContext;
-
-   funcrettype = exprType((Node *) funcexpr->expr);
-
-   returnsTuple = type_is_rowtype(funcrettype);
-
-   /*
-    * Prepare a resultinfo node for communication.  We always do this even if
-    * not expecting a set result, so that we can pass expectedDesc.  In the
-    * generic-expression case, the expression doesn't actually get to see the
-    * resultinfo, but set it up anyway because we use some of the fields as
-    * our own state variables.
-    */
-   rsinfo.type = T_ReturnSetInfo;
-   rsinfo.econtext = econtext;
-   rsinfo.expectedDesc = expectedDesc;
-   rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred);
-   if (randomAccess)
-       rsinfo.allowedModes |= (int) SFRM_Materialize_Random;
-   rsinfo.returnMode = SFRM_ValuePerCall;
-   /* isDone is filled below */
-   rsinfo.setResult = NULL;
-   rsinfo.setDesc = NULL;
-
-   /*
-    * Normally the passed expression tree will be a FuncExprState, since the
-    * grammar only allows a function call at the top level of a table
-    * function reference.  However, if the function doesn't return set then
-    * the planner might have replaced the function call via constant-folding
-    * or inlining.  So if we see any other kind of expression node, execute
-    * it via the general ExecEvalExpr() code; the only difference is that we
-    * don't get a chance to pass a special ReturnSetInfo to any functions
-    * buried in the expression.
-    */
-   if (funcexpr && IsA(funcexpr, FuncExprState) &&
-       IsA(funcexpr->expr, FuncExpr))
-   {
-       FuncExprState *fcache = (FuncExprState *) funcexpr;
-
-       /*
-        * This path is similar to ExecMakeFunctionResultSet.
-        */
-       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, func->inputcollid, fcache,
-                       econtext->ecxt_per_query_memory, true, false);
-       }
-       returnsSet = fcache->func.fn_retset;
-       InitFunctionCallInfoData(fcinfo, &(fcache->func),
-                                list_length(fcache->args),
-                                fcache->fcinfo_data.fncollation,
-                                NULL, (Node *) &rsinfo);
-
-       /*
-        * Evaluate the function's argument list.
-        *
-        * We can't do this in the per-tuple context: the argument values
-        * would disappear when we reset that context in the inner loop.  And
-        * the caller's CurrentMemoryContext is typically a query-lifespan
-        * context, so we don't want to leak memory there.  We require the
-        * caller to pass a separate memory context that can be used for this,
-        * and can be reset each time through to avoid bloat.
-        */
-       MemoryContextReset(argContext);
-       oldcontext = MemoryContextSwitchTo(argContext);
-       ExecEvalFuncArgs(&fcinfo, fcache->args, econtext);
-       MemoryContextSwitchTo(oldcontext);
-
-       /*
-        * If function is strict, and there are any NULL arguments, skip
-        * calling the function and act like it returned NULL (or an empty
-        * set, in the returns-set case).
-        */
-       if (fcache->func.fn_strict)
-       {
-           int         i;
-
-           for (i = 0; i < fcinfo.nargs; i++)
-           {
-               if (fcinfo.argnull[i])
-                   goto no_function_result;
-           }
-       }
-   }
-   else
-   {
-       /* Treat funcexpr as a generic expression */
-       direct_function_call = false;
-       InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-   }
-
-   /*
-    * Switch to short-lived context for calling the function or expression.
-    */
-   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;
-
-       CHECK_FOR_INTERRUPTS();
-
-       /*
-        * reset per-tuple memory context before each call of the function or
-        * expression. This cleans up any local memory the function may leak
-        * when called.
-        */
-       ResetExprContext(econtext);
-
-       /* Call the function or expression one time */
-       if (direct_function_call)
-       {
-           pgstat_init_function_usage(&fcinfo, &fcusage);
-
-           fcinfo.isnull = false;
-           rsinfo.isDone = ExprSingleResult;
-           result = FunctionCallInvoke(&fcinfo);
-
-           pgstat_end_function_usage(&fcusage,
-                                     rsinfo.isDone != ExprMultipleResult);
-       }
-       else
-       {
-           result = ExecEvalExpr(funcexpr, econtext, &fcinfo.isnull);
-           rsinfo.isDone = ExprSingleResult;
-       }
-
-       /* Which protocol does function want to use? */
-       if (rsinfo.returnMode == SFRM_ValuePerCall)
-       {
-           /*
-            * Check for end of result set.
-            */
-           if (rsinfo.isDone == ExprEndResult)
-               break;
-
-           /*
-            * If first time through, build tuplestore for result.  For a
-            * scalar function result type, also make a suitable tupdesc.
-            */
-           if (first_time)
-           {
-               oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-               tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
-               rsinfo.setResult = tupstore;
-               if (!returnsTuple)
-               {
-                   tupdesc = CreateTemplateTupleDesc(1, false);
-                   TupleDescInitEntry(tupdesc,
-                                      (AttrNumber) 1,
-                                      "column",
-                                      funcrettype,
-                                      -1,
-                                      0);
-                   rsinfo.setDesc = tupdesc;
-               }
-               MemoryContextSwitchTo(oldcontext);
-           }
-
-           /*
-            * Store current resultset item.
-            */
-           if (returnsTuple)
-           {
-               if (!fcinfo.isnull)
-               {
-                   HeapTupleHeader td = DatumGetHeapTupleHeader(result);
-
-                   if (tupdesc == NULL)
-                   {
-                       /*
-                        * This is the first non-NULL result from the
-                        * function.  Use the type info embedded in the
-                        * rowtype Datum to look up the needed tupdesc.  Make
-                        * a copy for the query.
-                        */
-                       oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-                       tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td),
-                                              HeapTupleHeaderGetTypMod(td));
-                       rsinfo.setDesc = tupdesc;
-                       MemoryContextSwitchTo(oldcontext);
-                   }
-                   else
-                   {
-                       /*
-                        * Verify all later returned rows have same subtype;
-                        * necessary in case the type is RECORD.
-                        */
-                       if (HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid ||
-                           HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod)
-                           ereport(ERROR,
-                                   (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                    errmsg("rows returned by function are not all of the same row type")));
-                   }
-
-                   /*
-                    * tuplestore_puttuple needs a HeapTuple not a bare
-                    * HeapTupleHeader, but it doesn't need all the fields.
-                    */
-                   tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
-                   tmptup.t_data = td;
-
-                   tuplestore_puttuple(tupstore, &tmptup);
-               }
-               else
-               {
-                   /*
-                    * NULL result from a tuple-returning function; expand it
-                    * to a row of all nulls.  We rely on the expectedDesc to
-                    * form such rows.  (Note: this would be problematic if
-                    * tuplestore_putvalues saved the tdtypeid/tdtypmod from
-                    * the provided descriptor, since that might not match
-                    * what we get from the function itself.  But it doesn't.)
-                    */
-                   int         natts = expectedDesc->natts;
-                   bool       *nullflags;
-
-                   nullflags = (bool *) palloc(natts * sizeof(bool));
-                   memset(nullflags, true, natts * sizeof(bool));
-                   tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
-               }
-           }
-           else
-           {
-               /* Scalar-type case: just store the function result */
-               tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull);
-           }
-
-           /*
-            * 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;
-   }
-
-no_function_result:
-
-   /*
-    * If we got nothing from the function (ie, an empty-set or NULL result),
-    * we have to create the tuplestore to return, and if it's a
-    * non-set-returning function then insert a single all-nulls row.  As
-    * above, we depend on the expectedDesc to manufacture the dummy row.
-    */
-   if (rsinfo.setResult == NULL)
-   {
-       MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-       tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
-       rsinfo.setResult = tupstore;
-       if (!returnsSet)
-       {
-           int         natts = expectedDesc->natts;
-           bool       *nullflags;
-
-           MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-           nullflags = (bool *) palloc(natts * sizeof(bool));
-           memset(nullflags, true, natts * sizeof(bool));
-           tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
-       }
-   }
-
-   /*
-    * If function provided a tupdesc, cross-check it.  We only really need to
-    * do this for functions returning RECORD, but might as well do it always.
-    */
-   if (rsinfo.setDesc)
-   {
-       tupledesc_match(expectedDesc, rsinfo.setDesc);
-
-       /*
-        * If it is a dynamically-allocated TupleDesc, free it: it is
-        * typically allocated in a per-query context, so we must avoid
-        * leaking it across multiple usages.
-        */
-       if (rsinfo.setDesc->tdrefcount == -1)
-           FreeTupleDesc(rsinfo.setDesc);
-   }
-
-   MemoryContextSwitchTo(callerContext);
-
-   /* All done, pass back the tuplestore */
-   return rsinfo.setResult;
-}
-
-
-/* ----------------------------------------------------------------
- *     ExecEvalFunc
- *     ExecEvalOper
- *
- *     Evaluate the functional result of a list of arguments by calling the
- *     function manager.
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- *     ExecEvalFunc
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalFunc(FuncExprState *fcache,
-            ExprContext *econtext,
-            bool *isNull)
-{
-   /* This is called only the first time through */
-   FuncExpr   *func = (FuncExpr *) fcache->xprstate.expr;
-
-   /* Initialize function lookup info */
-   init_fcache(func->funcid, func->inputcollid, fcache,
-               econtext->ecxt_per_query_memory, false, false);
-
-   /* Change the evalfunc pointer to save a few cycles in additional calls */
-   fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
-   return ExecMakeFunctionResultNoSets(fcache, econtext, isNull);
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalOper
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalOper(FuncExprState *fcache,
-            ExprContext *econtext,
-            bool *isNull)
-{
-   /* This is called only the first time through */
-   OpExpr     *op = (OpExpr *) fcache->xprstate.expr;
-
-   /* Initialize function lookup info */
-   init_fcache(op->opfuncid, op->inputcollid, fcache,
-               econtext->ecxt_per_query_memory, false, false);
-
-   /* Change the evalfunc pointer to save a few cycles in additional calls */
-   fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
-   return ExecMakeFunctionResultNoSets(fcache, econtext, isNull);
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalDistinct
- *
- * IS DISTINCT FROM must evaluate arguments to determine whether
- * they are NULL; if either is NULL then the result is already
- * known. If neither is NULL, then proceed to evaluate the
- * function. Note that this is *always* derived from the equals
- * operator, but since we need special processing of the arguments
- * we can not simply reuse ExecEvalOper() or ExecEvalFunc().
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalDistinct(FuncExprState *fcache,
-                ExprContext *econtext,
-                bool *isNull)
-{
-   Datum       result;
-   FunctionCallInfo fcinfo;
-
-   /* Set non-null as default */
-   *isNull = false;
-
-   /*
-    * Initialize function cache if first time through
-    */
-   if (fcache->func.fn_oid == InvalidOid)
-   {
-       DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr;
-
-       init_fcache(op->opfuncid, op->inputcollid, fcache,
-                   econtext->ecxt_per_query_memory, false, false);
-   }
-
-   /*
-    * Evaluate arguments
-    */
-   fcinfo = &fcache->fcinfo_data;
-   ExecEvalFuncArgs(fcinfo, fcache->args, econtext);
-   Assert(fcinfo->nargs == 2);
-
-   if (fcinfo->argnull[0] && fcinfo->argnull[1])
-   {
-       /* Both NULL? Then is not distinct... */
-       result = BoolGetDatum(FALSE);
-   }
-   else if (fcinfo->argnull[0] || fcinfo->argnull[1])
-   {
-       /* Only one is NULL? Then is distinct... */
-       result = BoolGetDatum(TRUE);
-   }
-   else
-   {
-       fcinfo->isnull = false;
-       result = FunctionCallInvoke(fcinfo);
-       *isNull = fcinfo->isnull;
-       /* Must invert result of "=" */
-       result = BoolGetDatum(!DatumGetBool(result));
-   }
-
-   return result;
-}
-
-/*
- * ExecEvalScalarArrayOp
- *
- * Evaluate "scalar op ANY/ALL (array)".  The operator always yields boolean,
- * and we combine the results across all array elements using OR and AND
- * (for ANY and ALL respectively).  Of course we short-circuit as soon as
- * the result is known.
- */
-static Datum
-ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
-                     ExprContext *econtext,
-                     bool *isNull)
-{
-   ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) sstate->fxprstate.xprstate.expr;
-   bool        useOr = opexpr->useOr;
-   ArrayType  *arr;
-   int         nitems;
-   Datum       result;
-   bool        resultnull;
-   FunctionCallInfo fcinfo;
-   int         i;
-   int16       typlen;
-   bool        typbyval;
-   char        typalign;
-   char       *s;
-   bits8      *bitmap;
-   int         bitmask;
-
-   /* Set non-null as default */
-   *isNull = false;
-
-   /*
-    * Initialize function cache if first time through
-    */
-   if (sstate->fxprstate.func.fn_oid == InvalidOid)
-   {
-       init_fcache(opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate,
-                   econtext->ecxt_per_query_memory, false, false);
-   }
-
-   /*
-    * Evaluate arguments
-    */
-   fcinfo = &sstate->fxprstate.fcinfo_data;
-   ExecEvalFuncArgs(fcinfo, sstate->fxprstate.args, econtext);
-   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; no
-    * point in iterating the loop.
-    */
-   if (fcinfo->argnull[0] && sstate->fxprstate.func.fn_strict)
-   {
-       *isNull = true;
-       return (Datum) 0;
-   }
-
-   /*
-    * We arrange to look up info about the element type only once per series
-    * of calls, assuming the element type doesn't change underneath us.
-    */
-   if (sstate->element_type != ARR_ELEMTYPE(arr))
-   {
-       get_typlenbyvalalign(ARR_ELEMTYPE(arr),
-                            &sstate->typlen,
-                            &sstate->typbyval,
-                            &sstate->typalign);
-       sstate->element_type = ARR_ELEMTYPE(arr);
-   }
-   typlen = sstate->typlen;
-   typbyval = sstate->typbyval;
-   typalign = sstate->typalign;
-
-   result = BoolGetDatum(!useOr);
-   resultnull = false;
-
-   /* Loop over the array elements */
-   s = (char *) ARR_DATA_PTR(arr);
-   bitmap = ARR_NULLBITMAP(arr);
-   bitmask = 1;
-
-   for (i = 0; i < nitems; i++)
-   {
-       Datum       elt;
-       Datum       thisresult;
-
-       /* Get array element, checking for NULL */
-       if (bitmap && (*bitmap & bitmask) == 0)
-       {
-           fcinfo->arg[1] = (Datum) 0;
-           fcinfo->argnull[1] = true;
-       }
-       else
-       {
-           elt = fetch_att(s, typbyval, typlen);
-           s = att_addlength_pointer(s, typlen, s);
-           s = (char *) att_align_nominal(s, typalign);
-           fcinfo->arg[1] = elt;
-           fcinfo->argnull[1] = false;
-       }
-
-       /* Call comparison function */
-       if (fcinfo->argnull[1] && sstate->fxprstate.func.fn_strict)
-       {
-           fcinfo->isnull = true;
-           thisresult = (Datum) 0;
-       }
-       else
-       {
-           fcinfo->isnull = false;
-           thisresult = FunctionCallInvoke(fcinfo);
-       }
-
-       /* Combine results per OR or AND semantics */
-       if (fcinfo->isnull)
-           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 */
-           }
-       }
-
-       /* advance bitmap pointer if any */
-       if (bitmap)
-       {
-           bitmask <<= 1;
-           if (bitmask == 0x100)
-           {
-               bitmap++;
-               bitmask = 1;
-           }
-       }
-   }
-
-   *isNull = resultnull;
-   return result;
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalNot
- *     ExecEvalOr
- *     ExecEvalAnd
- *
- *     Evaluate boolean expressions, with appropriate short-circuiting.
- *
- *     The query planner reformulates clause expressions in the
- *     qualification to conjunctive normal form.  If we ever get
- *     an AND to evaluate, we can be sure that it's not a top-level
- *     clause in the qualification, but appears lower (as a function
- *     argument, for example), or in the target list.  Not that you
- *     need to know this, mind you...
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
-           bool *isNull)
-{
-   ExprState  *clause = linitial(notclause->args);
-   Datum       expr_value;
-
-   expr_value = ExecEvalExpr(clause, econtext, isNull);
-
-   /*
-    * if the expression evaluates to null, then we just cascade the null back
-    * to whoever called us.
-    */
-   if (*isNull)
-       return expr_value;
-
-   /*
-    * evaluation of 'not' is simple.. expr is false, then return 'true' and
-    * vice versa.
-    */
-   return BoolGetDatum(!DatumGetBool(expr_value));
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalOr
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
-          bool *isNull)
-{
-   List       *clauses = orExpr->args;
-   ListCell   *clause;
-   bool        AnyNull;
-
-   AnyNull = false;
-
-   /*
-    * If any of the clauses is TRUE, the OR result is TRUE regardless of the
-    * states of the rest of the clauses, so we can stop evaluating and return
-    * TRUE immediately.  If none are TRUE and one or more is NULL, we return
-    * NULL; otherwise we return FALSE.  This makes sense when you interpret
-    * NULL as "don't know": if we have a TRUE then the OR is TRUE even if we
-    * aren't sure about some of the other inputs. If all the known inputs are
-    * FALSE, but we have one or more "don't knows", then we have to report
-    * that we "don't know" what the OR's result should be --- perhaps one of
-    * the "don't knows" would have been TRUE if we'd known its value.  Only
-    * when all the inputs are known to be FALSE can we state confidently that
-    * the OR's result is FALSE.
-    */
-   foreach(clause, clauses)
-   {
-       ExprState  *clausestate = (ExprState *) lfirst(clause);
-       Datum       clause_value;
-
-       clause_value = ExecEvalExpr(clausestate, econtext, isNull);
-
-       /*
-        * if we have a non-null true result, then return it.
-        */
-       if (*isNull)
-           AnyNull = true;     /* remember we got a null */
-       else if (DatumGetBool(clause_value))
-           return clause_value;
-   }
-
-   /* AnyNull is true if at least one clause evaluated to NULL */
-   *isNull = AnyNull;
-   return BoolGetDatum(false);
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalAnd
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
-           bool *isNull)
-{
-   List       *clauses = andExpr->args;
-   ListCell   *clause;
-   bool        AnyNull;
-
-   AnyNull = false;
-
-   /*
-    * If any of the clauses is FALSE, the AND result is FALSE regardless of
-    * the states of the rest of the clauses, so we can stop evaluating and
-    * return FALSE immediately.  If none are FALSE and one or more is NULL,
-    * we return NULL; otherwise we return TRUE.  This makes sense when you
-    * interpret NULL as "don't know", using the same sort of reasoning as for
-    * OR, above.
-    */
-
-   foreach(clause, clauses)
-   {
-       ExprState  *clausestate = (ExprState *) lfirst(clause);
-       Datum       clause_value;
-
-       clause_value = ExecEvalExpr(clausestate, econtext, isNull);
-
-       /*
-        * if we have a non-null false result, then return it.
-        */
-       if (*isNull)
-           AnyNull = true;     /* remember we got a null */
-       else if (!DatumGetBool(clause_value))
-           return clause_value;
-   }
-
-   /* AnyNull is true if at least one clause evaluated to NULL */
-   *isNull = AnyNull;
-   return BoolGetDatum(!AnyNull);
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalConvertRowtype
- *
- *     Evaluate a rowtype coercion operation.  This may require
- *     rearranging field positions.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
-                      ExprContext *econtext,
-                      bool *isNull)
-{
-   ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) cstate->xprstate.expr;
-   HeapTuple   result;
-   Datum       tupDatum;
-   HeapTupleHeader tuple;
-   HeapTupleData tmptup;
-
-   tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull);
-
-   /* this test covers the isDone exception too: */
-   if (*isNull)
-       return tupDatum;
-
-   tuple = DatumGetHeapTupleHeader(tupDatum);
-
-   /* Lookup tupdescs if first time through or after rescan */
-   if (cstate->indesc == NULL)
-   {
-       get_cached_rowtype(exprType((Node *) convert->arg), -1,
-                          &cstate->indesc, econtext);
-       cstate->initialized = false;
-   }
-   if (cstate->outdesc == NULL)
-   {
-       get_cached_rowtype(convert->resulttype, -1,
-                          &cstate->outdesc, econtext);
-       cstate->initialized = false;
-   }
-
-   /*
-    * We used to be able to assert that incoming tuples are marked with
-    * exactly the rowtype of cstate->indesc.  However, now that
-    * ExecEvalWholeRowVar might change the tuples' marking to plain RECORD
-    * due to inserting aliases, we can only make this weak test:
-    */
-   Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid ||
-          HeapTupleHeaderGetTypeId(tuple) == RECORDOID);
-
-   /* if first time through, initialize conversion map */
-   if (!cstate->initialized)
-   {
-       MemoryContext old_cxt;
-
-       /* allocate map in long-lived memory context */
-       old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-
-       /* prepare map from old to new attribute numbers */
-       cstate->map = convert_tuples_by_name(cstate->indesc,
-                                            cstate->outdesc,
-                                gettext_noop("could not convert row type"));
-       cstate->initialized = true;
-
-       MemoryContextSwitchTo(old_cxt);
-   }
-
-   /*
-    * No-op if no conversion needed (not clear this can happen here).
-    */
-   if (cstate->map == NULL)
-       return tupDatum;
-
-   /*
-    * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader.
-    */
-   tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
-   tmptup.t_data = tuple;
-
-   result = do_convert_tuple(&tmptup, cstate->map);
-
-   return HeapTupleGetDatum(result);
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalCase
- *
- *     Evaluate a CASE clause. Will have boolean expressions
- *     inside the WHEN clauses, and will have expressions
- *     for results.
- *     - thomas 1998-11-09
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
-            bool *isNull)
-{
-   List       *clauses = caseExpr->args;
-   ListCell   *clause;
-   Datum       save_datum;
-   bool        save_isNull;
-
-   /*
-    * If there's a test expression, we have to evaluate it and save the value
-    * where the CaseTestExpr placeholders can find it.  We must save and
-    * restore prior setting of econtext's caseValue fields, in case this node
-    * is itself within a larger CASE.  Furthermore, don't assign to the
-    * econtext fields until after returning from evaluation of the test
-    * expression.  We used to pass &econtext->caseValue_isNull to the
-    * recursive call, but that leads to aliasing that variable within said
-    * call, which can (and did) produce bugs when the test expression itself
-    * contains a CASE.
-    *
-    * If there's no test expression, we don't actually need to save and
-    * restore these fields; but it's less code to just do so unconditionally.
-    */
-   save_datum = econtext->caseValue_datum;
-   save_isNull = econtext->caseValue_isNull;
-
-   if (caseExpr->arg)
-   {
-       Datum       arg_value;
-       bool        arg_isNull;
-
-       arg_value = ExecEvalExpr(caseExpr->arg,
-                                econtext,
-                                &arg_isNull);
-       /* Since caseValue_datum may be read multiple times, force to R/O */
-       econtext->caseValue_datum =
-           MakeExpandedObjectReadOnly(arg_value,
-                                      arg_isNull,
-                                      caseExpr->argtyplen);
-       econtext->caseValue_isNull = arg_isNull;
-   }
-
-   /*
-    * we evaluate each of the WHEN clauses in turn, as soon as one is true we
-    * return the corresponding result. If none are true then we return the
-    * value of the default clause, or NULL if there is none.
-    */
-   foreach(clause, clauses)
-   {
-       CaseWhenState *wclause = lfirst(clause);
-       Datum       clause_value;
-       bool        clause_isNull;
-
-       clause_value = ExecEvalExpr(wclause->expr,
-                                   econtext,
-                                   &clause_isNull);
-
-       /*
-        * if we have a true test, then we return the result, since the case
-        * statement is satisfied.  A NULL result from the test is not
-        * considered true.
-        */
-       if (DatumGetBool(clause_value) && !clause_isNull)
-       {
-           econtext->caseValue_datum = save_datum;
-           econtext->caseValue_isNull = save_isNull;
-           return ExecEvalExpr(wclause->result,
-                               econtext,
-                               isNull);
-       }
-   }
-
-   econtext->caseValue_datum = save_datum;
-   econtext->caseValue_isNull = save_isNull;
-
-   if (caseExpr->defresult)
-   {
-       return ExecEvalExpr(caseExpr->defresult,
-                           econtext,
-                           isNull);
-   }
-
-   *isNull = true;
-   return (Datum) 0;
-}
-
-/*
- * ExecEvalCaseTestExpr
- *
- * Return the value stored by CASE.
- */
-static Datum
-ExecEvalCaseTestExpr(ExprState *exprstate,
-                    ExprContext *econtext,
-                    bool *isNull)
-{
-   *isNull = econtext->caseValue_isNull;
-   return econtext->caseValue_datum;
-}
-
-/*
- * ExecEvalGroupingFuncExpr
- *
- * Return a bitmask with a bit for each (unevaluated) argument expression
- * (rightmost arg is least significant bit).
- *
- * A bit is set if the corresponding expression is NOT part of the set of
- * grouping expressions in the current grouping set.
- */
-static Datum
-ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
-                        ExprContext *econtext,
-                        bool *isNull)
-{
-   int         result = 0;
-   int         attnum = 0;
-   Bitmapset  *grouped_cols = gstate->aggstate->grouped_cols;
-   ListCell   *lc;
-
-   *isNull = false;
-
-   foreach(lc, (gstate->clauses))
-   {
-       attnum = lfirst_int(lc);
-
-       result = result << 1;
-
-       if (!bms_is_member(attnum, grouped_cols))
-           result = result | 1;
-   }
-
-   return (Datum) result;
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalArray - ARRAY[] expressions
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
-             bool *isNull)
-{
-   ArrayExpr  *arrayExpr = (ArrayExpr *) astate->xprstate.expr;
-   ArrayType  *result;
-   ListCell   *element;
-   Oid         element_type = arrayExpr->element_typeid;
-   int         ndims = 0;
-   int         dims[MAXDIM];
-   int         lbs[MAXDIM];
-
-   /* Set non-null as default */
-   *isNull = false;
-
-   if (!arrayExpr->multidims)
-   {
-       /* Elements are presumably of scalar type */
-       int         nelems;
-       Datum      *dvalues;
-       bool       *dnulls;
-       int         i = 0;
-
-       ndims = 1;
-       nelems = list_length(astate->elements);
-
-       /* Shouldn't happen here, but if length is 0, return empty array */
-       if (nelems == 0)
-           return PointerGetDatum(construct_empty_array(element_type));
-
-       dvalues = (Datum *) palloc(nelems * sizeof(Datum));
-       dnulls = (bool *) palloc(nelems * sizeof(bool));
-
-       /* loop through and build array of datums */
-       foreach(element, astate->elements)
-       {
-           ExprState  *e = (ExprState *) lfirst(element);
-
-           dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i]);
-           i++;
-       }
-
-       /* setup for 1-D array of the given length */
-       dims[0] = nelems;
-       lbs[0] = 1;
-
-       result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
-                                   element_type,
-                                   astate->elemlength,
-                                   astate->elembyval,
-                                   astate->elemalign);
-   }
-   else
-   {
-       /* Must be nested array expressions */
-       int         nbytes = 0;
-       int         nitems = 0;
-       int         outer_nelems = 0;
-       int         elem_ndims = 0;
-       int        *elem_dims = NULL;
-       int        *elem_lbs = NULL;
-       bool        firstone = true;
-       bool        havenulls = false;
-       bool        haveempty = false;
-       char      **subdata;
-       bits8     **subbitmaps;
-       int        *subbytes;
-       int        *subnitems;
-       int         i;
-       int32       dataoffset;
-       char       *dat;
-       int         iitem;
-
-       i = list_length(astate->elements);
-       subdata = (char **) palloc(i * sizeof(char *));
-       subbitmaps = (bits8 **) palloc(i * sizeof(bits8 *));
-       subbytes = (int *) palloc(i * sizeof(int));
-       subnitems = (int *) palloc(i * sizeof(int));
-
-       /* loop through and get data area from each element */
-       foreach(element, astate->elements)
-       {
-           ExprState  *e = (ExprState *) lfirst(element);
-           bool        eisnull;
-           Datum       arraydatum;
-           ArrayType  *array;
-           int         this_ndims;
-
-           arraydatum = ExecEvalExpr(e, econtext, &eisnull);
-           /* temporarily ignore null subarrays */
-           if (eisnull)
-           {
-               haveempty = true;
-               continue;
-           }
-
-           array = DatumGetArrayTypeP(arraydatum);
-
-           /* run-time double-check on element type */
-           if (element_type != ARR_ELEMTYPE(array))
-               ereport(ERROR,
-                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                        errmsg("cannot merge incompatible arrays"),
-                        errdetail("Array with element type %s cannot be "
-                        "included in ARRAY construct with element type %s.",
-                                  format_type_be(ARR_ELEMTYPE(array)),
-                                  format_type_be(element_type))));
-
-           this_ndims = ARR_NDIM(array);
-           /* temporarily ignore zero-dimensional subarrays */
-           if (this_ndims <= 0)
-           {
-               haveempty = true;
-               continue;
-           }
-
-           if (firstone)
-           {
-               /* Get sub-array details from first member */
-               elem_ndims = this_ndims;
-               ndims = elem_ndims + 1;
-               if (ndims <= 0 || ndims > MAXDIM)
-                   ereport(ERROR,
-                           (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-                         errmsg("number of array dimensions (%d) exceeds " \
-                                "the maximum allowed (%d)", ndims, MAXDIM)));
-
-               elem_dims = (int *) palloc(elem_ndims * sizeof(int));
-               memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int));
-               elem_lbs = (int *) palloc(elem_ndims * sizeof(int));
-               memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int));
-
-               firstone = false;
-           }
-           else
-           {
-               /* Check other sub-arrays are compatible */
-               if (elem_ndims != this_ndims ||
-                   memcmp(elem_dims, ARR_DIMS(array),
-                          elem_ndims * sizeof(int)) != 0 ||
-                   memcmp(elem_lbs, ARR_LBOUND(array),
-                          elem_ndims * sizeof(int)) != 0)
-                   ereport(ERROR,
-                           (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
-                            errmsg("multidimensional arrays must have array "
-                                   "expressions with matching dimensions")));
-           }
-
-           subdata[outer_nelems] = ARR_DATA_PTR(array);
-           subbitmaps[outer_nelems] = ARR_NULLBITMAP(array);
-           subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array);
-           nbytes += subbytes[outer_nelems];
-           subnitems[outer_nelems] = ArrayGetNItems(this_ndims,
-                                                    ARR_DIMS(array));
-           nitems += subnitems[outer_nelems];
-           havenulls |= ARR_HASNULL(array);
-           outer_nelems++;
-       }
-
-       /*
-        * If all items were null or empty arrays, return an empty array;
-        * otherwise, if some were and some weren't, raise error.  (Note: we
-        * must special-case this somehow to avoid trying to generate a 1-D
-        * array formed from empty arrays.  It's not ideal...)
-        */
-       if (haveempty)
-       {
-           if (ndims == 0)     /* didn't find any nonempty array */
-               return PointerGetDatum(construct_empty_array(element_type));
-           ereport(ERROR,
-                   (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
-                    errmsg("multidimensional arrays must have array "
-                           "expressions with matching dimensions")));
-       }
-
-       /* setup for multi-D array */
-       dims[0] = outer_nelems;
-       lbs[0] = 1;
-       for (i = 1; i < ndims; i++)
-       {
-           dims[i] = elem_dims[i - 1];
-           lbs[i] = elem_lbs[i - 1];
-       }
-
-       if (havenulls)
-       {
-           dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
-           nbytes += dataoffset;
-       }
-       else
-       {
-           dataoffset = 0;     /* marker for no null bitmap */
-           nbytes += ARR_OVERHEAD_NONULLS(ndims);
-       }
-
-       result = (ArrayType *) palloc(nbytes);
-       SET_VARSIZE(result, nbytes);
-       result->ndim = ndims;
-       result->dataoffset = dataoffset;
-       result->elemtype = element_type;
-       memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
-       memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
-
-       dat = ARR_DATA_PTR(result);
-       iitem = 0;
-       for (i = 0; i < outer_nelems; i++)
-       {
-           memcpy(dat, subdata[i], subbytes[i]);
-           dat += subbytes[i];
-           if (havenulls)
-               array_bitmap_copy(ARR_NULLBITMAP(result), iitem,
-                                 subbitmaps[i], 0,
-                                 subnitems[i]);
-           iitem += subnitems[i];
-       }
-   }
-
-   return PointerGetDatum(result);
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalRow - ROW() expressions
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalRow(RowExprState *rstate,
-           ExprContext *econtext,
-           bool *isNull)
-{
-   HeapTuple   tuple;
-   Datum      *values;
-   bool       *isnull;
-   int         natts;
-   ListCell   *arg;
-   int         i;
-
-   /* Set non-null as default */
-   *isNull = false;
-
-   /* Allocate workspace */
-   natts = rstate->tupdesc->natts;
-   values = (Datum *) palloc0(natts * sizeof(Datum));
-   isnull = (bool *) palloc(natts * sizeof(bool));
-
-   /* preset to nulls in case rowtype has some later-added columns */
-   memset(isnull, true, natts * sizeof(bool));
-
-   /* Evaluate field values */
-   i = 0;
-   foreach(arg, rstate->args)
-   {
-       ExprState  *e = (ExprState *) lfirst(arg);
-
-       values[i] = ExecEvalExpr(e, econtext, &isnull[i]);
-       i++;
-   }
-
-   tuple = heap_form_tuple(rstate->tupdesc, values, isnull);
-
-   pfree(values);
-   pfree(isnull);
-
-   return HeapTupleGetDatum(tuple);
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalRowCompare - ROW() comparison-op ROW()
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalRowCompare(RowCompareExprState *rstate,
-                  ExprContext *econtext,
-                  bool *isNull)
-{
-   bool        result;
-   RowCompareType rctype = ((RowCompareExpr *) rstate->xprstate.expr)->rctype;
-   int32       cmpresult = 0;
-   ListCell   *l;
-   ListCell   *r;
-   int         i;
-
-   *isNull = true;             /* until we get a result */
-
-   i = 0;
-   forboth(l, rstate->largs, r, rstate->rargs)
-   {
-       ExprState  *le = (ExprState *) lfirst(l);
-       ExprState  *re = (ExprState *) lfirst(r);
-       FunctionCallInfoData locfcinfo;
-
-       InitFunctionCallInfoData(locfcinfo, &(rstate->funcs[i]), 2,
-                                rstate->collations[i],
-                                NULL, NULL);
-       locfcinfo.arg[0] = ExecEvalExpr(le, econtext,
-                                       &locfcinfo.argnull[0]);
-       locfcinfo.arg[1] = ExecEvalExpr(re, econtext,
-                                       &locfcinfo.argnull[1]);
-       if (rstate->funcs[i].fn_strict &&
-           (locfcinfo.argnull[0] || locfcinfo.argnull[1]))
-           return (Datum) 0;   /* force NULL result */
-       locfcinfo.isnull = false;
-       cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
-       if (locfcinfo.isnull)
-           return (Datum) 0;   /* force NULL result */
-       if (cmpresult != 0)
-           break;              /* no need to compare remaining columns */
-       i++;
-   }
-
-   switch (rctype)
-   {
-           /* EQ and NE cases aren't allowed here */
-       case ROWCOMPARE_LT:
-           result = (cmpresult < 0);
-           break;
-       case ROWCOMPARE_LE:
-           result = (cmpresult <= 0);
-           break;
-       case ROWCOMPARE_GE:
-           result = (cmpresult >= 0);
-           break;
-       case ROWCOMPARE_GT:
-           result = (cmpresult > 0);
-           break;
-       default:
-           elog(ERROR, "unrecognized RowCompareType: %d", (int) rctype);
-           result = 0;         /* keep compiler quiet */
-           break;
-   }
-
-   *isNull = false;
-   return BoolGetDatum(result);
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalCoalesce
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
-                bool *isNull)
-{
-   ListCell   *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);
-       if (!*isNull)
-           return value;
-   }
-
-   /* Else return NULL */
-   *isNull = true;
-   return (Datum) 0;
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalMinMax
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext,
-              bool *isNull)
-{
-   Datum       result = (Datum) 0;
-   MinMaxExpr *minmax = (MinMaxExpr *) minmaxExpr->xprstate.expr;
-   Oid         collation = minmax->inputcollid;
-   MinMaxOp    op = minmax->op;
-   FunctionCallInfoData locfcinfo;
-   ListCell   *arg;
-
-   *isNull = true;             /* until we get a result */
-
-   InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2,
-                            collation, NULL, NULL);
-   locfcinfo.argnull[0] = false;
-   locfcinfo.argnull[1] = false;
-
-   foreach(arg, minmaxExpr->args)
-   {
-       ExprState  *e = (ExprState *) lfirst(arg);
-       Datum       value;
-       bool        valueIsNull;
-       int32       cmpresult;
-
-       value = ExecEvalExpr(e, econtext, &valueIsNull);
-       if (valueIsNull)
-           continue;           /* ignore NULL inputs */
-
-       if (*isNull)
-       {
-           /* first nonnull input, adopt value */
-           result = value;
-           *isNull = false;
-       }
-       else
-       {
-           /* apply comparison function */
-           locfcinfo.arg[0] = result;
-           locfcinfo.arg[1] = value;
-           locfcinfo.isnull = false;
-           cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
-           if (locfcinfo.isnull)       /* probably should not happen */
-               continue;
-           if (cmpresult > 0 && op == IS_LEAST)
-               result = value;
-           else if (cmpresult < 0 && op == IS_GREATEST)
-               result = value;
-       }
-   }
-
-   return result;
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalSQLValueFunction
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalSQLValueFunction(ExprState *svfExpr,
-                        ExprContext *econtext,
-                        bool *isNull)
-{
-   Datum       result = (Datum) 0;
-   SQLValueFunction *svf = (SQLValueFunction *) svfExpr->expr;
-   FunctionCallInfoData fcinfo;
-
-   *isNull = false;
-
-   /*
-    * Note: current_schema() can return NULL.  current_user() etc currently
-    * cannot, but might as well code those cases the same way for safety.
-    */
-   switch (svf->op)
-   {
-       case SVFOP_CURRENT_DATE:
-           result = DateADTGetDatum(GetSQLCurrentDate());
-           break;
-       case SVFOP_CURRENT_TIME:
-       case SVFOP_CURRENT_TIME_N:
-           result = TimeTzADTPGetDatum(GetSQLCurrentTime(svf->typmod));
-           break;
-       case SVFOP_CURRENT_TIMESTAMP:
-       case SVFOP_CURRENT_TIMESTAMP_N:
-           result = TimestampTzGetDatum(GetSQLCurrentTimestamp(svf->typmod));
-           break;
-       case SVFOP_LOCALTIME:
-       case SVFOP_LOCALTIME_N:
-           result = TimeADTGetDatum(GetSQLLocalTime(svf->typmod));
-           break;
-       case SVFOP_LOCALTIMESTAMP:
-       case SVFOP_LOCALTIMESTAMP_N:
-           result = TimestampGetDatum(GetSQLLocalTimestamp(svf->typmod));
-           break;
-       case SVFOP_CURRENT_ROLE:
-       case SVFOP_CURRENT_USER:
-       case SVFOP_USER:
-           InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-           result = current_user(&fcinfo);
-           *isNull = fcinfo.isnull;
-           break;
-       case SVFOP_SESSION_USER:
-           InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-           result = session_user(&fcinfo);
-           *isNull = fcinfo.isnull;
-           break;
-       case SVFOP_CURRENT_CATALOG:
-           InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-           result = current_database(&fcinfo);
-           *isNull = fcinfo.isnull;
-           break;
-       case SVFOP_CURRENT_SCHEMA:
-           InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
-           result = current_schema(&fcinfo);
-           *isNull = fcinfo.isnull;
-           break;
-   }
-
-   return result;
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalXml
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
-           bool *isNull)
-{
-   XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
-   Datum       value;
-   bool        isnull;
-   ListCell   *arg;
-   ListCell   *narg;
-
-   *isNull = true;             /* until we get a result */
-
-   switch (xexpr->op)
-   {
-       case IS_XMLCONCAT:
-           {
-               List       *values = NIL;
-
-               foreach(arg, xmlExpr->args)
-               {
-                   ExprState  *e = (ExprState *) lfirst(arg);
-
-                   value = ExecEvalExpr(e, econtext, &isnull);
-                   if (!isnull)
-                       values = lappend(values, DatumGetPointer(value));
-               }
-
-               if (list_length(values) > 0)
-               {
-                   *isNull = false;
-                   return PointerGetDatum(xmlconcat(values));
-               }
-               else
-                   return (Datum) 0;
-           }
-           break;
-
-       case IS_XMLFOREST:
-           {
-               StringInfoData buf;
-
-               initStringInfo(&buf);
-               forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names)
-               {
-                   ExprState  *e = (ExprState *) lfirst(arg);
-                   char       *argname = strVal(lfirst(narg));
-
-                   value = ExecEvalExpr(e, econtext, &isnull);
-                   if (!isnull)
-                   {
-                       appendStringInfo(&buf, "<%s>%s</%s>",
-                                        argname,
-                                        map_sql_value_to_xml_value(value, exprType((Node *) e->expr), true),
-                                        argname);
-                       *isNull = false;
-                   }
-               }
-
-               if (*isNull)
-               {
-                   pfree(buf.data);
-                   return (Datum) 0;
-               }
-               else
-               {
-                   text       *result;
-
-                   result = cstring_to_text_with_len(buf.data, buf.len);
-                   pfree(buf.data);
-
-                   return PointerGetDatum(result);
-               }
-           }
-           break;
-
-       case IS_XMLELEMENT:
-           *isNull = false;
-           return PointerGetDatum(xmlelement(xmlExpr, econtext));
-           break;
-
-       case IS_XMLPARSE:
-           {
-               ExprState  *e;
-               text       *data;
-               bool        preserve_whitespace;
-
-               /* arguments are known to be text, bool */
-               Assert(list_length(xmlExpr->args) == 2);
-
-               e = (ExprState *) linitial(xmlExpr->args);
-               value = ExecEvalExpr(e, econtext, &isnull);
-               if (isnull)
-                   return (Datum) 0;
-               data = DatumGetTextPP(value);
-
-               e = (ExprState *) lsecond(xmlExpr->args);
-               value = ExecEvalExpr(e, econtext, &isnull);
-               if (isnull)     /* probably can't happen */
-                   return (Datum) 0;
-               preserve_whitespace = DatumGetBool(value);
-
-               *isNull = false;
-
-               return PointerGetDatum(xmlparse(data,
-                                               xexpr->xmloption,
-                                               preserve_whitespace));
-           }
-           break;
-
-       case IS_XMLPI:
-           {
-               ExprState  *e;
-               text       *arg;
-
-               /* optional argument is known to be text */
-               Assert(list_length(xmlExpr->args) <= 1);
-
-               if (xmlExpr->args)
-               {
-                   e = (ExprState *) linitial(xmlExpr->args);
-                   value = ExecEvalExpr(e, econtext, &isnull);
-                   if (isnull)
-                       arg = NULL;
-                   else
-                       arg = DatumGetTextPP(value);
-               }
-               else
-               {
-                   arg = NULL;
-                   isnull = false;
-               }
-
-               return PointerGetDatum(xmlpi(xexpr->name, arg, isnull, isNull));
-           }
-           break;
-
-       case IS_XMLROOT:
-           {
-               ExprState  *e;
-               xmltype    *data;
-               text       *version;
-               int         standalone;
-
-               /* arguments are known to be xml, text, int */
-               Assert(list_length(xmlExpr->args) == 3);
-
-               e = (ExprState *) linitial(xmlExpr->args);
-               value = ExecEvalExpr(e, econtext, &isnull);
-               if (isnull)
-                   return (Datum) 0;
-               data = DatumGetXmlP(value);
-
-               e = (ExprState *) lsecond(xmlExpr->args);
-               value = ExecEvalExpr(e, econtext, &isnull);
-               if (isnull)
-                   version = NULL;
-               else
-                   version = DatumGetTextPP(value);
-
-               e = (ExprState *) lthird(xmlExpr->args);
-               value = ExecEvalExpr(e, econtext, &isnull);
-               standalone = DatumGetInt32(value);
-
-               *isNull = false;
-
-               return PointerGetDatum(xmlroot(data,
-                                              version,
-                                              standalone));
-           }
-           break;
-
-       case IS_XMLSERIALIZE:
-           {
-               ExprState  *e;
-
-               /* argument type is known to be xml */
-               Assert(list_length(xmlExpr->args) == 1);
-
-               e = (ExprState *) linitial(xmlExpr->args);
-               value = ExecEvalExpr(e, econtext, &isnull);
-               if (isnull)
-                   return (Datum) 0;
-
-               *isNull = false;
-
-               return PointerGetDatum(xmltotext_with_xmloption(DatumGetXmlP(value), xexpr->xmloption));
-           }
-           break;
-
-       case IS_DOCUMENT:
-           {
-               ExprState  *e;
-
-               /* optional argument is known to be xml */
-               Assert(list_length(xmlExpr->args) == 1);
-
-               e = (ExprState *) linitial(xmlExpr->args);
-               value = ExecEvalExpr(e, econtext, &isnull);
-               if (isnull)
-                   return (Datum) 0;
-               else
-               {
-                   *isNull = false;
-                   return BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
-               }
-           }
-           break;
-   }
-
-   elog(ERROR, "unrecognized XML operation");
-   return (Datum) 0;
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalNullIf
- *
- * 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 *nullIfExpr,
-              ExprContext *econtext,
-              bool *isNull)
-{
-   Datum       result;
-   FunctionCallInfo fcinfo;
-
-   /*
-    * Initialize function cache if first time through
-    */
-   if (nullIfExpr->func.fn_oid == InvalidOid)
-   {
-       NullIfExpr *op = (NullIfExpr *) nullIfExpr->xprstate.expr;
-
-       init_fcache(op->opfuncid, op->inputcollid, nullIfExpr,
-                   econtext->ecxt_per_query_memory, false, false);
-   }
-
-   /*
-    * Evaluate arguments
-    */
-   fcinfo = &nullIfExpr->fcinfo_data;
-   ExecEvalFuncArgs(fcinfo, nullIfExpr->args, econtext);
-   Assert(fcinfo->nargs == 2);
-
-   /* if either argument is NULL they can't be equal */
-   if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
-   {
-       fcinfo->isnull = false;
-       result = FunctionCallInvoke(fcinfo);
-       /* if the arguments are equal return null */
-       if (!fcinfo->isnull && DatumGetBool(result))
-       {
-           *isNull = true;
-           return (Datum) 0;
-       }
-   }
-
-   /* else return first argument */
-   *isNull = fcinfo->argnull[0];
-   return fcinfo->arg[0];
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalNullTest
- *
- *     Evaluate a NullTest node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalNullTest(NullTestState *nstate,
-                ExprContext *econtext,
-                bool *isNull)
-{
-   NullTest   *ntest = (NullTest *) nstate->xprstate.expr;
-   Datum       result;
-
-   result = ExecEvalExpr(nstate->arg, econtext, isNull);
-
-   if (ntest->argisrow && !(*isNull))
-   {
-       /*
-        * The SQL standard defines IS [NOT] NULL for a non-null rowtype
-        * argument as:
-        *
-        * "R IS NULL" is true if every field is the null value.
-        *
-        * "R IS NOT NULL" is true if no field is the null value.
-        *
-        * This definition is (apparently intentionally) not recursive; so our
-        * tests on the fields are primitive attisnull tests, not recursive
-        * checks to see if they are all-nulls or no-nulls rowtypes.
-        *
-        * The standard does not consider the possibility of zero-field rows,
-        * but here we consider them to vacuously satisfy both predicates.
-        */
-       HeapTupleHeader tuple;
-       Oid         tupType;
-       int32       tupTypmod;
-       TupleDesc   tupDesc;
-       HeapTupleData tmptup;
-       int         att;
-
-       tuple = DatumGetHeapTupleHeader(result);
-
-       tupType = HeapTupleHeaderGetTypeId(tuple);
-       tupTypmod = HeapTupleHeaderGetTypMod(tuple);
-
-       /* Lookup tupdesc if first time through or if type changes */
-       tupDesc = get_cached_rowtype(tupType, tupTypmod,
-                                    &nstate->argdesc, econtext);
-
-       /*
-        * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
-        */
-       tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
-       tmptup.t_data = tuple;
-
-       for (att = 1; att <= tupDesc->natts; att++)
-       {
-           /* ignore dropped columns */
-           if (tupDesc->attrs[att - 1]->attisdropped)
-               continue;
-           if (heap_attisnull(&tmptup, att))
-           {
-               /* null field disproves IS NOT NULL */
-               if (ntest->nulltesttype == IS_NOT_NULL)
-                   return BoolGetDatum(false);
-           }
-           else
-           {
-               /* non-null field disproves IS NULL */
-               if (ntest->nulltesttype == IS_NULL)
-                   return BoolGetDatum(false);
-           }
-       }
-
-       return BoolGetDatum(true);
-   }
-   else
-   {
-       /* Simple scalar-argument case, or a null rowtype datum */
-       switch (ntest->nulltesttype)
-       {
-           case IS_NULL:
-               if (*isNull)
-               {
-                   *isNull = false;
-                   return BoolGetDatum(true);
-               }
-               else
-                   return BoolGetDatum(false);
-           case IS_NOT_NULL:
-               if (*isNull)
-               {
-                   *isNull = false;
-                   return BoolGetDatum(false);
-               }
-               else
-                   return BoolGetDatum(true);
-           default:
-               elog(ERROR, "unrecognized nulltesttype: %d",
-                    (int) ntest->nulltesttype);
-               return (Datum) 0;       /* keep compiler quiet */
-       }
-   }
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalBooleanTest
- *
- *     Evaluate a BooleanTest node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalBooleanTest(GenericExprState *bstate,
-                   ExprContext *econtext,
-                   bool *isNull)
-{
-   BooleanTest *btest = (BooleanTest *) bstate->xprstate.expr;
-   Datum       result;
-
-   result = ExecEvalExpr(bstate->arg, econtext, isNull);
-
-   switch (btest->booltesttype)
-   {
-       case IS_TRUE:
-           if (*isNull)
-           {
-               *isNull = false;
-               return BoolGetDatum(false);
-           }
-           else if (DatumGetBool(result))
-               return BoolGetDatum(true);
-           else
-               return BoolGetDatum(false);
-       case IS_NOT_TRUE:
-           if (*isNull)
-           {
-               *isNull = false;
-               return BoolGetDatum(true);
-           }
-           else if (DatumGetBool(result))
-               return BoolGetDatum(false);
-           else
-               return BoolGetDatum(true);
-       case IS_FALSE:
-           if (*isNull)
-           {
-               *isNull = false;
-               return BoolGetDatum(false);
-           }
-           else if (DatumGetBool(result))
-               return BoolGetDatum(false);
-           else
-               return BoolGetDatum(true);
-       case IS_NOT_FALSE:
-           if (*isNull)
-           {
-               *isNull = false;
-               return BoolGetDatum(true);
-           }
-           else if (DatumGetBool(result))
-               return BoolGetDatum(true);
-           else
-               return BoolGetDatum(false);
-       case IS_UNKNOWN:
-           if (*isNull)
-           {
-               *isNull = false;
-               return BoolGetDatum(true);
-           }
-           else
-               return BoolGetDatum(false);
-       case IS_NOT_UNKNOWN:
-           if (*isNull)
-           {
-               *isNull = false;
-               return BoolGetDatum(false);
-           }
-           else
-               return BoolGetDatum(true);
-       default:
-           elog(ERROR, "unrecognized booltesttype: %d",
-                (int) btest->booltesttype);
-           return (Datum) 0;   /* keep compiler quiet */
-   }
-}
-
-/*
- * ExecEvalCoerceToDomain
- *
- * Test the provided data against the domain constraint(s).  If the data
- * passes the constraint specifications, pass it through (return the
- * datum) otherwise throw an error.
- */
-static Datum
-ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
-                      bool *isNull)
-{
-   CoerceToDomain *ctest = (CoerceToDomain *) cstate->xprstate.expr;
-   Datum       result;
-   ListCell   *l;
-
-   result = ExecEvalExpr(cstate->arg, econtext, isNull);
-
-   /* Make sure we have up-to-date constraints */
-   UpdateDomainConstraintRef(cstate->constraint_ref);
-
-   foreach(l, cstate->constraint_ref->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)),
-                            errdatatype(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.
-                    *
-                    * Also, if we are working with a read-write expanded
-                    * datum, be sure that what we pass to CHECK expressions
-                    * is a read-only pointer; else called functions might
-                    * modify or even delete the expanded object.
-                    */
-                   save_datum = econtext->domainValue_datum;
-                   save_isNull = econtext->domainValue_isNull;
-
-                   econtext->domainValue_datum =
-                       MakeExpandedObjectReadOnly(result, *isNull,
-                                    cstate->constraint_ref->tcache->typlen);
-                   econtext->domainValue_isNull = *isNull;
-
-                   conResult = ExecEvalExpr(con->check_expr, econtext,
-                                            &conIsNull);
-
-                   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),
-                                errdomainconstraint(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(ExprState *exprstate,
-                           ExprContext *econtext,
-                           bool *isNull)
-{
-   *isNull = econtext->domainValue_isNull;
-   return econtext->domainValue_datum;
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalFieldSelect
- *
- *     Evaluate a FieldSelect node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalFieldSelect(FieldSelectState *fstate,
-                   ExprContext *econtext,
-                   bool *isNull)
-{
-   FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
-   AttrNumber  fieldnum = fselect->fieldnum;
-   Datum       result;
-   Datum       tupDatum;
-   HeapTupleHeader tuple;
-   Oid         tupType;
-   int32       tupTypmod;
-   TupleDesc   tupDesc;
-   Form_pg_attribute attr;
-   HeapTupleData tmptup;
-
-   tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull);
-
-   if (*isNull)
-       return tupDatum;
-
-   tuple = DatumGetHeapTupleHeader(tupDatum);
-
-   tupType = HeapTupleHeaderGetTypeId(tuple);
-   tupTypmod = HeapTupleHeaderGetTypMod(tuple);
-
-   /* Lookup tupdesc if first time through or if type changes */
-   tupDesc = get_cached_rowtype(tupType, tupTypmod,
-                                &fstate->argdesc, econtext);
-
-   /*
-    * Find field's attr record.  Note we don't support system columns here: a
-    * datum tuple doesn't have valid values for most of the interesting
-    * system columns anyway.
-    */
-   if (fieldnum <= 0)          /* should never happen */
-       elog(ERROR, "unsupported reference to system column %d in FieldSelect",
-            fieldnum);
-   if (fieldnum > tupDesc->natts)      /* should never happen */
-       elog(ERROR, "attribute number %d exceeds number of columns %d",
-            fieldnum, tupDesc->natts);
-   attr = tupDesc->attrs[fieldnum - 1];
-
-   /* Check for dropped column, and force a NULL result if so */
-   if (attr->attisdropped)
-   {
-       *isNull = true;
-       return (Datum) 0;
-   }
-
-   /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
-   /* As in ExecEvalScalarVar, we should but can't check typmod */
-   if (fselect->resulttype != attr->atttypid)
-       ereport(ERROR,
-               (errcode(ERRCODE_DATATYPE_MISMATCH),
-                errmsg("attribute %d has wrong type", fieldnum),
-                errdetail("Table has type %s, but query expects %s.",
-                          format_type_be(attr->atttypid),
-                          format_type_be(fselect->resulttype))));
-
-   /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
-   tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
-   tmptup.t_data = tuple;
-
-   result = heap_getattr(&tmptup,
-                         fieldnum,
-                         tupDesc,
-                         isNull);
-   return result;
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalFieldStore
- *
- *     Evaluate a FieldStore node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalFieldStore(FieldStoreState *fstate,
-                  ExprContext *econtext,
-                  bool *isNull)
-{
-   FieldStore *fstore = (FieldStore *) fstate->xprstate.expr;
-   HeapTuple   tuple;
-   Datum       tupDatum;
-   TupleDesc   tupDesc;
-   Datum      *values;
-   bool       *isnull;
-   Datum       save_datum;
-   bool        save_isNull;
-   ListCell   *l1,
-              *l2;
-
-   tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull);
-
-   /* Lookup tupdesc if first time through or after rescan */
-   tupDesc = get_cached_rowtype(fstore->resulttype, -1,
-                                &fstate->argdesc, econtext);
-
-   /* Allocate workspace */
-   values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
-   isnull = (bool *) palloc(tupDesc->natts * sizeof(bool));
-
-   if (!*isNull)
-   {
-       /*
-        * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
-        * set all the fields in the struct just in case.
-        */
-       HeapTupleHeader tuphdr;
-       HeapTupleData tmptup;
-
-       tuphdr = DatumGetHeapTupleHeader(tupDatum);
-       tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
-       ItemPointerSetInvalid(&(tmptup.t_self));
-       tmptup.t_tableOid = InvalidOid;
-       tmptup.t_data = tuphdr;
-
-       heap_deform_tuple(&tmptup, tupDesc, values, isnull);
-   }
-   else
-   {
-       /* Convert null input tuple into an all-nulls row */
-       memset(isnull, true, tupDesc->natts * sizeof(bool));
-   }
-
-   /* Result is never null */
-   *isNull = false;
-
-   save_datum = econtext->caseValue_datum;
-   save_isNull = econtext->caseValue_isNull;
-
-   forboth(l1, fstate->newvals, l2, fstore->fieldnums)
-   {
-       ExprState  *newval = (ExprState *) lfirst(l1);
-       AttrNumber  fieldnum = lfirst_int(l2);
-
-       Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
-
-       /*
-        * Use the CaseTestExpr mechanism to pass down the old value of the
-        * field being replaced; this is needed in case the newval is itself a
-        * FieldStore or ArrayRef that has to obtain and modify the old value.
-        * It's safe to reuse the CASE mechanism because there cannot be a
-        * CASE between here and where the value would be needed, and a field
-        * assignment can't be within a CASE either.  (So saving and restoring
-        * the caseValue is just paranoia, but let's do it anyway.)
-        */
-       econtext->caseValue_datum = values[fieldnum - 1];
-       econtext->caseValue_isNull = isnull[fieldnum - 1];
-
-       values[fieldnum - 1] = ExecEvalExpr(newval,
-                                           econtext,
-                                           &isnull[fieldnum - 1]);
-   }
-
-   econtext->caseValue_datum = save_datum;
-   econtext->caseValue_isNull = save_isNull;
-
-   tuple = heap_form_tuple(tupDesc, values, isnull);
-
-   pfree(values);
-   pfree(isnull);
-
-   return HeapTupleGetDatum(tuple);
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalRelabelType
- *
- *     Evaluate a RelabelType node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalRelabelType(GenericExprState *exprstate,
-                   ExprContext *econtext,
-                   bool *isNull)
-{
-   return ExecEvalExpr(exprstate->arg, econtext, isNull);
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalCoerceViaIO
- *
- *     Evaluate a CoerceViaIO node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
-                   ExprContext *econtext,
-                   bool *isNull)
-{
-   Datum       result;
-   Datum       inputval;
-   char       *string;
-
-   inputval = ExecEvalExpr(iostate->arg, econtext, isNull);
-
-   if (*isNull)
-       string = NULL;          /* output functions are not called on nulls */
-   else
-       string = OutputFunctionCall(&iostate->outfunc, inputval);
-
-   result = InputFunctionCall(&iostate->infunc,
-                              string,
-                              iostate->intypioparam,
-                              -1);
-
-   /* The input function cannot change the null/not-null status */
-   return result;
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalArrayCoerceExpr
- *
- *     Evaluate an ArrayCoerceExpr node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
-                       ExprContext *econtext,
-                       bool *isNull)
-{
-   ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) astate->xprstate.expr;
-   Datum       result;
-   FunctionCallInfoData locfcinfo;
-
-   result = ExecEvalExpr(astate->arg, econtext, isNull);
-
-   if (*isNull)
-       return result;          /* nothing to do */
-
-   /*
-    * If it's binary-compatible, modify the element type in the array header,
-    * but otherwise leave the array as we received it.
-    */
-   if (!OidIsValid(acoerce->elemfuncid))
-   {
-       /* Detoast input array if necessary, and copy in any case */
-       ArrayType  *array = DatumGetArrayTypePCopy(result);
-
-       ARR_ELEMTYPE(array) = astate->resultelemtype;
-       PG_RETURN_ARRAYTYPE_P(array);
-   }
-
-   /* Initialize function cache if first time through */
-   if (astate->elemfunc.fn_oid == InvalidOid)
-   {
-       AclResult   aclresult;
-
-       /* Check permission to call function */
-       aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(),
-                                    ACL_EXECUTE);
-       if (aclresult != ACLCHECK_OK)
-           aclcheck_error(aclresult, ACL_KIND_PROC,
-                          get_func_name(acoerce->elemfuncid));
-       InvokeFunctionExecuteHook(acoerce->elemfuncid);
-
-       /* Set up the primary fmgr lookup information */
-       fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
-                     econtext->ecxt_per_query_memory);
-       fmgr_info_set_expr((Node *) acoerce, &(astate->elemfunc));
-   }
-
-   /*
-    * Use array_map to apply the function to each array element.
-    *
-    * We pass on the desttypmod and isExplicit flags whether or not the
-    * function wants them.
-    *
-    * Note: coercion functions are assumed to not use collation.
-    */
-   InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3,
-                            InvalidOid, NULL, NULL);
-   locfcinfo.arg[0] = result;
-   locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
-   locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
-   locfcinfo.argnull[0] = false;
-   locfcinfo.argnull[1] = false;
-   locfcinfo.argnull[2] = false;
-
-   return array_map(&locfcinfo, astate->resultelemtype, astate->amstate);
-}
-
-/* ----------------------------------------------------------------
- *     ExecEvalCurrentOfExpr
- *
- * The planner should convert CURRENT OF into a TidScan qualification, or some
- * other special handling in a ForeignScan node.  So we have to be able to do
- * ExecInitExpr on a CurrentOfExpr, but we shouldn't ever actually execute it.
- * If we get here, we suppose we must be dealing with CURRENT OF on a foreign
- * table whose FDW doesn't handle it, and complain accordingly.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
-                     bool *isNull)
-{
-   ereport(ERROR,
-           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-          errmsg("WHERE CURRENT OF is not supported for this table type")));
-   return 0;                   /* keep compiler quiet */
-}
-
-
-/*
- * ExecEvalExprSwitchContext
- *
- * Same as ExecEvalExpr, but get into the right allocation context explicitly.
- */
-Datum
-ExecEvalExprSwitchContext(ExprState *expression,
-                         ExprContext *econtext,
-                         bool *isNull)
-{
-   Datum       retDatum;
-   MemoryContext oldContext;
-
-   oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-   retDatum = ExecEvalExpr(expression, econtext, isNull);
-   MemoryContextSwitchTo(oldContext);
-   return retDatum;
-}
-
-
-/*
- * ExecInitExpr: prepare an expression tree for execution
- *
- * This function builds and returns an ExprState tree paralleling the given
- * Expr node tree.  The ExprState tree can then be handed to ExecEvalExpr
- * for execution.  Because the Expr tree itself is read-only as far as
- * ExecInitExpr and ExecEvalExpr are concerned, several different executions
- * of the same plan tree can occur concurrently.
- *
- * This must be called in a memory context that will last as long as repeated
- * executions of the expression are needed.  Typically the context will be
- * the same as the per-query context of the associated ExprContext.
- *
- * Any Aggref, WindowFunc, or SubPlan nodes found in the tree are added to the
- * lists of such nodes held by the parent PlanState. Otherwise, we do very
- * little initialization here other than building the state-node tree.  Any
- * nontrivial work associated with initializing runtime info for a node should
- * happen during the first actual evaluation of that node.  (This policy lets
- * us avoid work if the node is never actually evaluated.)
- *
- * Note: there is no ExecEndExpr function; we assume that any resource
- * cleanup needed will be handled by just releasing the memory context
- * 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;
-
-   /* Guard against stack overflow due to overly complex expressions */
-   check_stack_depth();
-
-   switch (nodeTag(node))
-   {
-       case T_Var:
-           /* varattno == InvalidAttrNumber means it's a whole-row Var */
-           if (((Var *) node)->varattno == InvalidAttrNumber)
-           {
-               WholeRowVarExprState *wstate = makeNode(WholeRowVarExprState);
-
-               wstate->parent = parent;
-               wstate->wrv_tupdesc = NULL;
-               wstate->wrv_junkFilter = NULL;
-               state = (ExprState *) wstate;
-               state->evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowVar;
-           }
-           else
-           {
-               state = makeNode(ExprState);
-               state->evalfunc = ExecEvalScalarVar;
-           }
-           break;
-       case T_Const:
-           state = makeNode(ExprState);
-           state->evalfunc = ExecEvalConst;
-           break;
-       case T_Param:
-           state = makeNode(ExprState);
-           switch (((Param *) node)->paramkind)
-           {
-               case PARAM_EXEC:
-                   state->evalfunc = ExecEvalParamExec;
-                   break;
-               case PARAM_EXTERN:
-                   state->evalfunc = ExecEvalParamExtern;
-                   break;
-               default:
-                   elog(ERROR, "unrecognized paramkind: %d",
-                        (int) ((Param *) node)->paramkind);
-                   break;
-           }
-           break;
-       case T_CoerceToDomainValue:
-           state = makeNode(ExprState);
-           state->evalfunc = ExecEvalCoerceToDomainValue;
-           break;
-       case T_CaseTestExpr:
-           state = makeNode(ExprState);
-           state->evalfunc = ExecEvalCaseTestExpr;
-           break;
-       case T_Aggref:
-           {
-               AggrefExprState *astate = makeNode(AggrefExprState);
-
-               astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAggref;
-               if (parent && IsA(parent, AggState))
-               {
-                   AggState   *aggstate = (AggState *) parent;
-
-                   aggstate->aggs = lcons(astate, aggstate->aggs);
-                   aggstate->numaggs++;
-               }
-               else
-               {
-                   /* planner messed up */
-                   elog(ERROR, "Aggref found in non-Agg plan node");
-               }
-               state = (ExprState *) astate;
-           }
-           break;
-       case T_GroupingFunc:
-           {
-               GroupingFunc *grp_node = (GroupingFunc *) node;
-               GroupingFuncExprState *grp_state = makeNode(GroupingFuncExprState);
-               Agg        *agg = NULL;
-
-               if (!parent || !IsA(parent, AggState) ||!IsA(parent->plan, Agg))
-                   elog(ERROR, "parent of GROUPING is not Agg node");
-
-               grp_state->aggstate = (AggState *) parent;
-
-               agg = (Agg *) (parent->plan);
-
-               if (agg->groupingSets)
-                   grp_state->clauses = grp_node->cols;
-               else
-                   grp_state->clauses = NIL;
-
-               state = (ExprState *) grp_state;
-               state->evalfunc = (ExprStateEvalFunc) ExecEvalGroupingFuncExpr;
-           }
-           break;
-       case T_WindowFunc:
-           {
-               WindowFunc *wfunc = (WindowFunc *) node;
-               WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
-
-               wfstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWindowFunc;
-               if (parent && IsA(parent, WindowAggState))
-               {
-                   WindowAggState *winstate = (WindowAggState *) parent;
-                   int         nfuncs;
+   return state;
+}
 
-                   winstate->funcs = lcons(wfstate, winstate->funcs);
-                   nfuncs = ++winstate->numfuncs;
-                   if (wfunc->winagg)
-                       winstate->numaggs++;
 
-                   wfstate->args = (List *) ExecInitExpr((Expr *) wfunc->args,
-                                                         parent);
-                   wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter,
-                                                     parent);
+/*
+ *     ExecMakeTableFunctionResult
+ *
+ * Evaluate a table function, producing a materialized result in a Tuplestore
+ * object.
+ */
+Tuplestorestate *
+ExecMakeTableFunctionResult(SetExprState *setexpr,
+                           ExprContext *econtext,
+                           MemoryContext argContext,
+                           TupleDesc expectedDesc,
+                           bool randomAccess)
+{
+   Tuplestorestate *tupstore = NULL;
+   TupleDesc   tupdesc = NULL;
+   Oid         funcrettype;
+   bool        returnsTuple;
+   bool        returnsSet = false;
+   FunctionCallInfoData fcinfo;
+   PgStat_FunctionCallUsage fcusage;
+   ReturnSetInfo rsinfo;
+   HeapTupleData tmptup;
+   MemoryContext callerContext;
+   MemoryContext oldcontext;
+   bool        first_time = true;
 
-                   /*
-                    * Complain if the windowfunc's arguments contain any
-                    * windowfuncs; nested window functions are semantically
-                    * nonsensical.  (This should have been caught earlier,
-                    * but we defend against it here anyway.)
-                    */
-                   if (nfuncs != winstate->numfuncs)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_WINDOWING_ERROR),
-                         errmsg("window function calls cannot be nested")));
-               }
-               else
-               {
-                   /* planner messed up */
-                   elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
-               }
-               state = (ExprState *) wfstate;
-           }
-           break;
-       case T_ArrayRef:
-           {
-               ArrayRef   *aref = (ArrayRef *) node;
-               ArrayRefExprState *astate = makeNode(ArrayRefExprState);
+   callerContext = CurrentMemoryContext;
 
-               astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
-               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);
+   funcrettype = exprType((Node *) setexpr->expr);
 
-               fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFunc;
-               fstate->args = (List *)
-                   ExecInitExpr((Expr *) funcexpr->args, parent);
-               fstate->func.fn_oid = InvalidOid;       /* not initialized */
-               fstate->funcReturnsSet = funcexpr->funcretset;
-               state = (ExprState *) fstate;
-           }
-           break;
-       case T_OpExpr:
-           {
-               OpExpr     *opexpr = (OpExpr *) node;
-               FuncExprState *fstate = makeNode(FuncExprState);
+   returnsTuple = type_is_rowtype(funcrettype);
 
-               fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOper;
-               fstate->args = (List *)
-                   ExecInitExpr((Expr *) opexpr->args, parent);
-               fstate->func.fn_oid = InvalidOid;       /* not initialized */
-               fstate->funcReturnsSet = opexpr->opretset;
-               state = (ExprState *) fstate;
-           }
-           break;
-       case T_DistinctExpr:
-           {
-               DistinctExpr *distinctexpr = (DistinctExpr *) node;
-               FuncExprState *fstate = makeNode(FuncExprState);
+   /*
+    * 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.
+    */
+   rsinfo.type = T_ReturnSetInfo;
+   rsinfo.econtext = econtext;
+   rsinfo.expectedDesc = expectedDesc;
+   rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred);
+   if (randomAccess)
+       rsinfo.allowedModes |= (int) SFRM_Materialize_Random;
+   rsinfo.returnMode = SFRM_ValuePerCall;
+   /* isDone is filled below */
+   rsinfo.setResult = NULL;
+   rsinfo.setDesc = NULL;
 
-               fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalDistinct;
-               fstate->args = (List *)
-                   ExecInitExpr((Expr *) distinctexpr->args, parent);
-               fstate->func.fn_oid = InvalidOid;       /* not initialized */
-               fstate->funcReturnsSet = false; /* not supported */
-               state = (ExprState *) fstate;
-           }
-           break;
-       case T_NullIfExpr:
-           {
-               NullIfExpr *nullifexpr = (NullIfExpr *) node;
-               FuncExprState *fstate = makeNode(FuncExprState);
+   /*
+    * Normally the passed expression tree will be a SetExprState, 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 (!setexpr->elidedFuncState)
+   {
+       /*
+        * This path is similar to ExecMakeFunctionResultSet.
+        */
+       returnsSet = setexpr->funcReturnsSet;
+       InitFunctionCallInfoData(fcinfo, &(setexpr->func),
+                                list_length(setexpr->args),
+                                setexpr->fcinfo_data.fncollation,
+                                NULL, (Node *) &rsinfo);
 
-               fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
-               fstate->args = (List *)
-                   ExecInitExpr((Expr *) nullifexpr->args, parent);
-               fstate->func.fn_oid = InvalidOid;       /* not initialized */
-               fstate->funcReturnsSet = false; /* not supported */
-               state = (ExprState *) fstate;
-           }
-           break;
-       case T_ScalarArrayOpExpr:
-           {
-               ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
-               ScalarArrayOpExprState *sstate = makeNode(ScalarArrayOpExprState);
+       /*
+        * Evaluate the function's argument list.
+        *
+        * We can't do this in the per-tuple context: the argument values
+        * would disappear when we reset that context in the inner loop.  And
+        * the caller's CurrentMemoryContext is typically a query-lifespan
+        * context, so we don't want to leak memory there.  We require the
+        * caller to pass a separate memory context that can be used for this,
+        * and can be reset each time through to avoid bloat.
+        */
+       MemoryContextReset(argContext);
+       oldcontext = MemoryContextSwitchTo(argContext);
+       ExecEvalFuncArgs(&fcinfo, setexpr->args, econtext);
+       MemoryContextSwitchTo(oldcontext);
 
-               sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalScalarArrayOp;
-               sstate->fxprstate.args = (List *)
-                   ExecInitExpr((Expr *) opexpr->args, parent);
-               sstate->fxprstate.func.fn_oid = InvalidOid;     /* not initialized */
-               sstate->fxprstate.funcReturnsSet = false;       /* not supported */
-               sstate->element_type = InvalidOid;      /* ditto */
-               state = (ExprState *) sstate;
-           }
-           break;
-       case T_BoolExpr:
-           {
-               BoolExpr   *boolexpr = (BoolExpr *) node;
-               BoolExprState *bstate = makeNode(BoolExprState);
+       /*
+        * If function is strict, and there are any NULL arguments, skip
+        * calling the function and act like it returned NULL (or an empty
+        * set, in the returns-set case).
+        */
+       if (setexpr->func.fn_strict)
+       {
+           int         i;
 
-               switch (boolexpr->boolop)
-               {
-                   case AND_EXPR:
-                       bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAnd;
-                       break;
-                   case OR_EXPR:
-                       bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOr;
-                       break;
-                   case NOT_EXPR:
-                       bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNot;
-                       break;
-                   default:
-                       elog(ERROR, "unrecognized boolop: %d",
-                            (int) boolexpr->boolop);
-                       break;
-               }
-               bstate->args = (List *)
-                   ExecInitExpr((Expr *) boolexpr->args, parent);
-               state = (ExprState *) bstate;
-           }
-           break;
-       case T_SubPlan:
+           for (i = 0; i < fcinfo.nargs; i++)
            {
-               SubPlan    *subplan = (SubPlan *) node;
-               SubPlanState *sstate;
-
-               if (!parent)
-                   elog(ERROR, "SubPlan found with no parent plan");
-
-               sstate = ExecInitSubPlan(subplan, parent);
-
-               /* Add SubPlanState nodes to parent->subPlan */
-               parent->subPlan = lappend(parent->subPlan, sstate);
-
-               state = (ExprState *) sstate;
+               if (fcinfo.argnull[i])
+                   goto no_function_result;
            }
-           break;
-       case T_AlternativeSubPlan:
-           {
-               AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
-               AlternativeSubPlanState *asstate;
+       }
+   }
+   else
+   {
+       /* Treat setexpr as a generic expression */
+       InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+   }
 
-               if (!parent)
-                   elog(ERROR, "AlternativeSubPlan found with no parent plan");
+   /*
+    * Switch to short-lived context for calling the function or expression.
+    */
+   MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
 
-               asstate = ExecInitAlternativeSubPlan(asplan, parent);
+   /*
+    * Loop to handle the ValuePerCall protocol (which is also the same
+    * behavior needed in the generic ExecEvalExpr path).
+    */
+   for (;;)
+   {
+       Datum       result;
 
-               state = (ExprState *) asstate;
-           }
-           break;
-       case T_FieldSelect:
-           {
-               FieldSelect *fselect = (FieldSelect *) node;
-               FieldSelectState *fstate = makeNode(FieldSelectState);
+       CHECK_FOR_INTERRUPTS();
 
-               fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
-               fstate->arg = ExecInitExpr(fselect->arg, parent);
-               fstate->argdesc = NULL;
-               state = (ExprState *) fstate;
-           }
-           break;
-       case T_FieldStore:
-           {
-               FieldStore *fstore = (FieldStore *) node;
-               FieldStoreState *fstate = makeNode(FieldStoreState);
+       /*
+        * 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);
 
-               fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore;
-               fstate->arg = ExecInitExpr(fstore->arg, parent);
-               fstate->newvals = (List *) ExecInitExpr((Expr *) fstore->newvals, parent);
-               fstate->argdesc = NULL;
-               state = (ExprState *) fstate;
-           }
-           break;
-       case T_RelabelType:
-           {
-               RelabelType *relabel = (RelabelType *) node;
-               GenericExprState *gstate = makeNode(GenericExprState);
+       /* Call the function or expression one time */
+       if (!setexpr->elidedFuncState)
+       {
+           pgstat_init_function_usage(&fcinfo, &fcusage);
 
-               gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRelabelType;
-               gstate->arg = ExecInitExpr(relabel->arg, parent);
-               state = (ExprState *) gstate;
-           }
-           break;
-       case T_CoerceViaIO:
-           {
-               CoerceViaIO *iocoerce = (CoerceViaIO *) node;
-               CoerceViaIOState *iostate = makeNode(CoerceViaIOState);
-               Oid         iofunc;
-               bool        typisvarlena;
+           fcinfo.isnull = false;
+           rsinfo.isDone = ExprSingleResult;
+           result = FunctionCallInvoke(&fcinfo);
 
-               iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO;
-               iostate->arg = ExecInitExpr(iocoerce->arg, parent);
-               /* lookup the result type's input function */
-               getTypeInputInfo(iocoerce->resulttype, &iofunc,
-                                &iostate->intypioparam);
-               fmgr_info(iofunc, &iostate->infunc);
-               /* lookup the input type's output function */
-               getTypeOutputInfo(exprType((Node *) iocoerce->arg),
-                                 &iofunc, &typisvarlena);
-               fmgr_info(iofunc, &iostate->outfunc);
-               state = (ExprState *) iostate;
-           }
-           break;
-       case T_ArrayCoerceExpr:
-           {
-               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
-               ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
+           pgstat_end_function_usage(&fcusage,
+                                     rsinfo.isDone != ExprMultipleResult);
+       }
+       else
+       {
+           result =
+               ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.isnull);
+           rsinfo.isDone = ExprSingleResult;
+       }
 
-               astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
-               astate->arg = ExecInitExpr(acoerce->arg, parent);
-               astate->resultelemtype = get_element_type(acoerce->resulttype);
-               if (astate->resultelemtype == InvalidOid)
-                   ereport(ERROR,
-                           (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                            errmsg("target type is not an array")));
-               /* Arrays over domains aren't supported yet */
-               Assert(getBaseType(astate->resultelemtype) ==
-                      astate->resultelemtype);
-               astate->elemfunc.fn_oid = InvalidOid;   /* not initialized */
-               astate->amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState));
-               state = (ExprState *) astate;
-           }
-           break;
-       case T_ConvertRowtypeExpr:
-           {
-               ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
-               ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
+       /* Which protocol does function want to use? */
+       if (rsinfo.returnMode == SFRM_ValuePerCall)
+       {
+           /*
+            * Check for end of result set.
+            */
+           if (rsinfo.isDone == ExprEndResult)
+               break;
 
-               cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
-               cstate->arg = ExecInitExpr(convert->arg, parent);
-               state = (ExprState *) cstate;
-           }
-           break;
-       case T_CaseExpr:
+           /*
+            * If first time through, build tuplestore for result.  For a
+            * scalar function result type, also make a suitable tupdesc.
+            */
+           if (first_time)
            {
-               CaseExpr   *caseexpr = (CaseExpr *) node;
-               CaseExprState *cstate = makeNode(CaseExprState);
-               List       *outlist = NIL;
-               ListCell   *l;
-
-               cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase;
-               cstate->arg = ExecInitExpr(caseexpr->arg, parent);
-               foreach(l, caseexpr->args)
+               oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+               tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
+               rsinfo.setResult = tupstore;
+               if (!returnsTuple)
                {
-                   CaseWhen   *when = castNode(CaseWhen, lfirst(l));
-                   CaseWhenState *wstate = makeNode(CaseWhenState);
-
-                   wstate->xprstate.evalfunc = NULL;   /* not used */
-                   wstate->xprstate.expr = (Expr *) when;
-                   wstate->expr = ExecInitExpr(when->expr, parent);
-                   wstate->result = ExecInitExpr(when->result, parent);
-                   outlist = lappend(outlist, wstate);
+                   tupdesc = CreateTemplateTupleDesc(1, false);
+                   TupleDescInitEntry(tupdesc,
+                                      (AttrNumber) 1,
+                                      "column",
+                                      funcrettype,
+                                      -1,
+                                      0);
+                   rsinfo.setDesc = tupdesc;
                }
-               cstate->args = outlist;
-               cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
-               if (caseexpr->arg)
-                   cstate->argtyplen = get_typlen(exprType((Node *) caseexpr->arg));
-               state = (ExprState *) cstate;
+               MemoryContextSwitchTo(oldcontext);
            }
-           break;
-       case T_ArrayExpr:
-           {
-               ArrayExpr  *arrayexpr = (ArrayExpr *) node;
-               ArrayExprState *astate = makeNode(ArrayExprState);
-               List       *outlist = NIL;
-               ListCell   *l;
 
-               astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArray;
-               foreach(l, arrayexpr->elements)
-               {
-                   Expr       *e = (Expr *) lfirst(l);
-                   ExprState  *estate;
-
-                   estate = ExecInitExpr(e, parent);
-                   outlist = lappend(outlist, estate);
-               }
-               astate->elements = 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_RowExpr:
+           /*
+            * Store current resultset item.
+            */
+           if (returnsTuple)
            {
-               RowExpr    *rowexpr = (RowExpr *) node;
-               RowExprState *rstate = makeNode(RowExprState);
-               Form_pg_attribute *attrs;
-               List       *outlist = NIL;
-               ListCell   *l;
-               int         i;
-
-               rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRow;
-               /* Build tupdesc to describe result tuples */
-               if (rowexpr->row_typeid == RECORDOID)
-               {
-                   /* generic record, use types of given expressions */
-                   rstate->tupdesc = ExecTypeFromExprList(rowexpr->args);
-               }
-               else
-               {
-                   /* it's been cast to a named type, use that */
-                   rstate->tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
-               }
-               /* In either case, adopt RowExpr's column aliases */
-               ExecTypeSetColNames(rstate->tupdesc, rowexpr->colnames);
-               /* Bless the tupdesc in case it's now of type RECORD */
-               BlessTupleDesc(rstate->tupdesc);
-               /* Set up evaluation, skipping any deleted columns */
-               Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts);
-               attrs = rstate->tupdesc->attrs;
-               i = 0;
-               foreach(l, rowexpr->args)
+               if (!fcinfo.isnull)
                {
-                   Expr       *e = (Expr *) lfirst(l);
-                   ExprState  *estate;
+                   HeapTupleHeader td = DatumGetHeapTupleHeader(result);
 
-                   if (!attrs[i]->attisdropped)
+                   if (tupdesc == NULL)
                    {
                        /*
-                        * Guard against ALTER COLUMN TYPE on rowtype since
-                        * the RowExpr was created.  XXX should we check
-                        * typmod too?  Not sure we can be sure it'll be the
-                        * same.
+                        * This is the first non-NULL result from the
+                        * function.  Use the type info embedded in the
+                        * rowtype Datum to look up the needed tupdesc.  Make
+                        * a copy for the query.
                         */
-                       if (exprType((Node *) e) != attrs[i]->atttypid)
-                           ereport(ERROR,
-                                   (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                    errmsg("ROW() column has type %s instead of type %s",
-                                       format_type_be(exprType((Node *) e)),
-                                      format_type_be(attrs[i]->atttypid))));
+                       oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+                       tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td),
+                                              HeapTupleHeaderGetTypMod(td));
+                       rsinfo.setDesc = tupdesc;
+                       MemoryContextSwitchTo(oldcontext);
                    }
                    else
                    {
                        /*
-                        * Ignore original expression and insert a NULL. We
-                        * don't really care what type of NULL it is, so
-                        * always make an int4 NULL.
+                        * Verify all later returned rows have same subtype;
+                        * necessary in case the type is RECORD.
                         */
-                       e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
+                       if (HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid ||
+                           HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod)
+                           ereport(ERROR,
+                                   (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                    errmsg("rows returned by function are not all of the same row type")));
                    }
-                   estate = ExecInitExpr(e, parent);
-                   outlist = lappend(outlist, estate);
-                   i++;
-               }
-               rstate->args = outlist;
-               state = (ExprState *) rstate;
-           }
-           break;
-       case T_RowCompareExpr:
-           {
-               RowCompareExpr *rcexpr = (RowCompareExpr *) node;
-               RowCompareExprState *rstate = makeNode(RowCompareExprState);
-               int         nopers = list_length(rcexpr->opnos);
-               List       *outlist;
-               ListCell   *l;
-               ListCell   *l2;
-               ListCell   *l3;
-               int         i;
-
-               rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRowCompare;
-               Assert(list_length(rcexpr->largs) == nopers);
-               outlist = NIL;
-               foreach(l, rcexpr->largs)
-               {
-                   Expr       *e = (Expr *) lfirst(l);
-                   ExprState  *estate;
-
-                   estate = ExecInitExpr(e, parent);
-                   outlist = lappend(outlist, estate);
-               }
-               rstate->largs = outlist;
-               Assert(list_length(rcexpr->rargs) == nopers);
-               outlist = NIL;
-               foreach(l, rcexpr->rargs)
-               {
-                   Expr       *e = (Expr *) lfirst(l);
-                   ExprState  *estate;
-
-                   estate = ExecInitExpr(e, parent);
-                   outlist = lappend(outlist, estate);
-               }
-               rstate->rargs = outlist;
-               Assert(list_length(rcexpr->opfamilies) == nopers);
-               rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo));
-               rstate->collations = (Oid *) palloc(nopers * sizeof(Oid));
-               i = 0;
-               forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->inputcollids)
-               {
-                   Oid         opno = lfirst_oid(l);
-                   Oid         opfamily = lfirst_oid(l2);
-                   Oid         inputcollid = lfirst_oid(l3);
-                   int         strategy;
-                   Oid         lefttype;
-                   Oid         righttype;
-                   Oid         proc;
-
-                   get_op_opfamily_properties(opno, opfamily, false,
-                                              &strategy,
-                                              &lefttype,
-                                              &righttype);
-                   proc = get_opfamily_proc(opfamily,
-                                            lefttype,
-                                            righttype,
-                                            BTORDER_PROC);
 
                    /*
-                    * If we enforced permissions checks on index support
-                    * functions, we'd need to make a check here.  But the
-                    * index support machinery doesn't do that, and neither
-                    * does this code.
+                    * tuplestore_puttuple needs a HeapTuple not a bare
+                    * HeapTupleHeader, but it doesn't need all the fields.
                     */
-                   fmgr_info(proc, &(rstate->funcs[i]));
-                   rstate->collations[i] = inputcollid;
-                   i++;
-               }
-               state = (ExprState *) rstate;
-           }
-           break;
-       case T_CoalesceExpr:
-           {
-               CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
-               CoalesceExprState *cstate = makeNode(CoalesceExprState);
-               List       *outlist = NIL;
-               ListCell   *l;
-
-               cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoalesce;
-               foreach(l, coalesceexpr->args)
-               {
-                   Expr       *e = (Expr *) lfirst(l);
-                   ExprState  *estate;
-
-                   estate = ExecInitExpr(e, parent);
-                   outlist = lappend(outlist, estate);
-               }
-               cstate->args = outlist;
-               state = (ExprState *) cstate;
-           }
-           break;
-       case T_MinMaxExpr:
-           {
-               MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
-               MinMaxExprState *mstate = makeNode(MinMaxExprState);
-               List       *outlist = NIL;
-               ListCell   *l;
-               TypeCacheEntry *typentry;
-
-               mstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalMinMax;
-               foreach(l, minmaxexpr->args)
-               {
-                   Expr       *e = (Expr *) lfirst(l);
-                   ExprState  *estate;
-
-                   estate = ExecInitExpr(e, parent);
-                   outlist = lappend(outlist, estate);
-               }
-               mstate->args = outlist;
-               /* Look up the btree comparison function for the datatype */
-               typentry = lookup_type_cache(minmaxexpr->minmaxtype,
-                                            TYPECACHE_CMP_PROC);
-               if (!OidIsValid(typentry->cmp_proc))
-                   ereport(ERROR,
-                           (errcode(ERRCODE_UNDEFINED_FUNCTION),
-                            errmsg("could not identify a comparison function for type %s",
-                                   format_type_be(minmaxexpr->minmaxtype))));
-
-               /*
-                * If we enforced permissions checks on index support
-                * functions, we'd need to make a check here.  But the index
-                * support machinery doesn't do that, and neither does this
-                * code.
-                */
-               fmgr_info(typentry->cmp_proc, &(mstate->cfunc));
-               state = (ExprState *) mstate;
-           }
-           break;
-       case T_SQLValueFunction:
-           state = makeNode(ExprState);
-           state->evalfunc = ExecEvalSQLValueFunction;
-           break;
-       case T_XmlExpr:
-           {
-               XmlExpr    *xexpr = (XmlExpr *) node;
-               XmlExprState *xstate = makeNode(XmlExprState);
-               List       *outlist;
-               ListCell   *arg;
-
-               xstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalXml;
-               outlist = NIL;
-               foreach(arg, xexpr->named_args)
-               {
-                   Expr       *e = (Expr *) lfirst(arg);
-                   ExprState  *estate;
+                   tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+                   tmptup.t_data = td;
 
-                   estate = ExecInitExpr(e, parent);
-                   outlist = lappend(outlist, estate);
+                   tuplestore_puttuple(tupstore, &tmptup);
                }
-               xstate->named_args = outlist;
-
-               outlist = NIL;
-               foreach(arg, xexpr->args)
+               else
                {
-                   Expr       *e = (Expr *) lfirst(arg);
-                   ExprState  *estate;
+                   /*
+                    * NULL result from a tuple-returning function; expand it
+                    * to a row of all nulls.  We rely on the expectedDesc to
+                    * form such rows.  (Note: this would be problematic if
+                    * tuplestore_putvalues saved the tdtypeid/tdtypmod from
+                    * the provided descriptor, since that might not match
+                    * what we get from the function itself.  But it doesn't.)
+                    */
+                   int         natts = expectedDesc->natts;
+                   bool       *nullflags;
 
-                   estate = ExecInitExpr(e, parent);
-                   outlist = lappend(outlist, estate);
+                   nullflags = (bool *) palloc(natts * sizeof(bool));
+                   memset(nullflags, true, natts * sizeof(bool));
+                   tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
                }
-               xstate->args = outlist;
-
-               state = (ExprState *) xstate;
-           }
-           break;
-       case T_NullTest:
-           {
-               NullTest   *ntest = (NullTest *) node;
-               NullTestState *nstate = makeNode(NullTestState);
-
-               nstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
-               nstate->arg = ExecInitExpr(ntest->arg, parent);
-               nstate->argdesc = NULL;
-               state = (ExprState *) nstate;
            }
-           break;
-       case T_BooleanTest:
-           {
-               BooleanTest *btest = (BooleanTest *) node;
-               GenericExprState *gstate = makeNode(GenericExprState);
-
-               gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalBooleanTest;
-               gstate->arg = ExecInitExpr(btest->arg, parent);
-               state = (ExprState *) gstate;
-           }
-           break;
-       case T_CoerceToDomain:
+           else
            {
-               CoerceToDomain *ctest = (CoerceToDomain *) node;
-               CoerceToDomainState *cstate = makeNode(CoerceToDomainState);
-
-               cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceToDomain;
-               cstate->arg = ExecInitExpr(ctest->arg, parent);
-               /* We spend an extra palloc to reduce header inclusions */
-               cstate->constraint_ref = (DomainConstraintRef *)
-                   palloc(sizeof(DomainConstraintRef));
-               InitDomainConstraintRef(ctest->resulttype,
-                                       cstate->constraint_ref,
-                                       CurrentMemoryContext);
-               state = (ExprState *) cstate;
+               /* Scalar-type case: just store the function result */
+               tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull);
            }
-           break;
-       case T_CurrentOfExpr:
-           state = makeNode(ExprState);
-           state->evalfunc = ExecEvalCurrentOfExpr;
-           break;
-       case T_TargetEntry:
-           {
-               TargetEntry *tle = (TargetEntry *) node;
-               GenericExprState *gstate = makeNode(GenericExprState);
 
-               gstate->xprstate.evalfunc = NULL;       /* not used */
-               gstate->arg = ExecInitExpr(tle->expr, parent);
-               state = (ExprState *) gstate;
-           }
+           /*
+            * 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;
-       case T_List:
-           {
-               List       *outlist = NIL;
-               ListCell   *l;
+       }
+       else
+           ereport(ERROR,
+                   (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
+                    errmsg("unrecognized table-function returnMode: %d",
+                           (int) rsinfo.returnMode)));
 
-               foreach(l, (List *) node)
-               {
-                   outlist = lappend(outlist,
-                                     ExecInitExpr((Expr *) lfirst(l),
-                                                  parent));
-               }
-               /* Don't fall through to the "common" code below */
-               return (ExprState *) outlist;
-           }
-       default:
-           elog(ERROR, "unrecognized node type: %d",
-                (int) nodeTag(node));
-           state = NULL;       /* keep compiler quiet */
-           break;
+       first_time = false;
    }
 
-   /* Common code for all state-node types */
-   state->expr = node;
-
-   return state;
-}
-
-/*
- * 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 run the
- * passed expression tree through expression_planner() to prepare it for
- * execution.  (In ordinary Plan trees the regular planning process will have
- * made the appropriate transformations on expressions, but for standalone
- * expressions this won't have happened.)
- */
-ExprState *
-ExecPrepareExpr(Expr *node, EState *estate)
-{
-   ExprState  *result;
-   MemoryContext oldcontext;
-
-   oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
-
-   node = expression_planner(node);
-
-   result = ExecInitExpr(node, NULL);
-
-   MemoryContextSwitchTo(oldcontext);
-
-   return result;
-}
-
-
-/* ----------------------------------------------------------------
- *                  ExecQual / ExecTargetList / ExecProject
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- *     ExecQual
- *
- *     Evaluates a conjunctive boolean expression (qual list) and
- *     returns true iff none of the subexpressions are false.
- *     (We also return true if the list is empty.)
- *
- * If some of the subexpressions yield NULL but none yield FALSE,
- * then the result of the conjunction is NULL (ie, unknown)
- * according to three-valued boolean logic.  In this case,
- * we return the value specified by the "resultForNull" parameter.
- *
- * Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- * since SQL specifies that tuples with null WHERE results do not
- * get selected.  On the other hand, callers evaluating constraint
- * conditions should pass resultForNull=TRUE, since SQL also specifies
- * that NULL constraint conditions are not failures.
- *
- * NOTE: it would not be correct to use this routine to evaluate an
- * AND subclause of a boolean expression; for that purpose, a NULL
- * result must be returned as NULL so that it can be properly treated
- * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- * This routine is only used in contexts where a complete expression
- * is being evaluated and we know that NULL can be treated the same
- * as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-   bool        result;
-   MemoryContext oldContext;
-   ListCell   *l;
+no_function_result:
 
    /*
-    * debugging stuff
+    * If we got nothing from the function (ie, an empty-set or NULL result),
+    * we have to create the tuplestore to return, and if it's a
+    * non-set-returning function then insert a single all-nulls row.  As
+    * above, we depend on the expectedDesc to manufacture the dummy row.
     */
-   EV_printf("ExecQual: qual is ");
-   EV_nodeDisplay(qual);
-   EV_printf("\n");
+   if (rsinfo.setResult == NULL)
+   {
+       MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+       tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
+       rsinfo.setResult = tupstore;
+       if (!returnsSet)
+       {
+           int         natts = expectedDesc->natts;
+           bool       *nullflags;
 
-   /*
-    * Run in short-lived per-tuple context while computing expressions.
-    */
-   oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+           MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+           nullflags = (bool *) palloc(natts * sizeof(bool));
+           memset(nullflags, true, natts * sizeof(bool));
+           tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
+       }
+   }
 
    /*
-    * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-    * we can stop evaluating and return FALSE --- the AND result must be
-    * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-    * can stop and return FALSE --- the AND result must be FALSE or NULL in
-    * that case, and the caller doesn't care which.
-    *
-    * If we get to the end of the list, we can return TRUE.  This will happen
-    * when the AND result is indeed TRUE, or when the AND result is NULL (one
-    * or more NULL subresult, with all the rest TRUE) and the caller has
-    * specified resultForNull = TRUE.
+    * If function provided a tupdesc, cross-check it.  We only really need to
+    * do this for functions returning RECORD, but might as well do it always.
     */
-   result = true;
-
-   foreach(l, qual)
+   if (rsinfo.setDesc)
    {
-       ExprState  *clause = (ExprState *) lfirst(l);
-       Datum       expr_value;
-       bool        isNull;
-
-       expr_value = ExecEvalExpr(clause, econtext, &isNull);
+       tupledesc_match(expectedDesc, rsinfo.setDesc);
 
-       if (isNull)
-       {
-           if (resultForNull == false)
-           {
-               result = false; /* treat NULL as FALSE */
-               break;
-           }
-       }
-       else
-       {
-           if (!DatumGetBool(expr_value))
-           {
-               result = false; /* definitely FALSE */
-               break;
-           }
-       }
+       /*
+        * If it is a dynamically-allocated TupleDesc, free it: it is
+        * typically allocated in a per-query context, so we must avoid
+        * leaking it across multiple usages.
+        */
+       if (rsinfo.setDesc->tdrefcount == -1)
+           FreeTupleDesc(rsinfo.setDesc);
    }
 
-   MemoryContextSwitchTo(oldContext);
+   MemoryContextSwitchTo(callerContext);
 
-   return result;
+   /* All done, pass back the tuplestore */
+   return rsinfo.setResult;
 }
 
 /*
@@ -5137,177 +1075,11 @@ ExecCleanTargetListLength(List *targetlist)
 
    foreach(tl, targetlist)
    {
-       TargetEntry *curTle = castNode(TargetEntry, lfirst(tl));
+       TargetEntry *curTle = (TargetEntry *) lfirst(tl);
 
+       Assert(IsA(curTle, TargetEntry));
        if (!curTle->resjunk)
            len++;
    }
    return len;
 }
-
-/*
- * ExecTargetList
- *     Evaluates a targetlist with respect to the given
- *     expression context.
- *
- * tupdesc must describe the rowtype of the expected result.
- *
- * Results are stored into the passed values and isnull arrays.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static void
-ExecTargetList(List *targetlist,
-              TupleDesc tupdesc,
-              ExprContext *econtext,
-              Datum *values,
-              bool *isnull)
-{
-   Form_pg_attribute *att = tupdesc->attrs;
-   MemoryContext oldContext;
-   ListCell   *tl;
-
-   /*
-    * Run in short-lived per-tuple context while computing expressions.
-    */
-   oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-   /*
-    * evaluate all the expressions in the target list
-    */
-   foreach(tl, targetlist)
-   {
-       GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-       TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-       AttrNumber  resind = tle->resno - 1;
-
-       values[resind] = ExecEvalExpr(gstate->arg,
-                                     econtext,
-                                     &isnull[resind]);
-
-       values[resind] = MakeExpandedObjectReadOnly(values[resind],
-                                                   isnull[resind],
-                                                   att[resind]->attlen);
-   }
-
-   MemoryContextSwitchTo(oldContext);
-}
-
-/*
- * ExecProject
- *
- *     projects a tuple based on projection info and stores
- *     it in the previously specified tuple table slot.
- *
- *     Note: the result is always a virtual tuple; therefore it
- *     may reference the contents of the exprContext's scan tuples
- *     and/or temporary results constructed in the exprContext.
- *     If the caller wishes the result to be valid longer than that
- *     data will be valid, he must call ExecMaterializeSlot on the
- *     result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo)
-{
-   TupleTableSlot *slot;
-   ExprContext *econtext;
-   int         numSimpleVars;
-
-   /*
-    * sanity checks
-    */
-   Assert(projInfo != NULL);
-
-   /*
-    * get the projection info we want
-    */
-   slot = projInfo->pi_slot;
-   econtext = projInfo->pi_exprContext;
-
-   /*
-    * Clear any former contents of the result slot.  This makes it safe for
-    * us to use the slot's Datum/isnull arrays as workspace.
-    */
-   ExecClearTuple(slot);
-
-   /*
-    * Force extraction of all input values that we'll need.  The
-    * Var-extraction loops below depend on this, and we are also prefetching
-    * all attributes that will be referenced in the generic expressions.
-    */
-   if (projInfo->pi_lastInnerVar > 0)
-       slot_getsomeattrs(econtext->ecxt_innertuple,
-                         projInfo->pi_lastInnerVar);
-   if (projInfo->pi_lastOuterVar > 0)
-       slot_getsomeattrs(econtext->ecxt_outertuple,
-                         projInfo->pi_lastOuterVar);
-   if (projInfo->pi_lastScanVar > 0)
-       slot_getsomeattrs(econtext->ecxt_scantuple,
-                         projInfo->pi_lastScanVar);
-
-   /*
-    * Assign simple Vars to result by direct extraction of fields from source
-    * slots ... a mite ugly, but fast ...
-    */
-   numSimpleVars = projInfo->pi_numSimpleVars;
-   if (numSimpleVars > 0)
-   {
-       Datum      *values = slot->tts_values;
-       bool       *isnull = slot->tts_isnull;
-       int        *varSlotOffsets = projInfo->pi_varSlotOffsets;
-       int        *varNumbers = projInfo->pi_varNumbers;
-       int         i;
-
-       if (projInfo->pi_directMap)
-       {
-           /* especially simple case where vars go to output in order */
-           for (i = 0; i < numSimpleVars; i++)
-           {
-               char       *slotptr = ((char *) econtext) + varSlotOffsets[i];
-               TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-               int         varNumber = varNumbers[i] - 1;
-
-               values[i] = varSlot->tts_values[varNumber];
-               isnull[i] = varSlot->tts_isnull[varNumber];
-           }
-       }
-       else
-       {
-           /* we have to pay attention to varOutputCols[] */
-           int        *varOutputCols = projInfo->pi_varOutputCols;
-
-           for (i = 0; i < numSimpleVars; i++)
-           {
-               char       *slotptr = ((char *) econtext) + varSlotOffsets[i];
-               TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-               int         varNumber = varNumbers[i] - 1;
-               int         varOutputCol = varOutputCols[i] - 1;
-
-               values[varOutputCol] = varSlot->tts_values[varNumber];
-               isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-           }
-       }
-   }
-
-   /*
-    * If there are any generic expressions, evaluate them.
-    */
-   if (projInfo->pi_targetlist)
-   {
-       ExecTargetList(projInfo->pi_targetlist,
-                      slot->tts_tupleDescriptor,
-                      econtext,
-                      slot->tts_values,
-                      slot->tts_isnull);
-   }
-
-   /*
-    * Mark the result slot as containing a valid virtual tuple.
-    */
-   return ExecStoreVirtualTuple(slot);
-}
index 65196795d7004831e6959e3cef610b8928796471..0a30d46ff4155a81ed24434c58d988cc7968f87e 100644 (file)
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
         ExecScanRecheckMtd recheckMtd)
 {
    ExprContext *econtext;
-   List       *qual;
+   ExprState *qual;
    ProjectionInfo *projInfo;
 
    /*
@@ -170,7 +170,7 @@ ExecScan(ScanState *node,
        if (TupIsNull(slot))
        {
            if (projInfo)
-               return ExecClearTuple(projInfo->pi_slot);
+               return ExecClearTuple(projInfo->pi_state.resultslot);
            else
                return slot;
        }
@@ -187,7 +187,7 @@ ExecScan(ScanState *node,
         * when the qual is nil ... saves only a few cycles, but they add up
         * ...
         */
-       if (!qual || ExecQual(qual, econtext, false))
+       if (!qual || ExecQual(qual, econtext))
        {
            /*
             * Found a satisfactory scan tuple.
index d205101b89a0777bde3e105ddd6fb33130a07600..5bc89f8554a501e9503c25be427b28755e37f11f 100644 (file)
@@ -470,136 +470,6 @@ ExecGetResultType(PlanState *planstate)
    return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *     ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-                       ExprContext *econtext,
-                       TupleTableSlot *slot,
-                       TupleDesc inputDesc)
-{
-   ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-   int         len = ExecTargetListLength(targetList);
-   int        *workspace;
-   int        *varSlotOffsets;
-   int        *varNumbers;
-   int        *varOutputCols;
-   List       *exprlist;
-   int         numSimpleVars;
-   bool        directMap;
-   ListCell   *tl;
-
-   projInfo->pi_exprContext = econtext;
-   projInfo->pi_slot = slot;
-   /* since these are all int arrays, we need do just one palloc */
-   workspace = (int *) palloc(len * 3 * sizeof(int));
-   projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-   projInfo->pi_varNumbers = varNumbers = workspace + len;
-   projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-   projInfo->pi_lastInnerVar = 0;
-   projInfo->pi_lastOuterVar = 0;
-   projInfo->pi_lastScanVar = 0;
-
-   /*
-    * We separate the target list elements into simple Var references and
-    * expressions which require the full ExecTargetList machinery.  To be a
-    * simple Var, a Var has to be a user attribute and not mismatch the
-    * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-    * will probably throw an error at runtime, but we leave that to it.)
-    */
-   exprlist = NIL;
-   numSimpleVars = 0;
-   directMap = true;
-   foreach(tl, targetList)
-   {
-       GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-       Var        *variable = (Var *) gstate->arg->expr;
-       bool        isSimpleVar = false;
-
-       if (variable != NULL &&
-           IsA(variable, Var) &&
-           variable->varattno > 0)
-       {
-           if (!inputDesc)
-               isSimpleVar = true;     /* can't check type, assume OK */
-           else if (variable->varattno <= inputDesc->natts)
-           {
-               Form_pg_attribute attr;
-
-               attr = inputDesc->attrs[variable->varattno - 1];
-               if (!attr->attisdropped && variable->vartype == attr->atttypid)
-                   isSimpleVar = true;
-           }
-       }
-
-       if (isSimpleVar)
-       {
-           TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-           AttrNumber  attnum = variable->varattno;
-
-           varNumbers[numSimpleVars] = attnum;
-           varOutputCols[numSimpleVars] = tle->resno;
-           if (tle->resno != numSimpleVars + 1)
-               directMap = false;
-
-           switch (variable->varno)
-           {
-               case INNER_VAR:
-                   varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-                                                            ecxt_innertuple);
-                   if (projInfo->pi_lastInnerVar < attnum)
-                       projInfo->pi_lastInnerVar = attnum;
-                   break;
-
-               case OUTER_VAR:
-                   varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-                                                            ecxt_outertuple);
-                   if (projInfo->pi_lastOuterVar < attnum)
-                       projInfo->pi_lastOuterVar = attnum;
-                   break;
-
-                   /* INDEX_VAR is handled by default case */
-
-               default:
-                   varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-                                                            ecxt_scantuple);
-                   if (projInfo->pi_lastScanVar < attnum)
-                       projInfo->pi_lastScanVar = attnum;
-                   break;
-           }
-           numSimpleVars++;
-       }
-       else
-       {
-           /* Not a simple variable, add it to generic targetlist */
-           exprlist = lappend(exprlist, gstate);
-           /* Examine expr to include contained Vars in lastXXXVar counts */
-           ExecGetLastAttnums((Node *) variable,
-                              &projInfo->pi_lastOuterVar,
-                              &projInfo->pi_lastInnerVar,
-                              &projInfo->pi_lastScanVar);
-       }
-   }
-   projInfo->pi_targetlist = exprlist;
-   projInfo->pi_numSimpleVars = numSimpleVars;
-   projInfo->pi_directMap = directMap;
-
-   return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -680,9 +550,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
                         TupleDesc inputDesc)
 {
    planstate->ps_ProjInfo =
-       ExecBuildProjectionInfo(planstate->targetlist,
+       ExecBuildProjectionInfo(planstate->plan->targetlist,
                                planstate->ps_ExprContext,
                                planstate->ps_ResultTupleSlot,
+                               planstate,
                                inputDesc);
 }
 
index aa081523506c7b4cb67af626a342c85908c7ab2d..2f3355636daa81f39a85cd4f0c62c27d6f056315 100644 (file)
@@ -1639,7 +1639,7 @@ project_aggregates(AggState *aggstate)
    /*
     * Check the qual (HAVING clause); if the group does not match, ignore it.
     */
-   if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+   if (ExecQual(aggstate->ss.ps.qual, econtext))
    {
        /*
         * Form and return projection tuple using the aggregate results and
@@ -2506,13 +2506,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
     * under SQL semantics anyway (and it's forbidden by the spec). Because
     * that is true, we don't need to worry about evaluating the aggs in any
     * particular order.
+    * FIXME: adjust comment (now added in projection)
     */
-   aggstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->plan.targetlist,
-                    (PlanState *) aggstate);
-   aggstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->plan.qual,
-                    (PlanState *) aggstate);
+   aggstate->ss.ps.qual =
+       ExecInitQual(node->plan.qual, (PlanState *) aggstate);
 
    /*
     * Initialize child nodes.
@@ -2724,7 +2721,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
    foreach(l, aggstate->aggs)
    {
        AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(l);
-       Aggref     *aggref = (Aggref *) aggrefstate->xprstate.expr;
+       Aggref     *aggref = aggrefstate->aggref;
        AggStatePerAgg peragg;
        AggStatePerTrans pertrans;
        int         existing_aggno;
@@ -3024,11 +3021,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
    /* and then create a projection for that targetlist */
    aggstate->evaldesc = ExecTypeFromTL(combined_inputeval, false);
    aggstate->evalslot = ExecInitExtraTupleSlot(estate);
-   combined_inputeval = (List *) ExecInitExpr((Expr *) combined_inputeval,
-                                              (PlanState *) aggstate);
    aggstate->evalproj = ExecBuildProjectionInfo(combined_inputeval,
                                                 aggstate->tmpcontext,
                                                 aggstate->evalslot,
+                                                &aggstate->ss.ps,
                                                 NULL);
    ExecSetSlotDescriptor(aggstate->evalslot, aggstate->evaldesc);
 
@@ -3206,8 +3202,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
    naggs = aggstate->numaggs;
    pertrans->aggfilter = ExecInitExpr(aggref->aggfilter,
                                       (PlanState *) aggstate);
-   pertrans->aggdirectargs = (List *) ExecInitExpr((Expr *) aggref->aggdirectargs,
-                                                   (PlanState *) aggstate);
+   pertrans->aggdirectargs = ExecInitExprList(aggref->aggdirectargs,
+                                              (PlanState *) aggstate);
 
    /*
     * Complain if the aggregate's arguments contain any aggregates; nested
index 2e9ff7d1b90bfa6fe2909946b05190b37c1f7665..19eb1755bea869e1c5fe35ff3b74ea6dc00563c0 100644 (file)
@@ -319,7 +319,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
            econtext->ecxt_scantuple = slot;
            ResetExprContext(econtext);
 
-           if (!ExecQual(node->bitmapqualorig, econtext, false))
+           if (!ExecQual(node->bitmapqualorig, econtext))
            {
                /* Fails recheck, so drop it and loop back for another */
                InstrCountFiltered2(node, 1);
@@ -654,7 +654,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
    ResetExprContext(econtext);
 
-   return ExecQual(node->bitmapqualorig, econtext, false);
+   return ExecQual(node->bitmapqualorig, econtext);
 }
 
 /* ----------------------------------------------------------------
@@ -837,15 +837,10 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   scanstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.targetlist,
-                    (PlanState *) scanstate);
-   scanstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.qual,
-                    (PlanState *) scanstate);
-   scanstate->bitmapqualorig = (List *)
-       ExecInitExpr((Expr *) node->bitmapqualorig,
-                    (PlanState *) scanstate);
+   scanstate->ss.ps.qual =
+       ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+   scanstate->bitmapqualorig =
+       ExecInitQual(node->bitmapqualorig, (PlanState *) scanstate);
 
    /*
     * tuple table initialization
index 8f4e0f527e4f847c6d15c20b7898e5d0c4b1d286..bed7949c5a0c0c54a369118955f586b770806ca6 100644 (file)
@@ -242,12 +242,8 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   scanstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.targetlist,
-                    (PlanState *) scanstate);
-   scanstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.qual,
-                    (PlanState *) scanstate);
+   scanstate->ss.ps.qual =
+       ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
    /*
     * tuple table initialization
index d4647482905bb692f7d84541aab6c90e47328ffb..57a4ce3218aa018fca5ece7d414aee39cd904480 100644 (file)
@@ -49,11 +49,8 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
    ExecAssignExprContext(estate, &css->ss.ps);
 
    /* initialize child expressions */
-   css->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
-                    (PlanState *) css);
-   css->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) cscan->scan.plan.qual,
+   css->ss.ps.qual =
+       ExecInitQual(cscan->scan.plan.qual,
                     (PlanState *) css);
 
    /* tuple table initialization */
index 3b6d1390eb41285444a6daa55080984eabace7b9..9ae1561404b4ed84db2b26b2bef65d59760a5101 100644 (file)
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
        !fdwroutine->RecheckForeignScan(node, slot))
        return false;
 
-   return ExecQual(node->fdw_recheck_quals, econtext, false);
+   return ExecQual(node->fdw_recheck_quals, econtext);
 }
 
 /* ----------------------------------------------------------------
@@ -155,15 +155,10 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   scanstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.targetlist,
-                    (PlanState *) scanstate);
-   scanstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.qual,
-                    (PlanState *) scanstate);
-   scanstate->fdw_recheck_quals = (List *)
-       ExecInitExpr((Expr *) node->fdw_recheck_quals,
-                    (PlanState *) scanstate);
+   scanstate->ss.ps.qual =
+       ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+   scanstate->fdw_recheck_quals =
+       ExecInitQual(node->fdw_recheck_quals, (PlanState *) scanstate);
 
    /*
     * tuple table initialization
index 972022784d531ded41ab58d4338d5e49b7e82a16..250efcb449ec85a45c1cf171712afa5b97f85f43 100644 (file)
@@ -35,7 +35,7 @@
  */
 typedef struct FunctionScanPerFuncState
 {
-   ExprState  *funcexpr;       /* state of the expression being evaluated */
+   SetExprState *setexpr;      /* state of the expression being evaluated */
    TupleDesc   tupdesc;        /* desc of the function result type */
    int         colcount;       /* expected number of result columns */
    Tuplestorestate *tstore;    /* holds the function result set */
@@ -92,7 +92,7 @@ FunctionNext(FunctionScanState *node)
        if (tstore == NULL)
        {
            node->funcstates[0].tstore = tstore =
-               ExecMakeTableFunctionResult(node->funcstates[0].funcexpr,
+               ExecMakeTableFunctionResult(node->funcstates[0].setexpr,
                                            node->ss.ps.ps_ExprContext,
                                            node->argcontext,
                                            node->funcstates[0].tupdesc,
@@ -151,7 +151,7 @@ FunctionNext(FunctionScanState *node)
        if (fs->tstore == NULL)
        {
            fs->tstore =
-               ExecMakeTableFunctionResult(fs->funcexpr,
+               ExecMakeTableFunctionResult(fs->setexpr,
                                            node->ss.ps.ps_ExprContext,
                                            node->argcontext,
                                            fs->tupdesc,
@@ -340,11 +340,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   scanstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.targetlist,
-                    (PlanState *) scanstate);
-   scanstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.qual,
+   scanstate->ss.ps.qual =
+       ExecInitQual(node->scan.plan.qual,
                     (PlanState *) scanstate);
 
    scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
@@ -361,7 +358,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
        Oid         funcrettype;
        TupleDesc   tupdesc;
 
-       fs->funcexpr = ExecInitExpr((Expr *) funcexpr, (PlanState *) scanstate);
+       fs->setexpr =
+           ExecInitTableFunctionResult((Expr *) funcexpr,
+                                       scanstate->ss.ps.ps_ExprContext,
+                                       &scanstate->ss.ps);
 
        /*
         * Don't allocate the tuplestores; the actual calls to the functions
index 32c97d390e0a3c46d25c263a04e5f78cc3249ab2..1e5b1b7675c5a10d0f9fdb79f245be80be4ef1c2 100644 (file)
@@ -81,12 +81,8 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   gatherstate->ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->plan.targetlist,
-                    (PlanState *) gatherstate);
-   gatherstate->ps.qual = (List *)
-       ExecInitExpr((Expr *) node->plan.qual,
-                    (PlanState *) gatherstate);
+   gatherstate->ps.qual =
+       ExecInitQual(node->plan.qual, (PlanState *) gatherstate);
 
    /*
     * tuple table initialization
index 72f30ab4e6bbf9f9b1ff604bb35b6f31083dc91b..6b0561f4e36de00a7599288b04f4746ed2712cd7 100644 (file)
@@ -86,12 +86,9 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   gm_state->ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->plan.targetlist,
-                    (PlanState *) gm_state);
-   gm_state->ps.qual = (List *)
-       ExecInitExpr((Expr *) node->plan.qual,
-                    (PlanState *) gm_state);
+   gm_state->ps.qual =
+       ExecInitQual(node->plan.qual,
+                    &gm_state->ps);
 
    /*
     * tuple table initialization
index 66c095bc72305ed0ff8df76bb63b5d223c5117ae..af9ba4905eb24cc4b2ee80b271348f6ef4f05f21 100644 (file)
@@ -85,7 +85,7 @@ ExecGroup(GroupState *node)
         * Check the qual (HAVING clause); if the group does not match, ignore
         * it and fall into scan loop.
         */
-       if (ExecQual(node->ss.ps.qual, econtext, false))
+       if (ExecQual(node->ss.ps.qual, econtext))
        {
            /*
             * Form and return a projection tuple using the first input tuple.
@@ -139,7 +139,7 @@ ExecGroup(GroupState *node)
         * Check the qual (HAVING clause); if the group does not match, ignore
         * it and loop back to scan the rest of the group.
         */
-       if (ExecQual(node->ss.ps.qual, econtext, false))
+       if (ExecQual(node->ss.ps.qual, econtext))
        {
            /*
             * Form and return a projection tuple using the first input tuple.
@@ -188,12 +188,8 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   grpstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->plan.targetlist,
-                    (PlanState *) grpstate);
-   grpstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->plan.qual,
-                    (PlanState *) grpstate);
+   grpstate->ss.ps.qual =
+       ExecInitQual(node->plan.qual, (PlanState *) grpstate);
 
    /*
     * initialize child nodes
index e695d8834b550e6408e13fca4a05eea8d48b10a6..cfc6b9609355ceb426b07c1df21eb1ddaccad399 100644 (file)
@@ -190,12 +190,8 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   hashstate->ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->plan.targetlist,
-                    (PlanState *) hashstate);
-   hashstate->ps.qual = (List *)
-       ExecInitExpr((Expr *) node->plan.qual,
-                    (PlanState *) hashstate);
+   hashstate->ps.qual =
+       ExecInitQual(node->plan.qual, (PlanState *) hashstate);
 
    /*
     * initialize child nodes
@@ -1063,7 +1059,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
                   ExprContext *econtext)
 {
-   List       *hjclauses = hjstate->hashclauses;
+   ExprState  *hjclauses = hjstate->hashclauses;
    HashJoinTable hashtable = hjstate->hj_HashTable;
    HashJoinTuple hashTuple = hjstate->hj_CurTuple;
    uint32      hashvalue = hjstate->hj_CurHashValue;
@@ -1097,7 +1093,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
            /* reset temp memory each time to avoid leaks from qual expr */
            ResetExprContext(econtext);
 
-           if (ExecQual(hjclauses, econtext, false))
+           if (ExecQual(hjclauses, econtext))
            {
                hjstate->hj_CurTuple = hashTuple;
                return true;
index c50d93f43dbe19164e7cae4810bde02e83493d98..1aa133925f5d9d0d7e7ca6ea3f1c2e2e2e54c7cb 100644 (file)
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
    PlanState  *outerNode;
    HashState  *hashNode;
-   List       *joinqual;
-   List       *otherqual;
+   ExprState  *joinqual;
+   ExprState  *otherqual;
    ExprContext *econtext;
    HashJoinTable hashtable;
    TupleTableSlot *outerTupleSlot;
@@ -275,7 +275,7 @@ ExecHashJoin(HashJoinState *node)
                 * Only the joinquals determine tuple match status, but all
                 * quals must pass to actually return the tuple.
                 */
-               if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+               if (joinqual == NULL || ExecQual(joinqual, econtext))
                {
                    node->hj_MatchedOuter = true;
                    HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -294,8 +294,7 @@ ExecHashJoin(HashJoinState *node)
                    if (node->js.jointype == JOIN_SEMI)
                        node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-                   if (otherqual == NIL ||
-                       ExecQual(otherqual, econtext, false))
+                   if (otherqual == NULL || ExecQual(otherqual, econtext))
                        return ExecProject(node->js.ps.ps_ProjInfo);
                    else
                        InstrCountFiltered2(node, 1);
@@ -322,8 +321,7 @@ ExecHashJoin(HashJoinState *node)
                     */
                    econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-                   if (otherqual == NIL ||
-                       ExecQual(otherqual, econtext, false))
+                   if (otherqual == NULL || ExecQual(otherqual, econtext))
                        return ExecProject(node->js.ps.ps_ProjInfo);
                    else
                        InstrCountFiltered2(node, 1);
@@ -350,8 +348,7 @@ ExecHashJoin(HashJoinState *node)
                 */
                econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-               if (otherqual == NIL ||
-                   ExecQual(otherqual, econtext, false))
+               if (otherqual == NULL || ExecQual(otherqual, econtext))
                    return ExecProject(node->js.ps.ps_ProjInfo);
                else
                    InstrCountFiltered2(node, 1);
@@ -411,19 +408,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   hjstate->js.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->join.plan.targetlist,
-                    (PlanState *) hjstate);
-   hjstate->js.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->join.plan.qual,
-                    (PlanState *) hjstate);
+   hjstate->js.ps.qual =
+       ExecInitQual(node->join.plan.qual, (PlanState *) hjstate);
    hjstate->js.jointype = node->join.jointype;
-   hjstate->js.joinqual = (List *)
-       ExecInitExpr((Expr *) node->join.joinqual,
-                    (PlanState *) hjstate);
-   hjstate->hashclauses = (List *)
-       ExecInitExpr((Expr *) node->hashclauses,
-                    (PlanState *) hjstate);
+   hjstate->js.joinqual =
+       ExecInitQual(node->join.joinqual, (PlanState *) hjstate);
+   hjstate->hashclauses =
+       ExecInitQual(node->hashclauses, (PlanState *) hjstate);
 
    /*
     * initialize child nodes
@@ -517,13 +508,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
    lclauses = NIL;
    rclauses = NIL;
    hoperators = NIL;
-   foreach(l, hjstate->hashclauses)
+   foreach(l, node->hashclauses)
    {
-       FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
-       OpExpr     *hclause = castNode(OpExpr, fstate->xprstate.expr);
+       OpExpr     *hclause =  castNode(OpExpr, lfirst(l));
+
+       lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+       rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 
-       lclauses = lappend(lclauses, linitial(fstate->args));
-       rclauses = lappend(rclauses, lsecond(fstate->args));
        hoperators = lappend_oid(hoperators, hclause->opno);
    }
    hjstate->hj_OuterHashKeys = lclauses;
index db7f2e120ea85b99dd2d22e784d5d449b1d0101f..5550f6c0a4be70bbb3735c2c7a83ef4f5beed2a3 100644 (file)
@@ -211,7 +211,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
        {
            econtext->ecxt_scantuple = slot;
            ResetExprContext(econtext);
-           if (!ExecQual(node->indexqual, econtext, false))
+           if (!ExecQual(node->indexqual, econtext))
            {
                /* Fails recheck, so drop it and loop back for another */
                InstrCountFiltered2(node, 1);
@@ -488,15 +488,10 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
     * Note: we don't initialize all of the indexorderby expression, only the
     * sub-parts corresponding to runtime keys (see below).
     */
-   indexstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.targetlist,
-                    (PlanState *) indexstate);
-   indexstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.qual,
-                    (PlanState *) indexstate);
-   indexstate->indexqual = (List *)
-       ExecInitExpr((Expr *) node->indexqual,
-                    (PlanState *) indexstate);
+   indexstate->ss.ps.qual =
+       ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
+   indexstate->indexqual =
+       ExecInitQual(node->indexqual, (PlanState *) indexstate);
 
    /*
     * tuple table initialization
index cb6aff9137a42908c46e98d0323b80e9d06bf2d1..5afd02e09ddfad4417af5324e5986e7807ee37a5 100644 (file)
@@ -149,7 +149,7 @@ IndexNext(IndexScanState *node)
        {
            econtext->ecxt_scantuple = slot;
            ResetExprContext(econtext);
-           if (!ExecQual(node->indexqualorig, econtext, false))
+           if (!ExecQual(node->indexqualorig, econtext))
            {
                /* Fails recheck, so drop it and loop back for another */
                InstrCountFiltered2(node, 1);
@@ -295,7 +295,7 @@ next_indextuple:
        {
            econtext->ecxt_scantuple = slot;
            ResetExprContext(econtext);
-           if (!ExecQual(node->indexqualorig, econtext, false))
+           if (!ExecQual(node->indexqualorig, econtext))
            {
                /* Fails recheck, so drop it and loop back for another */
                InstrCountFiltered2(node, 1);
@@ -415,7 +415,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
    ResetExprContext(econtext);
 
-   return ExecQual(node->indexqualorig, econtext, false);
+   return ExecQual(node->indexqualorig, econtext);
 }
 
 
@@ -921,18 +921,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
     * would be nice to improve that.  (Problem is that any SubPlans present
     * in the expression must be found now...)
     */
-   indexstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.targetlist,
-                    (PlanState *) indexstate);
-   indexstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.qual,
-                    (PlanState *) indexstate);
-   indexstate->indexqualorig = (List *)
-       ExecInitExpr((Expr *) node->indexqualorig,
-                    (PlanState *) indexstate);
-   indexstate->indexorderbyorig = (List *)
-       ExecInitExpr((Expr *) node->indexorderbyorig,
-                    (PlanState *) indexstate);
+   indexstate->ss.ps.qual =
+       ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
+   indexstate->indexqualorig =
+       ExecInitQual(node->indexqualorig, (PlanState *) indexstate);
+   indexstate->indexorderbyorig =
+       ExecInitExprList(node->indexorderbyorig, (PlanState *) indexstate);
 
    /*
     * tuple table initialization
index 105e2dcedbe9e793d85d3080017a39b666ab2ef9..62784af304b0a939f8a55a33af53053823fe789b 100644 (file)
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
    ExprContext *econtext = node->js.ps.ps_ExprContext;
-   List       *otherqual = node->js.ps.qual;
+   ExprState  *otherqual = node->js.ps.qual;
 
    ResetExprContext(econtext);
 
    econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
    econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-   if (ExecQual(otherqual, econtext, false))
+   if (ExecQual(otherqual, econtext))
    {
        /*
         * qualification succeeded.  now form the desired projection tuple and
@@ -483,14 +483,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
    ExprContext *econtext = node->js.ps.ps_ExprContext;
-   List       *otherqual = node->js.ps.qual;
+   ExprState  *otherqual = node->js.ps.qual;
 
    ResetExprContext(econtext);
 
    econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
    econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-   if (ExecQual(otherqual, econtext, false))
+   if (ExecQual(otherqual, econtext))
    {
        /*
         * qualification succeeded.  now form the desired projection tuple and
@@ -598,8 +598,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-   List       *joinqual;
-   List       *otherqual;
+   ExprState  *joinqual;
+   ExprState  *otherqual;
    bool        qualResult;
    int         compareResult;
    PlanState  *innerPlan;
@@ -785,8 +785,8 @@ ExecMergeJoin(MergeJoinState *node)
                innerTupleSlot = node->mj_InnerTupleSlot;
                econtext->ecxt_innertuple = innerTupleSlot;
 
-               qualResult = (joinqual == NIL ||
-                             ExecQual(joinqual, econtext, false));
+               qualResult = (joinqual == NULL ||
+                             ExecQual(joinqual, econtext));
                MJ_DEBUG_QUAL(joinqual, qualResult);
 
                if (qualResult)
@@ -808,8 +808,8 @@ ExecMergeJoin(MergeJoinState *node)
                    if (node->js.jointype == JOIN_SEMI)
                        node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-                   qualResult = (otherqual == NIL ||
-                                 ExecQual(otherqual, econtext, false));
+                   qualResult = (otherqual == NULL ||
+                                 ExecQual(otherqual, econtext));
                    MJ_DEBUG_QUAL(otherqual, qualResult);
 
                    if (qualResult)
@@ -1455,16 +1455,11 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   mergestate->js.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->join.plan.targetlist,
-                    (PlanState *) mergestate);
-   mergestate->js.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->join.plan.qual,
-                    (PlanState *) mergestate);
+   mergestate->js.ps.qual =
+       ExecInitQual(node->join.plan.qual, (PlanState *) mergestate);
    mergestate->js.jointype = node->join.jointype;
-   mergestate->js.joinqual = (List *)
-       ExecInitExpr((Expr *) node->join.joinqual,
-                    (PlanState *) mergestate);
+   mergestate->js.joinqual =
+       ExecInitQual(node->join.joinqual, (PlanState *) mergestate);
    mergestate->mj_ConstFalseJoin = false;
    /* mergeclauses are handled below */
 
index 95e158970c30958596f23a842b5d156e965a280d..127a64eb31aa85364dc3d3c95b20443bb420b177 100644 (file)
@@ -1151,7 +1151,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
    ExprContext *econtext = mtstate->ps.ps_ExprContext;
    Relation    relation = resultRelInfo->ri_RelationDesc;
-   List       *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+   ExprState  *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
    HeapTupleData tuple;
    HeapUpdateFailureData hufd;
    LockTupleMode lockmode;
@@ -1270,7 +1270,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
    econtext->ecxt_innertuple = excludedSlot;
    econtext->ecxt_outertuple = NULL;
 
-   if (!ExecQual(onConflictSetWhere, econtext, false))
+   if (!ExecQual(onConflictSetWhere, econtext))
    {
        ReleaseBuffer(buffer);
        InstrCountFiltered1(&mtstate->ps, 1);
@@ -1645,7 +1645,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
    mtstate = makeNode(ModifyTableState);
    mtstate->ps.plan = (Plan *) node;
    mtstate->ps.state = estate;
-   mtstate->ps.targetlist = NIL;       /* not actually used */
 
    mtstate->operation = operation;
    mtstate->canSetTag = node->canSetTag;
@@ -1765,8 +1764,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
        foreach(ll, wcoList)
        {
            WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-           ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-                                              mtstate->mt_plans[i]);
+           ExprState  *wcoExpr = ExecInitQual((List *) wco->qual,
+                                               mtstate->mt_plans[i]);
 
            wcoExprs = lappend(wcoExprs, wcoExpr);
        }
@@ -1805,7 +1804,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
            foreach(ll, mapped_wcoList)
            {
                WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-               ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
+               ExprState  *wcoExpr = ExecInitQual((List *) wco->qual,
                                               mtstate->mt_plans[i]);
 
                wcoExprs = lappend(wcoExprs, wcoExpr);
@@ -1839,8 +1838,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
        slot = mtstate->ps.ps_ResultTupleSlot;
 
        /* Need an econtext too */
-       econtext = CreateExprContext(estate);
-       mtstate->ps.ps_ExprContext = econtext;
+       if (mtstate->ps.ps_ExprContext == NULL)
+           ExecAssignExprContext(estate, &mtstate->ps);
+       econtext = mtstate->ps.ps_ExprContext;
 
        /*
         * Build a projection for each result rel.
@@ -1849,11 +1849,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
        foreach(l, node->returningLists)
        {
            List       *rlist = (List *) lfirst(l);
-           List       *rliststate;
 
-           rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
            resultRelInfo->ri_projectReturning =
-               ExecBuildProjectionInfo(rliststate, econtext, slot,
+               ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
                                     resultRelInfo->ri_RelationDesc->rd_att);
            resultRelInfo++;
        }
@@ -1870,17 +1868,15 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
        for (i = 0; i < mtstate->mt_num_partitions; i++)
        {
            Relation    partrel = resultRelInfo->ri_RelationDesc;
-           List       *rlist,
-                      *rliststate;
+           List       *rlist;
 
            /* varno = node->nominalRelation */
            rlist = map_partition_varattnos(returningList,
                                            node->nominalRelation,
                                            partrel, rel);
-           rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
            resultRelInfo->ri_projectReturning =
-               ExecBuildProjectionInfo(rliststate, econtext, slot,
-                                    resultRelInfo->ri_RelationDesc->rd_att);
+               ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+                                       resultRelInfo->ri_RelationDesc->rd_att);
            resultRelInfo++;
        }
    }
@@ -1905,7 +1901,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
    if (node->onConflictAction == ONCONFLICT_UPDATE)
    {
        ExprContext *econtext;
-       ExprState  *setexpr;
        TupleDesc   tupDesc;
 
        /* insert may only have one plan, inheritance is not expanded */
@@ -1931,11 +1926,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
        mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
        ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-       /* build UPDATE SET expression and projection state */
-       setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+       /* build UPDATE SET projection state */
        resultRelInfo->ri_onConflictSetProj =
-           ExecBuildProjectionInfo((List *) setexpr, econtext,
-                                   mtstate->mt_conflproj,
+           ExecBuildProjectionInfo(node->onConflictSet, econtext,
+                                   mtstate->mt_conflproj, &mtstate->ps,
                                    resultRelInfo->ri_RelationDesc->rd_att);
 
        /* build DO UPDATE WHERE clause expression */
@@ -1943,10 +1937,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
        {
            ExprState  *qualexpr;
 
-           qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
+           qualexpr = ExecInitQual((List *) node->onConflictWhere,
                                    &mtstate->ps);
 
-           resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+           resultRelInfo->ri_onConflictSetWhere = qualexpr;
        }
    }
 
index cac7ba1b9bfd8c3914f861efc07ff874f0c0a8b1..53977e0b329f40d8ae8bbb1001d8868801950a3f 100644 (file)
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
    PlanState  *outerPlan;
    TupleTableSlot *outerTupleSlot;
    TupleTableSlot *innerTupleSlot;
-   List       *joinqual;
-   List       *otherqual;
+   ExprState  *joinqual;
+   ExprState  *otherqual;
    ExprContext *econtext;
    ListCell   *lc;
 
@@ -176,7 +176,7 @@ ExecNestLoop(NestLoopState *node)
 
                ENL1_printf("testing qualification for outer-join tuple");
 
-               if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+               if (otherqual == NULL || ExecQual(otherqual, econtext))
                {
                    /*
                     * qualification was satisfied so we project and return
@@ -207,7 +207,7 @@ ExecNestLoop(NestLoopState *node)
         */
        ENL1_printf("testing qualification");
 
-       if (ExecQual(joinqual, econtext, false))
+       if (ExecQual(joinqual, econtext))
        {
            node->nl_MatchedOuter = true;
 
@@ -225,7 +225,7 @@ ExecNestLoop(NestLoopState *node)
            if (node->js.jointype == JOIN_SEMI)
                node->nl_NeedNewOuter = true;
 
-           if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+           if (otherqual == NULL || ExecQual(otherqual, econtext))
            {
                /*
                 * qualification was satisfied so we project and return the
@@ -282,16 +282,11 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   nlstate->js.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->join.plan.targetlist,
-                    (PlanState *) nlstate);
-   nlstate->js.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->join.plan.qual,
-                    (PlanState *) nlstate);
+   nlstate->js.ps.qual =
+       ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
    nlstate->js.jointype = node->join.jointype;
-   nlstate->js.joinqual = (List *)
-       ExecInitExpr((Expr *) node->join.joinqual,
-                    (PlanState *) nlstate);
+   nlstate->js.joinqual =
+       ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
 
    /*
     * initialize child nodes
index eae0f1dad93026975a98aa71a1355a36b0058cec..60862cf1423d8ad356603e825707606162b96a81 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "executor/executor.h"
 #include "executor/nodeProjectSet.h"
+#include "nodes/nodeFuncs.h"
 #include "utils/memutils.h"
 
 
@@ -122,7 +123,6 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
    bool        hassrf PG_USED_FOR_ASSERTS_ONLY = false;
    bool        hasresult;
    int         argno;
-   ListCell   *lc;
 
    ExecClearTuple(resultSlot);
 
@@ -133,10 +133,9 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
    node->pending_srf_tuples = false;
 
    hasresult = false;
-   argno = 0;
-   foreach(lc, node->ps.targetlist)
+   for (argno = 0; argno < node->nelems; argno++)
    {
-       GenericExprState *gstate = (GenericExprState *) lfirst(lc);
+       Node *elem = node->elems[argno];
        ExprDoneCond *isdone = &node->elemdone[argno];
        Datum      *result = &resultSlot->tts_values[argno];
        bool       *isnull = &resultSlot->tts_isnull[argno];
@@ -151,13 +150,12 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
            *isnull = true;
            hassrf = true;
        }
-       else if (IsA(gstate->arg, FuncExprState) &&
-                ((FuncExprState *) gstate->arg)->funcReturnsSet)
+       else if (IsA(elem, SetExprState))
        {
            /*
             * Evaluate SRF - possibly continuing previously started output.
             */
-           *result = ExecMakeFunctionResultSet((FuncExprState *) gstate->arg,
+           *result = ExecMakeFunctionResultSet((SetExprState *) elem,
                                                econtext, isnull, isdone);
 
            if (*isdone != ExprEndResult)
@@ -169,11 +167,10 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
        else
        {
            /* Non-SRF tlist expression, just evaluate normally. */
-           *result = ExecEvalExpr(gstate->arg, econtext, isnull);
+           *result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
            *isdone = ExprSingleResult;
        }
 
-       argno++;
    }
 
    /* ProjectSet should not be used if there's no SRFs */
@@ -204,6 +201,8 @@ ProjectSetState *
 ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
 {
    ProjectSetState *state;
+   ListCell *lc;
+   int off;
 
    /* check for unsupported flags */
    Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
@@ -229,12 +228,6 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
     */
    ExecInitResultTupleSlot(estate, &state->ps);
 
-   /*
-    * initialize child expressions
-    */
-   state->ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->plan.targetlist,
-                    (PlanState *) state);
    Assert(node->plan.qual == NIL);
 
    /*
@@ -254,9 +247,38 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
 
    /* Create workspace for per-SRF is-done state */
    state->nelems = list_length(node->plan.targetlist);
+   state->elems = (Node **)
+       palloc(sizeof(Node *) * state->nelems);
    state->elemdone = (ExprDoneCond *)
        palloc(sizeof(ExprDoneCond) * state->nelems);
 
+   /*
+    * Build expressions to evaluate targetlist. Can't use
+    * ExecBuildProjectionInfo here, since that doesn't deal with
+    * SRFs. Instead evaluate all expressions individually, using
+    * ExecInitFunctionResultSet where applicable.
+    */
+   off = 0;
+   foreach(lc, node->plan.targetlist)
+   {
+       TargetEntry *te = (TargetEntry *) lfirst(lc);
+       Expr *expr = te->expr;
+
+       if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
+           (IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
+       {
+           state->elems[off] = (Node *)
+               ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
+                                         &state->ps);
+       }
+       else
+       {
+           state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
+           Assert(!expression_returns_set((Node *) expr));
+       }
+
+       off++;
+   }
    return state;
 }
 
index b5b50b21e9af8db0e413c7d5aba20ffef60f3ff3..a753a53419183cefebf5c3e0ed9fe6a9f8e8ac37 100644 (file)
@@ -77,9 +77,7 @@ ExecResult(ResultState *node)
     */
    if (node->rs_checkqual)
    {
-       bool        qualResult = ExecQual((List *) node->resconstantqual,
-                                         econtext,
-                                         false);
+       bool        qualResult = ExecQual(node->resconstantqual, econtext);
 
        node->rs_checkqual = false;
        if (!qualResult)
@@ -209,14 +207,10 @@ ExecInitResult(Result *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   resstate->ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->plan.targetlist,
-                    (PlanState *) resstate);
-   resstate->ps.qual = (List *)
-       ExecInitExpr((Expr *) node->plan.qual,
-                    (PlanState *) resstate);
-   resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-                                            (PlanState *) resstate);
+   resstate->ps.qual =
+       ExecInitQual(node->plan.qual, (PlanState *) resstate);
+   resstate->resconstantqual =
+       ExecInitQual((List *) node->resconstantqual, (PlanState *) resstate);
 
    /*
     * initialize child nodes
index d38265e8104825e382d3fb66dade78edc312858d..0247bd23479bf6c56e7121137e2657f392fb98c5 100644 (file)
@@ -164,19 +164,12 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   scanstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.targetlist,
-                    (PlanState *) scanstate);
-   scanstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.qual,
-                    (PlanState *) scanstate);
-
-   scanstate->args = (List *)
-       ExecInitExpr((Expr *) tsc->args,
-                    (PlanState *) scanstate);
+   scanstate->ss.ps.qual =
+       ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+
+   scanstate->args = ExecInitExprList(tsc->args, (PlanState *) scanstate);
    scanstate->repeatable =
-       ExecInitExpr(tsc->repeatable,
-                    (PlanState *) scanstate);
+       ExecInitExpr(tsc->repeatable, (PlanState *) scanstate);
 
    /*
     * tuple table initialization
index e61895de0a80d3704b6ee89510b52a0d50d51d91..5680464fa273d554e0266572811e1ae5941e5677 100644 (file)
@@ -188,12 +188,8 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   scanstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->plan.targetlist,
-                    (PlanState *) scanstate);
-   scanstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->plan.qual,
-                    (PlanState *) scanstate);
+   scanstate->ss.ps.qual =
+       ExecInitQual(node->plan.qual, (PlanState *) scanstate);
 
    /*
     * tuple table initialization
index 8f419a13acd41a147d27c4b2822858392e55d3bf..ebc5a07245e46772ad4aa78486ec7d5500cd6e7e 100644 (file)
 #include "utils/memutils.h"
 
 
-static Datum ExecSubPlan(SubPlanState *node,
-           ExprContext *econtext,
-           bool *isNull);
-static Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node,
-                      ExprContext *econtext,
-                      bool *isNull);
 static Datum ExecHashSubPlan(SubPlanState *node,
                ExprContext *econtext,
                bool *isNull);
@@ -64,12 +58,12 @@ static bool slotNoNulls(TupleTableSlot *slot);
  * This is the main entry point for execution of a regular SubPlan.
  * ----------------------------------------------------------------
  */
-static Datum
+Datum
 ExecSubPlan(SubPlanState *node,
            ExprContext *econtext,
            bool *isNull)
 {
-   SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+   SubPlan    *subplan = node->subplan;
 
    /* Set non-null as default */
    *isNull = false;
@@ -95,7 +89,7 @@ ExecHashSubPlan(SubPlanState *node,
                ExprContext *econtext,
                bool *isNull)
 {
-   SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+   SubPlan    *subplan = node->subplan;
    PlanState  *planstate = node->planstate;
    TupleTableSlot *slot;
 
@@ -217,7 +211,7 @@ ExecScanSubPlan(SubPlanState *node,
                ExprContext *econtext,
                bool *isNull)
 {
-   SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+   SubPlan    *subplan = node->subplan;
    PlanState  *planstate = node->planstate;
    SubLinkType subLinkType = subplan->subLinkType;
    MemoryContext oldcontext;
@@ -462,7 +456,7 @@ ExecScanSubPlan(SubPlanState *node,
 static void
 buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
 {
-   SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+   SubPlan    *subplan = node->subplan;
    PlanState  *planstate = node->planstate;
    int         ncols = list_length(subplan->paramIds);
    ExprContext *innerecontext = node->innerecontext;
@@ -596,7 +590,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
     * potential for a double free attempt.  (XXX possibly no longer needed,
     * but can't hurt.)
     */
-   ExecClearTuple(node->projRight->pi_slot);
+   ExecClearTuple(node->projRight->pi_state.resultslot);
 
    MemoryContextSwitchTo(oldcontext);
 }
@@ -694,8 +688,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
    SubPlanState *sstate = makeNode(SubPlanState);
    EState     *estate = parent->state;
 
-   sstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecSubPlan;
-   sstate->xprstate.expr = (Expr *) subplan;
+   sstate->subplan = subplan;
 
    /* Link the SubPlanState to already-initialized subplan */
    sstate->planstate = (PlanState *) list_nth(estate->es_subplanstates,
@@ -706,7 +699,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 
    /* Initialize subexpressions */
    sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent);
-   sstate->args = (List *) ExecInitExpr((Expr *) subplan->args, parent);
+   sstate->args = ExecInitExprList(subplan->args, parent);
 
    /*
     * initialize my state
@@ -763,9 +756,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
        TupleTableSlot *slot;
        List       *oplist,
                   *lefttlist,
-                  *righttlist,
-                  *leftptlist,
-                  *rightptlist;
+                  *righttlist;
        ListCell   *l;
 
        /* We need a memory context to hold the hash table(s) */
@@ -800,27 +791,27 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
         * We also extract the combining operators themselves to initialize
         * the equality and hashing functions for the hash tables.
         */
-       if (IsA(sstate->testexpr->expr, OpExpr))
+       if (IsA(subplan->testexpr, OpExpr))
        {
            /* single combining operator */
-           oplist = list_make1(sstate->testexpr);
+           oplist = list_make1(subplan->testexpr);
        }
-       else if (and_clause((Node *) sstate->testexpr->expr))
+       else if (and_clause((Node *) subplan->testexpr))
        {
            /* multiple combining operators */
-           oplist = castNode(BoolExprState, sstate->testexpr)->args;
+           Assert(IsA(subplan->testexpr, BoolExpr));
+           oplist = castNode(BoolExpr, subplan->testexpr)->args;
        }
        else
        {
            /* shouldn't see anything else in a hashable subplan */
            elog(ERROR, "unrecognized testexpr type: %d",
-                (int) nodeTag(sstate->testexpr->expr));
+                (int) nodeTag(subplan->testexpr));
            oplist = NIL;       /* keep compiler quiet */
        }
        Assert(list_length(oplist) == ncols);
 
        lefttlist = righttlist = NIL;
-       leftptlist = rightptlist = NIL;
        sstate->tab_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
        sstate->tab_eq_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
        sstate->lhs_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
@@ -828,45 +819,30 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
        i = 1;
        foreach(l, oplist)
        {
-           FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
-           OpExpr     *opexpr = castNode(OpExpr, fstate->xprstate.expr);
-           ExprState  *exstate;
+           OpExpr     *opexpr = castNode(OpExpr, lfirst(l));
            Expr       *expr;
            TargetEntry *tle;
-           GenericExprState *tlestate;
            Oid         rhs_eq_oper;
            Oid         left_hashfn;
            Oid         right_hashfn;
 
-           Assert(list_length(fstate->args) == 2);
+           Assert(list_length(opexpr->args) == 2);
 
            /* Process lefthand argument */
-           exstate = (ExprState *) linitial(fstate->args);
-           expr = exstate->expr;
+           expr = (Expr *) linitial(opexpr->args);
            tle = makeTargetEntry(expr,
                                  i,
                                  NULL,
                                  false);
-           tlestate = makeNode(GenericExprState);
-           tlestate->xprstate.expr = (Expr *) tle;
-           tlestate->xprstate.evalfunc = NULL;
-           tlestate->arg = exstate;
-           lefttlist = lappend(lefttlist, tlestate);
-           leftptlist = lappend(leftptlist, tle);
+           lefttlist = lappend(lefttlist, tle);
 
            /* Process righthand argument */
-           exstate = (ExprState *) lsecond(fstate->args);
-           expr = exstate->expr;
+           expr = (Expr *) lsecond(opexpr->args);
            tle = makeTargetEntry(expr,
                                  i,
                                  NULL,
                                  false);
-           tlestate = makeNode(GenericExprState);
-           tlestate->xprstate.expr = (Expr *) tle;
-           tlestate->xprstate.evalfunc = NULL;
-           tlestate->arg = exstate;
-           righttlist = lappend(righttlist, tlestate);
-           rightptlist = lappend(rightptlist, tle);
+           righttlist = lappend(righttlist, tle);
 
            /* Lookup the equality function (potentially cross-type) */
            fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]);
@@ -898,20 +874,22 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
         * (hack alert!).  The righthand expressions will be evaluated in our
         * own innerecontext.
         */
-       tupDesc = ExecTypeFromTL(leftptlist, false);
+       tupDesc = ExecTypeFromTL(lefttlist, false);
        slot = ExecInitExtraTupleSlot(estate);
        ExecSetSlotDescriptor(slot, tupDesc);
        sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-                                                  NULL,
+                                                  parent->ps_ExprContext,
                                                   slot,
+                                                  parent,
                                                   NULL);
 
-       tupDesc = ExecTypeFromTL(rightptlist, false);
+       tupDesc = ExecTypeFromTL(righttlist, false);
        slot = ExecInitExtraTupleSlot(estate);
        ExecSetSlotDescriptor(slot, tupDesc);
        sstate->projRight = ExecBuildProjectionInfo(righttlist,
                                                    sstate->innerecontext,
                                                    slot,
+                                                   sstate->planstate,
                                                    NULL);
    }
 
@@ -934,7 +912,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 void
 ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 {
-   SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+   SubPlan    *subplan = node->subplan;
    PlanState  *planstate = node->planstate;
    SubLinkType subLinkType = subplan->subLinkType;
    MemoryContext oldcontext;
@@ -1111,7 +1089,7 @@ void
 ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
 {
    PlanState  *planstate = node->planstate;
-   SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+   SubPlan    *subplan = node->subplan;
    EState     *estate = parent->state;
    ListCell   *l;
 
@@ -1162,16 +1140,21 @@ ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent)
    SubPlan    *subplan2;
    Cost        cost1;
    Cost        cost2;
+   ListCell   *lc;
 
-   asstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecAlternativeSubPlan;
-   asstate->xprstate.expr = (Expr *) asplan;
+   asstate->subplan = asplan;
 
    /*
     * Initialize subplans.  (Can we get away with only initializing the one
     * we're going to use?)
     */
-   asstate->subplans = (List *) ExecInitExpr((Expr *) asplan->subplans,
-                                             parent);
+   foreach(lc, asplan->subplans)
+   {
+       SubPlanState *sp = ExecInitSubPlan(lfirst(lc), parent);
+       asstate->subplans =
+           lappend(asstate->subplans, sp);
+       parent->subPlan = lappend(parent->subPlan, sp);
+   }
 
    /*
     * Select the one to be used.  For this, we need an estimate of the number
@@ -1209,7 +1192,7 @@ ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent)
  * Note: in future we might consider changing to different subplans on the
  * fly, in case the original rowcount estimate turns out to be way off.
  */
-static Datum
+Datum
 ExecAlternativeSubPlan(AlternativeSubPlanState *node,
                       ExprContext *econtext,
                       bool *isNull)
index 230a96f9d2ed266cc5891b4ac13be7755ab09f1f..ae184700a671a8d6bc0c90011831402088062bb8 100644 (file)
@@ -120,12 +120,8 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   subquerystate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.targetlist,
-                    (PlanState *) subquerystate);
-   subquerystate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.qual,
-                    (PlanState *) subquerystate);
+   subquerystate->ss.ps.qual =
+       ExecInitQual(node->scan.plan.qual, (PlanState *) subquerystate);
 
    /*
     * tuple table initialization
index 628f1ba074d57e565ad169dd842aef5c9d02e8e9..8e589d916fd5aa6d455c6ebfe7e6f92bc004fdec 100644 (file)
@@ -139,12 +139,9 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   scanstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.targetlist,
-                    (PlanState *) scanstate);
-   scanstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.qual,
-                    (PlanState *) scanstate);
+   scanstate->ss.ps.qual =
+       ExecInitQual(node->scan.plan.qual,
+                    &scanstate->ss.ps);
 
    /*
     * tuple table initialization
@@ -179,16 +176,16 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
 
    scanstate->ns_names = tf->ns_names;
 
-   scanstate->ns_uris = (List *)
-       ExecInitExpr((Expr *) tf->ns_uris, (PlanState *) scanstate);
+   scanstate->ns_uris =
+       ExecInitExprList(tf->ns_uris, (PlanState *) scanstate);
    scanstate->docexpr =
        ExecInitExpr((Expr *) tf->docexpr, (PlanState *) scanstate);
    scanstate->rowexpr =
        ExecInitExpr((Expr *) tf->rowexpr, (PlanState *) scanstate);
-   scanstate->colexprs = (List *)
-       ExecInitExpr((Expr *) tf->colexprs, (PlanState *) scanstate);
-   scanstate->coldefexprs = (List *)
-       ExecInitExpr((Expr *) tf->coldefexprs, (PlanState *) scanstate);
+   scanstate->colexprs =
+       ExecInitExprList(tf->colexprs, (PlanState *) scanstate);
+   scanstate->coldefexprs =
+       ExecInitExprList(tf->coldefexprs, (PlanState *) scanstate);
 
    scanstate->notnulls = tf->notnulls;
 
index 13ed886577a6327665901478146c3034aea80168..1763be5cf04dfbcaee9e9f17ddd2054e74ca8072 100644 (file)
@@ -52,7 +52,8 @@ static TupleTableSlot *TidNext(TidScanState *node);
 static void
 TidListCreate(TidScanState *tidstate)
 {
-   List       *evalList = tidstate->tss_tidquals;
+   TidScan    *node = (TidScan *) tidstate->ss.ps.plan;
+   List       *evalList = node->tidquals;
    ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
    BlockNumber nblocks;
    ItemPointerData *tidList;
@@ -81,28 +82,29 @@ TidListCreate(TidScanState *tidstate)
 
    foreach(l, evalList)
    {
-       ExprState  *exstate = (ExprState *) lfirst(l);
-       Expr       *expr = exstate->expr;
+       Expr       *expr = (Expr*) lfirst(l);
        ItemPointer itemptr;
        bool        isNull;
 
        if (is_opclause(expr))
        {
-           FuncExprState *fexstate = (FuncExprState *) exstate;
+           FuncExpr   *fex = (FuncExpr *) expr;
+           ExprState  *exprstate;
            Node       *arg1;
            Node       *arg2;
 
            arg1 = get_leftop(expr);
            arg2 = get_rightop(expr);
            if (IsCTIDVar(arg1))
-               exstate = (ExprState *) lsecond(fexstate->args);
+               exprstate = ExecInitExpr((Expr *) lsecond(fex->args),
+                                         &tidstate->ss.ps);
            else if (IsCTIDVar(arg2))
-               exstate = (ExprState *) linitial(fexstate->args);
-           else
+               exprstate = ExecInitExpr((Expr *) linitial(fex->args),
+                                         &tidstate->ss.ps);
                elog(ERROR, "could not identify CTID variable");
 
            itemptr = (ItemPointer)
-               DatumGetPointer(ExecEvalExprSwitchContext(exstate,
+               DatumGetPointer(ExecEvalExprSwitchContext(exprstate,
                                                          econtext,
                                                          &isNull));
            if (!isNull &&
@@ -121,7 +123,8 @@ TidListCreate(TidScanState *tidstate)
        }
        else if (expr && IsA(expr, ScalarArrayOpExpr))
        {
-           ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate;
+           ScalarArrayOpExpr *saex = (ScalarArrayOpExpr *) expr;
+           ExprState  *exprstate;
            Datum       arraydatum;
            ArrayType  *itemarray;
            Datum      *ipdatums;
@@ -129,8 +132,8 @@ TidListCreate(TidScanState *tidstate)
            int         ndatums;
            int         i;
 
-           exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
-           arraydatum = ExecEvalExprSwitchContext(exstate,
+           exprstate = ExecInitExpr(lsecond(saex->args), &tidstate->ss.ps);
+           arraydatum = ExecEvalExprSwitchContext(exprstate,
                                                   econtext,
                                                   &isNull);
            if (isNull)
@@ -470,16 +473,8 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   tidstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.targetlist,
-                    (PlanState *) tidstate);
-   tidstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.qual,
-                    (PlanState *) tidstate);
-
-   tidstate->tss_tidquals = (List *)
-       ExecInitExpr((Expr *) node->tidquals,
-                    (PlanState *) tidstate);
+   tidstate->ss.ps.qual =
+       ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate);
 
    /*
     * tuple table initialization
index 9883a8b1301fd3c34a34affc6bab0e91697d0dbd..9ee776c4c320c72aad2a1c63ceec9c5eab47f5ed 100644 (file)
@@ -120,7 +120,7 @@ ValuesNext(ValuesScanState *node)
         * is a SubPlan, and there shouldn't be any (any subselects in the
         * VALUES list should be InitPlans).
         */
-       exprstatelist = (List *) ExecInitExpr((Expr *) exprlist, NULL);
+       exprstatelist = ExecInitExprList(exprlist, NULL);
 
        /* parser should have checked all sublists are the same length */
        Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts);
@@ -242,12 +242,8 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   scanstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.targetlist,
-                    (PlanState *) scanstate);
-   scanstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.qual,
-                    (PlanState *) scanstate);
+   scanstate->ss.ps.qual =
+       ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
    /*
     * get info about values list
index 2a123e8452688c09afda0901a7be0e222a17e131..628bc9f00b70197c8c2f522970e6000a7bf512ab 100644 (file)
@@ -1826,16 +1826,12 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
    winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate);
    winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate);
 
-   winstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->plan.targetlist,
-                    (PlanState *) winstate);
-
    /*
     * WindowAgg nodes never have quals, since they can only occur at the
     * logical top level of a query (ie, after any WHERE or HAVING filters)
     */
    Assert(node->plan.qual == NIL);
-   winstate->ss.ps.qual = NIL;
+   winstate->ss.ps.qual = NULL;
 
    /*
     * initialize child nodes
@@ -1894,7 +1890,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
    foreach(l, winstate->funcs)
    {
        WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
-       WindowFunc *wfunc = (WindowFunc *) wfuncstate->xprstate.expr;
+       WindowFunc *wfunc = wfuncstate->wfunc;
        WindowStatePerFunc perfuncstate;
        AclResult   aclresult;
        int         i;
index 23b5b9498576512067bddfbfdcae0f64208f7a2b..d7616be06527534d62537a882e381848eadcbd1e 100644 (file)
@@ -156,12 +156,8 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
    /*
     * initialize child expressions
     */
-   scanstate->ss.ps.targetlist = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.targetlist,
-                    (PlanState *) scanstate);
-   scanstate->ss.ps.qual = (List *)
-       ExecInitExpr((Expr *) node->scan.plan.qual,
-                    (PlanState *) scanstate);
+   scanstate->ss.ps.qual =
+       ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
    /*
     * tuple table initialization
index b19380e1b1ef640e62cde7a4b6f0231296c0a14a..42bba543e934e0a6b9b5a94747b315480baa1363 100644 (file)
@@ -3395,7 +3395,7 @@ eval_const_expressions_mutator(Node *node,
                         * Else, make a scalar (argisrow == false) NullTest
                         * for this field.  Scalar semantics are required
                         * because IS [NOT] NULL doesn't recurse; see comments
-                        * in ExecEvalNullTest().
+                        * in ExecEvalRowNullInt().
                         */
                        newntest = makeNode(NullTest);
                        newntest->arg = (Expr *) relem;
@@ -3539,8 +3539,8 @@ eval_const_expressions_mutator(Node *node,
  *     FALSE: drop (does not affect result)
  *     TRUE: force result to TRUE
  *     NULL: keep only one
- * We must keep one NULL input because ExecEvalOr returns NULL when no input
- * is TRUE and at least one is NULL.  We don't actually include the NULL
+ * We must keep one NULL input because OR expressions evaluate to NULL when no
+ * input is TRUE and at least one is NULL.  We don't actually include the NULL
  * here, that's supposed to be done by the caller.
  *
  * The output arguments *haveNull and *forceTrue must be initialized FALSE
@@ -3651,9 +3651,9 @@ simplify_or_arguments(List *args,
  *     TRUE: drop (does not affect result)
  *     FALSE: force result to FALSE
  *     NULL: keep only one
- * We must keep one NULL input because ExecEvalAnd returns NULL when no input
- * is FALSE and at least one is NULL.  We don't actually include the NULL
- * here, that's supposed to be done by the caller.
+ * We must keep one NULL input because AND expressions evaluate to NULL when
+ * no input is FALSE and at least one is NULL.  We don't actually include the
+ * NULL here, that's supposed to be done by the caller.
  *
  * The output arguments *haveNull and *forceFalse must be initialized FALSE
  * by the caller.  They will be set TRUE if a null constant or false constant,
index c2ad440013668aa77189d248891e1abbf193dfcc..f85d3c8111495b39a5ebcbe682f68489c3dd70df 100644 (file)
@@ -122,7 +122,8 @@ domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
 /*
  * domain_check_input - apply the cached checks.
  *
- * This is extremely similar to ExecEvalCoerceToDomain in execQual.c.
+ * This is similar to the handling of CoerceToDomain nodes in
+ * execExpr.c/execInterpExpr.c.
  */
 static void
 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
@@ -165,19 +166,20 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
 
                    /*
                     * Set up value to be returned by CoerceToDomainValue
-                    * nodes.  Unlike ExecEvalCoerceToDomain, this econtext
-                    * couldn't be shared with anything else, so no need to
-                    * save and restore fields.  But we do need to protect the
-                    * passed-in value against being changed by called
-                    * functions.  (It couldn't be a R/W expanded object for
-                    * most uses, but that seems possible for domain_check().)
+                    * nodes.  Unlike in the generic expression case, this
+                    * econtext couldn't be shared with anything else, so no
+                    * need to save and restore fields.  But we do need to
+                    * protect the passed-in value against being changed by
+                    * called functions.  (It couldn't be a R/W expanded
+                    * object for most uses, but that seems possible for
+                    * domain_check().)
                     */
                    econtext->domainValue_datum =
                        MakeExpandedObjectReadOnly(value, isnull,
                                    my_extra->constraint_ref.tcache->typlen);
                    econtext->domainValue_isNull = isnull;
 
-                   conResult = ExecEvalExprSwitchContext(con->check_expr,
+                   conResult = ExecEvalExprSwitchContext(con->check_exprstate,
                                                          econtext,
                                                          &conIsNull);
 
index 5c823250bc21d4b054c205b76c7cf265ae8240ad..d777f1a2ee20327be02b8e098ea5bba776e5fc27 100644 (file)
@@ -6919,7 +6919,7 @@ find_param_referent(Param *param, deparse_context *context,
            foreach(lc2, ps->subPlan)
            {
                SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
-               SubPlan    *subplan = (SubPlan *) sstate->xprstate.expr;
+               SubPlan    *subplan = sstate->subplan;
                ListCell   *lc3;
                ListCell   *lc4;
 
@@ -6960,7 +6960,7 @@ find_param_referent(Param *param, deparse_context *context,
                    continue;
 
                /* No parameters to be had here. */
-               Assert(((SubPlan *) sstate->xprstate.expr)->parParam == NIL);
+               Assert(sstate->subplan->parParam == NIL);
 
                /* Keep looking, but we are emerging from an initplan. */
                in_same_plan_level = false;
index 1908b13db5c7f4328c25316decf6d28b34137db6..06ade88736728ffb5f72837914b0c5c46d3e6eb6 100644 (file)
@@ -72,7 +72,6 @@
 #include "catalog/pg_class.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
-#include "executor/executor.h"
 #include "executor/spi.h"
 #include "executor/tablefunc.h"
 #include "fmgr.h"
@@ -620,10 +619,9 @@ xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg)
 
 
 xmltype *
-xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
+xmlelement(XmlExpr *xexpr, bool *named_argnull, Datum *named_argvalue, bool *argnull, Datum *argvalue)
 {
 #ifdef USE_LIBXML
-   XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
    xmltype    *result;
    List       *named_arg_strings;
    List       *arg_strings;
@@ -635,45 +633,40 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
    volatile xmlTextWriterPtr writer = NULL;
 
    /*
-    * We first evaluate all the arguments, then start up libxml and create
-    * the result.  This avoids issues if one of the arguments involves a call
-    * to some other function or subsystem that wants to use libxml on its own
-    * terms.
+    * All arguments are already evaluated.  This avoids issues if one of the
+    * arguments involves a call to some other function or subsystem that
+    * wants to use libxml on its own terms.
     */
    named_arg_strings = NIL;
    i = 0;
-   foreach(arg, xmlExpr->named_args)
+   foreach(arg, xexpr->named_args)
    {
-       ExprState  *e = (ExprState *) lfirst(arg);
-       Datum       value;
-       bool        isnull;
+       Expr       *e = (Expr *) lfirst(arg);
        char       *str;
 
-       value = ExecEvalExpr(e, econtext, &isnull);
-       if (isnull)
+       if (named_argnull[i])
            str = NULL;
        else
-           str = map_sql_value_to_xml_value(value, exprType((Node *) e->expr), false);
+           str = map_sql_value_to_xml_value(named_argvalue[i], exprType((Node *) e), false);
        named_arg_strings = lappend(named_arg_strings, str);
        i++;
    }
 
    arg_strings = NIL;
-   foreach(arg, xmlExpr->args)
+   i = 0;
+   foreach(arg, xexpr->args)
    {
-       ExprState  *e = (ExprState *) lfirst(arg);
-       Datum       value;
-       bool        isnull;
+       Expr       *e = (Expr *) lfirst(arg);
        char       *str;
 
-       value = ExecEvalExpr(e, econtext, &isnull);
        /* here we can just forget NULL elements immediately */
-       if (!isnull)
+       if (!argnull[i])
        {
-           str = map_sql_value_to_xml_value(value,
-                                          exprType((Node *) e->expr), true);
+           str = map_sql_value_to_xml_value(argvalue[i],
+                                            exprType((Node *) e), true);
            arg_strings = lappend(arg_strings, str);
        }
+       i++;
    }
 
    /* now safe to run libxml */
index 6992634c397095d87d0f384b492d2ebbcd691264..07cae44069ac3452da4c6d83019b2e555a01c22e 100644 (file)
@@ -102,6 +102,7 @@ static TypeCacheEntry *firstDomainTypeEntry = NULL;
  * the DomainConstraintState nodes and applying ExecInitExpr to check_expr.
  * Such a state tree is not part of the DomainConstraintCache, but is
  * considered to belong to a DomainConstraintRef.
+ * FIXME: update.
  */
 struct DomainConstraintCache
 {
@@ -779,8 +780,8 @@ load_domaintype_info(TypeCacheEntry *typentry)
            r = makeNode(DomainConstraintState);
            r->constrainttype = DOM_CONSTRAINT_CHECK;
            r->name = pstrdup(NameStr(c->conname));
-           /* Must cast here because we're not storing an expr state node */
-           r->check_expr = (ExprState *) check_expr;
+           r->check_expr = check_expr;
+           r->check_exprstate = NULL;
 
            MemoryContextSwitchTo(oldcxt);
 
@@ -859,6 +860,7 @@ load_domaintype_info(TypeCacheEntry *typentry)
        r->constrainttype = DOM_CONSTRAINT_NOTNULL;
        r->name = pstrdup("NOT NULL");
        r->check_expr = NULL;
+       r->check_exprstate = NULL;
 
        /* lcons to apply the nullness check FIRST */
        dcc->constraints = lcons(r, dcc->constraints);
@@ -946,8 +948,8 @@ prep_domain_constraints(List *constraints, MemoryContext execctx)
        newr = makeNode(DomainConstraintState);
        newr->constrainttype = r->constrainttype;
        newr->name = r->name;
-       /* Must cast here because cache items contain expr plan trees */
-       newr->check_expr = ExecInitExpr((Expr *) r->check_expr, NULL);
+       newr->check_expr = r->check_expr;
+       newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
 
        result = lappend(result, newr);
    }
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
new file mode 100644 (file)
index 0000000..003f4fa
--- /dev/null
@@ -0,0 +1,456 @@
+/*-------------------------------------------------------------------------
+ *
+ * execExpr.h
+ *   Low level infrastructure related to expression evaluation
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/execExpr.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXEC_EXPR_H
+#define EXEC_EXPR_H
+
+#include "nodes/execnodes.h"
+
+struct ArrayRefState;
+
+#define EEO_FLAG_JUMP_THREADED (1 << 0)
+
+typedef enum ExprEvalOp
+{
+   EEO_DONE,
+   EEO_INNER_FETCHSOME,
+   EEO_OUTER_FETCHSOME,
+   EEO_SCAN_FETCHSOME,
+   EEO_INNER_VAR,
+   EEO_OUTER_VAR,
+   EEO_SCAN_VAR,
+   EEO_ASSIGN_INNER_VAR,
+   EEO_ASSIGN_OUTER_VAR,
+   EEO_ASSIGN_SCAN_VAR,
+   EEO_ASSIGN_TMP,
+   EEO_ASSIGN_TMP_UNEXPAND,
+   EEO_INNER_SYSVAR,
+   EEO_OUTER_SYSVAR,
+   EEO_SCAN_SYSVAR,
+   EEO_CONST,
+   EEO_FUNCEXPR,
+   EEO_FUNCEXPR_STRICT,
+   EEO_FUNCEXPR_FUSAGE,
+   EEO_FUNCEXPR_STRICT_FUSAGE,
+   EEO_BOOL_AND_STEP_FIRST,
+   EEO_BOOL_AND_STEP,
+   EEO_BOOL_AND_STEP_LAST,
+   EEO_BOOL_OR_STEP_FIRST,
+   EEO_BOOL_OR_STEP,
+   EEO_BOOL_OR_STEP_LAST,
+   EEO_BOOL_NOT_STEP,
+   EEO_QUAL,
+   EEO_NULLTEST_ISNULL,
+   EEO_NULLTEST_ISNOTNULL,
+   EEO_NULLTEST_ROWISNULL,
+   EEO_NULLTEST_ROWISNOTNULL,
+   EEO_PARAM_EXEC,
+   EEO_PARAM_EXTERN,
+   EEO_CASE_WHEN_STEP,
+   EEO_CASE_THEN_STEP,
+   EEO_CASE_TESTVAL,
+   EEO_CASE_TESTVAL_UNEXPAND,
+   EEO_COALESCE,
+   EEO_BOOLTEST_IS_TRUE,
+   EEO_BOOLTEST_IS_NOT_TRUE,
+   EEO_BOOLTEST_IS_FALSE,
+   EEO_BOOLTEST_IS_NOT_FALSE,
+   EEO_BOOLTEST_IS_UNKNOWN,
+   EEO_BOOLTEST_IS_NOT_UNKNOWN,
+   EEO_WHOLEROW,
+   EEO_IOCOERCE,
+   EEO_DISTINCT,
+   EEO_NULLIF,
+   EEO_SQLVALUEFUNCTION,
+   EEO_CURRENTOFEXPR,
+   EEO_ARRAYEXPR,
+   EEO_ARRAYCOERCE,
+   EEO_ROW,
+   EEO_ROWCOMPARE_STEP,
+   EEO_ROWCOMPARE_FINAL,
+   EEO_MINMAX,
+   EEO_FIELDSELECT,
+   EEO_FIELDSTORE_DEFORM,
+   EEO_FIELDSTORE_FORM,
+   EEO_ARRAYREF_CHECKINPUT,
+   EEO_ARRAYREF_CHECKSUBSCRIPT,
+   EEO_ARRAYREF_OLD,
+   EEO_ARRAYREF_ASSIGN,
+   EEO_ARRAYREF_FETCH,
+   EEO_CONVERT_ROWTYPE,
+   EEO_SCALARARRAYOP,
+   EEO_DOMAIN_TESTVAL,
+   EEO_DOMAIN_TESTVAL_UNEXPAND,
+   EEO_DOMAIN_NOTNULL,
+   EEO_DOMAIN_CHECK,
+   EEO_XMLEXPR,
+   EEO_AGGREF,
+   EEO_GROUPING_FUNC,
+   EEO_WINDOW_FUNC,
+   EEO_SUBPLAN,
+   EEO_ALTERNATIVE_SUBPLAN,
+   EEO_LAST
+} ExprEvalOp;
+
+
+typedef struct ExprEvalStep
+{
+   /*
+    * Instruction to be executed. During instruction preparation this is an
+    * ExprEvalOp, but during execution it can be swapped out to some other
+    * type, e.g. a pointer for computed goto (that's why it's a size_t).
+    */
+   size_t      opcode;
+
+   /* target for the result of the current instruction */
+   bool       *resnull;
+   Datum      *resvalue;
+
+   /*
+    * Data for an operation. Inline stored data is faster to access, but also
+    * bloats the size of all instructions. The union should be kept below 48
+    * bytes (so the entire struct is below 64bytes, a single cacheline on
+    * common systems).
+    */
+   union
+   {
+       struct
+       {
+           int         attnum;
+       }           var;
+
+       struct
+       {
+           Var        *var;
+           bool        first;  /* first time through, initialize */
+           TupleDesc   tupdesc;    /* descriptor for resulting tuples */
+           JunkFilter *junkFilter;     /* JunkFilter to remove resjunk cols */
+       }           wholerow;
+
+       struct
+       {
+           Datum       value;
+           bool        isnull;
+       }           constval;
+
+       struct
+       {
+           FmgrInfo   *finfo;
+           FunctionCallInfo fcinfo_data;
+           /* faster to access without additional indirection */
+           PGFunction  fn_addr;
+           int         nargs;
+       }           func;
+
+       struct
+       {
+           Datum      *value;
+           bool       *isnull;
+           bool       *anynull;
+           int         jumpdone;
+       }           boolexpr;
+
+       struct
+       {
+           int         jumpdone;
+       }           qualexpr;
+
+       struct
+       {
+           TupleDesc   argdesc;
+       }           nulltest_row;
+
+       struct
+       {
+           int         paramid;
+           int         paramtype;
+       }           param;
+
+       struct
+       {
+           Datum      *value;
+           bool       *isnull;
+           int         jumpfalse;
+       }           casewhen;
+
+       struct
+       {
+           Datum      *value;
+           bool       *isnull;
+           int         jumpdone;
+       }           casethen;
+
+       struct
+       {
+           Datum      *value;
+           bool       *isnull;
+       }           casetest;
+
+       struct
+       {
+           int         jumpdone;
+       }           coalesce;
+
+       struct
+       {
+           /* lookup info for source output function */
+           FmgrInfo   *finfo_out;
+           FunctionCallInfo fcinfo_data_out;
+           /* lookup info for result input function */
+           FmgrInfo   *finfo_in;
+           FunctionCallInfo fcinfo_data_in;
+           Oid         intypioparam;   /* argument needed for input function */
+       }           iocoerce;
+
+       struct
+       {
+           SQLValueFunction *svf;
+       }           sqlvaluefunction;
+
+       struct
+       {
+           ArrayExpr  *arrayexpr;
+           Datum      *elemvalues;
+           bool       *elemnulls;
+           int         nelems;
+           int16       elemlength;     /* typlen of the array element type */
+           bool        elembyval;      /* is the element type pass-by-value? */
+           char        elemalign;      /* typalign of the element type */
+       }           arrayexpr;
+
+       struct
+       {
+           ArrayCoerceExpr *coerceexpr;
+           Oid         resultelemtype; /* element type of result array */
+           FmgrInfo   *elemfunc;       /* lookup info for element coercion
+                                        * function */
+           struct ArrayMapState *amstate;      /* workspace for array_map */
+       }           arraycoerce;
+
+       struct
+       {
+           RowExpr    *rowexpr;
+           /* the arguments */
+           Datum      *elemvalues;
+           bool       *elemnulls;
+           TupleDesc   tupdesc;    /* descriptor for result tuples */
+       }           row;
+
+       struct
+       {
+           FmgrInfo   *finfo;
+           FunctionCallInfo fcinfo_data;
+           PGFunction  fn_addr;
+           int         jumpnull;
+           int         jumpdone;
+       }           rowcompare_step;
+
+       struct
+       {
+           RowCompareType rctype;
+       }           rowcompare_final;
+
+       struct
+       {
+           /* the arguments */
+           Datum      *values;
+           bool       *nulls;
+           int         nelems;
+
+           MinMaxOp    op;
+           FmgrInfo   *finfo;
+           FunctionCallInfo fcinfo_data;
+       }           minmax;
+
+       struct
+       {
+           /* tupdesc for most recent input */
+           TupleDesc   argdesc;
+           AttrNumber  fieldnum;
+           Oid         resulttype;
+       }           fieldselect;
+
+       struct
+       {
+           /* tupdesc for most recent input */
+           TupleDesc  *argdesc;
+           FieldStore *fstore;
+
+           /* the arguments */
+           Datum      *values;
+           bool       *nulls;
+       }           fieldstore;
+
+       struct
+       {
+           /* too big to have inline */
+           struct ArrayRefState *state;
+           int         jumpdone;
+       }           arrayref;
+
+       struct
+       {
+           /* too big to have inline */
+           struct ArrayRefState *state;
+           int         off;
+           int         jumpdone;
+           bool        isupper;
+       }           arrayref_checksubscript;
+
+       struct
+       {
+           ConvertRowtypeExpr *convert;
+           TupleDesc   indesc;
+           TupleDesc   outdesc;
+           TupleConversionMap *map;
+           bool        initialized;
+       }           convert_rowtype;
+
+       struct
+       {
+           ScalarArrayOpExpr *opexpr;
+           Oid         element_type;
+           int16       typlen;
+           bool        typbyval;
+           char        typalign;
+           FmgrInfo   *finfo;
+           FunctionCallInfo fcinfo_data;
+           PGFunction  fn_addr;
+       }           scalararrayop;
+
+       struct
+       {
+           char       *constraintname;
+           Datum      *checkvalue;
+           bool       *checknull;
+           Oid         resulttype;
+       }           domaincheck;
+
+       struct
+       {
+           XmlExpr    *xexpr;
+           Datum      *named_argvalue;
+           bool       *named_argnull;
+           Datum      *argvalue;
+           bool       *argnull;
+       }           xmlexpr;
+
+       struct
+       {
+           AggrefExprState *astate;
+       }           aggref;
+
+       struct
+       {
+           AggState   *parent;
+           List       *clauses;
+       }           grouping_func;
+
+       struct
+       {
+           WindowFuncExprState *wfstate;
+       }           window_func;
+
+       struct
+       {
+           SubPlanState *sstate;
+       }           subplan;
+
+       struct
+       {
+           AlternativeSubPlanState *asstate;
+       }           alternative_subplan;
+
+       struct
+       {
+           size_t      resultnum;
+           int         attnum;
+       }           assign_var;
+
+       struct
+       {
+           size_t      resultnum;
+       }           assign_tmp;
+
+       struct
+       {
+           int         last_var;
+       }           fetch;
+   }           d;
+} ExprEvalStep;
+
+
+typedef struct ArrayRefState
+{
+   bool        isassignment;
+   int         numupper;
+   Datum       upper[MAXDIM];
+   int         upperindex[MAXDIM];
+   bool        uppernull[MAXDIM];
+   bool        upperprovided[MAXDIM];
+   int         numlower;
+   Datum       lower[MAXDIM];
+   int         lowerindex[MAXDIM];
+   bool        lowernull[MAXDIM];
+   bool        lowerprovided[MAXDIM];
+
+   Oid         refelemtype;
+   int16       refattrlength;  /* typlen of array type */
+   int16       refelemlength;  /* typlen of the array element type */
+   bool        refelembyval;   /* is the element type pass-by-value? */
+   char        refelemalign;   /* typalign of the element type */
+
+   Datum       replacevalue;
+   bool        replacenull;
+
+   Datum       prevvalue;
+   bool        prevnull;
+}  ArrayRefState;
+
+extern void ExecInstantiateInterpretedExpr(ExprState *state);
+
+extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op);
+
+/*
+ * Non fast-path execution functions. These are externs instead of static in
+ * execInterpExpr.c, because that allows them to be used by other methods of
+ * expression evaluation.
+ */
+extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRow(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern bool ExecEvalArrayRefCheckSubscript(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRowNull(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+
+#endif   /* EXEC_EXPR_H */
index cf44c3edbbc49acf0240e5eb806a371db20519a7..d828a3f9074d18be14f94741c57c45ca4d07f0b1 100644 (file)
 #undef EXEC_NESTLOOPDEBUG
  */
 
-/* ----------------
- *     EXEC_EVALDEBUG is a flag which turns on debugging of
- *     ExecEval and ExecTargetList() stuff by EV_printf() in execQual.c
- * ----------------
-#undef EXEC_EVALDEBUG
- */
-
 /* ----------------
  *     EXEC_SORTDEBUG is a flag which turns on debugging of
  *     the ExecSort() stuff by SO_printf() in nodeSort.c
 #define ENL1_printf(message)
 #endif   /* EXEC_NESTLOOPDEBUG */
 
-/* ----------------
- *     exec eval / target list debugging defines
- * ----------------
- */
-#ifdef EXEC_EVALDEBUG
-#define EV_nodeDisplay(l)              nodeDisplay(l)
-#define EV_printf(s)                   printf(s)
-#define EV1_printf(s, a)               printf(s, a)
-#else
-#define EV_nodeDisplay(l)
-#define EV_printf(s)
-#define EV1_printf(s, a)
-#endif   /* EXEC_EVALDEBUG */
-
 /* ----------------
  *     sort node debugging defines
  * ----------------
index f722f33a667d76812d62d73dfe2e1e8bbc966edd..1e6609f1668e71f8aa086b44bcd8b161a8ad5a19 100644 (file)
@@ -70,8 +70,8 @@
  * now it's just a macro invoking the function pointed to by an ExprState
  * node.  Beware of double evaluation of the ExprState argument!
  */
-#define ExecEvalExpr(expr, econtext, isNull) \
-   ((*(expr)->evalfunc) (expr, econtext, isNull))
+#define ExecEvalExpr(state, econtext, isNull) \
+   (state)->evalfunc((state), econtext, isNull)
 
 
 /* Hook for plugins to get control in ExecutorStart() */
@@ -247,23 +247,118 @@ extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno,
                  bool *isNull);
 extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname,
                   bool *isNull);
-extern Tuplestorestate *ExecMakeTableFunctionResult(ExprState *funcexpr,
+extern Tuplestorestate *ExecMakeTableFunctionResult(SetExprState *setexpr,
                            ExprContext *econtext,
                            MemoryContext argContext,
                            TupleDesc expectedDesc,
                            bool randomAccess);
-extern Datum ExecMakeFunctionResultSet(FuncExprState *fcache,
+extern Datum ExecMakeFunctionResultSet(SetExprState *setexpr,
                          ExprContext *econtext,
                          bool *isNull,
                          ExprDoneCond *isDone);
-extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,
-                         bool *isNull);
+extern SetExprState *ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, PlanState *parent);
+extern SetExprState *ExecInitTableFunctionResult(Expr *expr, ExprContext *econtext, PlanState *parent);
+
+/*
+ * prototypes from functions in execExpr.c
+ */
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
+extern List *ExecInitExprList(List *nodes, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern List *ExecPrepareExprList(List *nodes, EState *estate);
+extern ExprState *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState *ExecInitQual(List *qual, PlanState *parent);
+extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
+
+extern bool ExecCheck(ExprState *state, ExprContext *context);
 extern int ExecTargetListLength(List *targetlist);
 extern int ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo);
+
+/*
+ * ExecEvalExprSwitchContext
+ *
+ * Same as ExecEvalExpr, but get into the right allocation context explicitly.
+ */
+#ifndef FRONTEND
+static inline Datum
+ExecEvalExprSwitchContext(ExprState *state,
+                         ExprContext *econtext,
+                         bool *isNull)
+{
+   Datum       retDatum;
+   MemoryContext oldContext;
+
+   oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+   retDatum = ExecEvalExpr(state, econtext, isNull);
+   MemoryContextSwitchTo(oldContext);
+   return retDatum;
+}
+#endif
+
+/*
+ * ExecProject
+ *
+ * Projects a tuple based on projection info and stores it in the slot passed
+ * to ExecBuildProjectInfo().
+ *
+ * Note: the result is always a virtual tuple; therefore it may reference the
+ * contents of the exprContext's scan tuples and/or temporary results
+ * constructed in the exprContext.  If the caller wishes the result to be
+ * valid longer than that data will be valid, he must call ExecMaterializeSlot
+ * on the result slot.
+ */
+#ifndef FRONTEND
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo)
+{
+   bool        isnull;
+   ExprContext *econtext = projInfo->pi_exprContext;
+   ExprState  *state = &projInfo->pi_state;
+   TupleTableSlot *slot = state->resultslot;
+
+   /*
+    * Clear any former contents of the result slot.  This makes it safe for
+    * us to use the slot's Datum/isnull arrays as workspace.
+    */
+   ExecClearTuple(slot);
+
+   ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+   /*
+    * Successfully formed a result row.  Mark the result slot as containing a
+    * valid virtual tuple.
+    */
+   slot->tts_isempty = false;
+   slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
+
+   return slot;
+}
+#endif
+
+/*
+ * ExecQual - evaluate a qual prepared with ExecInitQual (possibly via
+ * ExecPrepareQual).
+ */
+#ifndef FRONTEND
+static inline bool
+ExecQual(ExprState *state, ExprContext *econtext)
+{
+   bool        isnull;
+   Datum       ret;
+
+   /* short-circuit (here and in ExecInitQual) for empty restriction list */
+   if (state == NULL)
+       return true;
+
+   ret = ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+   /* EEO_QUAL should never return NULL */
+   Assert(!isnull);
+
+   return DatumGetBool(ret);
+}
+#endif
 
 /*
  * prototypes from functions in execScan.c
@@ -361,6 +456,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
                        ExprContext *econtext,
                        TupleTableSlot *slot,
+                       PlanState *planstate,
                        TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
                         TupleDesc inputDesc);
index 0f821dc8f67b19f17d2bad42d41ddb27c8142e7d..0bdaa548df2f51ccbe9f76af8fb6ee15a36ebbd6 100644 (file)
@@ -20,6 +20,9 @@ extern SubPlanState *ExecInitSubPlan(SubPlan *subplan, PlanState *parent);
 
 extern AlternativeSubPlanState *ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent);
 
+extern Datum ExecSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull);
+extern Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node, ExprContext *econtext, bool *isNull);
+
 extern void ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent);
 
 extern void ExecSetParamPlan(SubPlanState *node, ExprContext *econtext);
index f856f6036f664f0b341106593a8d1eb77a6a3bf3..8d303827466fee70ae8f194c5d1b50953e8e31b1 100644 (file)
 #include "storage/condition_variable.h"
 
 
-/* ----------------
- *   IndexInfo information
- *
- *     this struct holds the information needed to construct new index
- *     entries for a particular index.  Used for both index_build and
- *     retail creation of index entries.
- *
- *     NumIndexAttrs       number of columns in this index
- *     KeyAttrNumbers      underlying-rel attribute numbers used as keys
- *                         (zeroes indicate expressions)
- *     Expressions         expr trees for expression entries, or NIL if none
- *     ExpressionsState    exec state for expressions, or NIL if none
- *     Predicate           partial-index predicate, or NIL if none
- *     PredicateState      exec state for predicate, or NIL if none
- *     ExclusionOps        Per-column exclusion operators, or NULL if none
- *     ExclusionProcs      Underlying function OIDs for ExclusionOps
- *     ExclusionStrats     Opclass strategy numbers for ExclusionOps
- *     UniqueOps           Theses are like Exclusion*, but for unique indexes
- *     UniqueProcs
- *     UniqueStrats
- *     Unique              is it a unique index?
- *     ReadyForInserts     is it valid for inserts?
- *     Concurrent          are we doing a concurrent index build?
- *     BrokenHotChain      did we detect any broken HOT chains?
- *     AmCache             private cache area for index AM
- *     Context             memory context holding this IndexInfo
- *
- * ii_Concurrent and ii_BrokenHotChain are used only during index build;
- * they're conventionally set to false otherwise.
- * ----------------
- */
-typedef struct IndexInfo
-{
-   NodeTag     type;
-   int         ii_NumIndexAttrs;
-   AttrNumber  ii_KeyAttrNumbers[INDEX_MAX_KEYS];
-   List       *ii_Expressions; /* list of Expr */
-   List       *ii_ExpressionsState;    /* list of ExprState */
-   List       *ii_Predicate;   /* list of Expr */
-   List       *ii_PredicateState;      /* list of ExprState */
-   Oid        *ii_ExclusionOps;    /* array with one entry per column */
-   Oid        *ii_ExclusionProcs;      /* array with one entry per column */
-   uint16     *ii_ExclusionStrats;     /* array with one entry per column */
-   Oid        *ii_UniqueOps;   /* array with one entry per column */
-   Oid        *ii_UniqueProcs; /* array with one entry per column */
-   uint16     *ii_UniqueStrats;    /* array with one entry per column */
-   bool        ii_Unique;
-   bool        ii_ReadyForInserts;
-   bool        ii_Concurrent;
-   bool        ii_BrokenHotChain;
-   void       *ii_AmCache;
-   MemoryContext ii_Context;
-} IndexInfo;
-
 /* ----------------
  *   ExprContext_CB
  *
@@ -207,6 +153,50 @@ typedef struct ReturnSetInfo
    TupleDesc   setDesc;        /* actual descriptor for returned tuples */
 } ReturnSetInfo;
 
+/* ----------------
+ *     ExprState node
+ *
+ * ExprState is the top-level node for expression evaluation. It contains
+ * instructions (in ->steps) to evaluate the expression.
+ * ----------------
+ */
+typedef struct ExprState ExprState;
+
+typedef Datum (*ExprStateEvalFunc) (ExprState *expression,
+                                               ExprContext *econtext,
+                                               bool *isNull);
+struct ExprState
+{
+   Node        tag;
+
+   bool        resnull;
+   int8        flags;
+   Datum       resvalue;
+
+   /* forward declaration, to avoid including execExpr.h everywhere */
+   struct ExprEvalStep *steps;
+
+   TupleTableSlot *resultslot; /* slot for projections */
+
+   /* evaluate expression */
+   ExprStateEvalFunc evalfunc;
+
+   /* original expression, for debugging only */
+   Expr       *expr;
+
+   /*
+    * XXX: following only needed during "compilation", could be thrown away.
+    */
+   size_t      steps_len;
+   size_t      steps_alloc;
+
+   Datum      *innermost_caseval;
+   bool       *innermost_casenull;
+
+   Datum      *innermost_domainval;
+   bool       *innermost_domainnull;
+};
+
 /* ----------------
  *     ProjectionInfo node information
  *
@@ -214,51 +204,16 @@ typedef struct ReturnSetInfo
  *     that is, form new tuples by evaluation of targetlist expressions.
  *     Nodes which need to do projections create one of these.
  *
- *     ExecProject() evaluates the tlist, forms a tuple, and stores it
- *     in the given slot.  Note that the result will be a "virtual" tuple
- *     unless ExecMaterializeSlot() is then called to force it to be
- *     converted to a physical tuple.  The slot must have a tupledesc
- *     that matches the output of the tlist!
- *
- *     The planner very often produces tlists that consist entirely of
- *     simple Var references (lower levels of a plan tree almost always
- *     look like that).  And top-level tlists are often mostly Vars too.
- *     We therefore optimize execution of simple-Var tlist entries.
- *     The pi_targetlist list actually contains only the tlist entries that
- *     aren't simple Vars, while those that are Vars are processed using the
- *     varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *     The lastXXXVar fields are used to optimize fetching of fields from
- *     input tuples: they let us do a slot_getsomeattrs() call to ensure
- *     that all needed attributes are extracted in one pass.
- *
- *     targetlist      target list for projection (non-Var expressions only)
+ *     pi_state        instructions to evaluate projection
  *     exprContext     expression context in which to evaluate targetlist
  *     slot            slot to place projection result in
- *     directMap       true if varOutputCols[] is an identity map
- *     numSimpleVars   number of simple Vars found in original tlist
- *     varSlotOffsets  array indicating which slot each simple Var is from
- *     varNumbers      array containing input attr numbers of simple Vars
- *     varOutputCols   array containing output attr numbers of simple Vars
- *     lastInnerVar    highest attnum from inner tuple slot (0 if none)
- *     lastOuterVar    highest attnum from outer tuple slot (0 if none)
- *     lastScanVar     highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
    NodeTag     type;
-   List       *pi_targetlist;
+   ExprState   pi_state;
    ExprContext *pi_exprContext;
-   TupleTableSlot *pi_slot;
-   bool        pi_directMap;
-   int         pi_numSimpleVars;
-   int        *pi_varSlotOffsets;
-   int        *pi_varNumbers;
-   int        *pi_varOutputCols;
-   int         pi_lastInnerVar;
-   int         pi_lastOuterVar;
-   int         pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -299,6 +254,60 @@ typedef struct JunkFilter
    AttrNumber  jf_junkAttNo;
 } JunkFilter;
 
+/* ----------------
+ *   IndexInfo information
+ *
+ *     this struct holds the information needed to construct new index
+ *     entries for a particular index.  Used for both index_build and
+ *     retail creation of index entries.
+ *
+ *     NumIndexAttrs       number of columns in this index
+ *     KeyAttrNumbers      underlying-rel attribute numbers used as keys
+ *                         (zeroes indicate expressions)
+ *     Expressions         expr trees for expression entries, or NIL if none
+ *     ExpressionsState    exec state for expressions, or NIL if none
+ *     Predicate           partial-index predicate, or NIL if none
+ *     PredicateState      exec state for predicate, or NIL if none
+ *     ExclusionOps        Per-column exclusion operators, or NULL if none
+ *     ExclusionProcs      Underlying function OIDs for ExclusionOps
+ *     ExclusionStrats     Opclass strategy numbers for ExclusionOps
+ *     UniqueOps           Theses are like Exclusion*, but for unique indexes
+ *     UniqueProcs
+ *     UniqueStrats
+ *     Unique              is it a unique index?
+ *     ReadyForInserts     is it valid for inserts?
+ *     Concurrent          are we doing a concurrent index build?
+ *     BrokenHotChain      did we detect any broken HOT chains?
+ *     AmCache             private cache area for index AM
+ *     Context             memory context holding this IndexInfo
+ *
+ * ii_Concurrent and ii_BrokenHotChain are used only during index build;
+ * they're conventionally set to false otherwise.
+ * ----------------
+ */
+typedef struct IndexInfo
+{
+   NodeTag     type;
+   int         ii_NumIndexAttrs;
+   AttrNumber  ii_KeyAttrNumbers[INDEX_MAX_KEYS];
+   List       *ii_Expressions; /* list of Expr */
+   List       *ii_ExpressionsState;    /* list of ExprState */
+   List       *ii_Predicate;   /* list of Expr */
+   ExprState  *ii_PredicateState;
+   Oid        *ii_ExclusionOps;    /* array with one entry per column */
+   Oid        *ii_ExclusionProcs;      /* array with one entry per column */
+   uint16     *ii_ExclusionStrats;     /* array with one entry per column */
+   Oid        *ii_UniqueOps;   /* array with one entry per column */
+   Oid        *ii_UniqueProcs; /* array with one entry per column */
+   uint16     *ii_UniqueStrats;    /* array with one entry per column */
+   bool        ii_Unique;
+   bool        ii_ReadyForInserts;
+   bool        ii_Concurrent;
+   bool        ii_BrokenHotChain;
+   void       *ii_AmCache;
+   MemoryContext ii_Context;
+} IndexInfo;
+
 /* ----------------
  *   ResultRelInfo information
  *
@@ -340,20 +349,20 @@ typedef struct ResultRelInfo
    IndexInfo **ri_IndexRelationInfo;
    TriggerDesc *ri_TrigDesc;
    FmgrInfo   *ri_TrigFunctions;
-   List      **ri_TrigWhenExprs;
+   ExprState **ri_TrigWhenExprs;
    Instrumentation *ri_TrigInstrument;
    struct FdwRoutine *ri_FdwRoutine;
    void       *ri_FdwState;
    bool        ri_usesFdwDirectModify;
    List       *ri_WithCheckOptions;
    List       *ri_WithCheckOptionExprs;
-   List      **ri_ConstraintExprs;
+   ExprState **ri_ConstraintExprs;
    JunkFilter *ri_junkFilter;
    ProjectionInfo *ri_projectReturning;
    ProjectionInfo *ri_onConflictSetProj;
-   List       *ri_onConflictSetWhere;
+   ExprState  *ri_onConflictSetWhere;
    List       *ri_PartitionCheck;
-   List       *ri_PartitionCheckExpr;
+   ExprState  *ri_PartitionCheckExpr;
    Relation    ri_PartitionRoot;
 } ResultRelInfo;
 
@@ -436,7 +445,7 @@ typedef struct EState
    bool       *es_epqScanDone; /* true if EPQ tuple has been fetched */
 
    /* The per-query shared memory area to use for parallel execution. */
-   struct dsa_area   *es_query_dsa;
+   struct dsa_area *es_query_dsa;
 } EState;
 
 
@@ -562,142 +571,53 @@ typedef tuplehash_iterator TupleHashIterator;
 #define ScanTupleHashTable(htable, iter) \
    tuplehash_iterate(htable->hashtab, iter)
 
-
-/* ----------------------------------------------------------------
- *              Expression State Trees
- *
- * Each executable expression tree has a parallel ExprState tree.
- *
- * Unlike PlanState, there is not an exact one-for-one correspondence between
- * ExprState node types and Expr node types.  Many Expr node types have no
- * need for node-type-specific run-time state, and so they can use plain
- * ExprState or GenericExprState as their associated ExprState node type.
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- *     ExprState node
- *
- * ExprState is the common superclass for all ExprState-type nodes.
- *
- * It can also be instantiated directly for leaf Expr nodes that need no
- * local run-time state (such as Var, Const, or Param).
- *
- * To save on dispatch overhead, each ExprState node contains a function
- * pointer to the routine to execute to evaluate the node.
- * ----------------
- */
-
-typedef struct ExprState ExprState;
-
-typedef Datum (*ExprStateEvalFunc) (ExprState *expression,
-                                               ExprContext *econtext,
-                                               bool *isNull);
-
-struct ExprState
-{
-   NodeTag     type;
-   Expr       *expr;           /* associated Expr node */
-   ExprStateEvalFunc evalfunc; /* routine to run to execute node */
-};
-
-/* ----------------
- *     GenericExprState node
- *
- * This is used for Expr node types that need no local run-time state,
- * but have one child Expr node.
- * ----------------
- */
-typedef struct GenericExprState
-{
-   ExprState   xprstate;
-   ExprState  *arg;            /* state of my child node */
-} GenericExprState;
-
-/* ----------------
- *     WholeRowVarExprState node
- * ----------------
- */
-typedef struct WholeRowVarExprState
-{
-   ExprState   xprstate;
-   struct PlanState *parent;   /* parent PlanState, or NULL if none */
-   TupleDesc   wrv_tupdesc;    /* descriptor for resulting tuples */
-   JunkFilter *wrv_junkFilter; /* JunkFilter to remove resjunk cols */
-} WholeRowVarExprState;
-
 /* ----------------
  *     AggrefExprState node
  * ----------------
  */
 typedef struct AggrefExprState
 {
-   ExprState   xprstate;
+   NodeTag     type;
+   Aggref     *aggref;
    int         aggno;          /* ID number for agg within its plan node */
 } AggrefExprState;
 
-/* ----------------
- *     GroupingFuncExprState node
- *
- * The list of column numbers refers to the input tuples of the Agg node to
- * which the GroupingFunc belongs, and may contain 0 for references to columns
- * that are only present in grouping sets processed by different Agg nodes (and
- * which are therefore always considered "grouping" here).
- * ----------------
- */
-typedef struct GroupingFuncExprState
-{
-   ExprState   xprstate;
-   struct AggState *aggstate;
-   List       *clauses;        /* integer list of column numbers */
-} GroupingFuncExprState;
-
 /* ----------------
  *     WindowFuncExprState node
  * ----------------
  */
 typedef struct WindowFuncExprState
 {
-   ExprState   xprstate;
+   NodeTag     type;
+   WindowFunc *wfunc;
    List       *args;           /* states of argument expressions */
    ExprState  *aggfilter;      /* FILTER expression */
    int         wfuncno;        /* ID number for wfunc within its plan node */
 } WindowFuncExprState;
 
-/* ----------------
- *     ArrayRefExprState node
- *
- * Note: array types can be fixed-length (typlen > 0), but only when the
- * element type is itself fixed-length.  Otherwise they are varlena structures
- * and have typlen = -1.  In any case, an array type is never pass-by-value.
- * ----------------
- */
-typedef struct ArrayRefExprState
-{
-   ExprState   xprstate;
-   List       *refupperindexpr;    /* states for child nodes */
-   List       *reflowerindexpr;
-   ExprState  *refexpr;
-   ExprState  *refassgnexpr;
-   int16       refattrlength;  /* typlen of array type */
-   int16       refelemlength;  /* typlen of the array element type */
-   bool        refelembyval;   /* is the element type pass-by-value? */
-   char        refelemalign;   /* typalign of the element type */
-} ArrayRefExprState;
 
 /* ----------------
- *     FuncExprState node
+ *     SetExprState node
  *
- * Although named for FuncExpr, this is also used for OpExpr, DistinctExpr,
- * and NullIf nodes; be careful to check what xprstate.expr is actually
- * pointing at!
+ * State for evaluating a potentially set returning expression (like FuncExpr
+ * or OpExpr).  In some cases, like some of the expressions in ROWS FROM(...)
+ * the expression might not be a SRF, but uses the same machinery as SRFs
+ * (i.e. are treated like a SRF returning a single row).
  * ----------------
  */
-typedef struct FuncExprState
+typedef struct SetExprState
 {
-   ExprState   xprstate;
+   NodeTag     type;
+   Expr       *expr;
    List       *args;           /* states of argument expressions */
 
+   /*
+    * In ROWS FROM functions possibly can be inlined removing the FuncExpr
+    * normally inside, this is the containing expression (which cannot return
+    * a set) which'll be evaluated using the normal ExecEvalExpr().
+    */
+   ExprState  *elidedFuncState;
+
    /*
     * Function manager's lookup info for the target function.  If func.fn_oid
     * is InvalidOid, we haven't initialized it yet (nor any of the following
@@ -750,33 +670,7 @@ typedef struct FuncExprState
     * argument values between calls, when setArgsValid is true.
     */
    FunctionCallInfoData fcinfo_data;
-} FuncExprState;
-
-/* ----------------
- *     ScalarArrayOpExprState node
- *
- * This is a FuncExprState plus some additional data.
- * ----------------
- */
-typedef struct ScalarArrayOpExprState
-{
-   FuncExprState fxprstate;
-   /* Cached info about array element type */
-   Oid         element_type;
-   int16       typlen;
-   bool        typbyval;
-   char        typalign;
-} ScalarArrayOpExprState;
-
-/* ----------------
- *     BoolExprState node
- * ----------------
- */
-typedef struct BoolExprState
-{
-   ExprState   xprstate;
-   List       *args;           /* states of argument expression(s) */
-} BoolExprState;
+} SetExprState;
 
 /* ----------------
  *     SubPlanState node
@@ -784,7 +678,8 @@ typedef struct BoolExprState
  */
 typedef struct SubPlanState
 {
-   ExprState   xprstate;
+   NodeTag     type;
+   SubPlan    *subplan;
    struct PlanState *planstate;    /* subselect plan's state tree */
    struct PlanState *parent;   /* parent plan node's state tree */
    ExprState  *testexpr;       /* state of combining expression */
@@ -814,197 +709,12 @@ typedef struct SubPlanState
  */
 typedef struct AlternativeSubPlanState
 {
-   ExprState   xprstate;
+   NodeTag     type;
+   AlternativeSubPlan *subplan;
    List       *subplans;       /* states of alternative subplans */
    int         active;         /* list index of the one we're using */
 } AlternativeSubPlanState;
 
-/* ----------------
- *     FieldSelectState node
- * ----------------
- */
-typedef struct FieldSelectState
-{
-   ExprState   xprstate;
-   ExprState  *arg;            /* input expression */
-   TupleDesc   argdesc;        /* tupdesc for most recent input */
-} FieldSelectState;
-
-/* ----------------
- *     FieldStoreState node
- * ----------------
- */
-typedef struct FieldStoreState
-{
-   ExprState   xprstate;
-   ExprState  *arg;            /* input tuple value */
-   List       *newvals;        /* new value(s) for field(s) */
-   TupleDesc   argdesc;        /* tupdesc for most recent input */
-} FieldStoreState;
-
-/* ----------------
- *     CoerceViaIOState node
- * ----------------
- */
-typedef struct CoerceViaIOState
-{
-   ExprState   xprstate;
-   ExprState  *arg;            /* input expression */
-   FmgrInfo    outfunc;        /* lookup info for source output function */
-   FmgrInfo    infunc;         /* lookup info for result input function */
-   Oid         intypioparam;   /* argument needed for input function */
-} CoerceViaIOState;
-
-/* ----------------
- *     ArrayCoerceExprState node
- * ----------------
- */
-typedef struct ArrayCoerceExprState
-{
-   ExprState   xprstate;
-   ExprState  *arg;            /* input array value */
-   Oid         resultelemtype; /* element type of result array */
-   FmgrInfo    elemfunc;       /* lookup info for element coercion function */
-   /* use struct pointer to avoid including array.h here */
-   struct ArrayMapState *amstate;      /* workspace for array_map */
-} ArrayCoerceExprState;
-
-/* ----------------
- *     ConvertRowtypeExprState node
- * ----------------
- */
-typedef struct ConvertRowtypeExprState
-{
-   ExprState   xprstate;
-   ExprState  *arg;            /* input tuple value */
-   TupleDesc   indesc;         /* tupdesc for source rowtype */
-   TupleDesc   outdesc;        /* tupdesc for result rowtype */
-   /* use "struct" so we needn't include tupconvert.h here */
-   struct TupleConversionMap *map;
-   bool        initialized;
-} ConvertRowtypeExprState;
-
-/* ----------------
- *     CaseExprState node
- * ----------------
- */
-typedef struct CaseExprState
-{
-   ExprState   xprstate;
-   ExprState  *arg;            /* implicit equality comparison argument */
-   List       *args;           /* the arguments (list of WHEN clauses) */
-   ExprState  *defresult;      /* the default result (ELSE clause) */
-   int16       argtyplen;      /* if arg is provided, its typlen */
-} CaseExprState;
-
-/* ----------------
- *     CaseWhenState node
- * ----------------
- */
-typedef struct CaseWhenState
-{
-   ExprState   xprstate;
-   ExprState  *expr;           /* condition expression */
-   ExprState  *result;         /* substitution result */
-} CaseWhenState;
-
-/* ----------------
- *     ArrayExprState node
- *
- * Note: ARRAY[] expressions always produce varlena arrays, never fixed-length
- * arrays.
- * ----------------
- */
-typedef struct ArrayExprState
-{
-   ExprState   xprstate;
-   List       *elements;       /* states for child nodes */
-   int16       elemlength;     /* typlen of the array element type */
-   bool        elembyval;      /* is the element type pass-by-value? */
-   char        elemalign;      /* typalign of the element type */
-} ArrayExprState;
-
-/* ----------------
- *     RowExprState node
- * ----------------
- */
-typedef struct RowExprState
-{
-   ExprState   xprstate;
-   List       *args;           /* the arguments */
-   TupleDesc   tupdesc;        /* descriptor for result tuples */
-} RowExprState;
-
-/* ----------------
- *     RowCompareExprState node
- * ----------------
- */
-typedef struct RowCompareExprState
-{
-   ExprState   xprstate;
-   List       *largs;          /* the left-hand input arguments */
-   List       *rargs;          /* the right-hand input arguments */
-   FmgrInfo   *funcs;          /* array of comparison function info */
-   Oid        *collations;     /* array of collations to use */
-} RowCompareExprState;
-
-/* ----------------
- *     CoalesceExprState node
- * ----------------
- */
-typedef struct CoalesceExprState
-{
-   ExprState   xprstate;
-   List       *args;           /* the arguments */
-} CoalesceExprState;
-
-/* ----------------
- *     MinMaxExprState node
- * ----------------
- */
-typedef struct MinMaxExprState
-{
-   ExprState   xprstate;
-   List       *args;           /* the arguments */
-   FmgrInfo    cfunc;          /* lookup info for comparison func */
-} MinMaxExprState;
-
-/* ----------------
- *     XmlExprState node
- * ----------------
- */
-typedef struct XmlExprState
-{
-   ExprState   xprstate;
-   List       *named_args;     /* ExprStates for named arguments */
-   List       *args;           /* ExprStates for other arguments */
-} XmlExprState;
-
-/* ----------------
- *     NullTestState node
- * ----------------
- */
-typedef struct NullTestState
-{
-   ExprState   xprstate;
-   ExprState  *arg;            /* input expression */
-   /* used only if input is of composite type: */
-   TupleDesc   argdesc;        /* tupdesc for most recent input */
-} NullTestState;
-
-/* ----------------
- *     CoerceToDomainState node
- * ----------------
- */
-typedef struct CoerceToDomainState
-{
-   ExprState   xprstate;
-   ExprState  *arg;            /* input expression */
-   /* Cached set of constraints that need to be checked */
-   /* use struct pointer to avoid including typcache.h here */
-   struct DomainConstraintRef *constraint_ref;
-} CoerceToDomainState;
-
 /*
  * DomainConstraintState - one item to check during CoerceToDomain
  *
@@ -1023,7 +733,8 @@ typedef struct DomainConstraintState
    NodeTag     type;
    DomainConstraintType constrainttype;        /* constraint type */
    char       *name;           /* name of constraint (for error msgs) */
-   ExprState  *check_expr;     /* for CHECK, a boolean expression */
+   Expr       *check_expr;     /* for CHECK, a boolean expression */
+   ExprState  *check_exprstate;    /* check_expr's evaluation state */
 } DomainConstraintState;
 
 
@@ -1060,8 +771,7 @@ typedef struct PlanState
     * state trees parallel links in the associated plan tree (except for the
     * subPlan list, which does not exist in the plan tree).
     */
-   List       *targetlist;     /* target list to be computed at this node */
-   List       *qual;           /* implicitly-ANDed qual conditions */
+   ExprState  *qual;           /* implicitly-ANDed qual conditions */
    struct PlanState *lefttree; /* input plan tree(s) */
    struct PlanState *righttree;
    List       *initPlan;       /* Init SubPlanState nodes (un-correlated expr
@@ -1138,6 +848,7 @@ typedef struct ResultState
 typedef struct ProjectSetState
 {
    PlanState   ps;             /* its first field is NodeTag */
+   Node      **elems;              /* array of expression states */
    ExprDoneCond *elemdone;     /* array of per-SRF is-done states */
    int         nelems;         /* length of elemdone[] array */
    bool        pending_srf_tuples;     /* still evaluating srfs in tlist? */
@@ -1372,7 +1083,7 @@ typedef struct
 typedef struct IndexScanState
 {
    ScanState   ss;             /* its first field is NodeTag */
-   List       *indexqualorig;
+   ExprState  *indexqualorig;
    List       *indexorderbyorig;
    ScanKey     iss_ScanKeys;
    int         iss_NumScanKeys;
@@ -1418,7 +1129,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
    ScanState   ss;             /* its first field is NodeTag */
-   List       *indexqual;
+   ExprState  *indexqual;
    ScanKey     ioss_ScanKeys;
    int         ioss_NumScanKeys;
    ScanKey     ioss_OrderByKeys;
@@ -1534,7 +1245,7 @@ typedef struct ParallelBitmapHeapState
 typedef struct BitmapHeapScanState
 {
    ScanState   ss;             /* its first field is NodeTag */
-   List       *bitmapqualorig;
+   ExprState  *bitmapqualorig;
    TIDBitmap  *tbm;
    TBMIterator *tbmiterator;
    TBMIterateResult *tbmres;
@@ -1563,7 +1274,6 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
    ScanState   ss;             /* its first field is NodeTag */
-   List       *tss_tidquals;   /* list of ExprState nodes */
    bool        tss_isCurrentOf;
    int         tss_NumTids;
    int         tss_TidPtr;
@@ -1712,7 +1422,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
    ScanState   ss;             /* its first field is NodeTag */
-   List       *fdw_recheck_quals;      /* original quals not in ss.ps.qual */
+   ExprState *fdw_recheck_quals;       /* original quals not in ss.ps.qual */
    Size        pscan_len;      /* size of parallel coordination information */
    /* use struct pointer to avoid including fdwapi.h here */
    struct FdwRoutine *fdwroutine;
@@ -1759,7 +1469,7 @@ typedef struct JoinState
 {
    PlanState   ps;
    JoinType    jointype;
-   List       *joinqual;       /* JOIN quals (in addition to ps.qual) */
+   ExprState  *joinqual;       /* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1857,7 +1567,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
    JoinState   js;             /* its first field is NodeTag */
-   List       *hashclauses;    /* list of ExprState nodes */
+   ExprState  *hashclauses;
    List       *hj_OuterHashKeys;       /* list of ExprState nodes */
    List       *hj_InnerHashKeys;       /* list of ExprState nodes */
    List       *hj_HashOperators;       /* list of operator OIDs */
index 2bc7a5df11be76d8a22f4796d21d8afb7e26d1b8..b39fcd197ab08763c2e394e24c5d1ce71d4996d6 100644 (file)
@@ -192,36 +192,19 @@ typedef enum NodeTag
    /*
     * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
     *
-    * These correspond (not always one-for-one) to primitive nodes derived
-    * from Expr.
+    * ExprState represents the evaluation state for a whole expression tree.
+    * Most Expr based nodes do not have a corresponding expression state
+    * node, they're solely handled within execExpr* - but sometimes the state
+    * needs to be shared with other parts of the executor, as e.g. the case
+    * for AggrefExprState, which nodeAgg.c has to modify.
     */
    T_ExprState,
-   T_GenericExprState,
-   T_WholeRowVarExprState,
    T_AggrefExprState,
    T_GroupingFuncExprState,
    T_WindowFuncExprState,
-   T_ArrayRefExprState,
-   T_FuncExprState,
-   T_ScalarArrayOpExprState,
-   T_BoolExprState,
+   T_SetExprState,
    T_SubPlanState,
    T_AlternativeSubPlanState,
-   T_FieldSelectState,
-   T_FieldStoreState,
-   T_CoerceViaIOState,
-   T_ArrayCoerceExprState,
-   T_ConvertRowtypeExprState,
-   T_CaseExprState,
-   T_CaseWhenState,
-   T_ArrayExprState,
-   T_RowExprState,
-   T_RowCompareExprState,
-   T_CoalesceExprState,
-   T_MinMaxExprState,
-   T_XmlExprState,
-   T_NullTestState,
-   T_CoerceToDomainState,
    T_DomainConstraintState,
 
    /*
index e570b71c04f9da23fb96e9dbf8b016155fccb9d8..c6857e536dc82c186607b47d93f704224c52020f 100644 (file)
@@ -61,7 +61,7 @@ extern void xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode,
            const char *msg);
 
 extern xmltype *xmlconcat(List *args);
-extern xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext);
+extern xmltype *xmlelement(XmlExpr *xexpr, bool *named_argnull, Datum *named_argvalue, bool *argnull, Datum *argvalue);
 extern xmltype *xmlparse(text *data, XmlOptionType xmloption, bool preserve_whitespace);
 extern xmltype *xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null);
 extern xmltype *xmlroot(xmltype *data, text *version, int standalone);
index 196e518e0df48c257407cb1d60e891b424e5e6db..be4983e8c125f35e10c057d326d53c7359724ce5 100644 (file)
@@ -145,7 +145,7 @@ typedef struct                  /* cast_hash table entry */
 {
    plpgsql_CastHashKey key;    /* hash key --- MUST BE FIRST */
    Expr       *cast_expr;      /* cast expression, or NULL if no-op cast */
-   /* The ExprState tree is valid only when cast_lxid matches current LXID */
+   /* ExprState is valid only when cast_lxid matches current LXID */
    ExprState  *cast_exprstate; /* expression's eval tree */
    bool        cast_in_use;    /* true while we're executing eval tree */
    LocalTransactionId cast_lxid;
@@ -4711,7 +4711,8 @@ exec_assign_value(PLpgSQL_execstate *estate,
 
                /*
                 * Evaluate the subscripts, switch into left-to-right order.
-                * Like ExecEvalArrayRef(), complain if any subscript is null.
+                * Like the expression built by ExecInitArrayRef(), complain
+                * if any subscript is null.
                 */
                for (i = 0; i < nsubscripts; i++)
                {
index 4cc4851475dd095abf9eda44641f7e1415a67dd6..36bf15c4acb7aa412da688baa9ea0a36b255377c 100644 (file)
@@ -308,7 +308,7 @@ SELECT * FROM CASE_TBL;
 -- Nested CASE expressions
 --
 -- This test exercises a bug caused by aliasing econtext->caseValue_isNull
--- with the isNull argument of the inner CASE's ExecEvalCase() call.  After
+-- with the isNull argument of the inner CASE's CaseExpr evaluation.  After
 -- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
 -- the isNull flag for the case test value incorrectly became true, causing
 -- the third WHEN-clause not to match.  The volatile function calls are needed
index 59268f8cdfa6a81f476dad1fd2a9765b863cb882..66b6e98fb1869d7602cd3a157615760e2ace5145 100644 (file)
@@ -166,7 +166,7 @@ SELECT * FROM CASE_TBL;
 --
 
 -- This test exercises a bug caused by aliasing econtext->caseValue_isNull
--- with the isNull argument of the inner CASE's ExecEvalCase() call.  After
+-- with the isNull argument of the inner CASE's CaseExpr evaluation.  After
 -- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
 -- the isNull flag for the case test value incorrectly became true, causing
 -- the third WHEN-clause not to match.  The volatile function calls are needed
index 3487f7becb72d017785dfb209a1330e5fc2be302..76ed6830a9e97590579f5f75e7c1df1f8622a598 100644 (file)
@@ -578,6 +578,8 @@ ExprContext_CB
 ExprDoneCond
 ExprState
 ExprStateEvalFunc
+ExprEvalOp
+ExprEvalStep
 ExtensibleNode
 ExtensibleNodeEntry
 ExtensibleNodeMethods
@@ -1908,6 +1910,7 @@ Session
 SetConstraintState
 SetConstraintStateData
 SetConstraintTriggerData
+SetExprState
 SetFunctionReturnMode
 SetOp
 SetOpCmd