]> granicus.if.org Git - postgresql/commitdiff
Faster expression evaluation and targetlist projection.
authorAndres Freund <andres@anarazel.de>
Tue, 14 Mar 2017 22:45:36 +0000 (15:45 -0700)
committerAndres Freund <andres@anarazel.de>
Sat, 25 Mar 2017 21:52:06 +0000 (14:52 -0700)
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.

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 performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.

The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
  overhead of expression evaluation, by caching state in prepared
  statements.  That'd be helpful in OLTPish scenarios where
  initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
  work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
  been made here too.

The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
  initialization, whereas previously they were done during
  execution. In edge cases this can lead to errors being raised that
  previously wouldn't have been, e.g. a NULL array being coerced to a
  different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
  during expression initialization, previously it was re-built
  every time a domain check was evaluated. For normal queries this
  doesn't change much, but e.g. for plpgsql functions, which caches
  ExprStates, the old set could stick around longer.  The behavior
  around might still change.

Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de

71 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/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/README
src/backend/executor/execExpr.c [new file with mode: 0644]
src/backend/executor/execExprInterp.c [new file with mode: 0644]
src/backend/executor/execIndexing.c
src/backend/executor/execMain.c
src/backend/executor/execQual.c
src/backend/executor/execSRF.c [new file with mode: 0644]
src/backend/executor/execScan.c
src/backend/executor/execUtils.c
src/backend/executor/functions.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/path/costsize.c
src/backend/optimizer/plan/planner.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/fmgr.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/utils/typcache.h
src/include/utils/xml.h
src/pl/plpgsql/src/pl_exec.c
src/test/regress/expected/case.out
src/test/regress/expected/privileges.out
src/test/regress/sql/case.sql
src/test/regress/sql/privileges.sql
src/tools/pgindent/typedefs.list

index e8cb2d0b4da69cff407cda4a95501dab95acc1be..03f14800b0e375cb83e1260fb1c05c4aad8a7831 100644 (file)
@@ -3421,7 +3421,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 7924c30369ffadb1ff358862f612712e863ceeda..1eb163f5392e9805682f73285d9cd3ef22f5ba4a 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..2b5b8e89bbc75e8dabe62ed56b539a53de757bc8 100644 (file)
@@ -1618,8 +1618,7 @@ 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 c5b5c54babf0994567ad6bf729f49bf6ba888d52..404acb2debb83e1411a9c085bb28ce0be0c2746b 100644 (file)
@@ -713,7 +713,7 @@ compute_index_stats(Relation onerel, double totalrows,
                TupleTableSlot *slot;
                EState     *estate;
                ExprContext *econtext;
-               List       *predicate;
+               ExprState  *predicate;
                Datum      *exprvals;
                bool       *exprnulls;
                int                     numindexrows,
@@ -739,9 +739,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));
@@ -764,9 +762,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 b4c7466666b1d529fb338ab5d19f5d0904617d00..1036b96aaea00f686c18ebb4b6a102ad4b487069 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 9618032356af50a000d546702ee4903a9d4a0d18..486179938c3e8537dc2cf631f4693a488168a53d 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;
@@ -551,7 +551,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 992ba1c9a2e2f95ae56e76c6844414dc2f449979..a92461097748f5ce5957eee5e1945b579344a8aa 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 3b28e8c34f633965920118a73fb5b2f5a026c45b..96cf42a7f8266d58217004a8bef7ccda241c6544 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 expr */
 } 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 = ExecPrepareExpr((Expr *) 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 (!ExecCheck(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")));
@@ -6607,8 +6604,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
                        newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
                        newcon->name = ccon->name;
                        newcon->contype = ccon->contype;
-                       /* ExecQual wants implicit-AND format */
-                       newcon->qual = (Node *) make_ands_implicit((Expr *) ccon->expr);
+                       newcon->qual = ccon->expr;
 
                        tab->constraints = lappend(tab->constraints, newcon);
                }
@@ -7786,7 +7782,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
        Datum           val;
        char       *conbin;
        Expr       *origexpr;
-       List       *exprstate;
+       ExprState  *exprstate;
        TupleDesc       tupdesc;
        HeapScanDesc scan;
        HeapTuple       tuple;
@@ -7817,8 +7813,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 = ExecPrepareExpr(origexpr, estate);
 
        econtext = GetPerTupleExprContext(estate);
        tupdesc = RelationGetDescr(rel);
@@ -7838,7 +7833,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
        {
                ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-               if (!ExecQual(exprstate, econtext, true))
+               if (!ExecCheck(exprstate, econtext))
                        ereport(ERROR,
                                        (errcode(ERRCODE_CHECK_VIOLATION),
                                         errmsg("check constraint \"%s\" is violated by some row",
index a1bb3e958c768e8e0d30a3f82b395cf0bf1671df..f3b1a5268267b10b0ce3307017dda205c6fe059b 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;
 
@@ -3087,9 +3087,9 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
                        /* Change references to OLD and NEW to INNER_VAR and OUTER_VAR */
                        ChangeVarNodes(tgqual, PRS2_OLD_VARNO, INNER_VAR, 0);
                        ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
-                       /* ExecQual wants implicit-AND form */
+                       /* ExecPrepareQual 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..d1c1324399a05e9020df6c6b1fd927a402d06726 100644 (file)
@@ -12,9 +12,10 @@ 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 execExprInterp.o \
+       execGrouping.o execIndexing.o execJunk.o \
+       execMain.o execParallel.o execProcnode.o \
+       execReplication.o execScan.o execSRF.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
        nodeBitmapHeapscan.o nodeBitmapIndexscan.o \
index f1d1e4c76ce9d78cfc70bc6da7348e2a03dae52d..a0045067fb84ac27593ce400ff25f5a8450e290e 100644 (file)
@@ -44,21 +44,171 @@ Plan Trees and State Trees
 --------------------------
 
 The plan tree delivered by the planner contains a tree of Plan nodes (struct
-types derived from struct Plan).  Each Plan node may have expression trees
-associated with it, to represent its target list, qualification conditions,
-etc.  During executor startup we build a parallel tree of identical structure
-containing executor state nodes --- every plan and expression node type has
-a corresponding executor state node type.  Each node in the state tree has a
-pointer to its corresponding node in the plan tree, plus executor state data
-as needed to implement that node type.  This arrangement allows the plan
-tree to be completely read-only as far as the executor is concerned: all data
-that is modified during execution is in the state tree.  Read-only plan trees
-make life much simpler for plan caching and reuse.
+types derived from struct Plan).  During executor startup we build a parallel
+tree of identical structure containing executor state nodes --- every plan
+node type has a corresponding executor state node type.  Each node in the
+state tree has a pointer to its corresponding node in the plan tree, plus
+executor state data as needed to implement that node type.  This arrangement
+allows the plan tree to be completely read-only so far as the executor is
+concerned: all data that is modified during execution is in the state tree.
+Read-only plan trees make life much simpler for plan caching and reuse.
+
+Each Plan node may have expression trees associated with it, to represent
+its target list, qualification conditions, etc.  These trees are also
+read-only to the executor, but the executor state for expression evaluation
+does not mirror the Plan expression's tree shape, as explained below.
+Rather, there's just one ExprState node per expression tree, although this
+may have sub-nodes for some complex expression node types.
 
 Altogether there are four classes of nodes used in these trees: Plan nodes,
-their corresponding PlanState nodes, Expr nodes, and their corresponding
-ExprState nodes.  (Actually, there are also List nodes, which are used as
-"glue" in all four kinds of tree.)
+their corresponding PlanState nodes, Expr nodes, and ExprState nodes.
+(Actually, there are also List nodes, which are used as "glue" in all
+three tree-based representations.)
+
+
+Expression Trees and ExprState nodes
+------------------------------------
+
+Expression trees, in contrast to Plan trees, are not mirrored into a
+corresponding tree of state nodes.  Instead each separately executable
+expression tree (e.g. a Plan's qual or targetlist) is represented by one
+ExprState node.  The ExprState node contains the information needed to
+evaluate the expression in a compact, linear form.  That compact form is
+stored as a flat array in ExprState->steps[] (an array of ExprEvalStep,
+not ExprEvalStep *).
+
+The reasons for choosing such a representation include:
+- commonly the amount of work needed to evaluate one Expr-type node is
+  small enough that the overhead of having to perform a tree-walk
+  during evaluation is significant.
+- the flat representation can be evaluated non-recursively within a single
+  function, reducing stack depth and function call overhead.
+- such a representation is usable both for fast interpreted execution,
+  and for compiling into native code.
+
+The Plan-tree representation of an expression is compiled into an
+ExprState node by ExecInitExpr().  As much complexity as possible should
+be handled by ExecInitExpr() (and helpers), instead of execution time
+where both interpreted and compiled versions would need to deal with the
+complexity.  Besides duplicating effort between execution approaches,
+runtime initialization checks also have a small but noticeable cost every
+time the expression is evaluated.  Therefore, we allow ExecInitExpr() to
+precompute information that we do not expect to vary across execution of a
+single query, for example the set of CHECK constraint expressions to be
+applied to a domain type.  This could not be done at plan time without
+greatly increasing the number of events that require plan invalidation.
+(Previously, some information of this kind was rechecked on each
+expression evaluation, but that seems like unnecessary overhead.)
+
+
+Expression Initialization
+-------------------------
+
+During ExecInitExpr() and similar routines, Expr trees are converted
+into the flat representation.  Each Expr node might be represented by
+zero, one, or more ExprEvalSteps.
+
+Each ExprEvalStep's work is determined by its opcode (of enum ExprEvalOp)
+and it stores the result of its work into the Datum variable and boolean
+null flag variable pointed to by ExprEvalStep->resvalue/resnull.
+Complex expressions are performed by chaining together several steps.
+For example, "a + b" (one OpExpr, with two Var expressions) would be
+represented as two steps to fetch the Var values, and one step for the
+evaluation of the function underlying the + operator.  The steps for the
+Vars would have their resvalue/resnull pointing directly to the appropriate
+arg[] and argnull[] array elements in the FunctionCallInfoData struct that
+is used by the function evaluation step, thus avoiding extra work to copy
+the result values around.
+
+The last entry in a completed ExprState->steps array is always an
+EEOP_DONE step; this removes the need to test for end-of-array while
+iterating.  Also, if the expression contains any variable references (to
+user columns of the ExprContext's INNER, OUTER, or SCAN tuples), the steps
+array begins with EEOP_*_FETCHSOME steps that ensure that the relevant
+tuples have been deconstructed to make the required columns directly
+available (cf. slot_getsomeattrs()).  This allows individual Var-fetching
+steps to be little more than an array lookup.
+
+Most of ExecInitExpr()'s work is done by the recursive function
+ExecInitExprRec() and its subroutines.  ExecInitExprRec() maps one Expr
+node into the steps required for execution, recursing as needed for
+sub-expressions.
+
+Each ExecInitExprRec() call has to specify where that subexpression's
+results are to be stored (via the resv/resnull parameters).  This allows
+the above scenario of evaluating a (sub-)expression directly into
+fcinfo->arg/argnull, but also requires some care: target Datum/isnull
+variables may not be shared with another ExecInitExprRec() unless the
+results are only needed by steps executing before further usages of those
+target Datum/isnull variables.  Due to the non-recursiveness of the
+ExprEvalStep representation that's usually easy to guarantee.
+
+ExecInitExprRec() pushes new operations into the ExprState->steps array
+using ExprEvalPushStep().  To keep the steps as a consecutively laid out
+array, ExprEvalPushStep() has to repalloc the entire array when there's
+not enough space.  Because of that it is *not* allowed to point directly
+into any of the steps during expression initialization.  Therefore, the
+resv/resnull for a subexpression usually point to some storage that is
+palloc'd separately from the steps array.  For instance, the
+FunctionCallInfoData for a function call step is separately allocated
+rather than being part of the ExprEvalStep array.  The overall result
+of a complete expression is typically returned into the resvalue/resnull
+fields of the ExprState node itself.
+
+Some steps, e.g. boolean expressions, allow skipping evaluation of
+certain subexpressions.  In the flat representation this amounts to
+jumping to some later step rather than just continuing consecutively
+with the next step.  The target for such a jump is represented by
+the integer index in the ExprState->steps array of the step to execute
+next.  (Compare the EEO_NEXT and EEO_JUMP macros in execExprInterp.c.)
+
+Typically, ExecInitExprRec() has to push a jumping step into the steps
+array, then recursively generate steps for the subexpression that might
+get skipped over, then go back and fix up the jump target index using
+the now-known length of the subexpression's steps.  This is handled by
+adjust_jumps lists in execExpr.c.
+
+The last step in constructing an ExprState is to apply ExecReadyExpr(),
+which readies it for execution using whichever execution method has been
+selected.
+
+
+Expression Evaluation
+---------------------
+
+To allow for different methods of expression evaluation, and for
+better branch/jump target prediction, expressions are evaluated by
+calling ExprState->evalfunc (via ExprEvalExpr() and friends).
+
+ExprReadyExpr() can choose the method of interpretation by setting
+evalfunc to an appropriate function.  The default execution function,
+ExecInterpExpr, is implemented in execExprInterp.c; see its header
+comment for details.  Special-case evalfuncs are used for certain
+especially-simple expressions.
+
+Note that a lot of the more complex expression evaluation steps, which are
+less performance-critical than the simpler ones, are implemented as
+separate functions outside the fast-path of expression execution, allowing
+their implementation to be shared between interpreted and compiled
+expression evaluation.  This means that these helper functions are not
+allowed to perform expression step dispatch themselves, as the method of
+dispatch will vary based on the caller.  The helpers therefore cannot call
+for the execution of subexpressions; all subexpression results they need
+must be computed by earlier steps.  And dispatch to the following
+expression step must be performed after returning from the helper.
+
+
+Targetlist Evaluation
+---------------------
+
+ExecBuildProjectionInfo builds an ExprState that has the effect of
+evaluating a targetlist into ExprState->resultslot.  A generic targetlist
+expression is executed by evaluating it as discussed above (storing the
+result into the ExprState's resvalue/resnull fields) and then using an
+EEOP_ASSIGN_TMP step to move the result into the appropriate tts_values[]
+and tts_isnull[] array elements of the result slot.  There are special
+fast-path step types (EEOP_ASSIGN_*_VAR) to handle targetlist entries that
+are simple Vars using only one step instead of two.
 
 
 Memory Management
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644 (file)
index 0000000..766dbbb
--- /dev/null
@@ -0,0 +1,2665 @@
+/*-------------------------------------------------------------------------
+ *
+ * execExpr.c
+ *       Expression evaluation infrastructure.
+ *
+ *     During executor startup, we compile each expression tree (which has
+ *     previously been processed by the parser and planner) into an ExprState,
+ *     using ExecInitExpr() et al.  This converts the tree into a flat array
+ *     of ExprEvalSteps, which may be thought of as instructions in a program.
+ *     At runtime, we'll execute steps, starting with the first, until we reach
+ *     an EEOP_DONE opcode.
+ *
+ *     This file contains the "compilation" logic.  It is independent of the
+ *     specific execution technology we use (switch statement, computed goto,
+ *     JIT compilation, etc).
+ *
+ *     See src/backend/executor/README for some background, specifically the
+ *     "Expression Trees and ExprState nodes", "Expression Initialization",
+ *     and "Expession Evaluation" sections.
+ *
+ *
+ * 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/nbtree.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execExpr.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/clauses.h"
+#include "optimizer/planner.h"
+#include "pgstat.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/typcache.h"
+
+
+typedef struct LastAttnumInfo
+{
+       AttrNumber      last_inner;
+       AttrNumber      last_outer;
+       AttrNumber      last_scan;
+} LastAttnumInfo;
+
+static void ExecReadyExpr(ExprState *state);
+static void ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
+                               Datum *resv, bool *resnull);
+static void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s);
+static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args,
+                        Oid funcid, Oid inputcollid, PlanState *parent,
+                        ExprState *state);
+static void ExecInitExprSlots(ExprState *state, Node *node);
+static bool get_last_attnums_walker(Node *node, LastAttnumInfo *info);
+static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable,
+                                       PlanState *parent);
+static void ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref,
+                                PlanState *parent, ExprState *state,
+                                Datum *resv, bool *resnull);
+static bool isAssignmentIndirectionExpr(Expr *expr);
+static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
+                                          PlanState *parent, ExprState *state,
+                                          Datum *resv, bool *resnull);
+
+
+/*
+ * 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.  (But note that an ExprState
+ * does mutate at runtime, so it can't be re-used 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 (or more accurately,
+ * the AggrefExprState etc. nodes created for them are added).
+ *
+ * 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 compile.
+ *     '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.)
+ * Such cases should usually come through ExecPrepareExpr, not directly here.
+ *
+ * Also, if 'node' is NULL, we just return NULL.  This is convenient for some
+ * callers that may or may not have an expression that needs to be compiled.
+ * Note that a NULL ExprState pointer *cannot* be handed to ExecEvalExpr,
+ * although ExecQual and ExecCheck will accept one (and treat it as "true").
+ */
+ExprState *
+ExecInitExpr(Expr *node, PlanState *parent)
+{
+       ExprState  *state;
+       ExprEvalStep scratch;
+
+       /* Special case: NULL expression produces a NULL ExprState pointer */
+       if (node == NULL)
+               return NULL;
+
+       /* Initialize ExprState with empty step list */
+       state = makeNode(ExprState);
+       state->expr = node;
+
+       /* Insert EEOP_*_FETCHSOME steps as needed */
+       ExecInitExprSlots(state, (Node *) node);
+
+       /* Compile the expression proper */
+       ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
+
+       /* Finally, append a DONE step */
+       scratch.opcode = EEOP_DONE;
+       ExprEvalPushStep(state, &scratch);
+
+       ExecReadyExpr(state);
+
+       return state;
+}
+
+/*
+ * ExecInitQual: prepare a qual for execution by ExecQual
+ *
+ * Prepares for the evaluation of a conjunctive boolean expression (qual list
+ * with implicit AND semantics) that returns true if none of the
+ * subexpressions are false.
+ *
+ * We must return true if the list is empty.  Since that's a very common case,
+ * we optimize it a bit further by translating to a NULL ExprState pointer
+ * rather than setting up an ExprState that computes constant TRUE.  (Some
+ * especially hot-spot callers of ExecQual detect this and avoid calling
+ * ExecQual at all.)
+ *
+ * If any of the subexpressions yield NULL, then the result of the conjunction
+ * is false.  This makes ExecQual 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;
+       ExprEvalStep scratch;
+       List       *adjust_jumps = NIL;
+       ListCell   *lc;
+
+       /* short-circuit (here and in ExecQual) for empty restriction list */
+       if (qual == NIL)
+               return NULL;
+
+       Assert(IsA(qual, List));
+
+       state = makeNode(ExprState);
+       state->expr = (Expr *) qual;
+       /* mark expression as to be used with ExecQual() */
+       state->flags = EEO_FLAG_IS_QUAL;
+
+       /* Insert EEOP_*_FETCHSOME steps as needed */
+       ExecInitExprSlots(state, (Node *) qual);
+
+       /*
+        * ExecQual() needs to return false for an expression returning NULL. That
+        * allows us 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).
+        */
+       scratch.opcode = EEOP_QUAL;
+
+       /*
+        * We can use ExprState's resvalue/resnull as target for each qual expr.
+        */
+       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 emit EEOP_QUAL to detect if it's false (or null) */
+               scratch.d.qualexpr.jumpdone = -1;
+               ExprEvalPushStep(state, &scratch);
+               adjust_jumps = lappend_int(adjust_jumps,
+                                                                  state->steps_len - 1);
+       }
+
+       /* adjust jump targets */
+       foreach(lc, adjust_jumps)
+       {
+               ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+               Assert(as->opcode == EEOP_QUAL);
+               Assert(as->d.qualexpr.jumpdone == -1);
+               as->d.qualexpr.jumpdone = state->steps_len;
+       }
+
+       /*
+        * At the end, we don't need to do anything more.  The last qual expr must
+        * have yielded TRUE, and since its result is stored in the desired output
+        * location, we're done.
+        */
+       scratch.opcode = EEOP_DONE;
+       ExprEvalPushStep(state, &scratch);
+
+       ExecReadyExpr(state);
+
+       return state;
+}
+
+/*
+ * ExecInitCheck: prepare a check constraint for execution by ExecCheck
+ *
+ * This is much like ExecInitQual/ExecQual, except that a null result from
+ * the conjunction is treated as TRUE.  This behavior is appropriate for
+ * evaluating CHECK constraints, since SQL specifies that NULL constraint
+ * conditions are not failures.
+ *
+ * Note that like ExecInitQual, this expects input in implicit-AND format.
+ * Users of ExecCheck that have expressions in normal explicit-AND format
+ * can just apply ExecInitExpr to produce suitable input for ExecCheck.
+ */
+ExprState *
+ExecInitCheck(List *qual, PlanState *parent)
+{
+       /* short-circuit (here and in ExecCheck) for empty restriction list */
+       if (qual == NIL)
+               return NULL;
+
+       Assert(IsA(qual, List));
+
+       /*
+        * Just convert the implicit-AND list to an explicit AND (if there's more
+        * than one entry), and compile normally.  Unlike ExecQual, we can't
+        * short-circuit on NULL results, so the regular AND behavior is needed.
+        */
+       return ExecInitExpr(make_ands_explicit(qual), parent);
+}
+
+/*
+ * 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;
+}
+
+/*
+ *             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!)
+ *
+ * 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
+ * whole projection in one go.
+ *
+ * Caution: before PG v10, the targetList was a list of ExprStates; now it
+ * should be the planner-created targetlist, since we do the compilation here.
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+                                               ExprContext *econtext,
+                                               TupleTableSlot *slot,
+                                               PlanState *parent,
+                                               TupleDesc inputDesc)
+{
+       ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+       ExprState  *state;
+       ExprEvalStep scratch;
+       ListCell   *lc;
+
+       projInfo->pi_exprContext = econtext;
+       /* We embed ExprState into ProjectionInfo instead of doing extra palloc */
+       projInfo->pi_state.tag.type = T_ExprState;
+       state = &projInfo->pi_state;
+       state->expr = (Expr *) targetList;
+       state->resultslot = slot;
+
+       /* Insert EEOP_*_FETCHSOME steps as needed */
+       ExecInitExprSlots(state, (Node *) targetList);
+
+       /* Now compile each tlist column */
+       foreach(lc, targetList)
+       {
+               TargetEntry *tle = castNode(TargetEntry, lfirst(lc));
+               Var                *variable = NULL;
+               AttrNumber      attnum = 0;
+               bool            isSafeVar = false;
+
+               /*
+                * If tlist expression is a safe non-system Var, use the fast-path
+                * ASSIGN_*_VAR opcodes.  "Safe" means that we don't need to apply
+                * CheckVarSlotCompatibility() during plan startup.  If a source slot
+                * was provided, we make the equivalent tests here; if a slot was not
+                * provided, we assume that no check is needed because we're dealing
+                * with a non-relation-scan-level expression.
+                */
+               if (tle->expr != NULL &&
+                       IsA(tle->expr, Var) &&
+                       ((Var *) tle->expr)->varattno > 0)
+               {
+                       /* Non-system Var, but how safe is it? */
+                       variable = (Var *) tle->expr;
+                       attnum = variable->varattno;
+
+                       if (inputDesc == NULL)
+                               isSafeVar = true;               /* can't check, just assume OK */
+                       else if (attnum <= inputDesc->natts)
+                       {
+                               Form_pg_attribute attr = inputDesc->attrs[attnum - 1];
+
+                               /*
+                                * If user attribute is dropped or has a type mismatch, don't
+                                * use ASSIGN_*_VAR.  Instead let the normal expression
+                                * machinery handle it (which'll possibly error out).
+                                */
+                               if (!attr->attisdropped && variable->vartype == attr->atttypid)
+                               {
+                                       isSafeVar = true;
+                               }
+                       }
+               }
+
+               if (isSafeVar)
+               {
+                       /* Fast-path: just generate an EEOP_ASSIGN_*_VAR step */
+                       switch (variable->varno)
+                       {
+                               case INNER_VAR:
+                                       /* get the tuple from the inner node */
+                                       scratch.opcode = EEOP_ASSIGN_INNER_VAR;
+                                       break;
+
+                               case OUTER_VAR:
+                                       /* get the tuple from the outer node */
+                                       scratch.opcode = EEOP_ASSIGN_OUTER_VAR;
+                                       break;
+
+                                       /* INDEX_VAR is handled by default case */
+
+                               default:
+                                       /* get the tuple from the relation being scanned */
+                                       scratch.opcode = EEOP_ASSIGN_SCAN_VAR;
+                                       break;
+                       }
+
+                       scratch.d.assign_var.attnum = attnum - 1;
+                       scratch.d.assign_var.resultnum = tle->resno - 1;
+                       ExprEvalPushStep(state, &scratch);
+               }
+               else
+               {
+                       /*
+                        * Otherwise, compile the column expression normally.
+                        *
+                        * We can't tell the expression to evaluate directly into the
+                        * result slot, as the result slot (and the exprstate for that
+                        * matter) can change between executions.  We instead evaluate
+                        * into the ExprState's resvalue/resnull and then move.
+                        */
+                       ExecInitExprRec(tle->expr, parent, state,
+                                                       &state->resvalue, &state->resnull);
+
+                       /*
+                        * Column might be referenced multiple times in upper nodes, so
+                        * force value to R/O - but only if it could be an expanded datum.
+                        */
+                       if (get_typlen(exprType((Node *) tle->expr)) == -1)
+                               scratch.opcode = EEOP_ASSIGN_TMP_MAKE_RO;
+                       else
+                               scratch.opcode = EEOP_ASSIGN_TMP;
+                       scratch.d.assign_tmp.resultnum = tle->resno - 1;
+                       ExprEvalPushStep(state, &scratch);
+               }
+       }
+
+       scratch.opcode = EEOP_DONE;
+       ExprEvalPushStep(state, &scratch);
+
+       ExecReadyExpr(state);
+
+       return projInfo;
+}
+
+/*
+ * 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 ExecInitQual 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 check constraint for execution outside a
+ * normal Plan tree context.
+ *
+ * See ExecPrepareExpr() and ExecInitCheck() 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 a list of Exprs, and return
+ * a list of ExprStates.
+ *
+ * See ExecPrepareExpr() for details.
+ */
+List *
+ExecPrepareExprList(List *nodes, EState *estate)
+{
+       List       *result = NIL;
+       ListCell   *lc;
+
+       foreach(lc, nodes)
+       {
+               Expr       *e = (Expr *) lfirst(lc);
+
+               result = lappend(result, ExecPrepareExpr(e, estate));
+       }
+
+       return result;
+}
+
+/*
+ * ExecCheck - evaluate a check constraint
+ *
+ * For check constraints, a null result is taken as TRUE, ie the constraint
+ * passes.
+ *
+ * The check constraint may have been prepared with ExecInitCheck
+ * (possibly via ExecPrepareCheck) if the caller had it in implicit-AND
+ * format, but a regular boolean expression prepared with ExecInitExpr or
+ * ExecPrepareExpr works too.
+ */
+bool
+ExecCheck(ExprState *state, ExprContext *econtext)
+{
+       Datum           ret;
+       bool            isnull;
+
+       /* short-circuit (here and in ExecInitCheck) for empty restriction list */
+       if (state == NULL)
+               return true;
+
+       /* verify that expression was not compiled using ExecInitQual */
+       Assert(!(state->flags & EEO_FLAG_IS_QUAL));
+
+       ret = ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+       if (isnull)
+               return true;
+
+       return DatumGetBool(ret);
+}
+
+/*
+ * Prepare a compiled expression for execution.  This has to be called for
+ * every ExprState before it can be executed.
+ *
+ * NB: While this currently only calls ExecReadyInterpretedExpr(),
+ * this will likely get extended to further expression evaluation methods.
+ * Therefore this should be used instead of directly calling
+ * ExecReadyInterpretedExpr().
+ */
+static void
+ExecReadyExpr(ExprState *state)
+{
+       ExecReadyInterpretedExpr(state);
+}
+
+/*
+ * Append the steps necessary for the evaluation of node to ExprState->steps,
+ * possibly recursing into sub-expressions of node.
+ *
+ * node - expression to evaluate
+ * parent - parent executor node (or NULL if a standalone expression)
+ * state - ExprState to whose ->steps to append the necessary operations
+ * resv / resnull - where to store the result of the node into
+ */
+static void
+ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
+                               Datum *resv, bool *resnull)
+{
+       ExprEvalStep scratch;
+
+       /* Guard against stack overflow due to overly complex expressions */
+       check_stack_depth();
+
+       /* Step's output location is always what the caller gave us */
+       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;
+
+                               if (variable->varattno == InvalidAttrNumber)
+                               {
+                                       /* whole-row Var */
+                                       ExecInitWholeRowVar(&scratch, variable, parent);
+                               }
+                               else if (variable->varattno <= 0)
+                               {
+                                       /* system column */
+                                       scratch.d.var.attnum = variable->varattno;
+                                       scratch.d.var.vartype = variable->vartype;
+                                       switch (variable->varno)
+                                       {
+                                               case INNER_VAR:
+                                                       scratch.opcode = EEOP_INNER_SYSVAR;
+                                                       break;
+                                               case OUTER_VAR:
+                                                       scratch.opcode = EEOP_OUTER_SYSVAR;
+                                                       break;
+
+                                                       /* INDEX_VAR is handled by default case */
+
+                                               default:
+                                                       scratch.opcode = EEOP_SCAN_SYSVAR;
+                                                       break;
+                                       }
+                               }
+                               else
+                               {
+                                       /* regular user column */
+                                       scratch.d.var.attnum = variable->varattno - 1;
+                                       scratch.d.var.vartype = variable->vartype;
+                                       /* select EEOP_*_FIRST opcode to force one-time checks */
+                                       switch (variable->varno)
+                                       {
+                                               case INNER_VAR:
+                                                       scratch.opcode = EEOP_INNER_VAR_FIRST;
+                                                       break;
+                                               case OUTER_VAR:
+                                                       scratch.opcode = EEOP_OUTER_VAR_FIRST;
+                                                       break;
+
+                                                       /* INDEX_VAR is handled by default case */
+
+                                               default:
+                                                       scratch.opcode = EEOP_SCAN_VAR_FIRST;
+                                                       break;
+                                       }
+                               }
+
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_Const:
+                       {
+                               Const      *con = (Const *) node;
+
+                               scratch.opcode = EEOP_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 = EEOP_PARAM_EXEC;
+                                               scratch.d.param.paramid = param->paramid;
+                                               scratch.d.param.paramtype = param->paramtype;
+                                               break;
+                                       case PARAM_EXTERN:
+                                               scratch.opcode = EEOP_PARAM_EXTERN;
+                                               scratch.d.param.paramid = param->paramid;
+                                               scratch.d.param.paramtype = param->paramtype;
+                                               break;
+                                       default:
+                                               elog(ERROR, "unrecognized paramkind: %d",
+                                                        (int) param->paramkind);
+                                               break;
+                               }
+
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_Aggref:
+                       {
+                               Aggref     *aggref = (Aggref *) node;
+                               AggrefExprState *astate = makeNode(AggrefExprState);
+
+                               scratch.opcode = EEOP_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;
+
+                               if (!parent || !IsA(parent, AggState) ||
+                                       !IsA(parent->plan, Agg))
+                                       elog(ERROR, "GroupingFunc found in non-Agg plan node");
+
+                               scratch.opcode = EEOP_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 initialize agg using old style expressions */
+                                       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 = EEOP_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);
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_OpExpr:
+                       {
+                               OpExpr     *op = (OpExpr *) node;
+
+                               ExecInitFunc(&scratch, node,
+                                                        op->args, op->opfuncid, op->inputcollid,
+                                                        parent, state);
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_DistinctExpr:
+                       {
+                               DistinctExpr *op = (DistinctExpr *) node;
+
+                               ExecInitFunc(&scratch, node,
+                                                        op->args, op->opfuncid, op->inputcollid,
+                                                        parent, state);
+
+                               /*
+                                * Change opcode of call instruction to EEOP_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.  If
+                                * we decided to do that here, we'd probably want separate
+                                * opcodes for FUSAGE or not.
+                                */
+                               scratch.opcode = EEOP_DISTINCT;
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_NullIfExpr:
+                       {
+                               NullIfExpr *op = (NullIfExpr *) node;
+
+                               ExecInitFunc(&scratch, node,
+                                                        op->args, op->opfuncid, op->inputcollid,
+                                                        parent, state);
+
+                               /*
+                                * Change opcode of call instruction to EEOP_NULLIF.
+                                *
+                                * 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.  If
+                                * we decided to do that here, we'd probably want separate
+                                * opcodes for FUSAGE or not.
+                                */
+                               scratch.opcode = EEOP_NULLIF;
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_ScalarArrayOpExpr:
+                       {
+                               ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
+                               Expr       *scalararg;
+                               Expr       *arrayarg;
+                               FmgrInfo   *finfo;
+                               FunctionCallInfo fcinfo;
+                               AclResult       aclresult;
+
+                               Assert(list_length(opexpr->args) == 2);
+                               scalararg = (Expr *) linitial(opexpr->args);
+                               arrayarg = (Expr *) lsecond(opexpr->args);
+
+                               /* Check permission to call function */
+                               aclresult = pg_proc_aclcheck(opexpr->opfuncid,
+                                                                                        GetUserId(),
+                                                                                        ACL_EXECUTE);
+                               if (aclresult != ACLCHECK_OK)
+                                       aclcheck_error(aclresult, ACL_KIND_PROC,
+                                                                  get_func_name(opexpr->opfuncid));
+                               InvokeFunctionExecuteHook(opexpr->opfuncid);
+
+                               /* Set up the primary fmgr lookup information */
+                               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);
+
+                               /* Evaluate scalar directly into left function argument */
+                               ExecInitExprRec(scalararg, parent, state,
+                                                               &fcinfo->arg[0], &fcinfo->argnull[0]);
+
+                               /*
+                                * Evaluate array argument into our return value.  There's no
+                                * danger in that, because the return value is guaranteed to
+                                * be overwritten by EEOP_SCALARARRAYOP, and will not be
+                                * passed to any other expression.
+                                */
+                               ExecInitExprRec(arrayarg, parent, state, resv, resnull);
+
+                               /* And perform the operation */
+                               scratch.opcode = EEOP_SCALARARRAYOP;
+                               scratch.d.scalararrayop.element_type = InvalidOid;
+                               scratch.d.scalararrayop.useOr = opexpr->useOr;
+                               scratch.d.scalararrayop.finfo = finfo;
+                               scratch.d.scalararrayop.fcinfo_data = fcinfo;
+                               scratch.d.scalararrayop.fn_addr = finfo->fn_addr;
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_BoolExpr:
+                       {
+                               BoolExpr   *boolexpr = (BoolExpr *) node;
+                               int                     nargs = list_length(boolexpr->args);
+                               List       *adjust_jumps = NIL;
+                               int                     off;
+                               ListCell   *lc;
+
+                               /* allocate scratch memory used by all steps of AND/OR */
+                               if (boolexpr->boolop != NOT_EXPR)
+                                       scratch.d.boolexpr.anynull = (bool *) palloc(sizeof(bool));
+
+                               /*
+                                * For each argument evaluate the argument itself, then
+                                * perform the bool operation's appropriate handling.
+                                *
+                                * We can evaluate each argument into our result area, since
+                                * the short-circuiting logic means we only need to remember
+                                * previous NULL values.
+                                *
+                                * AND/OR is split into separate STEP_FIRST (one) / STEP (zero
+                                * or more) / STEP_LAST (one) steps, as each of those has to
+                                * perform different work.  The FIRST/LAST split is valid
+                                * because AND/OR have at least two arguments.
+                                */
+                               off = 0;
+                               foreach(lc, boolexpr->args)
+                               {
+                                       Expr       *arg = (Expr *) lfirst(lc);
+
+                                       /* Evaluate argument into our output variable */
+                                       ExecInitExprRec(arg, parent, state, resv, resnull);
+
+                                       /* Perform the appropriate step type */
+                                       switch (boolexpr->boolop)
+                                       {
+                                               case AND_EXPR:
+                                                       Assert(nargs >= 2);
+
+                                                       if (off == 0)
+                                                               scratch.opcode = EEOP_BOOL_AND_STEP_FIRST;
+                                                       else if (off + 1 == nargs)
+                                                               scratch.opcode = EEOP_BOOL_AND_STEP_LAST;
+                                                       else
+                                                               scratch.opcode = EEOP_BOOL_AND_STEP;
+                                                       break;
+                                               case OR_EXPR:
+                                                       Assert(nargs >= 2);
+
+                                                       if (off == 0)
+                                                               scratch.opcode = EEOP_BOOL_OR_STEP_FIRST;
+                                                       else if (off + 1 == nargs)
+                                                               scratch.opcode = EEOP_BOOL_OR_STEP_LAST;
+                                                       else
+                                                               scratch.opcode = EEOP_BOOL_OR_STEP;
+                                                       break;
+                                               case NOT_EXPR:
+                                                       Assert(nargs == 1);
+
+                                                       scratch.opcode = EEOP_BOOL_NOT_STEP;
+                                                       break;
+                                               default:
+                                                       elog(ERROR, "unrecognized boolop: %d",
+                                                                (int) boolexpr->boolop);
+                                                       break;
+                                       }
+
+                                       scratch.d.boolexpr.jumpdone = -1;
+                                       ExprEvalPushStep(state, &scratch);
+                                       adjust_jumps = lappend_int(adjust_jumps,
+                                                                                          state->steps_len - 1);
+                                       off++;
+                               }
+
+                               /* adjust jump targets */
+                               foreach(lc, adjust_jumps)
+                               {
+                                       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 = EEOP_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 = EEOP_ALTERNATIVE_SUBPLAN;
+                               scratch.d.alternative_subplan.asstate = asstate;
+
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_FieldSelect:
+                       {
+                               FieldSelect *fselect = (FieldSelect *) node;
+
+                               /* evaluate row/record argument into result area */
+                               ExecInitExprRec(fselect->arg, parent, state, resv, resnull);
+
+                               /* and extract field */
+                               scratch.opcode = EEOP_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;
+                               TupleDesc       tupDesc;
+                               TupleDesc  *descp;
+                               Datum      *values;
+                               bool       *nulls;
+                               int                     ncolumns;
+                               ListCell   *l1,
+                                                  *l2;
+
+                               /* find out the number of columns in the composite type */
+                               tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1);
+                               ncolumns = tupDesc->natts;
+                               DecrTupleDescRefCount(tupDesc);
+
+                               /* create workspace for column values */
+                               values = (Datum *) palloc(sizeof(Datum) * ncolumns);
+                               nulls = (bool *) palloc(sizeof(bool) * ncolumns);
+
+                               /* create workspace for runtime tupdesc cache */
+                               descp = (TupleDesc *) palloc(sizeof(TupleDesc));
+                               *descp = NULL;
+
+                               /* emit code to evaluate the composite input value */
+                               ExecInitExprRec(fstore->arg, parent, state, resv, resnull);
+
+                               /* next, deform the input tuple into our workspace */
+                               scratch.opcode = EEOP_FIELDSTORE_DEFORM;
+                               scratch.d.fieldstore.fstore = fstore;
+                               scratch.d.fieldstore.argdesc = descp;
+                               scratch.d.fieldstore.values = values;
+                               scratch.d.fieldstore.nulls = nulls;
+                               scratch.d.fieldstore.ncolumns = ncolumns;
+                               ExprEvalPushStep(state, &scratch);
+
+                               /* evaluate new field values, store in workspace columns */
+                               forboth(l1, fstore->newvals, l2, fstore->fieldnums)
+                               {
+                                       Expr       *e = (Expr *) lfirst(l1);
+                                       AttrNumber      fieldnum = lfirst_int(l2);
+                                       Datum      *save_innermost_caseval;
+                                       bool       *save_innermost_casenull;
+
+                                       if (fieldnum <= 0 || fieldnum > ncolumns)
+                                               elog(ERROR, "field number %d is out of range in FieldStore",
+                                                        fieldnum);
+
+                                       /*
+                                        * 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 innermost_caseval 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;
+                               }
+
+                               /* finally, form result tuple */
+                               scratch.opcode = EEOP_FIELDSTORE_FORM;
+                               scratch.d.fieldstore.fstore = fstore;
+                               scratch.d.fieldstore.argdesc = descp;
+                               scratch.d.fieldstore.values = values;
+                               scratch.d.fieldstore.nulls = nulls;
+                               scratch.d.fieldstore.ncolumns = ncolumns;
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_RelabelType:
+                       {
+                               /* relabel doesn't need to do anything at runtime */
+                               RelabelType *relabel = (RelabelType *) node;
+
+                               ExecInitExprRec(relabel->arg, parent, state, resv, resnull);
+                               break;
+                       }
+
+               case T_CoerceViaIO:
+                       {
+                               CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+                               Oid                     iofunc;
+                               bool            typisvarlena;
+                               Oid                     typioparam;
+                               FunctionCallInfo fcinfo_in;
+
+                               /* evaluate argument into step's result area */
+                               ExecInitExprRec(iocoerce->arg, parent, state, resv, resnull);
+
+                               /*
+                                * Prepare both output and input function calls, to be
+                                * evaluated inside a single evaluation step for speed - this
+                                * can be a very common operation.
+                                *
+                                * We don't check permissions here as a type's input/output
+                                * function are assumed to be executable by everyone.
+                                */
+                               scratch.opcode = EEOP_IOCOERCE;
+
+                               /* lookup the source type's output function */
+                               scratch.d.iocoerce.finfo_out = palloc0(sizeof(FmgrInfo));
+                               scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(FunctionCallInfoData));
+
+                               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(FmgrInfo));
+                               scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(FunctionCallInfoData));
+
+                               getTypeInputInfo(iocoerce->resulttype,
+                                                                &iofunc, &typioparam);
+                               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);
+
+                               /*
+                                * We can preload the second and third arguments for the input
+                                * function, since they're constants.
+                                */
+                               fcinfo_in = scratch.d.iocoerce.fcinfo_data_in;
+                               fcinfo_in->arg[1] = ObjectIdGetDatum(typioparam);
+                               fcinfo_in->argnull[1] = false;
+                               fcinfo_in->arg[2] = Int32GetDatum(-1);
+                               fcinfo_in->argnull[2] = false;
+
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_ArrayCoerceExpr:
+                       {
+                               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+                               Oid                     resultelemtype;
+
+                               /* evaluate argument into step's result area */
+                               ExecInitExprRec(acoerce->arg, parent, state, resv, resnull);
+
+                               resultelemtype = get_element_type(acoerce->resulttype);
+                               if (!OidIsValid(resultelemtype))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                        errmsg("target type is not an array")));
+                               /* Arrays over domains aren't supported yet */
+                               Assert(getBaseType(resultelemtype) == resultelemtype);
+
+                               scratch.opcode = EEOP_ARRAYCOERCE;
+                               scratch.d.arraycoerce.coerceexpr = acoerce;
+                               scratch.d.arraycoerce.resultelemtype = resultelemtype;
+
+                               if (OidIsValid(acoerce->elemfuncid))
+                               {
+                                       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 */
+                                       scratch.d.arraycoerce.elemfunc =
+                                               (FmgrInfo *) palloc0(sizeof(FmgrInfo));
+                                       fmgr_info(acoerce->elemfuncid,
+                                                         scratch.d.arraycoerce.elemfunc);
+                                       fmgr_info_set_expr((Node *) acoerce,
+                                                                          scratch.d.arraycoerce.elemfunc);
+
+                                       /* Set up workspace for array_map */
+                                       scratch.d.arraycoerce.amstate =
+                                               (ArrayMapState *) palloc0(sizeof(ArrayMapState));
+                               }
+                               else
+                               {
+                                       /* Don't need workspace if there's no conversion func */
+                                       scratch.d.arraycoerce.elemfunc = NULL;
+                                       scratch.d.arraycoerce.amstate = NULL;
+                               }
+
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_ConvertRowtypeExpr:
+                       {
+                               ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
+
+                               /* evaluate argument into step's result area */
+                               ExecInitExprRec(convert->arg, parent, state, resv, resnull);
+
+                               /* and push conversion step */
+                               scratch.opcode = EEOP_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;
+                               List       *adjust_jumps = NIL;
+                               Datum      *caseval = NULL;
+                               bool       *casenull = NULL;
+                               ListCell   *lc;
+
+                               /*
+                                * If there's a test expression, we have to evaluate it and
+                                * save the value where the CaseTestExpr placeholders can find
+                                * it.
+                                */
+                               if (caseExpr->arg != NULL)
+                               {
+                                       /* Evaluate testexpr into caseval/casenull workspace */
+                                       caseval = palloc(sizeof(Datum));
+                                       casenull = palloc(sizeof(bool));
+
+                                       ExecInitExprRec(caseExpr->arg, parent, state,
+                                                                       caseval, casenull);
+
+                                       /*
+                                        * Since value might be read multiple times, force to R/O
+                                        * - but only if it could be an expanded datum.
+                                        */
+                                       if (get_typlen(exprType((Node *) caseExpr->arg)) == -1)
+                                       {
+                                               /* change caseval in-place */
+                                               scratch.opcode = EEOP_MAKE_READONLY;
+                                               scratch.resvalue = caseval;
+                                               scratch.resnull = casenull;
+                                               scratch.d.make_readonly.value = caseval;
+                                               scratch.d.make_readonly.isnull = casenull;
+                                               ExprEvalPushStep(state, &scratch);
+                                               /* restore normal settings of scratch fields */
+                                               scratch.resvalue = resv;
+                                               scratch.resnull = resnull;
+                                       }
+                               }
+
+                               /*
+                                * Prepare to evaluate each of the WHEN clauses in turn; as
+                                * soon as one is true we return the value of the
+                                * corresponding THEN clause.  If none are true then we return
+                                * the value of the ELSE clause, or NULL if there is none.
+                                */
+                               foreach(lc, caseExpr->args)
+                               {
+                                       CaseWhen   *when = (CaseWhen *) lfirst(lc);
+                                       Datum      *save_innermost_caseval;
+                                       bool       *save_innermost_casenull;
+                                       int                     whenstep;
+
+                                       /*
+                                        * Make testexpr result available to CaseTestExpr nodes
+                                        * within the condition.  We must save and restore prior
+                                        * setting of innermost_caseval 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.
+                                        */
+                                       save_innermost_caseval = state->innermost_caseval;
+                                       save_innermost_casenull = state->innermost_casenull;
+                                       state->innermost_caseval = caseval;
+                                       state->innermost_casenull = casenull;
+
+                                       /* evaluate condition into CASE's result variables */
+                                       ExecInitExprRec(when->expr, parent, state, resv, resnull);
+
+                                       state->innermost_caseval = save_innermost_caseval;
+                                       state->innermost_casenull = save_innermost_casenull;
+
+                                       /* If WHEN result isn't true, jump to next CASE arm */
+                                       scratch.opcode = EEOP_JUMP_IF_NOT_TRUE;
+                                       scratch.d.jump.jumpdone = -1;           /* computed later */
+                                       ExprEvalPushStep(state, &scratch);
+                                       whenstep = state->steps_len - 1;
+
+                                       /*
+                                        * If WHEN result is true, evaluate THEN result, storing
+                                        * it into the CASE's result variables.
+                                        */
+                                       ExecInitExprRec(when->result, parent, state, resv, resnull);
+
+                                       /* Emit JUMP step to jump to end of CASE's code */
+                                       scratch.opcode = EEOP_JUMP;
+                                       scratch.d.jump.jumpdone = -1;           /* computed later */
+                                       ExprEvalPushStep(state, &scratch);
+
+                                       /*
+                                        * Don't know address for that jump yet, compute once the
+                                        * whole CASE expression is built.
+                                        */
+                                       adjust_jumps = lappend_int(adjust_jumps,
+                                                                                          state->steps_len - 1);
+
+                                       /*
+                                        * But we can set WHEN test's jump target now, to make it
+                                        * jump to the next WHEN subexpression or the ELSE.
+                                        */
+                                       state->steps[whenstep].d.jump.jumpdone = state->steps_len;
+                               }
+
+                               if (caseExpr->defresult)
+                               {
+                                       /* evaluate ELSE expr into CASE's result variables */
+                                       ExecInitExprRec(caseExpr->defresult, parent, state,
+                                                                       resv, resnull);
+                               }
+                               else
+                               {
+                                       /* default ELSE is to return NULL */
+                                       scratch.opcode = EEOP_CONST;
+                                       scratch.d.constval.value = (Datum) 0;
+                                       scratch.d.constval.isnull = true;
+                                       ExprEvalPushStep(state, &scratch);
+                               }
+
+                               /* adjust jump targets */
+                               foreach(lc, adjust_jumps)
+                               {
+                                       ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+                                       Assert(as->opcode == EEOP_JUMP);
+                                       Assert(as->d.jump.jumpdone == -1);
+                                       as->d.jump.jumpdone = state->steps_len;
+                               }
+
+                               break;
+                       }
+
+               case T_CaseTestExpr:
+                       {
+                               /*
+                                * Read from location identified by innermost_caseval.  Note
+                                * that innermost_caseval could be NULL, if this node isn't
+                                * actually within a CASE structure; some parts of the system
+                                * abuse CaseTestExpr to cause a read of a value externally
+                                * supplied in econtext->caseValue_datum.  We'll take care of
+                                * that scenario at runtime.
+                                */
+                               scratch.opcode = EEOP_CASE_TESTVAL;
+                               scratch.d.casetest.value = state->innermost_caseval;
+                               scratch.d.casetest.isnull = state->innermost_casenull;
+
+                               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.  Elements are computed into scratch arrays
+                                * associated with the ARRAYEXPR step.
+                                */
+                               scratch.opcode = EEOP_ARRAYEXPR;
+                               scratch.d.arrayexpr.elemvalues =
+                                       (Datum *) palloc(sizeof(Datum) * nelems);
+                               scratch.d.arrayexpr.elemnulls =
+                                       (bool *) palloc(sizeof(bool) * nelems);
+                               scratch.d.arrayexpr.nelems = nelems;
+
+                               /* fill remaining fields of step */
+                               scratch.d.arrayexpr.multidims = arrayexpr->multidims;
+                               scratch.d.arrayexpr.elemtype = arrayexpr->element_typeid;
+
+                               /* 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 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;
+
+                               /* 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);
+                               }
+                               /* 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);
+
+                               /*
+                                * In the named-type case, the tupdesc could have more columns
+                                * than are in the args list, since the type might have had
+                                * columns added since the ROW() was parsed.  We want those
+                                * extra columns to go to nulls, so we make sure that the
+                                * workspace arrays are large enough and then initialize any
+                                * extra columns to read as NULLs.
+                                */
+                               Assert(nelems <= tupdesc->natts);
+                               nelems = Max(nelems, tupdesc->natts);
+
+                               /*
+                                * Evaluate by first building datums for each field, and then
+                                * a final step forming the composite datum.
+                                */
+                               scratch.opcode = EEOP_ROW;
+                               scratch.d.row.tupdesc = tupdesc;
+
+                               /* space for the individual field datums */
+                               scratch.d.row.elemvalues =
+                                       (Datum *) palloc(sizeof(Datum) * nelems);
+                               scratch.d.row.elemnulls =
+                                       (bool *) palloc(sizeof(bool) * nelems);
+                               /* as explained above, make sure any extra columns are null */
+                               memset(scratch.d.row.elemnulls, true, sizeof(bool) * nelems);
+
+                               /* 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);
+                                       }
+
+                                       /* Evaluate column expr into appropriate workspace slot */
+                                       ExecInitExprRec(e, parent, state,
+                                                                       &scratch.d.row.elemvalues[i],
+                                                                       &scratch.d.row.elemnulls[i]);
+                                       i++;
+                               }
+
+                               /* And finally build the row value */
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_RowCompareExpr:
+                       {
+                               RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+                               int                     nopers = list_length(rcexpr->opnos);
+                               List       *adjust_jumps = 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 a
+                                * comparison yields a != 0 result, jump to the final step.
+                                */
+                               Assert(list_length(rcexpr->largs) == nopers);
+                               Assert(list_length(rcexpr->rargs) == nopers);
+                               Assert(list_length(rcexpr->opfamilies) == nopers);
+                               Assert(list_length(rcexpr->inputcollids) == 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 args 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 = EEOP_ROWCOMPARE_STEP;
+                                       scratch.d.rowcompare_step.finfo = finfo;
+                                       scratch.d.rowcompare_step.fcinfo_data = fcinfo;
+                                       scratch.d.rowcompare_step.fn_addr = finfo->fn_addr;
+                                       /* jump targets filled below */
+                                       scratch.d.rowcompare_step.jumpnull = -1;
+                                       scratch.d.rowcompare_step.jumpdone = -1;
+
+                                       ExprEvalPushStep(state, &scratch);
+                                       adjust_jumps = lappend_int(adjust_jumps,
+                                                                                          state->steps_len - 1);
+                               }
+
+                               /*
+                                * We could have a zero-column rowtype, in which case the rows
+                                * necessarily compare equal.
+                                */
+                               if (nopers == 0)
+                               {
+                                       scratch.opcode = EEOP_CONST;
+                                       scratch.d.constval.value = Int32GetDatum(0);
+                                       scratch.d.constval.isnull = false;
+                                       ExprEvalPushStep(state, &scratch);
+                               }
+
+                               /* Finally, examine the last comparison result */
+                               scratch.opcode = EEOP_ROWCOMPARE_FINAL;
+                               scratch.d.rowcompare_final.rctype = rcexpr->rctype;
+                               ExprEvalPushStep(state, &scratch);
+
+                               /* adjust jump targetss */
+                               foreach(lc, adjust_jumps)
+                               {
+                                       ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+                                       Assert(as->opcode == EEOP_ROWCOMPARE_STEP);
+                                       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_jumps = NIL;
+                               ListCell   *lc;
+
+                               /* We assume there's at least one arg */
+                               Assert(coalesce->args != NIL);
+
+                               /*
+                                * Prepare evaluation of all coalesced arguments, after each
+                                * one push a step that short-circuits if not null.
+                                */
+                               foreach(lc, coalesce->args)
+                               {
+                                       Expr       *e = (Expr *) lfirst(lc);
+
+                                       /* evaluate argument, directly into result datum */
+                                       ExecInitExprRec(e, parent, state, resv, resnull);
+
+                                       /* if it's not null, skip to end of COALESCE expr */
+                                       scratch.opcode = EEOP_JUMP_IF_NOT_NULL;
+                                       scratch.d.jump.jumpdone = -1;           /* adjust later */
+                                       ExprEvalPushStep(state, &scratch);
+
+                                       adjust_jumps = lappend_int(adjust_jumps,
+                                                                                          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 jump targets */
+                               foreach(lc, adjust_jumps)
+                               {
+                                       ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+                                       Assert(as->opcode == EEOP_JUMP_IF_NOT_NULL);
+                                       Assert(as->d.jump.jumpdone == -1);
+                                       as->d.jump.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.
+                                */
+
+                               /* Perform function lookup */
+                               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 = EEOP_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 = EEOP_SQLVALUEFUNCTION;
+                               scratch.d.sqlvaluefunction.svf = svf;
+
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_XmlExpr:
+                       {
+                               XmlExpr    *xexpr = (XmlExpr *) node;
+                               int                     nnamed = list_length(xexpr->named_args);
+                               int                     nargs = list_length(xexpr->args);
+                               int                     off;
+                               ListCell   *arg;
+
+                               scratch.opcode = EEOP_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 = EEOP_NULLTEST_ROWISNULL;
+                                       else
+                                               scratch.opcode = EEOP_NULLTEST_ISNULL;
+                               }
+                               else if (ntest->nulltesttype == IS_NOT_NULL)
+                               {
+                                       if (ntest->argisrow)
+                                               scratch.opcode = EEOP_NULLTEST_ROWISNOTNULL;
+                                       else
+                                               scratch.opcode = EEOP_NULLTEST_ISNOTNULL;
+                               }
+                               else
+                               {
+                                       elog(ERROR, "unrecognized nulltesttype: %d",
+                                                (int) ntest->nulltesttype);
+                               }
+                               /* initialize cache in case it's a row test */
+                               scratch.d.nulltest_row.argdesc = NULL;
+
+                               /* first evaluate argument into result variable */
+                               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 ok,
+                                * because resv/resnull is definitely not used anywhere else,
+                                * and will get overwritten by the below EEOP_BOOLTEST_IS_*
+                                * step.
+                                */
+                               ExecInitExprRec(btest->arg, parent, state, resv, resnull);
+
+                               switch (btest->booltesttype)
+                               {
+                                       case IS_TRUE:
+                                               scratch.opcode = EEOP_BOOLTEST_IS_TRUE;
+                                               break;
+                                       case IS_NOT_TRUE:
+                                               scratch.opcode = EEOP_BOOLTEST_IS_NOT_TRUE;
+                                               break;
+                                       case IS_FALSE:
+                                               scratch.opcode = EEOP_BOOLTEST_IS_FALSE;
+                                               break;
+                                       case IS_NOT_FALSE:
+                                               scratch.opcode = EEOP_BOOLTEST_IS_NOT_FALSE;
+                                               break;
+                                       case IS_UNKNOWN:
+                                               /* Same as scalar IS NULL test */
+                                               scratch.opcode = EEOP_NULLTEST_ISNULL;
+                                               break;
+                                       case IS_NOT_UNKNOWN:
+                                               /* Same as scalar IS NOT NULL test */
+                                               scratch.opcode = EEOP_NULLTEST_ISNOTNULL;
+                                               break;
+                                       default:
+                                               elog(ERROR, "unrecognized booltesttype: %d",
+                                                        (int) btest->booltesttype);
+                               }
+
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_CoerceToDomain:
+                       {
+                               CoerceToDomain *ctest = (CoerceToDomain *) node;
+
+                               ExecInitCoerceToDomain(&scratch, ctest, parent, state,
+                                                                          resv, resnull);
+                               break;
+                       }
+
+               case T_CoerceToDomainValue:
+                       {
+                               /*
+                                * Read from location identified by innermost_domainval.  Note
+                                * that innermost_domainval could be NULL, if we're compiling
+                                * a standalone domain check rather than one embedded in a
+                                * larger expression.  In that case we must read from
+                                * econtext->domainValue_datum.  We'll take care of that
+                                * scenario at runtime.
+                                */
+                               scratch.opcode = EEOP_DOMAIN_TESTVAL;
+                               /* we share instruction union variant with case testval */
+                               scratch.d.casetest.value = state->innermost_domainval;
+                               scratch.d.casetest.isnull = state->innermost_domainnull;
+
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               case T_CurrentOfExpr:
+                       {
+                               scratch.opcode = EEOP_CURRENTOFEXPR;
+                               ExprEvalPushStep(state, &scratch);
+                               break;
+                       }
+
+               default:
+                       elog(ERROR, "unrecognized node type: %d",
+                                (int) nodeTag(node));
+                       break;
+       }
+}
+
+/*
+ * Add another expression evaluation step to ExprState->steps.
+ *
+ * Note that this potentially re-allocates es->steps, therefore no pointer
+ * into that array may be used while the expression is still being built.
+ */
+static void
+ExprEvalPushStep(ExprState *es, const 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));
+}
+
+/*
+ * Perform setup necessary for the evaluation of a function-like expression,
+ * appending argument evaluation steps to the steps list in *state, and
+ * setting up *scratch so it is ready to be pushed.
+ *
+ * *scratch is not pushed here, so that callers may override the opcode,
+ * which is useful for function-like cases like DISTINCT.
+ */
+static void
+ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
+                        Oid inputcollid, PlanState *parent, ExprState *state)
+{
+       int                     nargs = list_length(args);
+       AclResult       aclresult;
+       FmgrInfo   *flinfo;
+       FunctionCallInfo fcinfo;
+       int                     argno;
+       ListCell   *lc;
+
+       /* 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)));
+
+       /* Allocate function lookup data and parameter workspace for this call */
+       scratch->d.func.finfo = palloc0(sizeof(FmgrInfo));
+       scratch->d.func.fcinfo_data = palloc0(sizeof(FunctionCallInfoData));
+       flinfo = scratch->d.func.finfo;
+       fcinfo = scratch->d.func.fcinfo_data;
+
+       /* Set up the primary fmgr lookup information */
+       fmgr_info(funcid, flinfo);
+       fmgr_info_set_expr((Node *) node, flinfo);
+
+       /* Initialize function call parameter structure too */
+       InitFunctionCallInfoData(*fcinfo, flinfo,
+                                                        nargs, inputcollid, NULL, NULL);
+
+       /* Keep extra copies of this info to save an indirection at runtime */
+       scratch->d.func.fn_addr = flinfo->fn_addr;
+       scratch->d.func.nargs = nargs;
+
+       /* We only support non-set functions here */
+       if (flinfo->fn_retset)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("set-valued function called in context that cannot accept a set")));
+
+       /* Build code to evaluate arguments directly into the fcinfo struct */
+       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++;
+       }
+
+       /* Insert appropriate opcode depending on strictness and stats level */
+       if (pgstat_track_functions <= flinfo->fn_stats)
+       {
+               if (flinfo->fn_strict && nargs > 0)
+                       scratch->opcode = EEOP_FUNCEXPR_STRICT;
+               else
+                       scratch->opcode = EEOP_FUNCEXPR;
+       }
+       else
+       {
+               if (flinfo->fn_strict && nargs > 0)
+                       scratch->opcode = EEOP_FUNCEXPR_STRICT_FUSAGE;
+               else
+                       scratch->opcode = EEOP_FUNCEXPR_FUSAGE;
+       }
+}
+
+/*
+ * Add expression steps deforming the ExprState's inner/outer/scan slots
+ * as much as required by the expression.
+ */
+static void
+ExecInitExprSlots(ExprState *state, Node *node)
+{
+       LastAttnumInfo info = {0, 0, 0};
+       ExprEvalStep scratch;
+
+       /*
+        * Figure out which attributes we're going to need.
+        */
+       get_last_attnums_walker(node, &info);
+
+       /* Emit steps as needed */
+       if (info.last_inner > 0)
+       {
+               scratch.opcode = EEOP_INNER_FETCHSOME;
+               scratch.d.fetch.last_var = info.last_inner;
+               ExprEvalPushStep(state, &scratch);
+       }
+       if (info.last_outer > 0)
+       {
+               scratch.opcode = EEOP_OUTER_FETCHSOME;
+               scratch.d.fetch.last_var = info.last_outer;
+               ExprEvalPushStep(state, &scratch);
+       }
+       if (info.last_scan > 0)
+       {
+               scratch.opcode = EEOP_SCAN_FETCHSOME;
+               scratch.d.fetch.last_var = info.last_scan;
+               ExprEvalPushStep(state, &scratch);
+       }
+}
+
+/*
+ * get_last_attnums_walker: expression walker for ExecInitExprSlots
+ */
+static bool
+get_last_attnums_walker(Node *node, LastAttnumInfo *info)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Var))
+       {
+               Var                *variable = (Var *) node;
+               AttrNumber      attnum = variable->varattno;
+
+               switch (variable->varno)
+               {
+                       case INNER_VAR:
+                               info->last_inner = Max(info->last_inner, attnum);
+                               break;
+
+                       case OUTER_VAR:
+                               info->last_outer = Max(info->last_outer, attnum);
+                               break;
+
+                               /* INDEX_VAR is handled by default case */
+
+                       default:
+                               info->last_scan = Max(info->last_scan, attnum);
+                               break;
+               }
+               return false;
+       }
+
+       /*
+        * Don't examine the arguments or filters of Aggrefs or WindowFuncs,
+        * because those do not represent expressions to be evaluated within the
+        * calling expression's econtext.  GroupingFunc arguments are never
+        * evaluated at all.
+        */
+       if (IsA(node, Aggref))
+               return false;
+       if (IsA(node, WindowFunc))
+               return false;
+       if (IsA(node, GroupingFunc))
+               return false;
+       return expression_tree_walker(node, get_last_attnums_walker,
+                                                                 (void *) info);
+}
+
+/*
+ * Prepare step for the evaluation of a whole-row variable.
+ * The caller still has to push the step.
+ */
+static void
+ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, PlanState *parent)
+{
+       /* fill in all but the target */
+       scratch->opcode = EEOP_WHOLEROW;
+       scratch->d.wholerow.var = variable;
+       scratch->d.wholerow.first = true;
+       scratch->d.wholerow.slow = false;
+       scratch->d.wholerow.tupdesc = NULL; /* filled at runtime */
+       scratch->d.wholerow.junkFilter = NULL;
+
+       /*
+        * 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 so, build the junkfilter now */
+                       if (junk_filter_needed)
+                       {
+                               scratch->d.wholerow.junkFilter =
+                                       ExecInitJunkFilter(subplan->plan->targetlist,
+                                                                          ExecGetResultType(subplan)->tdhasoid,
+                                                                          ExecInitExtraTupleSlot(parent->state));
+                       }
+               }
+       }
+}
+
+/*
+ * Prepare evaluation of an ArrayRef expression.
+ */
+static void
+ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
+                                ExprState *state, Datum *resv, bool *resnull)
+{
+       bool            isAssignment = (aref->refassgnexpr != NULL);
+       ArrayRefState *arefstate = palloc0(sizeof(ArrayRefState));
+       List       *adjust_jumps = NIL;
+       ListCell   *lc;
+       int                     i;
+
+       /* Fill constant fields of ArrayRefState */
+       arefstate->isassignment = isAssignment;
+       arefstate->refelemtype = aref->refelemtype;
+       arefstate->refattrlength = get_typlen(aref->refarraytype);
+       get_typlenbyvalalign(aref->refelemtype,
+                                                &arefstate->refelemlength,
+                                                &arefstate->refelembyval,
+                                                &arefstate->refelemalign);
+
+       /*
+        * Evaluate array input.  It's safe to do so into resv/resnull, because we
+        * won't use that as target for any of the other subexpressions, and it'll
+        * be overwritten by the final EEOP_ARRAYREF_FETCH/ASSIGN step, which is
+        * pushed last.
+        */
+       ExecInitExprRec(aref->refexpr, parent, state, resv, resnull);
+
+       /*
+        * If refexpr yields NULL, and it's a fetch, then result is NULL.  We can
+        * implement this with just JUMP_IF_NULL, since we evaluated the array
+        * into the desired target location.
+        */
+       if (!isAssignment)
+       {
+               scratch->opcode = EEOP_JUMP_IF_NULL;
+               scratch->d.jump.jumpdone = -1;  /* adjust later */
+               ExprEvalPushStep(state, scratch);
+               adjust_jumps = lappend_int(adjust_jumps,
+                                                                  state->steps_len - 1);
+       }
+
+       /* Verify subscript list lengths are within limit */
+       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->reflowerindexpr), MAXDIM)));
+
+       /* Evaluate upper subscripts */
+       i = 0;
+       foreach(lc, aref->refupperindexpr)
+       {
+               Expr       *e = (Expr *) lfirst(lc);
+
+               /* When slicing, individual subscript bounds can be omitted */
+               if (!e)
+               {
+                       arefstate->upperprovided[i] = false;
+                       i++;
+                       continue;
+               }
+
+               arefstate->upperprovided[i] = true;
+
+               /* Each subscript is evaluated into subscriptvalue/subscriptnull */
+               ExecInitExprRec(e, parent, state,
+                                         &arefstate->subscriptvalue, &arefstate->subscriptnull);
+
+               /* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */
+               scratch->opcode = EEOP_ARRAYREF_SUBSCRIPT;
+               scratch->d.arrayref_subscript.state = arefstate;
+               scratch->d.arrayref_subscript.off = i;
+               scratch->d.arrayref_subscript.isupper = true;
+               scratch->d.arrayref_subscript.jumpdone = -1;    /* adjust later */
+               ExprEvalPushStep(state, scratch);
+               adjust_jumps = lappend_int(adjust_jumps,
+                                                                  state->steps_len - 1);
+               i++;
+       }
+       arefstate->numupper = i;
+
+       /* Evaluate lower subscripts similarly */
+       i = 0;
+       foreach(lc, aref->reflowerindexpr)
+       {
+               Expr       *e = (Expr *) lfirst(lc);
+
+               /* When slicing, individual subscript bounds can be omitted */
+               if (!e)
+               {
+                       arefstate->lowerprovided[i] = false;
+                       i++;
+                       continue;
+               }
+
+               arefstate->lowerprovided[i] = true;
+
+               /* Each subscript is evaluated into subscriptvalue/subscriptnull */
+               ExecInitExprRec(e, parent, state,
+                                         &arefstate->subscriptvalue, &arefstate->subscriptnull);
+
+               /* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */
+               scratch->opcode = EEOP_ARRAYREF_SUBSCRIPT;
+               scratch->d.arrayref_subscript.state = arefstate;
+               scratch->d.arrayref_subscript.off = i;
+               scratch->d.arrayref_subscript.isupper = false;
+               scratch->d.arrayref_subscript.jumpdone = -1;    /* adjust later */
+               ExprEvalPushStep(state, scratch);
+               adjust_jumps = lappend_int(adjust_jumps,
+                                                                  state->steps_len - 1);
+               i++;
+       }
+       arefstate->numlower = i;
+
+       /* Should be impossible if parser is sane, but check anyway: */
+       if (arefstate->numlower != 0 &&
+               arefstate->numupper != arefstate->numlower)
+               elog(ERROR, "upper and lower index lists are not same length");
+
+       if (isAssignment)
+       {
+               Datum      *save_innermost_caseval;
+               bool       *save_innermost_casenull;
+
+               /*
+                * 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 CaseTextExpr mechanism.  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
+                * innermost_caseval 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 = EEOP_ARRAYREF_OLD;
+                       scratch->d.arrayref.state = arefstate;
+                       ExprEvalPushStep(state, scratch);
+               }
+
+               /* ARRAYREF_OLD puts extracted value into prevvalue/prevnull */
+               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 into replacevalue/replacenull */
+               ExecInitExprRec(aref->refassgnexpr, parent, state,
+                                               &arefstate->replacevalue, &arefstate->replacenull);
+
+               state->innermost_caseval = save_innermost_caseval;
+               state->innermost_casenull = save_innermost_casenull;
+
+               /* and perform the assignment */
+               scratch->opcode = EEOP_ARRAYREF_ASSIGN;
+               scratch->d.arrayref.state = arefstate;
+               ExprEvalPushStep(state, scratch);
+       }
+       else
+       {
+               /* array fetch is much simpler */
+               scratch->opcode = EEOP_ARRAYREF_FETCH;
+               scratch->d.arrayref.state = arefstate;
+               ExprEvalPushStep(state, scratch);
+       }
+
+       /* adjust jump targets */
+       foreach(lc, adjust_jumps)
+       {
+               ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+               if (as->opcode == EEOP_ARRAYREF_SUBSCRIPT)
+               {
+                       Assert(as->d.arrayref_subscript.jumpdone == -1);
+                       as->d.arrayref_subscript.jumpdone = state->steps_len;
+               }
+               else
+               {
+                       Assert(as->opcode == EEOP_JUMP_IF_NULL);
+                       Assert(as->d.jump.jumpdone == -1);
+                       as->d.jump.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;
+}
+
+/*
+ * Prepare evaluation of a CoerceToDomain expression.
+ */
+static void
+ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
+                                          PlanState *parent, ExprState *state,
+                                          Datum *resv, bool *resnull)
+{
+       ExprEvalStep scratch2;
+       DomainConstraintRef *constraint_ref;
+       Datum      *domainval = NULL;
+       bool       *domainnull = NULL;
+       Datum      *save_innermost_domainval;
+       bool       *save_innermost_domainnull;
+       ListCell   *l;
+
+       scratch->d.domaincheck.resulttype = ctest->resulttype;
+       /* we'll allocate workspace only if needed */
+       scratch->d.domaincheck.checkvalue = NULL;
+       scratch->d.domaincheck.checknull = NULL;
+
+       /*
+        * Evaluate argument - it's fine to directly store it into resv/resnull,
+        * if there's constraint failures there'll be errors, otherwise it's what
+        * needs to be returned.
+        */
+       ExecInitExprRec(ctest->arg, parent, state, resv, resnull);
+
+       /*
+        * Note: if the argument is of varlena type, it could be a R/W expanded
+        * object.  We want to return the R/W pointer as the final result, but we
+        * have to pass a R/O pointer as the value to be tested by any functions
+        * in check expressions.  We don't bother to emit a MAKE_READONLY step
+        * unless there's actually at least one check expression, though.  Until
+        * we've tested that, domainval/domainnull are NULL.
+        */
+
+       /*
+        * Collect the constraints associated with the domain.
+        *
+        * Note: before PG v10 we'd recheck the set of constraints during each
+        * evaluation of the expression.  Now we bake them into the ExprState
+        * during executor initialization.  That means we don't need typcache.c to
+        * provide compiled exprs.
+        */
+       constraint_ref = (DomainConstraintRef *)
+               palloc(sizeof(DomainConstraintRef));
+       InitDomainConstraintRef(ctest->resulttype,
+                                                       constraint_ref,
+                                                       CurrentMemoryContext,
+                                                       false);
+
+       /*
+        * Compile code to check each domain constraint.  NOTNULL constraints can
+        * just be applied on the resv/resnull value, but for CHECK constraints we
+        * need more pushups.
+        */
+       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 = EEOP_DOMAIN_NOTNULL;
+                               ExprEvalPushStep(state, scratch);
+                               break;
+                       case DOM_CONSTRAINT_CHECK:
+                               /* Allocate workspace for CHECK output if we didn't yet */
+                               if (scratch->d.domaincheck.checkvalue == NULL)
+                               {
+                                       scratch->d.domaincheck.checkvalue =
+                                               (Datum *) palloc(sizeof(Datum));
+                                       scratch->d.domaincheck.checknull =
+                                               (bool *) palloc(sizeof(bool));
+                               }
+
+                               /*
+                                * If first time through, determine where CoerceToDomainValue
+                                * nodes should read from.
+                                */
+                               if (domainval == NULL)
+                               {
+                                       /*
+                                        * Since value might be read multiple times, force to R/O
+                                        * - but only if it could be an expanded datum.
+                                        */
+                                       if (get_typlen(ctest->resulttype) == -1)
+                                       {
+                                               /* Yes, so make output workspace for MAKE_READONLY */
+                                               domainval = (Datum *) palloc(sizeof(Datum));
+                                               domainnull = (bool *) palloc(sizeof(bool));
+
+                                               /* Emit MAKE_READONLY */
+                                               scratch2.opcode = EEOP_MAKE_READONLY;
+                                               scratch2.resvalue = domainval;
+                                               scratch2.resnull = domainnull;
+                                               scratch2.d.make_readonly.value = resv;
+                                               scratch2.d.make_readonly.isnull = resnull;
+                                               ExprEvalPushStep(state, &scratch2);
+                                       }
+                                       else
+                                       {
+                                               /* No, so it's fine to read from resv/resnull */
+                                               domainval = resv;
+                                               domainnull = resnull;
+                                       }
+                               }
+
+                               /*
+                                * 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 = domainval;
+                               state->innermost_domainnull = domainnull;
+
+                               /* evaluate check expression value */
+                               ExecInitExprRec(con->check_expr, parent, state,
+                                                               scratch->d.domaincheck.checkvalue,
+                                                               scratch->d.domaincheck.checknull);
+
+                               state->innermost_domainval = save_innermost_domainval;
+                               state->innermost_domainnull = save_innermost_domainnull;
+
+                               /* now test result */
+                               scratch->opcode = EEOP_DOMAIN_CHECK;
+                               ExprEvalPushStep(state, scratch);
+
+                               break;
+                       default:
+                               elog(ERROR, "unrecognized constraint type: %d",
+                                        (int) con->constrainttype);
+                               break;
+               }
+       }
+}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
new file mode 100644 (file)
index 0000000..de7fe89
--- /dev/null
@@ -0,0 +1,3525 @@
+/*-------------------------------------------------------------------------
+ *
+ * execExprInterp.c
+ *       Interpreted evaluation of an expression step list.
+ *
+ * This file provides either a "direct threaded" (for gcc, clang and
+ * compatible) or a "switch threaded" (for all compilers) implementation of
+ * expression evaluation.  The former is amongst the fastest known methods
+ * of interpreting programs without resorting to assembly level work, or
+ * just-in-time compilation, but it requires support for computed gotos.
+ * The latter is amongst the fastest approaches doable in standard C.
+ *
+ * In either case we use ExprEvalStep->opcode to dispatch to the code block
+ * within ExecInterpExpr() that implements the specific opcode type.
+ *
+ * Switch-threading uses a plain switch() statement to perform the
+ * dispatch.  This has the advantages of being plain C and allowing the
+ * compiler 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, requiring more jumps and causing
+ * bad branch prediction.
+ *
+ * In direct threading, we use gcc's label-as-values extension - also adopted
+ * by some other compilers - to replace ExprEvalStep->opcode with the address
+ * of the block implementing the instruction. Dispatch to the next instruction
+ * is done by a "computed goto".  This allows for better branch prediction
+ * (as the jumps are happening from different locations) and fewer jumps
+ * (as no preparatory jump to a common dispatch location is needed).
+ *
+ * When using direct threading, ExecReadyInterpretedExpr will replace
+ * each step's opcode field with the address of the relevant code block and
+ * ExprState->flags will contain EEO_FLAG_DIRECT_THREADED to remember that
+ * that's been done.
+ *
+ * For very simple instructions the overhead of the full interpreter
+ * "startup", as minimal as it is, is noticeable.  Therefore
+ * ExecReadyInterpretedExpr will choose to implement simple scalar Var
+ * and Const expressions using special fast-path routines (ExecJust*).
+ * Benchmarking shows anything more complex than those may as well use the
+ * "full interpreter".
+ *
+ * Complex or uncommon instructions are not implemented in-line in
+ * ExecInterpExpr(), rather we call out to a helper function appearing later
+ * in this file.  For one reason, there'd not be a noticeable performance
+ * benefit, but more importantly those complex routines are intended to be
+ * shared between different expression evaluation approaches.  For instance
+ * a JIT compiler would generate calls to them.  (This is why they are
+ * exported rather than being "static" in this file.)
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *       src/backend/executor/execExprInterp.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "catalog/pg_type.h"
+#include "executor/execExpr.h"
+#include "executor/nodeSubplan.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 use a separate symbol so that it's easy to adjust locally in this file
+ * for development and testing.
+ */
+#ifdef HAVE_COMPUTED_GOTO
+#define EEO_USE_COMPUTED_GOTO
+#endif   /* HAVE_COMPUTED_GOTO */
+
+/*
+ * Macros for opcode dispatch.
+ *
+ * EEO_SWITCH - just hides the switch if not in use.
+ * EEO_CASE - labels the implementation of named expression step type.
+ * EEO_DISPATCH - jump to the implementation of the step type for 'op'.
+ * EEO_OPCODE - compute opcode required by used expression evaluation method.
+ * EEO_NEXT - increment 'op' and jump to correct next step type.
+ * EEO_JUMP - jump to the specified step number within the current expression.
+ */
+#if defined(EEO_USE_COMPUTED_GOTO)
+
+/* to make dispatch_table accessible outside ExecInterpExpr() */
+static const void **dispatch_table = NULL;
+
+#define EEO_SWITCH()
+#define EEO_CASE(name)         CASE_##name:
+#define EEO_DISPATCH()         goto *((void *) op->opcode)
+#define EEO_OPCODE(opcode)     ((intptr_t) dispatch_table[opcode])
+
+#else                                                  /* !EEO_USE_COMPUTED_GOTO */
+
+#define EEO_SWITCH()           starteval: switch ((ExprEvalOp) op->opcode)
+#define EEO_CASE(name)         case name:
+#define EEO_DISPATCH()         goto starteval
+#define EEO_OPCODE(opcode)     (opcode)
+
+#endif   /* EEO_USE_COMPUTED_GOTO */
+
+#define EEO_NEXT() \
+       do { \
+               op++; \
+               EEO_DISPATCH(); \
+       } while (0)
+
+#define EEO_JUMP(stepno) \
+       do { \
+               op = &state->steps[stepno]; \
+               EEO_DISPATCH(); \
+       } while (0)
+
+
+static Datum ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull);
+static void ExecInitInterpreter(void);
+
+/* support functions */
+static void CheckVarSlotCompatibility(TupleTableSlot *slot, int attnum, Oid vartype);
+static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
+                                  TupleDesc *cache_field, ExprContext *econtext);
+static void ShutdownTupleDescRef(Datum arg);
+static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
+                                  ExprContext *econtext, bool checkisnull);
+
+/* fast-path evaluation functions */
+static Datum ExecJustInnerVarFirst(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustOuterVarFirst(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVarFirst(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 ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
+
+
+/*
+ * Prepare ExprState for interpreted execution.
+ */
+void
+ExecReadyInterpretedExpr(ExprState *state)
+{
+       /* Ensure one-time interpreter setup has been done */
+       ExecInitInterpreter();
+
+       /* Simple validity checks on expression */
+       Assert(state->steps_len >= 1);
+       Assert(state->steps[state->steps_len - 1].opcode == EEOP_DONE);
+
+       /*
+        * Don't perform redundant initialization. This is unreachable in current
+        * cases, but might be hit if there's additional expression evaluation
+        * methods that rely on interpreted execution to work.
+        */
+       if (state->flags & EEO_FLAG_INTERPRETER_INITIALIZED)
+               return;
+
+       /* DIRECT_THREADED should not already be set */
+       Assert((state->flags & EEO_FLAG_DIRECT_THREADED) == 0);
+
+       /*
+        * There shouldn't be any errors before the expression is fully
+        * initialized, and even if so, it'd lead to the expression being
+        * abandoned.  So we can set the flag now and save some code.
+        */
+       state->flags |= EEO_FLAG_INTERPRETER_INITIALIZED;
+
+       /*
+        * Select fast-path evalfuncs 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 always.
+        */
+       if (state->steps_len == 3)
+       {
+               ExprEvalOp      step0 = state->steps[0].opcode;
+               ExprEvalOp      step1 = state->steps[1].opcode;
+
+               if (step0 == EEOP_INNER_FETCHSOME &&
+                       step1 == EEOP_INNER_VAR_FIRST)
+               {
+                       state->evalfunc = ExecJustInnerVarFirst;
+                       return;
+               }
+               else if (step0 == EEOP_OUTER_FETCHSOME &&
+                                step1 == EEOP_OUTER_VAR_FIRST)
+               {
+                       state->evalfunc = ExecJustOuterVarFirst;
+                       return;
+               }
+               else if (step0 == EEOP_SCAN_FETCHSOME &&
+                                step1 == EEOP_SCAN_VAR_FIRST)
+               {
+                       state->evalfunc = ExecJustScanVarFirst;
+                       return;
+               }
+               else if (step0 == EEOP_INNER_FETCHSOME &&
+                                step1 == EEOP_ASSIGN_INNER_VAR)
+               {
+                       state->evalfunc = ExecJustAssignInnerVar;
+                       return;
+               }
+               else if (step0 == EEOP_OUTER_FETCHSOME &&
+                                step1 == EEOP_ASSIGN_OUTER_VAR)
+               {
+                       state->evalfunc = ExecJustAssignOuterVar;
+                       return;
+               }
+               else if (step0 == EEOP_SCAN_FETCHSOME &&
+                                step1 == EEOP_ASSIGN_SCAN_VAR)
+               {
+                       state->evalfunc = ExecJustAssignScanVar;
+                       return;
+               }
+       }
+       else if (state->steps_len == 2 &&
+                        state->steps[0].opcode == EEOP_CONST)
+       {
+               state->evalfunc = ExecJustConst;
+               return;
+       }
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+
+       /*
+        * In the direct-threaded implementation, replace each opcode with the
+        * address to jump to.  (Use ExecEvalStepOp() to get back the opcode.)
+        */
+       {
+               int                     off;
+
+               for (off = 0; off < state->steps_len; off++)
+               {
+                       ExprEvalStep *op = &state->steps[off];
+
+                       op->opcode = EEO_OPCODE(op->opcode);
+               }
+
+               state->flags |= EEO_FLAG_DIRECT_THREADED;
+       }
+#endif   /* EEO_USE_COMPUTED_GOTO */
+
+       state->evalfunc = ExecInterpExpr;
+}
+
+
+/*
+ * Evaluate expression identified by "state" in the execution context
+ * given by "econtext".  *isnull is set to the is-null flag for the result,
+ * and the Datum value is the function result.
+ *
+ * As a special case, return the dispatch table's address if state is NULL.
+ * This is used by ExecInitInterpreter to set up the dispatch_table global.
+ * (Only applies when EEO_USE_COMPUTED_GOTO is defined.)
+ */
+static Datum
+ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+       ExprEvalStep *op;
+       TupleTableSlot *resultslot;
+       TupleTableSlot *innerslot;
+       TupleTableSlot *outerslot;
+       TupleTableSlot *scanslot;
+
+       /*
+        * This array has to be in the same order as enum ExprEvalOp.
+        */
+#if defined(EEO_USE_COMPUTED_GOTO)
+       static const void *const dispatch_table[] = {
+               &&CASE_EEOP_DONE,
+               &&CASE_EEOP_INNER_FETCHSOME,
+               &&CASE_EEOP_OUTER_FETCHSOME,
+               &&CASE_EEOP_SCAN_FETCHSOME,
+               &&CASE_EEOP_INNER_VAR_FIRST,
+               &&CASE_EEOP_INNER_VAR,
+               &&CASE_EEOP_OUTER_VAR_FIRST,
+               &&CASE_EEOP_OUTER_VAR,
+               &&CASE_EEOP_SCAN_VAR_FIRST,
+               &&CASE_EEOP_SCAN_VAR,
+               &&CASE_EEOP_INNER_SYSVAR,
+               &&CASE_EEOP_OUTER_SYSVAR,
+               &&CASE_EEOP_SCAN_SYSVAR,
+               &&CASE_EEOP_WHOLEROW,
+               &&CASE_EEOP_ASSIGN_INNER_VAR,
+               &&CASE_EEOP_ASSIGN_OUTER_VAR,
+               &&CASE_EEOP_ASSIGN_SCAN_VAR,
+               &&CASE_EEOP_ASSIGN_TMP,
+               &&CASE_EEOP_ASSIGN_TMP_MAKE_RO,
+               &&CASE_EEOP_CONST,
+               &&CASE_EEOP_FUNCEXPR,
+               &&CASE_EEOP_FUNCEXPR_STRICT,
+               &&CASE_EEOP_FUNCEXPR_FUSAGE,
+               &&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
+               &&CASE_EEOP_BOOL_AND_STEP_FIRST,
+               &&CASE_EEOP_BOOL_AND_STEP,
+               &&CASE_EEOP_BOOL_AND_STEP_LAST,
+               &&CASE_EEOP_BOOL_OR_STEP_FIRST,
+               &&CASE_EEOP_BOOL_OR_STEP,
+               &&CASE_EEOP_BOOL_OR_STEP_LAST,
+               &&CASE_EEOP_BOOL_NOT_STEP,
+               &&CASE_EEOP_QUAL,
+               &&CASE_EEOP_JUMP,
+               &&CASE_EEOP_JUMP_IF_NULL,
+               &&CASE_EEOP_JUMP_IF_NOT_NULL,
+               &&CASE_EEOP_JUMP_IF_NOT_TRUE,
+               &&CASE_EEOP_NULLTEST_ISNULL,
+               &&CASE_EEOP_NULLTEST_ISNOTNULL,
+               &&CASE_EEOP_NULLTEST_ROWISNULL,
+               &&CASE_EEOP_NULLTEST_ROWISNOTNULL,
+               &&CASE_EEOP_BOOLTEST_IS_TRUE,
+               &&CASE_EEOP_BOOLTEST_IS_NOT_TRUE,
+               &&CASE_EEOP_BOOLTEST_IS_FALSE,
+               &&CASE_EEOP_BOOLTEST_IS_NOT_FALSE,
+               &&CASE_EEOP_PARAM_EXEC,
+               &&CASE_EEOP_PARAM_EXTERN,
+               &&CASE_EEOP_CASE_TESTVAL,
+               &&CASE_EEOP_MAKE_READONLY,
+               &&CASE_EEOP_IOCOERCE,
+               &&CASE_EEOP_DISTINCT,
+               &&CASE_EEOP_NULLIF,
+               &&CASE_EEOP_SQLVALUEFUNCTION,
+               &&CASE_EEOP_CURRENTOFEXPR,
+               &&CASE_EEOP_ARRAYEXPR,
+               &&CASE_EEOP_ARRAYCOERCE,
+               &&CASE_EEOP_ROW,
+               &&CASE_EEOP_ROWCOMPARE_STEP,
+               &&CASE_EEOP_ROWCOMPARE_FINAL,
+               &&CASE_EEOP_MINMAX,
+               &&CASE_EEOP_FIELDSELECT,
+               &&CASE_EEOP_FIELDSTORE_DEFORM,
+               &&CASE_EEOP_FIELDSTORE_FORM,
+               &&CASE_EEOP_ARRAYREF_SUBSCRIPT,
+               &&CASE_EEOP_ARRAYREF_OLD,
+               &&CASE_EEOP_ARRAYREF_ASSIGN,
+               &&CASE_EEOP_ARRAYREF_FETCH,
+               &&CASE_EEOP_DOMAIN_TESTVAL,
+               &&CASE_EEOP_DOMAIN_NOTNULL,
+               &&CASE_EEOP_DOMAIN_CHECK,
+               &&CASE_EEOP_CONVERT_ROWTYPE,
+               &&CASE_EEOP_SCALARARRAYOP,
+               &&CASE_EEOP_XMLEXPR,
+               &&CASE_EEOP_AGGREF,
+               &&CASE_EEOP_GROUPING_FUNC,
+               &&CASE_EEOP_WINDOW_FUNC,
+               &&CASE_EEOP_SUBPLAN,
+               &&CASE_EEOP_ALTERNATIVE_SUBPLAN,
+               &&CASE_EEOP_LAST
+       };
+
+       StaticAssertStmt(EEOP_LAST + 1 == lengthof(dispatch_table),
+                                        "dispatch_table out of whack with ExprEvalOp");
+
+       if (unlikely(state == NULL))
+               return PointerGetDatum(dispatch_table);
+#else
+       Assert(state != NULL);
+#endif   /* EEO_USE_COMPUTED_GOTO */
+
+       /* 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();
+#endif
+
+       EEO_SWITCH()
+       {
+               EEO_CASE(EEOP_DONE)
+               {
+                       goto out;
+               }
+
+               EEO_CASE(EEOP_INNER_FETCHSOME)
+               {
+                       /* XXX: worthwhile to check tts_nvalid inline first? */
+                       slot_getsomeattrs(innerslot, op->d.fetch.last_var);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_OUTER_FETCHSOME)
+               {
+                       slot_getsomeattrs(outerslot, op->d.fetch.last_var);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_SCAN_FETCHSOME)
+               {
+                       slot_getsomeattrs(scanslot, op->d.fetch.last_var);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_INNER_VAR_FIRST)
+               {
+                       int                     attnum = op->d.var.attnum;
+
+                       /*
+                        * First time through, check whether attribute matches Var.  Might
+                        * not be ok anymore, due to schema changes.
+                        */
+                       CheckVarSlotCompatibility(innerslot, attnum + 1, op->d.var.vartype);
+
+                       /* Skip that check on subsequent evaluations */
+                       op->opcode = EEO_OPCODE(EEOP_INNER_VAR);
+
+                       /* FALL THROUGH to EEOP_INNER_VAR */
+               }
+
+               EEO_CASE(EEOP_INNER_VAR)
+               {
+                       int                     attnum = op->d.var.attnum;
+
+                       /*
+                        * Since we already extracted all referenced columns from the
+                        * tuple with a FETCHSOME step, we can just grab the value
+                        * directly out of the slot's decomposed-data arrays.  But let's
+                        * have an Assert to check that that did happen.
+                        */
+                       Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
+                       *op->resvalue = innerslot->tts_values[attnum];
+                       *op->resnull = innerslot->tts_isnull[attnum];
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_OUTER_VAR_FIRST)
+               {
+                       int                     attnum = op->d.var.attnum;
+
+                       /* See EEOP_INNER_VAR_FIRST comments */
+
+                       CheckVarSlotCompatibility(outerslot, attnum + 1, op->d.var.vartype);
+                       op->opcode = EEO_OPCODE(EEOP_OUTER_VAR);
+
+                       /* FALL THROUGH to EEOP_OUTER_VAR */
+               }
+
+               EEO_CASE(EEOP_OUTER_VAR)
+               {
+                       int                     attnum = op->d.var.attnum;
+
+                       /* See EEOP_INNER_VAR comments */
+
+                       Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
+                       *op->resvalue = outerslot->tts_values[attnum];
+                       *op->resnull = outerslot->tts_isnull[attnum];
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_SCAN_VAR_FIRST)
+               {
+                       int                     attnum = op->d.var.attnum;
+
+                       /* See EEOP_INNER_VAR_FIRST comments */
+
+                       CheckVarSlotCompatibility(scanslot, attnum + 1, op->d.var.vartype);
+                       op->opcode = EEO_OPCODE(EEOP_SCAN_VAR);
+
+                       /* FALL THROUGH to EEOP_SCAN_VAR */
+               }
+
+               EEO_CASE(EEOP_SCAN_VAR)
+               {
+                       int                     attnum = op->d.var.attnum;
+
+                       /* See EEOP_INNER_VAR comments */
+
+                       Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
+                       *op->resvalue = scanslot->tts_values[attnum];
+                       *op->resnull = scanslot->tts_isnull[attnum];
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_INNER_SYSVAR)
+               {
+                       int                     attnum = op->d.var.attnum;
+
+                       /* these asserts must match defenses in slot_getattr */
+                       Assert(innerslot->tts_tuple != NULL);
+                       Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
+                       /* heap_getsysattr has sufficient defenses against bad attnums */
+
+                       *op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
+                                                                                       innerslot->tts_tupleDescriptor,
+                                                                                       op->resnull);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_OUTER_SYSVAR)
+               {
+                       int                     attnum = op->d.var.attnum;
+
+                       /* these asserts must match defenses in slot_getattr */
+                       Assert(outerslot->tts_tuple != NULL);
+                       Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
+
+                       /* heap_getsysattr has sufficient defenses against bad attnums */
+                       *op->resvalue = heap_getsysattr(outerslot->tts_tuple, attnum,
+                                                                                       outerslot->tts_tupleDescriptor,
+                                                                                       op->resnull);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_SCAN_SYSVAR)
+               {
+                       int                     attnum = op->d.var.attnum;
+
+                       /* these asserts must match defenses in slot_getattr */
+                       Assert(scanslot->tts_tuple != NULL);
+                       Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
+                       /* heap_getsysattr has sufficient defenses against bad attnums */
+
+                       *op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
+                                                                                       scanslot->tts_tupleDescriptor,
+                                                                                       op->resnull);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_WHOLEROW)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalWholeRowVar(state, op, econtext);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_ASSIGN_INNER_VAR)
+               {
+                       int                     resultnum = op->d.assign_var.resultnum;
+                       int                     attnum = op->d.assign_var.attnum;
+
+                       /*
+                        * We do not need CheckVarSlotCompatibility here; that was taken
+                        * care of at compilation time.  But see EEOP_INNER_VAR comments.
+                        */
+                       Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
+                       resultslot->tts_values[resultnum] = innerslot->tts_values[attnum];
+                       resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum];
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_ASSIGN_OUTER_VAR)
+               {
+                       int                     resultnum = op->d.assign_var.resultnum;
+                       int                     attnum = op->d.assign_var.attnum;
+
+                       /*
+                        * We do not need CheckVarSlotCompatibility here; that was taken
+                        * care of at compilation time.  But see EEOP_INNER_VAR comments.
+                        */
+                       Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
+                       resultslot->tts_values[resultnum] = outerslot->tts_values[attnum];
+                       resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum];
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_ASSIGN_SCAN_VAR)
+               {
+                       int                     resultnum = op->d.assign_var.resultnum;
+                       int                     attnum = op->d.assign_var.attnum;
+
+                       /*
+                        * We do not need CheckVarSlotCompatibility here; that was taken
+                        * care of at compilation time.  But see EEOP_INNER_VAR comments.
+                        */
+                       Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
+                       resultslot->tts_values[resultnum] = scanslot->tts_values[attnum];
+                       resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum];
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_ASSIGN_TMP)
+               {
+                       int                     resultnum = op->d.assign_tmp.resultnum;
+
+                       resultslot->tts_values[resultnum] = state->resvalue;
+                       resultslot->tts_isnull[resultnum] = state->resnull;
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_ASSIGN_TMP_MAKE_RO)
+               {
+                       int                     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_NEXT();
+               }
+
+               EEO_CASE(EEOP_CONST)
+               {
+                       *op->resnull = op->d.constval.isnull;
+                       *op->resvalue = op->d.constval.value;
+
+                       EEO_NEXT();
+               }
+
+               /*
+                * 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!), it's worth having so many separate opcodes.
+                */
+               EEO_CASE(EEOP_FUNCEXPR)
+               {
+                       FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+                       fcinfo->isnull = false;
+                       *op->resvalue = (op->d.func.fn_addr) (fcinfo);
+                       *op->resnull = fcinfo->isnull;
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_FUNCEXPR_STRICT)
+               {
+                       FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+                       bool       *argnull = fcinfo->argnull;
+                       int                     argno;
+
+                       /* strict function, so 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_NEXT();
+               }
+
+               EEO_CASE(EEOP_FUNCEXPR_FUSAGE)
+               {
+                       FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+                       PgStat_FunctionCallUsage fcusage;
+
+                       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_NEXT();
+               }
+
+               EEO_CASE(EEOP_FUNCEXPR_STRICT_FUSAGE)
+               {
+                       FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+                       PgStat_FunctionCallUsage fcusage;
+                       bool       *argnull = fcinfo->argnull;
+                       int                     argno;
+
+                       /* strict function, so 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;
+
+                       pgstat_end_function_usage(&fcusage, true);
+
+       strictfail_fusage:
+                       EEO_NEXT();
+               }
+
+               /*
+                * If any of its clauses is FALSE, an AND's 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": perhaps one of the "don't
+                * knows" would have been FALSE if we'd known its value.  Only when
+                * all the inputs are known to be TRUE can we state confidently that
+                * the AND's result is TRUE.
+                */
+               EEO_CASE(EEOP_BOOL_AND_STEP_FIRST)
+               {
+                       *op->d.boolexpr.anynull = false;
+
+                       /*
+                        * EEOP_BOOL_AND_STEP_FIRST resets anynull, otherwise it's the
+                        * same as EEOP_BOOL_AND_STEP - so fall through to that.
+                        */
+
+                       /* FALL THROUGH */
+               }
+
+               EEO_CASE(EEOP_BOOL_AND_STEP)
+               {
+                       if (*op->resnull)
+                       {
+                               *op->d.boolexpr.anynull = true;
+                       }
+                       else if (!DatumGetBool(*op->resvalue))
+                       {
+                               /* result is already set to FALSE, need not change it */
+                               /* bail out early */
+                               EEO_JUMP(op->d.boolexpr.jumpdone);
+                       }
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_BOOL_AND_STEP_LAST)
+               {
+                       if (*op->resnull)
+                       {
+                               /* result is already set to NULL, need not change it */
+                       }
+                       else if (!DatumGetBool(*op->resvalue))
+                       {
+                               /* result is already set to FALSE, need not change it */
+
+                               /*
+                                * 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->resvalue = (Datum) 0;
+                               *op->resnull = true;
+                       }
+                       else
+                       {
+                               /* result is already set to TRUE, need not change it */
+                       }
+
+                       EEO_NEXT();
+               }
+
+               /*
+                * If any of its clauses is TRUE, an OR's 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": 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(EEOP_BOOL_OR_STEP_FIRST)
+               {
+                       *op->d.boolexpr.anynull = false;
+
+                       /*
+                        * EEOP_BOOL_OR_STEP_FIRST resets anynull, otherwise it's the same
+                        * as EEOP_BOOL_OR_STEP - so fall through to that.
+                        */
+
+                       /* FALL THROUGH */
+               }
+
+               EEO_CASE(EEOP_BOOL_OR_STEP)
+               {
+                       if (*op->resnull)
+                       {
+                               *op->d.boolexpr.anynull = true;
+                       }
+                       else if (DatumGetBool(*op->resvalue))
+                       {
+                               /* result is already set to TRUE, need not change it */
+                               /* bail out early */
+                               EEO_JUMP(op->d.boolexpr.jumpdone);
+                       }
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_BOOL_OR_STEP_LAST)
+               {
+                       if (*op->resnull)
+                       {
+                               /* result is already set to NULL, need not change it */
+                       }
+                       else if (DatumGetBool(*op->resvalue))
+                       {
+                               /* result is already set to TRUE, need not change it */
+
+                               /*
+                                * 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->resvalue = (Datum) 0;
+                               *op->resnull = true;
+                       }
+                       else
+                       {
+                               /* result is already set to FALSE, need not change it */
+                       }
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_BOOL_NOT_STEP)
+               {
+                       /*
+                        * Evaluation of 'not' is simple... if expr is false, then return
+                        * 'true' and vice versa.  It's safe to do this even on a
+                        * nominally null value, so we ignore resnull; that means that
+                        * NULL in produces NULL out, which is what we want.
+                        */
+                       *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_QUAL)
+               {
+                       /* simplified version of BOOL_AND_STEP for use by ExecQual() */
+
+                       /* If argument (also result) is false or null ... */
+                       if (*op->resnull ||
+                               !DatumGetBool(*op->resvalue))
+                       {
+                               /* ... bail out early, returning FALSE */
+                               *op->resnull = false;
+                               *op->resvalue = BoolGetDatum(false);
+                               EEO_JUMP(op->d.qualexpr.jumpdone);
+                       }
+
+                       /*
+                        * Otherwise, leave the TRUE value in place, in case this is the
+                        * last qual.  Then, TRUE is the correct answer.
+                        */
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_JUMP)
+               {
+                       /* Unconditionally jump to target step */
+                       EEO_JUMP(op->d.jump.jumpdone);
+               }
+
+               EEO_CASE(EEOP_JUMP_IF_NULL)
+               {
+                       /* Transfer control if current result is null */
+                       if (*op->resnull)
+                               EEO_JUMP(op->d.jump.jumpdone);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_JUMP_IF_NOT_NULL)
+               {
+                       /* Transfer control if current result is non-null */
+                       if (!*op->resnull)
+                               EEO_JUMP(op->d.jump.jumpdone);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_JUMP_IF_NOT_TRUE)
+               {
+                       /* Transfer control if current result is null or false */
+                       if (*op->resnull || !DatumGetBool(*op->resvalue))
+                               EEO_JUMP(op->d.jump.jumpdone);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_NULLTEST_ISNULL)
+               {
+                       *op->resvalue = BoolGetDatum(*op->resnull);
+                       *op->resnull = false;
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_NULLTEST_ISNOTNULL)
+               {
+                       *op->resvalue = BoolGetDatum(!*op->resnull);
+                       *op->resnull = false;
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_NULLTEST_ROWISNULL)
+               {
+                       /* out of line implementation: too large */
+                       ExecEvalRowNull(state, op, econtext);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_NULLTEST_ROWISNOTNULL)
+               {
+                       /* out of line implementation: too large */
+                       ExecEvalRowNotNull(state, op, econtext);
+
+                       EEO_NEXT();
+               }
+
+               /* BooleanTest implementations for all booltesttypes */
+
+               EEO_CASE(EEOP_BOOLTEST_IS_TRUE)
+               {
+                       if (*op->resnull)
+                               *op->resvalue = BoolGetDatum(false);
+                       else
+                               *op->resvalue = *op->resvalue;
+                       *op->resnull = false;
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_BOOLTEST_IS_NOT_TRUE)
+               {
+                       if (*op->resnull)
+                               *op->resvalue = BoolGetDatum(true);
+                       else
+                               *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+                       *op->resnull = false;
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_BOOLTEST_IS_FALSE)
+               {
+                       if (*op->resnull)
+                               *op->resvalue = BoolGetDatum(false);
+                       else
+                               *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+                       *op->resnull = false;
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_BOOLTEST_IS_NOT_FALSE)
+               {
+                       if (*op->resnull)
+                               *op->resvalue = BoolGetDatum(true);
+                       else
+                               *op->resvalue = *op->resvalue;
+                       *op->resnull = false;
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_PARAM_EXEC)
+               {
+                       /* out of line implementation: too large */
+                       ExecEvalParamExec(state, op, econtext);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_PARAM_EXTERN)
+               {
+                       /* out of line implementation: too large */
+                       ExecEvalParamExtern(state, op, econtext);
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_CASE_TESTVAL)
+               {
+                       /*
+                        * Normally upper parts of 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 enough to
+                        * worry about an extra 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_NEXT();
+               }
+
+               EEO_CASE(EEOP_DOMAIN_TESTVAL)
+               {
+                       /*
+                        * See EEOP_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_NEXT();
+               }
+
+               EEO_CASE(EEOP_MAKE_READONLY)
+               {
+                       /*
+                        * Force a varlena value that might be read multiple times to R/O
+                        */
+                       if (!*op->d.make_readonly.isnull)
+                               *op->resvalue =
+                                       MakeExpandedObjectReadOnlyInternal(*op->d.make_readonly.value);
+                       *op->resnull = *op->d.make_readonly.isnull;
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_IOCOERCE)
+               {
+                       /*
+                        * Evaluate a CoerceViaIO node.  This can be quite a hot path, so
+                        * inline as much work as possible.  The source value is in our
+                        * result variable.
+                        */
+                       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;
+
+                               fcinfo_out->isnull = false;
+                               str = DatumGetCString(FunctionCallInvoke(fcinfo_out));
+
+                               /* OutputFunctionCall assumes result isn't null */
+                               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] = *op->resnull;
+                               /* second and third arguments are already set up */
+
+                               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_NEXT();
+               }
+
+               EEO_CASE(EEOP_DISTINCT)
+               {
+                       /*
+                        * 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 determined.  If neither is
+                        * NULL, then proceed to evaluate the comparison function, which
+                        * is just the type's standard equality operator.  We need not
+                        * care whether that function is strict.  Because the handling of
+                        * nulls is different, we can't just reuse EEOP_FUNCEXPR.
+                        */
+                       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->resvalue = BoolGetDatum(false);
+                               *op->resnull = false;
+                       }
+                       else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+                       {
+                               /* Only one is NULL? Then is distinct... */
+                               *op->resvalue = BoolGetDatum(true);
+                               *op->resnull = false;
+                       }
+                       else
+                       {
+                               /* Neither null, so apply the equality function */
+                               Datum           eqresult;
+
+                               fcinfo->isnull = false;
+                               eqresult = (op->d.func.fn_addr) (fcinfo);
+                               /* Must invert result of "="; safe to do even if null */
+                               *op->resvalue = BoolGetDatum(!DatumGetBool(eqresult));
+                               *op->resnull = fcinfo->isnull;
+                       }
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_NULLIF)
+               {
+                       /*
+                        * The arguments are already evaluated into fcinfo->arg/argnull.
+                        */
+                       FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+                       /* if either argument is NULL they can't be equal */
+                       if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
+                       {
+                               Datum           result;
+
+                               fcinfo->isnull = false;
+                               result = (op->d.func.fn_addr) (fcinfo);
+
+                               /* if the arguments are equal return null */
+                               if (!fcinfo->isnull && DatumGetBool(result))
+                               {
+                                       *op->resvalue = (Datum) 0;
+                                       *op->resnull = true;
+
+                                       EEO_NEXT();
+                               }
+                       }
+
+                       /* Arguments aren't equal, so return the first one */
+                       *op->resvalue = fcinfo->arg[0];
+                       *op->resnull = fcinfo->argnull[0];
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_SQLVALUEFUNCTION)
+               {
+                       /*
+                        * Doesn't seem worthwhile to have an inline implementation
+                        * efficiency-wise.
+                        */
+                       ExecEvalSQLValueFunction(state, op);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_CURRENTOFEXPR)
+               {
+                       /* error invocation uses space, and shouldn't ever occur */
+                       ExecEvalCurrentOfExpr(state, op);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_ARRAYEXPR)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalArrayExpr(state, op);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_ARRAYCOERCE)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalArrayCoerce(state, op);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_ROW)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalRow(state, op);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_ROWCOMPARE_STEP)
+               {
+                       FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data;
+
+                       /* force NULL result if strict fn and NULL input */
+                       if (op->d.rowcompare_step.finfo->fn_strict &&
+                               (fcinfo->argnull[0] || fcinfo->argnull[1]))
+                       {
+                               *op->resnull = true;
+                               EEO_JUMP(op->d.rowcompare_step.jumpnull);
+                       }
+
+                       /* Apply comparison function */
+                       fcinfo->isnull = false;
+                       *op->resvalue = (op->d.rowcompare_step.fn_addr) (fcinfo);
+
+                       /* force NULL result if NULL function result */
+                       if (fcinfo->isnull)
+                       {
+                               *op->resnull = true;
+                               EEO_JUMP(op->d.rowcompare_step.jumpnull);
+                       }
+                       *op->resnull = false;
+
+                       /* If unequal, no need to compare remaining columns */
+                       if (DatumGetInt32(*op->resvalue) != 0)
+                       {
+                               EEO_JUMP(op->d.rowcompare_step.jumpdone);
+                       }
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_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_NEXT();
+               }
+
+               EEO_CASE(EEOP_MINMAX)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalMinMax(state, op);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_FIELDSELECT)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalFieldSelect(state, op, econtext);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_FIELDSTORE_DEFORM)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalFieldStoreDeForm(state, op, econtext);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_FIELDSTORE_FORM)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalFieldStoreForm(state, op, econtext);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_ARRAYREF_SUBSCRIPT)
+               {
+                       /* Process an array subscript */
+
+                       /* too complex for an inline implementation */
+                       if (ExecEvalArrayRefSubscript(state, op))
+                       {
+                               EEO_NEXT();
+                       }
+                       else
+                       {
+                               /* Subscript is null, short-circuit ArrayRef to NULL */
+                               EEO_JUMP(op->d.arrayref_subscript.jumpdone);
+                       }
+               }
+
+               EEO_CASE(EEOP_ARRAYREF_OLD)
+               {
+                       /*
+                        * Fetch the old value in an arrayref assignment, in case it's
+                        * referenced (via a CaseTestExpr) inside the assignment
+                        * expression.
+                        */
+
+                       /* too complex for an inline implementation */
+                       ExecEvalArrayRefOld(state, op);
+
+                       EEO_NEXT();
+               }
+
+               /*
+                * Perform ArrayRef assignment
+                */
+               EEO_CASE(EEOP_ARRAYREF_ASSIGN)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalArrayRefAssign(state, op);
+
+                       EEO_NEXT();
+               }
+
+               /*
+                * Fetch subset of an array.
+                */
+               EEO_CASE(EEOP_ARRAYREF_FETCH)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalArrayRefFetch(state, op);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_CONVERT_ROWTYPE)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalConvertRowtype(state, op, econtext);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_SCALARARRAYOP)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalScalarArrayOp(state, op);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_DOMAIN_NOTNULL)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalConstraintNotNull(state, op);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_DOMAIN_CHECK)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalConstraintCheck(state, op);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_XMLEXPR)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalXmlExpr(state, op);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_AGGREF)
+               {
+                       /*
+                        * Returns a Datum whose value is the precomputed aggregate value
+                        * found in the given expression context.
+                        */
+                       AggrefExprState *aggref = op->d.aggref.astate;
+
+                       Assert(econtext->ecxt_aggvalues != NULL);
+
+                       *op->resvalue = econtext->ecxt_aggvalues[aggref->aggno];
+                       *op->resnull = econtext->ecxt_aggnulls[aggref->aggno];
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_GROUPING_FUNC)
+               {
+                       /* too complex/uncommon for an inline implementation */
+                       ExecEvalGroupingFunc(state, op);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_WINDOW_FUNC)
+               {
+                       /*
+                        * Like Aggref, just return a precomputed value from the econtext.
+                        */
+                       WindowFuncExprState *wfunc = op->d.window_func.wfstate;
+
+                       Assert(econtext->ecxt_aggvalues != NULL);
+
+                       *op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno];
+                       *op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno];
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_SUBPLAN)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalSubPlan(state, op, econtext);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_ALTERNATIVE_SUBPLAN)
+               {
+                       /* too complex for an inline implementation */
+                       ExecEvalAlternativeSubPlan(state, op, econtext);
+
+                       EEO_NEXT();
+               }
+
+               EEO_CASE(EEOP_LAST)
+               {
+                       /* unreachable */
+                       Assert(false);
+                       goto out;
+               }
+       }
+
+out:
+       *isnull = state->resnull;
+       return state->resvalue;
+}
+
+/*
+ * Check whether a user attribute in a slot can be referenced by a Var
+ * expression.  This should succeed unless there have been schema changes
+ * since the expression tree has been created.
+ */
+static void
+CheckVarSlotCompatibility(TupleTableSlot *slot, int attnum, Oid vartype)
+{
+       /*
+        * 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.
+        *
+        * System attributes don't require checking since their types never
+        * change.
+        *
+        * 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)
+       {
+               TupleDesc       slot_tupdesc = slot->tts_tupleDescriptor;
+               Form_pg_attribute attr;
+
+               if (attnum > slot_tupdesc->natts)               /* should never happen */
+                       elog(ERROR, "attribute number %d exceeds number of columns %d",
+                                attnum, slot_tupdesc->natts);
+
+               attr = slot_tupdesc->attrs[attnum - 1];
+
+               /* can't check type if dropped, since atttypid is probably 0 */
+               if (!attr->attisdropped)
+               {
+                       if (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(vartype))));
+               }
+       }
+}
+
+/*
+ * 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 econtext shutdown
+ */
+static void
+ShutdownTupleDescRef(Datum arg)
+{
+       TupleDesc  *cache_field = (TupleDesc *) DatumGetPointer(arg);
+
+       if (*cache_field)
+               ReleaseTupleDesc(*cache_field);
+       *cache_field = NULL;
+}
+
+/*
+ * Fast-path functions, for very simple expressions
+ */
+
+/* Simple reference to inner Var, first time through */
+static Datum
+ExecJustInnerVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+       ExprEvalStep *op = &state->steps[1];
+       int                     attnum = op->d.var.attnum + 1;
+       TupleTableSlot *slot = econtext->ecxt_innertuple;
+
+       /* See ExecInterpExpr()'s comments for EEOP_INNER_VAR_FIRST */
+
+       CheckVarSlotCompatibility(slot, attnum, op->d.var.vartype);
+       op->opcode = EEOP_INNER_VAR;    /* just for cleanliness */
+       state->evalfunc = ExecJustInnerVar;
+
+       /*
+        * Since we use slot_getattr(), we don't need to implement the FETCHSOME
+        * step explicitly, and we also needn't Assert that the attnum is in range
+        * --- slot_getattr() will take care of any problems.
+        */
+       return slot_getattr(slot, attnum, isnull);
+}
+
+/* Simple reference to inner Var */
+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;
+
+       /* See comments in ExecJustInnerVarFirst */
+       return slot_getattr(slot, attnum, isnull);
+}
+
+/* Simple reference to outer Var, first time through */
+static Datum
+ExecJustOuterVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+       ExprEvalStep *op = &state->steps[1];
+       int                     attnum = op->d.var.attnum + 1;
+       TupleTableSlot *slot = econtext->ecxt_outertuple;
+
+       CheckVarSlotCompatibility(slot, attnum, op->d.var.vartype);
+       op->opcode = EEOP_OUTER_VAR;    /* just for cleanliness */
+       state->evalfunc = ExecJustOuterVar;
+
+       /* See comments in ExecJustInnerVarFirst */
+       return slot_getattr(slot, attnum, isnull);
+}
+
+/* Simple reference to outer Var */
+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;
+
+       /* See comments in ExecJustInnerVarFirst */
+       return slot_getattr(slot, attnum, isnull);
+}
+
+/* Simple reference to scan Var, first time through */
+static Datum
+ExecJustScanVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+       ExprEvalStep *op = &state->steps[1];
+       int                     attnum = op->d.var.attnum + 1;
+       TupleTableSlot *slot = econtext->ecxt_scantuple;
+
+       CheckVarSlotCompatibility(slot, attnum, op->d.var.vartype);
+       op->opcode = EEOP_SCAN_VAR; /* just for cleanliness */
+       state->evalfunc = ExecJustScanVar;
+
+       /* See comments in ExecJustInnerVarFirst */
+       return slot_getattr(slot, attnum, isnull);
+}
+
+/* Simple reference to scan Var */
+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;
+
+       /* See comments in ExecJustInnerVarFirst */
+       return slot_getattr(slot, attnum, isnull);
+}
+
+/* Simple Const expression */
+static Datum
+ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+       ExprEvalStep *op = &state->steps[0];
+
+       *isnull = op->d.constval.isnull;
+       return op->d.constval.value;
+}
+
+/* Evaluate inner Var and assign to appropriate column of result tuple */
+static Datum
+ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+       ExprEvalStep *op = &state->steps[1];
+       int                     attnum = op->d.assign_var.attnum + 1;
+       int                     resultnum = op->d.assign_var.resultnum;
+       TupleTableSlot *inslot = econtext->ecxt_innertuple;
+       TupleTableSlot *outslot = state->resultslot;
+
+       /*
+        * We do not need CheckVarSlotCompatibility here; that was taken care of
+        * at compilation time.
+        *
+        * Since we use slot_getattr(), we don't need to implement the FETCHSOME
+        * step explicitly, and we also needn't Assert that the attnum is in range
+        * --- slot_getattr() will take care of any problems.
+        */
+       outslot->tts_values[resultnum] =
+               slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+       return 0;
+}
+
+/* Evaluate outer Var and assign to appropriate column of result tuple */
+static Datum
+ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+       ExprEvalStep *op = &state->steps[1];
+       int                     attnum = op->d.assign_var.attnum + 1;
+       int                     resultnum = op->d.assign_var.resultnum;
+       TupleTableSlot *inslot = econtext->ecxt_outertuple;
+       TupleTableSlot *outslot = state->resultslot;
+
+       /* See comments in ExecJustAssignInnerVar */
+       outslot->tts_values[resultnum] =
+               slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+       return 0;
+}
+
+/* Evaluate scan Var and assign to appropriate column of result tuple */
+static Datum
+ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+       ExprEvalStep *op = &state->steps[1];
+       int                     attnum = op->d.assign_var.attnum + 1;
+       int                     resultnum = op->d.assign_var.resultnum;
+       TupleTableSlot *inslot = econtext->ecxt_scantuple;
+       TupleTableSlot *outslot = state->resultslot;
+
+       /* See comments in ExecJustAssignInnerVar */
+       outslot->tts_values[resultnum] =
+               slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+       return 0;
+}
+
+
+/*
+ * Do one-time initialization of interpretation machinery.
+ */
+static void
+ExecInitInterpreter(void)
+{
+#if defined(EEO_USE_COMPUTED_GOTO)
+       /* Set up externally-visible pointer to dispatch table */
+       if (dispatch_table == NULL)
+               dispatch_table = (const void **)
+                       DatumGetPointer(ExecInterpExpr(NULL, NULL, NULL));
+#endif
+}
+
+/*
+ * Function to return the opcode of an expression step.
+ *
+ * When direct-threading is in use, ExprState->opcode isn't easily
+ * decipherable. This function returns the appropriate enum member.
+ *
+ * This currently is only supposed to be used in paths that aren't critical
+ * performance-wise.  If that changes, we could add an inverse dispatch_table
+ * that's sorted on the address, so a binary search can be performed.
+ */
+ExprEvalOp
+ExecEvalStepOp(ExprState *state, ExprEvalStep *op)
+{
+#if defined(EEO_USE_COMPUTED_GOTO)
+       if (state->flags & EEO_FLAG_DIRECT_THREADED)
+       {
+               int                     i;
+
+               for (i = 0; i < EEOP_LAST; i++)
+               {
+                       if ((void *) op->opcode == dispatch_table[i])
+                       {
+                               return (ExprEvalOp) i;
+                       }
+               }
+               elog(ERROR, "unknown opcode");
+       }
+#endif
+       return (ExprEvalOp) op->opcode;
+}
+
+
+/*
+ * Out-of-line helper functions for complex instructions.
+ */
+
+/*
+ * Evaluate a PARAM_EXEC parameter.
+ *
+ * PARAM_EXEC params (internal executor parameters) are stored in the
+ * ecxt_param_exec_vals array, and can be accessed by array index.
+ */
+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))
+       {
+               /* Parameter not evaluated yet, so go do it */
+               ExecSetParamPlan(prm->execPlan, econtext);
+               /* ExecSetParamPlan should have processed this param... */
+               Assert(prm->execPlan == NULL);
+       }
+       *op->resvalue = prm->value;
+       *op->resnull = prm->isnull;
+}
+
+/*
+ * Evaluate a PARAM_EXTERN parameter.
+ *
+ * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
+ */
+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 */
+               if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
+                       (*paramInfo->paramFetch) (paramInfo, paramId);
+
+               if (likely(OidIsValid(prm->ptype)))
+               {
+                       /* safety check in case hook did something unexpected */
+                       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 a SQLValueFunction expression.
+ */
+void
+ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
+{
+       SQLValueFunction *svf = op->d.sqlvaluefunction.svf;
+       FunctionCallInfoData fcinfo;
+
+       *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;
+       }
+}
+
+/*
+ * Raise error if a CURRENT OF expression is evaluated.
+ *
+ * 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.
+ */
+void
+ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op)
+{
+       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, ExprContext *econtext)
+{
+       ExecEvalRowNullInt(state, op, econtext, true);
+}
+
+/*
+ * Evaluate NullTest / IS NOT NULL for rows.
+ */
+void
+ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+       ExecEvalRowNullInt(state, op, econtext, false);
+}
+
+/* Common code for IS [NOT] NULL on a row value */
+static void
+ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
+                                  ExprContext *econtext, 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)
+       {
+               *op->resvalue = BoolGetDatum(checkisnull);
+               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 = get_cached_rowtype(tupType, tupTypmod,
+                                                                &op->d.nulltest_row.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 (!checkisnull)
+                       {
+                               *op->resvalue = BoolGetDatum(false);
+                               return;
+                       }
+               }
+               else
+               {
+                       /* non-null field disproves IS NULL */
+                       if (checkisnull)
+                       {
+                               *op->resvalue = BoolGetDatum(false);
+                               return;
+                       }
+               }
+       }
+
+       *op->resvalue = BoolGetDatum(true);
+}
+
+/*
+ * Evaluate an ARRAY[] expression.
+ *
+ * The individual array elements (or subarrays) have already been evaluated
+ * into op->d.arrayexpr.elemvalues[]/elemnulls[].
+ */
+void
+ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
+{
+       ArrayType  *result;
+       Oid                     element_type = op->d.arrayexpr.elemtype;
+       int                     nelems = op->d.arrayexpr.nelems;
+       int                     ndims = 0;
+       int                     dims[MAXDIM];
+       int                     lbs[MAXDIM];
+
+       /* Set non-null as default */
+       *op->resnull = false;
+
+       if (!op->d.arrayexpr.multidims)
+       {
+               /* Elements are presumably of scalar type */
+               Datum      *dvalues = op->d.arrayexpr.elemvalues;
+               bool       *dnulls = op->d.arrayexpr.elemnulls;
+
+               /* 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 */
+               ndims = 1;
+               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++)
+               {
+                       Datum           arraydatum;
+                       bool            eisnull;
+                       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.
+ *
+ * Source array is in step's result variable.
+ */
+void
+ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
+{
+       ArrayCoerceExpr *acoerce = op->d.arraycoerce.coerceexpr;
+       Datum           arraydatum;
+       FunctionCallInfoData locfcinfo;
+
+       /* NULL array -> NULL result */
+       if (*op->resnull)
+               return;
+
+       arraydatum = *op->resvalue;
+
+       /*
+        * 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(arraydatum);
+
+               ARR_ELEMTYPE(array) = op->d.arraycoerce.resultelemtype;
+               *op->resvalue = PointerGetDatum(array);
+               return;
+       }
+
+       /*
+        * 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] = arraydatum;
+       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 a ROW() expression.
+ *
+ * The individual columns have already been evaluated into
+ * op->d.row.elemvalues[]/elemnulls[].
+ */
+void
+ExecEvalRow(ExprState *state, ExprEvalStep *op)
+{
+       HeapTuple       tuple;
+
+       /* 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);
+       *op->resnull = false;
+}
+
+/*
+ * Evaluate GREATEST() or LEAST() 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)
+{
+       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;
+       int                     off;
+
+       /* set at initialization */
+       Assert(fcinfo->argnull[0] == false);
+       Assert(fcinfo->argnull[1] == false);
+
+       /* default to null result */
+       *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->arg[0] = *op->resvalue;
+                       fcinfo->arg[1] = values[off];
+
+                       fcinfo->isnull = false;
+                       cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo));
+                       if (fcinfo->isnull) /* probably should not happen */
+                               continue;
+
+                       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.
+ *
+ * Source record is in step's result variable.
+ */
+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;
+
+       /* NULL record -> NULL result */
+       if (*op->resnull)
+               return;
+
+       /* Get the composite datum and extract its type fields */
+       tupDatum = *op->resvalue;
+       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 CheckVarSlotCompatibility, 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;
+
+       /* extract the field */
+       *op->resvalue = heap_getattr(&tmptup,
+                                                                fieldnum,
+                                                                tupDesc,
+                                                                op->resnull);
+}
+
+/*
+ * Deform source tuple, filling in the step's values/nulls arrays, before
+ * evaluating individual new values as part of a FieldStore expression.
+ * Subsequent steps will overwrite individual elements of the values/nulls
+ * arrays with the new field values, and then FIELDSTORE_FORM will build the
+ * new tuple value.
+ *
+ * Source record is in step's result variable.
+ */
+void
+ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+       TupleDesc       tupDesc;
+
+       /* Lookup tupdesc if first time through or after rescan */
+       tupDesc = get_cached_rowtype(op->d.fieldstore.fstore->resulttype, -1,
+                                                                op->d.fieldstore.argdesc, econtext);
+
+       /* Check that current tupdesc doesn't have more fields than we allocated */
+       if (unlikely(tupDesc->natts > op->d.fieldstore.ncolumns))
+               elog(ERROR, "too many columns in composite type %u",
+                        op->d.fieldstore.fstore->resulttype);
+
+       if (*op->resnull)
+       {
+               /* Convert null input tuple into an all-nulls row */
+               memset(op->d.fieldstore.nulls, true,
+                          op->d.fieldstore.ncolumns * 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, tupDesc,
+                                                 op->d.fieldstore.values,
+                                                 op->d.fieldstore.nulls);
+       }
+}
+
+/*
+ * Compute the new composite datum after each individual field value of a
+ * FieldStore expression has been evaluated.
+ */
+void
+ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+       HeapTuple       tuple;
+
+       /* argdesc should already be valid from the DeForm step */
+       tuple = heap_form_tuple(*op->d.fieldstore.argdesc,
+                                                       op->d.fieldstore.values,
+                                                       op->d.fieldstore.nulls);
+
+       *op->resvalue = HeapTupleGetDatum(tuple);
+       *op->resnull = false;
+}
+
+/*
+ * Process a subscript in an ArrayRef expression.
+ *
+ * If subscript is NULL, throw error in assignment case, or in fetch case
+ * set result to NULL and return false (instructing caller to skip the rest
+ * of the ArrayRef sequence).
+ *
+ * Subscript expression result is in subscriptvalue/subscriptnull.
+ * On success, integer subscript value has been saved in upperindex[] or
+ * lowerindex[] for use later.
+ */
+bool
+ExecEvalArrayRefSubscript(ExprState *state, ExprEvalStep *op)
+{
+       ArrayRefState *arefstate = op->d.arrayref_subscript.state;
+       int                *indexes;
+       int                     off;
+
+       /* If any index expr yields NULL, result is NULL or error */
+       if (arefstate->subscriptnull)
+       {
+               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, save in appropriate place */
+       if (op->d.arrayref_subscript.isupper)
+               indexes = arefstate->upperindex;
+       else
+               indexes = arefstate->lowerindex;
+       off = op->d.arrayref_subscript.off;
+
+       indexes[off] = DatumGetInt32(arefstate->subscriptvalue);
+
+       return true;
+}
+
+/*
+ * Evaluate ArrayRef fetch.
+ *
+ * Source array is in step's result variable.
+ */
+void
+ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op)
+{
+       ArrayRefState *arefstate = op->d.arrayref.state;
+
+       /* Should not get here if source array (or any subscript) is null */
+       Assert(!(*op->resnull));
+
+       if (arefstate->numlower == 0)
+       {
+               /* Scalar case */
+               *op->resvalue = array_get_element(*op->resvalue,
+                                                                                 arefstate->numupper,
+                                                                                 arefstate->upperindex,
+                                                                                 arefstate->refattrlength,
+                                                                                 arefstate->refelemlength,
+                                                                                 arefstate->refelembyval,
+                                                                                 arefstate->refelemalign,
+                                                                                 op->resnull);
+       }
+       else
+       {
+               /* Slice case */
+               *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);
+       }
+}
+
+/*
+ * Compute old array element/slice value for an ArrayRef assignment
+ * expression.  Will only be generated if the new-value subexpression
+ * contains ArrayRef or FieldStore.  The value is stored into the
+ * ArrayRefState's prevvalue/prevnull fields.
+ */
+void
+ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op)
+{
+       ArrayRefState *arefstate = op->d.arrayref.state;
+
+       if (*op->resnull)
+       {
+               /* whole array is null, so any element or slice is too */
+               arefstate->prevvalue = (Datum) 0;
+               arefstate->prevnull = true;
+       }
+       else if (arefstate->numlower == 0)
+       {
+               /* Scalar case */
+               arefstate->prevvalue = array_get_element(*op->resvalue,
+                                                                                                arefstate->numupper,
+                                                                                                arefstate->upperindex,
+                                                                                                arefstate->refattrlength,
+                                                                                                arefstate->refelemlength,
+                                                                                                arefstate->refelembyval,
+                                                                                                arefstate->refelemalign,
+                                                                                                &arefstate->prevnull);
+       }
+       else
+       {
+               /* Slice case */
+               /* 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);
+               arefstate->prevnull = false;
+       }
+}
+
+/*
+ * Evaluate ArrayRef assignment.
+ *
+ * Input array (possibly null) is in result area, replacement value is in
+ * ArrayRefState's replacevalue/replacenull.
+ */
+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)       /* fixed-length array? */
+       {
+               if (*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)
+       {
+               /* Scalar case */
+               *op->resvalue = array_set_element(*op->resvalue,
+                                                                                 arefstate->numupper,
+                                                                                 arefstate->upperindex,
+                                                                                 arefstate->replacevalue,
+                                                                                 arefstate->replacenull,
+                                                                                 arefstate->refattrlength,
+                                                                                 arefstate->refelemlength,
+                                                                                 arefstate->refelembyval,
+                                                                                 arefstate->refelemalign);
+       }
+       else
+       {
+               /* Slice case */
+               *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);
+       }
+}
+
+/*
+ * Evaluate a rowtype coercion operation.
+ * This may require rearranging field positions.
+ *
+ * Source record is in step's result variable.
+ */
+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;
+
+       /* NULL in -> NULL out */
+       if (*op->resnull)
+               return;
+
+       tupDatum = *op->resvalue;
+       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 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);
+}
+
+/*
+ * Evaluate "scalar op ANY/ALL (array)".
+ *
+ * Source array is in our result area, scalar arg is already evaluated into
+ * fcinfo->arg[0]/argnull[0].
+ *
+ * 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)
+{
+       FunctionCallInfo fcinfo = op->d.scalararrayop.fcinfo_data;
+       bool            useOr = op->d.scalararrayop.useOr;
+       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;
+
+       /* Else 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->resvalue = BoolGetDatum(!useOr);
+               *op->resnull = false;
+               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;
+
+       /* Initialize result appropriately depending on useOr */
+       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->resvalue = result;
+       *op->resnull = resultnull;
+}
+
+/*
+ * Evaluate a NOT NULL domain 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 domain 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.
+ *
+ * Arguments have been evaluated into named_argvalue/named_argnull
+ * and/or argvalue/argnull arrays.
+ */
+void
+ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
+{
+       XmlExpr    *xexpr = op->d.xmlexpr.xexpr;
+       Datum           value;
+       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 (values != NIL)
+                               {
+                                       *op->resvalue = PointerGetDatum(xmlconcat(values));
+                                       *op->resnull = false;
+                               }
+                       }
+                       break;
+
+               case IS_XMLFOREST:
+                       {
+                               Datum      *argvalue = op->d.xmlexpr.named_argvalue;
+                               bool       *argnull = op->d.xmlexpr.named_argnull;
+                               StringInfoData buf;
+                               ListCell   *lc;
+                               ListCell   *lc2;
+
+                               initStringInfo(&buf);
+
+                               i = 0;
+                               forboth(lc, xexpr->named_args, lc2, xexpr->arg_names)
+                               {
+                                       Expr       *e = (Expr *) lfirst(lc);
+                                       char       *argname = strVal(lfirst(lc2));
+
+                                       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)
+                               {
+                                       text       *result;
+
+                                       result = cstring_to_text_with_len(buf.data, buf.len);
+                                       *op->resvalue = PointerGetDatum(result);
+                               }
+
+                               pfree(buf.data);
+                       }
+                       break;
+
+               case IS_XMLELEMENT:
+                       *op->resvalue = PointerGetDatum(xmlelement(xexpr,
+                                                                                               op->d.xmlexpr.named_argvalue,
+                                                                                                op->d.xmlexpr.named_argnull,
+                                                                                                          op->d.xmlexpr.argvalue,
+                                                                                                        op->d.xmlexpr.argnull));
+                       *op->resnull = false;
+                       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->resvalue = PointerGetDatum(xmlparse(data,
+                                                                                                                xexpr->xmloption,
+                                                                                                          preserve_whitespace));
+                               *op->resnull = false;
+                       }
+                       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->resvalue = PointerGetDatum(xmlroot(data,
+                                                                                                               version,
+                                                                                                               standalone));
+                               *op->resnull = false;
+                       }
+                       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->resvalue = PointerGetDatum(
+                                                               xmltotext_with_xmloption(DatumGetXmlP(value),
+                                                                                                                xexpr->xmloption));
+                               *op->resnull = false;
+                       }
+                       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])
+                                       return;
+                               value = argvalue[0];
+
+                               *op->resvalue =
+                                       BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
+                               *op->resnull = false;
+                       }
+                       break;
+
+               default:
+                       elog(ERROR, "unrecognized XML operation");
+                       break;
+       }
+}
+
+/*
+ * 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;
+       Bitmapset  *grouped_cols = op->d.grouping_func.parent->grouped_cols;
+       ListCell   *lc;
+
+       foreach(lc, op->d.grouping_func.clauses)
+       {
+               int                     attnum = lfirst_int(lc);
+
+               result <<= 1;
+
+               if (!bms_is_member(attnum, grouped_cols))
+                       result |= 1;
+       }
+
+       *op->resvalue = Int32GetDatum(result);
+       *op->resnull = false;
+}
+
+/*
+ * 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;
+       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);
+
+       /*
+        * If first time through, obtain tuple descriptor and check compatibility.
+        *
+        * XXX: It'd be great if this could be moved to the expression
+        * initialization phase, but due to using slots that's currently not
+        * feasible.
+        */
+       if (op->d.wholerow.first)
+       {
+               /* optimistically assume we don't need slow path */
+               op->d.wholerow.slow = false;
+
+               /*
+                * 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 the slow path 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)
+                                       op->d.wholerow.slow = true; /* need to check for nulls */
+                       }
+
+                       /*
+                        * 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;
+       }
+
+       /*
+        * Make sure all columns of the slot are accessible in the slot's
+        * Datum/isnull arrays.
+        */
+       slot_getallattrs(slot);
+
+       if (op->d.wholerow.slow)
+       {
+               /* Check to see if any dropped attributes are non-null */
+               TupleDesc       tupleDesc = slot->tts_tupleDescriptor;
+               TupleDesc       var_tupdesc = op->d.wholerow.tupdesc;
+               int                     i;
+
+               Assert(var_tupdesc->natts == tupleDesc->natts);
+
+               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 (slot->tts_isnull[i])
+                               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.
+        *
+        * (The intermediate copy is a tad annoying here, but we currently have no
+        * primitive that will do the right thing.  Note it is critical that we
+        * not change the slot's state, so we can't use ExecFetchSlotTupleDatum.)
+        */
+       tuple = ExecCopySlotTuple(slot);
+       dtuple = (HeapTupleHeader)
+               DatumGetPointer(heap_copy_tuple_as_datum(tuple,
+                                                                                                slot->tts_tupleDescriptor));
+       heap_freetuple(tuple);
+
+       /*
+        * 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);
+}
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;
                }
 
index c28cf9c8eab7f2b678918f62a29429c40648e4d3..f2995f2e7ba2242eb1c7e8246a09745fe53757c1 100644 (file)
@@ -600,8 +600,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 
        /*
         * Only plain-relation RTEs need to be checked here.  Function RTEs are
-        * checked by init_fcache when the function is prepared for execution.
-        * Join, subquery, and special RTEs need no checks.
+        * checked when the function is prepared for execution.  Join, subquery,
+        * and special RTEs need no checks.
         */
        if (rte->rtekind != RTE_RELATION)
                return true;
@@ -1275,8 +1275,8 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
                resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
                        palloc0(n * sizeof(FmgrInfo));
-               resultRelInfo->ri_TrigWhenExprs = (List **)
-                       palloc0(n * sizeof(List *));
+               resultRelInfo->ri_TrigWhenExprs = (ExprState **)
+                       palloc0(n * sizeof(ExprState *));
                if (instrument_options)
                        resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
        }
@@ -1723,7 +1723,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
        ConstrCheck *check = rel->rd_att->constr->check;
        ExprContext *econtext;
        MemoryContext oldContext;
-       List       *qual;
        int                     i;
 
        /*
@@ -1735,13 +1734,14 @@ 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 */
-                       qual = make_ands_implicit(stringToNode(check[i].ccbin));
-                       resultRelInfo->ri_ConstraintExprs[i] = (List *)
-                               ExecPrepareExpr((Expr *) qual, estate);
+                       Expr       *checkconstr;
+
+                       checkconstr = stringToNode(check[i].ccbin);
+                       resultRelInfo->ri_ConstraintExprs[i] =
+                               ExecPrepareExpr(checkconstr, estate);
                }
                MemoryContextSwitchTo(oldContext);
        }
@@ -1758,14 +1758,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
        /* And evaluate the constraints */
        for (i = 0; i < ncheck; i++)
        {
-               qual = resultRelInfo->ri_ConstraintExprs[i];
+               ExprState  *checkconstr = 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(checkconstr, econtext))
                        return check[i].ccname;
        }
 
@@ -1793,8 +1793,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);
        }
 
        /*
@@ -1810,7 +1809,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);
 }
 
 /*
@@ -1990,11 +1989,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..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
-/*-------------------------------------------------------------------------
- *
- * execQual.c
- *       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
- *
- *
- * IDENTIFICATION
- *       src/backend/executor/execQual.c
- *
- *-------------------------------------------------------------------------
- */
-/*
- *      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"
-#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 "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"
-#include "utils/xml.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 ExecEvalFuncArgs(FunctionCallInfo fcinfo,
-                                List *argList, ExprContext *econtext);
-static void ExecPrepareTuplestoreResult(FuncExprState *fcache,
-                                                       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).
- *
- * 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.
- *----------
- */
-static Datum
-ExecEvalArrayRef(ArrayRefExprState *astate,
-                                ExprContext *econtext,
-                                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;
-
-       array_source = ExecEvalExpr(astate->refexpr,
-                                                               econtext,
-                                                               isNull);
-
-       /*
-        * 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;
-       }
-
-       foreach(l, astate->refupperindexpr)
-       {
-               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;
-               }
-       }
-
-       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;
-
-       if (isAssignment)
-       {
-               Datum           sourceData;
-               Datum           save_datum;
-               bool            save_isNull;
-
-               /*
-                * 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;
-
-               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;
-               }
-
-               /*
-                * Evaluate the value to be assigned into the array.
-                */
-               sourceData = ExecEvalExpr(astate->refassgnexpr,
-                                                                 econtext,
-                                                                 &eisnull);
-
-               econtext->caseValue_datum = save_datum;
-               econtext->caseValue_isNull = save_isNull;
-
-               /*
-                * 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;
-
-               /*
-                * 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 (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 (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);
-}
-
-/*
- * 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))
-       {
-               FieldStore *fstore = (FieldStore *) exprstate->expr;
-
-               if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
-                       return true;
-       }
-       else if (IsA(exprstate, ArrayRefExprState))
-       {
-               ArrayRef   *arrayRef = (ArrayRef *) exprstate->expr;
-
-               if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
-                       return true;
-       }
-       return false;
-}
-
-/* ----------------------------------------------------------------
- *             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");
-
-       *isNull = econtext->ecxt_aggnulls[aggref->aggno];
-       return econtext->ecxt_aggvalues[aggref->aggno];
-}
-
-/* ----------------------------------------------------------------
- *             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");
-
-       *isNull = econtext->ecxt_aggnulls[wfunc->wfuncno];
-       return econtext->ecxt_aggvalues[wfunc->wfuncno];
-}
-
-/* ----------------------------------------------------------------
- *             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.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
-                                 bool *isNull)
-{
-       Var                *variable = (Var *) exprstate->expr;
-       TupleTableSlot *slot;
-       AttrNumber      attnum;
-
-       /* 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 */
-
-               default:                                /* get the tuple from the relation being
-                                                                * scanned */
-                       slot = econtext->ecxt_scantuple;
-                       break;
-       }
-
-       attnum = variable->varattno;
-
-       /* This was checked by ExecInitExpr */
-       Assert(attnum != InvalidAttrNumber);
-
-       /*
-        * 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)
-       {
-               TupleDesc       slot_tupdesc = slot->tts_tupleDescriptor;
-               Form_pg_attribute attr;
-
-               if (attnum > slot_tupdesc->natts)               /* should never happen */
-                       elog(ERROR, "attribute number %d exceeds number of columns %d",
-                                attnum, slot_tupdesc->natts);
-
-               attr = slot_tupdesc->attrs[attnum - 1];
-
-               /* can't check type if dropped, since atttypid is probably 0 */
-               if (!attr->attisdropped)
-               {
-                       if (variable->vartype != attr->atttypid)
-                               ereport(ERROR,
-                                               (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))));
-               }
-       }
-
-       /* Skip the checking on future executions of node */
-       exprstate->evalfunc = ExecEvalScalarVarFast;
-
-       /* Fetch the value from the slot */
-       return slot_getattr(slot, attnum, isNull);
-}
-
-/* ----------------------------------------------------------------
- *             ExecEvalScalarVarFast
- *
- *             Returns a Datum for a scalar variable.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
-                                         bool *isNull)
-{
-       Var                *variable = (Var *) exprstate->expr;
-       TupleTableSlot *slot;
-       AttrNumber      attnum;
-
-       /* 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 */
-
-               default:                                /* get the tuple from the relation being
-                                                                * scanned */
-                       slot = econtext->ecxt_scantuple;
-                       break;
-       }
-
-       attnum = variable->varattno;
-
-       /* Fetch the value from the slot */
-       return slot_getattr(slot, attnum, isNull);
-}
-
-/* ----------------------------------------------------------------
- *             ExecEvalWholeRowVar
- *
- *             Returns a Datum 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.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-                                       bool *isNull)
-{
-       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);
-
-       /* 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;
-       }
-
-       /*
-        * 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);
-                       }
-               }
-       }
-
-       /* Apply the junkfilter if any */
-       if (wrvstate->wrv_junkFilter != NULL)
-               slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
-
-       /*
-        * 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 */
-       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)
-       {
-               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);
-
-       /*
-        * 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
- *
- *             Returns a Datum for a whole-row variable, in the "slow" case where
- *             we can't just copy the subplan's output.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-                                        bool *isNull)
-{
-       Var                *variable = (Var *) wrvstate->xprstate.expr;
-       TupleTableSlot *slot;
-       HeapTuple       tuple;
-       TupleDesc       tupleDesc;
-       TupleDesc       var_tupdesc;
-       HeapTupleHeader dtuple;
-       int                     i;
-
-       *isNull = false;
-
-       /* 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 (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;
-
-       /* 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, wrvstate->wrv_tupdesc->tdtypeid);
-       HeapTupleHeaderSetTypMod(dtuple, wrvstate->wrv_tupdesc->tdtypmod);
-
-       return PointerGetDatum(dtuple);
-}
-
-/* ----------------------------------------------------------------
- *             ExecEvalConst
- *
- *             Returns the value of a constant.
- *
- *             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.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
-                         bool *isNull)
-{
-       Const      *con = (Const *) exprstate->expr;
-
-       *isNull = con->constisnull;
-       return con->constvalue;
-}
-
-/* ----------------------------------------------------------------
- *             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;
-
-       /*
-        * PARAM_EXEC params (internal executor parameters) are stored in the
-        * ecxt_param_exec_vals array, and can be accessed by array index.
-        */
-       prm = &(econtext->ecxt_param_exec_vals[thisParamId]);
-       if (prm->execPlan != NULL)
-       {
-               /* Parameter not evaluated yet, so go do it */
-               ExecSetParamPlan(prm->execPlan, econtext);
-               /* ExecSetParamPlan should have processed this param... */
-               Assert(prm->execPlan == NULL);
-       }
-       *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.
-        */
-       if (paramInfo &&
-               thisParamId > 0 && thisParamId <= paramInfo->numParams)
-       {
-               ParamExternData *prm = &paramInfo->params[thisParamId - 1];
-
-               /* give hook a chance in case parameter is dynamic */
-               if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
-                       (*paramInfo->paramFetch) (paramInfo, thisParamId);
-
-               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))));
-
-                       *isNull = prm->isnull;
-                       return prm->value;
-               }
-       }
-
-       ereport(ERROR,
-                       (errcode(ERRCODE_UNDEFINED_OBJECT),
-                        errmsg("no value found for parameter %d", thisParamId)));
-       return (Datum) 0;                       /* keep compiler quiet */
-}
-
-
-/* ----------------------------------------------------------------
- *             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 (isNull == NULL)
-               elog(ERROR, "a NULL isNull pointer was passed");
-
-       if (tuple == NULL)
-       {
-               /* Kinda bogus but compatible with old behavior... */
-               *isNull = true;
-               return (Datum) 0;
-       }
-
-       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);
-
-       return result;
-}
-
-Datum
-GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
-{
-       AttrNumber      attrno;
-       Datum           result;
-       Oid                     tupType;
-       int32           tupTypmod;
-       TupleDesc       tupDesc;
-       HeapTupleData tmptup;
-       int                     i;
-
-       if (attname == NULL)
-               elog(ERROR, "invalid attribute name");
-
-       if (isNull == NULL)
-               elog(ERROR, "a NULL isNull pointer was passed");
-
-       if (tuple == NULL)
-       {
-               /* Kinda bogus but compatible with old behavior... */
-               *isNull = true;
-               return (Datum) 0;
-       }
-
-       tupType = HeapTupleHeaderGetTypeId(tuple);
-       tupTypmod = HeapTupleHeaderGetTypMod(tuple);
-       tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
-
-       attrno = InvalidAttrNumber;
-       for (i = 0; i < tupDesc->natts; i++)
-       {
-               if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0)
-               {
-                       attrno = tupDesc->attrs[i]->attnum;
-                       break;
-               }
-       }
-
-       if (attrno == InvalidAttrNumber)
-               elog(ERROR, "attribute \"%s\" does not exist", attname);
-
-       /*
-        * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set all
-        * the fields in the struct just in case user tries to inspect system
-        * columns.
-        */
-       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);
-
-       return result;
-}
-
-/*
- * init_fcache - initialize a FuncExprState node during first use
- */
-static void
-init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
-                       MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF)
-{
-       AclResult       aclresult;
-
-       /* 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);
-
-       /*
-        * Safety check on nargs.  Under normal circumstances this should never
-        * fail, as parser should check sooner.  But possibly it might fail if
-        * server has been compiled with FUNC_MAX_ARGS smaller than some functions
-        * declared in pg_proc?
-        */
-       if (list_length(fcache->args) > FUNC_MAX_ARGS)
-               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 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")));
-
-       /* Otherwise, ExecInitExpr should have marked the fcache correctly */
-       Assert(fcache->func.fn_retset == fcache->funcReturnsSet);
-
-       /* If function returns set, prepare expected tuple descriptor */
-       if (fcache->func.fn_retset && needDescForSRF)
-       {
-               TypeFuncClass functypclass;
-               Oid                     funcrettype;
-               TupleDesc       tupdesc;
-               MemoryContext oldcontext;
-
-               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;
-
-                                       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);
-
-                                       /*
-                                        * 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);
-
-                               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);
-
-                               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);
-
-                               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);
-
-                               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);
-
-                               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);
-
-                               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);
-
-                               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:
-                       {
-                               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;
-                       }
-                       break;
-               case T_AlternativeSubPlan:
-                       {
-                               AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
-                               AlternativeSubPlanState *asstate;
-
-                               if (!parent)
-                                       elog(ERROR, "AlternativeSubPlan found with no parent plan");
-
-                               asstate = ExecInitAlternativeSubPlan(asplan, parent);
-
-                               state = (ExprState *) asstate;
-                       }
-                       break;
-               case T_FieldSelect:
-                       {
-                               FieldSelect *fselect = (FieldSelect *) node;
-                               FieldSelectState *fstate = makeNode(FieldSelectState);
-
-                               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);
-
-                               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);
-
-                               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;
-
-                               iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO;
-                               iostate->arg = ExecInitExpr(iocoerce->arg, parent);
-                               /* lookup the result type's input function */
-                               getTypeInputInfo(iocoerce->resulttype, &iofunc,
-                                                                &iostate->intypioparam);
-                               fmgr_info(iofunc, &iostate->infunc);
-                               /* lookup the input type's output function */
-                               getTypeOutputInfo(exprType((Node *) iocoerce->arg),
-                                                                 &iofunc, &typisvarlena);
-                               fmgr_info(iofunc, &iostate->outfunc);
-                               state = (ExprState *) iostate;
-                       }
-                       break;
-               case T_ArrayCoerceExpr:
-                       {
-                               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
-                               ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
-
-                               astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
-                               astate->arg = ExecInitExpr(acoerce->arg, parent);
-                               astate->resultelemtype = get_element_type(acoerce->resulttype);
-                               if (astate->resultelemtype == InvalidOid)
-                                       ereport(ERROR,
-                                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                        errmsg("target type is not an array")));
-                               /* Arrays over domains aren't supported yet */
-                               Assert(getBaseType(astate->resultelemtype) ==
-                                          astate->resultelemtype);
-                               astate->elemfunc.fn_oid = InvalidOid;   /* not initialized */
-                               astate->amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState));
-                               state = (ExprState *) astate;
-                       }
-                       break;
-               case T_ConvertRowtypeExpr:
-                       {
-                               ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
-                               ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
-
-                               cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
-                               cstate->arg = ExecInitExpr(convert->arg, parent);
-                               state = (ExprState *) cstate;
-                       }
-                       break;
-               case T_CaseExpr:
-                       {
-                               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)
-                               {
-                                       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);
-                               }
-                               cstate->args = outlist;
-                               cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
-                               if (caseexpr->arg)
-                                       cstate->argtyplen = get_typlen(exprType((Node *) caseexpr->arg));
-                               state = (ExprState *) cstate;
-                       }
-                       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:
-                       {
-                               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)
-                               {
-                                       Expr       *e = (Expr *) lfirst(l);
-                                       ExprState  *estate;
-
-                                       if (!attrs[i]->attisdropped)
-                                       {
-                                               /*
-                                                * Guard against ALTER COLUMN TYPE on rowtype since
-                                                * the RowExpr was created.  XXX should we check
-                                                * typmod too?  Not sure we can be sure it'll be the
-                                                * same.
-                                                */
-                                               if (exprType((Node *) e) != attrs[i]->atttypid)
-                                                       ereport(ERROR,
-                                                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                                        errmsg("ROW() column has type %s instead of type %s",
-                                                                               format_type_be(exprType((Node *) e)),
-                                                                          format_type_be(attrs[i]->atttypid))));
-                                       }
-                                       else
-                                       {
-                                               /*
-                                                * Ignore original expression and insert a NULL. We
-                                                * don't really care what type of NULL it is, so
-                                                * always make an int4 NULL.
-                                                */
-                                               e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
-                                       }
-                                       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.
-                                        */
-                                       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;
-
-                                       estate = ExecInitExpr(e, parent);
-                                       outlist = lappend(outlist, estate);
-                               }
-                               xstate->named_args = outlist;
-
-                               outlist = NIL;
-                               foreach(arg, xexpr->args)
-                               {
-                                       Expr       *e = (Expr *) lfirst(arg);
-                                       ExprState  *estate;
-
-                                       estate = ExecInitExpr(e, parent);
-                                       outlist = lappend(outlist, estate);
-                               }
-                               xstate->args = outlist;
-
-                               state = (ExprState *) xstate;
-                       }
-                       break;
-               case T_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:
-                       {
-                               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;
-                       }
-                       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;
-                       }
-                       break;
-               case T_List:
-                       {
-                               List       *outlist = NIL;
-                               ListCell   *l;
-
-                               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;
-       }
-
-       /* 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;
-
-       /*
-        * debugging stuff
-        */
-       EV_printf("ExecQual: qual is ");
-       EV_nodeDisplay(qual);
-       EV_printf("\n");
-
-       /*
-        * Run in short-lived per-tuple context while computing expressions.
-        */
-       oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-       /*
-        * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-        * we can stop evaluating and return FALSE --- the AND result must be
-        * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-        * can stop and return FALSE --- the AND result must be FALSE or NULL in
-        * that case, and the caller doesn't care which.
-        *
-        * If we get to the end of the list, we can return TRUE.  This will happen
-        * when the AND result is indeed TRUE, or when the AND result is NULL (one
-        * or more NULL subresult, with all the rest TRUE) and the caller has
-        * specified resultForNull = TRUE.
-        */
-       result = true;
-
-       foreach(l, qual)
-       {
-               ExprState  *clause = (ExprState *) lfirst(l);
-               Datum           expr_value;
-               bool            isNull;
-
-               expr_value = ExecEvalExpr(clause, econtext, &isNull);
-
-               if (isNull)
-               {
-                       if (resultForNull == false)
-                       {
-                               result = false; /* treat NULL as FALSE */
-                               break;
-                       }
-               }
-               else
-               {
-                       if (!DatumGetBool(expr_value))
-                       {
-                               result = false; /* definitely FALSE */
-                               break;
-                       }
-               }
-       }
-
-       MemoryContextSwitchTo(oldContext);
-
-       return result;
-}
-
-/*
- * Number of items in a tlist (including any resjunk items!)
- */
-int
-ExecTargetListLength(List *targetlist)
-{
-       /* This used to be more complex, but fjoins are dead */
-       return list_length(targetlist);
-}
-
-/*
- * Number of items in a tlist, not including any resjunk items
- */
-int
-ExecCleanTargetListLength(List *targetlist)
-{
-       int                     len = 0;
-       ListCell   *tl;
-
-       foreach(tl, targetlist)
-       {
-               TargetEntry *curTle = castNode(TargetEntry, lfirst(tl));
-
-               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);
-}
diff --git a/src/backend/executor/execSRF.c b/src/backend/executor/execSRF.c
new file mode 100644 (file)
index 0000000..4badd5c
--- /dev/null
@@ -0,0 +1,924 @@
+/*-------------------------------------------------------------------------
+ *
+ * execSRF.c
+ *       Routines implementing the API for set-returning functions
+ *
+ * This file serves nodeFunctionscan.c and nodeProjectSet.c, providing
+ * common code for calling set-returning functions according to the
+ * ReturnSetInfo API.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/executor/execSRF.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "catalog/objectaccess.h"
+#include "executor/execdebug.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+
+
+/* static function decls */
+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(SetExprState *sexpr,
+                                                       ExprContext *econtext,
+                                                       Tuplestorestate *resultStore,
+                                                       TupleDesc resultDesc);
+static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
+
+
+/*
+ * Prepare function call in FROM (ROWS FROM) for execution.
+ *
+ * This is used by nodeFunctionscan.c.
+ */
+SetExprState *
+ExecInitTableFunctionResult(Expr *expr,
+                                                       ExprContext *econtext, PlanState *parent)
+{
+       SetExprState *state = makeNode(SetExprState);
+
+       state->funcReturnsSet = false;
+       state->expr = expr;
+       state->func.fn_oid = InvalidOid;
+
+       /*
+        * 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.  That code path will not
+        * support set-returning functions buried in the expression, though.
+        */
+       if (IsA(expr, FuncExpr))
+       {
+               FuncExpr   *func = (FuncExpr *) expr;
+
+               state->funcReturnsSet = func->funcretset;
+               state->args = ExecInitExprList(func->args, parent);
+
+               init_sexpr(func->funcid, func->inputcollid, state,
+                                  econtext->ecxt_per_query_memory, func->funcretset, false);
+       }
+       else
+       {
+               state->elidedFuncState = ExecInitExpr(expr, parent);
+       }
+
+       return state;
+}
+
+/*
+ *             ExecMakeTableFunctionResult
+ *
+ * Evaluate a table function, producing a materialized result in a Tuplestore
+ * object.
+ *
+ * This is used by nodeFunctionscan.c.
+ */
+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;
+
+       callerContext = CurrentMemoryContext;
+
+       funcrettype = exprType((Node *) setexpr->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 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);
+
+               /*
+                * 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);
+
+               /*
+                * 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;
+
+                       for (i = 0; i < fcinfo.nargs; i++)
+                       {
+                               if (fcinfo.argnull[i])
+                                       goto no_function_result;
+                       }
+               }
+       }
+       else
+       {
+               /* Treat setexpr as a generic expression */
+               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 (!setexpr->elidedFuncState)
+               {
+                       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(setexpr->elidedFuncState, 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;
+}
+
+
+/*
+ * Prepare targetlist SRF function call for execution.
+ *
+ * This is used by nodeProjectSet.c.
+ */
+SetExprState *
+ExecInitFunctionResultSet(Expr *expr,
+                                                 ExprContext *econtext, PlanState *parent)
+{
+       SetExprState *state = makeNode(SetExprState);
+
+       state->funcReturnsSet = true;
+       state->expr = expr;
+       state->func.fn_oid = InvalidOid;
+
+       /*
+        * Initialize metadata.  The expression node could be either a FuncExpr or
+        * an OpExpr.
+        */
+       if (IsA(expr, FuncExpr))
+       {
+               FuncExpr   *func = (FuncExpr *) expr;
+
+               state->args = ExecInitExprList(func->args, parent);
+               init_sexpr(func->funcid, func->inputcollid, state,
+                                  econtext->ecxt_per_query_memory, true, true);
+       }
+       else if (IsA(expr, OpExpr))
+       {
+               OpExpr     *op = (OpExpr *) expr;
+
+               state->args = ExecInitExprList(op->args, parent);
+               init_sexpr(op->opfuncid, op->inputcollid, state,
+                                  econtext->ecxt_per_query_memory, true, true);
+       }
+       else
+               elog(ERROR, "unrecognized node type: %d",
+                        (int) nodeTag(expr));
+
+       /* shouldn't get here unless the selected function returns set */
+       Assert(state->func.fn_retset);
+
+       return state;
+}
+
+/*
+ *             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).
+ *
+ * This is used by nodeProjectSet.c.
+ */
+Datum
+ExecMakeFunctionResultSet(SetExprState *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();
+
+       /*
+        * 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,
+                                                                                               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;
+}
+
+
+/*
+ * init_sexpr - initialize a SetExprState node during first use
+ */
+static void
+init_sexpr(Oid foid, Oid input_collation, SetExprState *sexpr,
+                  MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF)
+{
+       AclResult       aclresult;
+
+       /* 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);
+
+       /*
+        * 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)));
+
+       /* Set up the primary fmgr lookup information */
+       fmgr_info_cxt(foid, &(sexpr->func), sexprCxt);
+       fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
+
+       /* Initialize the function call parameter struct as well */
+       InitFunctionCallInfoData(sexpr->fcinfo_data, &(sexpr->func),
+                                                        list_length(sexpr->args),
+                                                        input_collation, NULL, NULL);
+
+       /* 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")));
+
+       /* Otherwise, caller should have marked the sexpr correctly */
+       Assert(sexpr->func.fn_retset == sexpr->funcReturnsSet);
+
+       /* If function returns set, prepare expected tuple descriptor */
+       if (sexpr->func.fn_retset && needDescForSRF)
+       {
+               TypeFuncClass functypclass;
+               Oid                     funcrettype;
+               TupleDesc       tupdesc;
+               MemoryContext oldcontext;
+
+               functypclass = get_expr_result_type(sexpr->func.fn_expr,
+                                                                                       &funcrettype,
+                                                                                       &tupdesc);
+
+               /* Must save tupdesc in sexpr's context */
+               oldcontext = MemoryContextSwitchTo(sexprCxt);
+
+               if (functypclass == TYPEFUNC_COMPOSITE)
+               {
+                       /* 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;
+               }
+
+               MemoryContextSwitchTo(oldcontext);
+       }
+       else
+               sexpr->funcResultDesc = NULL;
+
+       /* Initialize additional state */
+       sexpr->funcResultStore = NULL;
+       sexpr->funcResultSlot = NULL;
+       sexpr->shutdown_reg = false;
+}
+
+/*
+ * callback function in case a SetExprState needs to be shut down before it
+ * has been run to completion
+ */
+static void
+ShutdownSetExpr(Datum arg)
+{
+       SetExprState *sexpr = castNode(SetExprState, DatumGetPointer(arg));
+
+       /* If we have a slot, make sure it's let go of any tuplestore pointer */
+       if (sexpr->funcResultSlot)
+               ExecClearTuple(sexpr->funcResultSlot);
+
+       /* Release any open tuplestore */
+       if (sexpr->funcResultStore)
+               tuplestore_end(sexpr->funcResultStore);
+       sexpr->funcResultStore = NULL;
+
+       /* Clear any active set-argument state */
+       sexpr->setArgsValid = false;
+
+       /* execUtils will deregister the callback... */
+       sexpr->shutdown_reg = false;
+}
+
+/*
+ * 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(SetExprState *sexpr,
+                                                       ExprContext *econtext,
+                                                       Tuplestorestate *resultStore,
+                                                       TupleDesc resultDesc)
+{
+       sexpr->funcResultStore = resultStore;
+
+       if (sexpr->funcResultSlot == NULL)
+       {
+               /* Create a slot so we can read data out of the tuplestore */
+               TupleDesc       slotDesc;
+               MemoryContext oldcontext;
+
+               oldcontext = MemoryContextSwitchTo(sexpr->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 (sexpr->funcResultDesc)
+                       slotDesc = sexpr->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 */
+               }
+
+               sexpr->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 (sexpr->funcResultDesc)
+                       tupledesc_match(sexpr->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 (!sexpr->shutdown_reg)
+       {
+               RegisterExprContextCallback(econtext,
+                                                                       ShutdownSetExpr,
+                                                                       PointerGetDatum(sexpr));
+               sexpr->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)));
+       }
+}
index 65196795d7004831e6959e3cef610b8928796471..4f131b3ee0df60619e1e7b332470f875092b0731 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;
                }
@@ -183,11 +183,11 @@ ExecScan(ScanState *node,
                /*
                 * check that the current tuple satisfies the qual-clause
                 *
-                * check for non-nil qual here to avoid a function call to ExecQual()
-                * when the qual is nil ... saves only a few cycles, but they add up
+                * check for non-null qual here to avoid a function call to ExecQual()
+                * when the qual is null ... saves only a few cycles, but they add up
                 * ...
                 */
-               if (!qual || ExecQual(qual, econtext, false))
+               if (qual == NULL || ExecQual(qual, econtext))
                {
                        /*
                         * Found a satisfactory scan tuple.
index a72cffeb6e5c8de949e02ffc78ef757b9fd138fc..2613ffbb718a6fc9ddbbbfb77da791648b2aacc8 100644 (file)
@@ -31,6 +31,9 @@
  *             RegisterExprContextCallback    Register function shutdown callback
  *             UnregisterExprContextCallback  Deregister function shutdown callback
  *
+ *             GetAttributeByName              Runtime extraction of columns from tuples.
+ *             GetAttributeByNum
+ *
  *      NOTES
  *             This file has traditionally been the place to stick misc.
  *             executor support stuff that doesn't really go anyplace else.
 #include "nodes/nodeFuncs.h"
 #include "parser/parsetree.h"
 #include "storage/lmgr.h"
+#include "utils/builtins.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
+#include "utils/typcache.h"
 
 
-static bool get_last_attnums(Node *node, ProjectionInfo *projInfo);
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -464,186 +468,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 */
-                       get_last_attnums((Node *) variable, projInfo);
-               }
-       }
-       projInfo->pi_targetlist = exprlist;
-       projInfo->pi_numSimpleVars = numSimpleVars;
-       projInfo->pi_directMap = directMap;
-
-       return projInfo;
-}
-
-/*
- * get_last_attnums: expression walker for ExecBuildProjectionInfo
- *
- *     Update the lastXXXVar counts to be at least as large as the largest
- *     attribute numbers found in the expression
- */
-static bool
-get_last_attnums(Node *node, ProjectionInfo *projInfo)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, Var))
-       {
-               Var                *variable = (Var *) node;
-               AttrNumber      attnum = variable->varattno;
-
-               switch (variable->varno)
-               {
-                       case INNER_VAR:
-                               if (projInfo->pi_lastInnerVar < attnum)
-                                       projInfo->pi_lastInnerVar = attnum;
-                               break;
-
-                       case OUTER_VAR:
-                               if (projInfo->pi_lastOuterVar < attnum)
-                                       projInfo->pi_lastOuterVar = attnum;
-                               break;
-
-                               /* INDEX_VAR is handled by default case */
-
-                       default:
-                               if (projInfo->pi_lastScanVar < attnum)
-                                       projInfo->pi_lastScanVar = attnum;
-                               break;
-               }
-               return false;
-       }
-
-       /*
-        * Don't examine the arguments or filters of Aggrefs or WindowFuncs,
-        * because those do not represent expressions to be evaluated within the
-        * overall targetlist's econtext.  GroupingFunc arguments are never
-        * evaluated at all.
-        */
-       if (IsA(node, Aggref))
-               return false;
-       if (IsA(node, WindowFunc))
-               return false;
-       if (IsA(node, GroupingFunc))
-               return false;
-       return expression_tree_walker(node, get_last_attnums,
-                                                                 (void *) projInfo);
-}
 
 /* ----------------
  *             ExecAssignProjectionInfo
@@ -659,9 +483,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
                                                 TupleDesc inputDesc)
 {
        planstate->ps_ProjInfo =
-               ExecBuildProjectionInfo(planstate->targetlist,
+               ExecBuildProjectionInfo(planstate->plan->targetlist,
                                                                planstate->ps_ExprContext,
                                                                planstate->ps_ResultTupleSlot,
+                                                               planstate,
                                                                inputDesc);
 }
 
@@ -1009,3 +834,152 @@ ExecLockNonLeafAppendTables(List *partitioned_rels, EState *estate)
                }
        }
 }
+
+/*
+ *             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
+GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
+{
+       AttrNumber      attrno;
+       Datum           result;
+       Oid                     tupType;
+       int32           tupTypmod;
+       TupleDesc       tupDesc;
+       HeapTupleData tmptup;
+       int                     i;
+
+       if (attname == NULL)
+               elog(ERROR, "invalid attribute name");
+
+       if (isNull == NULL)
+               elog(ERROR, "a NULL isNull pointer was passed");
+
+       if (tuple == NULL)
+       {
+               /* Kinda bogus but compatible with old behavior... */
+               *isNull = true;
+               return (Datum) 0;
+       }
+
+       tupType = HeapTupleHeaderGetTypeId(tuple);
+       tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+       tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+
+       attrno = InvalidAttrNumber;
+       for (i = 0; i < tupDesc->natts; i++)
+       {
+               if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0)
+               {
+                       attrno = tupDesc->attrs[i]->attnum;
+                       break;
+               }
+       }
+
+       if (attrno == InvalidAttrNumber)
+               elog(ERROR, "attribute \"%s\" does not exist", attname);
+
+       /*
+        * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set all
+        * the fields in the struct just in case user tries to inspect system
+        * columns.
+        */
+       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);
+
+       return result;
+}
+
+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 (isNull == NULL)
+               elog(ERROR, "a NULL isNull pointer was passed");
+
+       if (tuple == NULL)
+       {
+               /* Kinda bogus but compatible with old behavior... */
+               *isNull = true;
+               return (Datum) 0;
+       }
+
+       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);
+
+       return result;
+}
+
+/*
+ * Number of items in a tlist (including any resjunk items!)
+ */
+int
+ExecTargetListLength(List *targetlist)
+{
+       /* This used to be more complex, but fjoins are dead */
+       return list_length(targetlist);
+}
+
+/*
+ * Number of items in a tlist, not including any resjunk items
+ */
+int
+ExecCleanTargetListLength(List *targetlist)
+{
+       int                     len = 0;
+       ListCell   *tl;
+
+       foreach(tl, targetlist)
+       {
+               TargetEntry *curTle = castNode(TargetEntry, lfirst(tl));
+
+               if (!curTle->resjunk)
+                       len++;
+       }
+       return len;
+}
index 65e52cca630f40fec1d28875f1904f90d69e5a98..3e4b0191c7ecdef3005ee2bd590875dbe54db8a3 100644 (file)
@@ -1279,7 +1279,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
                        rsi->returnMode = SFRM_Materialize;
                        rsi->setResult = fcache->tstore;
                        fcache->tstore = NULL;
-                       /* must copy desc because execQual will free it */
+                       /* must copy desc because execSRF.c will free it */
                        if (fcache->junkFilter)
                                rsi->setDesc = CreateTupleDescCopy(fcache->junkFilter->jf_cleanTupType);
 
index 3207ee460c270290dd305bdf46720f3d9b56b9be..471acc4b3ec91db7a4c5a6670dbed526cc244560 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
@@ -2501,18 +2501,17 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
        /*
         * initialize child expressions
         *
-        * Note: ExecInitExpr finds Aggrefs for us, and also checks that no aggs
-        * contain other agg calls in their arguments.  This would make no sense
-        * 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.
+        * We rely on the parser to have checked that no aggs contain other agg
+        * calls in their arguments.  This would make no sense under SQL semantics
+        * (and it's forbidden by the spec).  Because it is true, we don't need to
+        * worry about evaluating the aggs in any particular order.
+        *
+        * Note: execExpr.c finds Aggrefs for us, and adds their AggrefExprState
+        * nodes to aggstate->aggs.  Aggrefs in the qual are found here; Aggrefs
+        * in the targetlist are found during ExecAssignProjectionInfo, below.
         */
-       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.
@@ -2540,7 +2539,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
        ExecAssignProjectionInfo(&aggstate->ss.ps, NULL);
 
        /*
-        * get the count of aggregates in targetlist and quals
+        * We should now have found all Aggrefs in the targetlist and quals.
         */
        numaggs = aggstate->numaggs;
        Assert(numaggs == list_length(aggstate->aggs));
@@ -2724,7 +2723,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 +3023,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 +3204,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..5d309828ef185616b8d8c3afdbac6c954f9ad61d 100644 (file)
@@ -49,12 +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,
-                                        (PlanState *) css);
+       css->ss.ps.qual =
+               ExecInitQual(cscan->scan.plan.qual, (PlanState *) css);
 
        /* tuple table initialization */
        ExecInitScanTupleSlot(estate, &css->ss);
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..426527d2a2ac7a54aac1e8e9bbf3cd0c36cf52c0 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,12 +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,
-                                        (PlanState *) scanstate);
+       scanstate->ss.ps.qual =
+               ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
        scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
@@ -361,7 +357,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..3f0c3ee4d180187a16b84b509b85b6c2320f4133 100644 (file)
@@ -86,12 +86,8 @@ 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..f2c885afbe2dfbdfb7d39ef0ec9dde55fb967ca4 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,14 @@ 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, linitial(fstate->args));
-               rclauses = lappend(rclauses, lsecond(fstate->args));
+               lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args),
+                                                                                                 (PlanState *) hjstate));
+               rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args),
+                                                                                                 (PlanState *) hjstate));
                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 29c6a6e1d8d6124902f259788b14edc085e324c5..0b524e0b7cfb1634707f5ace7477dade752db42d 100644 (file)
@@ -1152,7 +1152,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;
@@ -1271,7 +1271,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);
@@ -1646,7 +1646,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;
@@ -1778,7 +1777,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
                foreach(ll, 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);
@@ -1818,8 +1817,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
                        foreach(ll, mapped_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);
                        }
@@ -1852,8 +1851,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.
@@ -1862,11 +1862,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++;
                }
@@ -1883,16 +1881,14 @@ 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,
+                               ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
                                                                         resultRelInfo->ri_RelationDesc->rd_att);
                        resultRelInfo++;
                }
@@ -1922,7 +1918,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 */
@@ -1948,11 +1943,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 */
@@ -1960,10 +1954,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..01048cc8268e0b59239e2a60163f90d7f8ba8327 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "executor/executor.h"
 #include "executor/nodeProjectSet.h"
+#include "nodes/nodeFuncs.h"
 #include "utils/memutils.h"
 
 
@@ -119,10 +120,9 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
 {
        TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot;
        ExprContext *econtext = node->ps.ps_ExprContext;
-       bool            hassrf PG_USED_FOR_ASSERTS_ONLY = false;
+       bool            hassrf PG_USED_FOR_ASSERTS_ONLY;
        bool            hasresult;
        int                     argno;
-       ListCell   *lc;
 
        ExecClearTuple(resultSlot);
 
@@ -132,11 +132,10 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
         */
        node->pending_srf_tuples = false;
 
-       hasresult = false;
-       argno = 0;
-       foreach(lc, node->ps.targetlist)
+       hassrf = hasresult = false;
+       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,9 @@ 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 +200,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 +227,7 @@ 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);
+       /* We don't support any qual on ProjectSet nodes */
        Assert(node->plan.qual == NIL);
 
        /*
@@ -252,11 +245,41 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
         */
        ExecAssignResultTypeFromTL(&state->ps);
 
-       /* Create workspace for per-SRF is-done state */
+       /* Create workspace for per-tlist-entry expr state & 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.  We can't use
+        * ExecBuildProjectionInfo here, since that doesn't deal with SRFs.
+        * Instead compile each expression separately, 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
+               {
+                       Assert(!expression_returns_set((Node *) expr));
+                       state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
+               }
+
+               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..b3a025879a1fdc38bde9f9b0203039e0ea2c6697 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) */
@@ -792,35 +783,33 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
                 * use the sub-select's output tuples directly, but that is not the
                 * case if we had to insert any run-time coercions of the sub-select's
                 * output datatypes; anyway this avoids storing any resjunk columns
-                * that might be in the sub-select's output.) Run through the
+                * that might be in the sub-select's output.)  Run through the
                 * combining expressions to build tlists for the lefthand and
-                * righthand sides.  We need both the ExprState list (for ExecProject)
-                * and the underlying parse Exprs (for ExecTypeFromTL).
+                * righthand sides.
                 *
                 * 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;
+                       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 +817,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 +872,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,
                                                                                                   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 +910,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 +1087,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 +1138,22 @@ 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)
+       {
+               SubPlan    *sp = castNode(SubPlan, lfirst(lc));
+               SubPlanState *sps = ExecInitSubPlan(sp, parent);
+
+               asstate->subplans = lappend(asstate->subplans, sps);
+               parent->subPlan = lappend(parent->subPlan, sps);
+       }
 
        /*
         * Select the one to be used.  For this, we need an estimate of the number
@@ -1209,7 +1191,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..e9df48044e36c6ed61f10ee85c885fce5d6622c2 100644 (file)
@@ -139,12 +139,8 @@ 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 +175,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..4860ec0f4dac077fea06abff3fbbd127d40db639 100644 (file)
         ((Var *) (node))->varattno == SelfItemPointerAttributeNumber && \
         ((Var *) (node))->varlevelsup == 0)
 
-static void TidListCreate(TidScanState *tidstate);
+/* one element in tss_tidexprs */
+typedef struct TidExpr
+{
+       ExprState  *exprstate;          /* ExprState for a TID-yielding subexpr */
+       bool            isarray;                /* if true, it yields tid[] not just tid */
+       CurrentOfExpr *cexpr;           /* alternatively, we can have CURRENT OF */
+} TidExpr;
+
+static void TidExprListCreate(TidScanState *tidstate);
+static void TidListEval(TidScanState *tidstate);
 static int     itemptr_comparator(const void *a, const void *b);
 static TupleTableSlot *TidNext(TidScanState *node);
 
 
+/*
+ * Extract the qual subexpressions that yield TIDs to search for,
+ * and compile them into ExprStates if they're ordinary expressions.
+ *
+ * CURRENT OF is a special case that we can't compile usefully;
+ * just drop it into the TidExpr list as-is.
+ */
+static void
+TidExprListCreate(TidScanState *tidstate)
+{
+       TidScan    *node = (TidScan *) tidstate->ss.ps.plan;
+       ListCell   *l;
+
+       tidstate->tss_tidexprs = NIL;
+       tidstate->tss_isCurrentOf = false;
+
+       foreach(l, node->tidquals)
+       {
+               Expr       *expr = (Expr *) lfirst(l);
+               TidExpr    *tidexpr = (TidExpr *) palloc0(sizeof(TidExpr));
+
+               if (is_opclause(expr))
+               {
+                       Node       *arg1;
+                       Node       *arg2;
+
+                       arg1 = get_leftop(expr);
+                       arg2 = get_rightop(expr);
+                       if (IsCTIDVar(arg1))
+                               tidexpr->exprstate = ExecInitExpr((Expr *) arg2,
+                                                                                                 &tidstate->ss.ps);
+                       else if (IsCTIDVar(arg2))
+                               tidexpr->exprstate = ExecInitExpr((Expr *) arg1,
+                                                                                                 &tidstate->ss.ps);
+                       else
+                               elog(ERROR, "could not identify CTID variable");
+                       tidexpr->isarray = false;
+               }
+               else if (expr && IsA(expr, ScalarArrayOpExpr))
+               {
+                       ScalarArrayOpExpr *saex = (ScalarArrayOpExpr *) expr;
+
+                       Assert(IsCTIDVar(linitial(saex->args)));
+                       tidexpr->exprstate = ExecInitExpr(lsecond(saex->args),
+                                                                                         &tidstate->ss.ps);
+                       tidexpr->isarray = true;
+               }
+               else if (expr && IsA(expr, CurrentOfExpr))
+               {
+                       CurrentOfExpr *cexpr = (CurrentOfExpr *) expr;
+
+                       tidexpr->cexpr = cexpr;
+                       tidstate->tss_isCurrentOf = true;
+               }
+               else
+                       elog(ERROR, "could not identify CTID expression");
+
+               tidstate->tss_tidexprs = lappend(tidstate->tss_tidexprs, tidexpr);
+       }
+
+       /* CurrentOfExpr could never appear OR'd with something else */
+       Assert(list_length(tidstate->tss_tidexprs) == 1 ||
+                  !tidstate->tss_isCurrentOf);
+}
+
 /*
  * Compute the list of TIDs to be visited, by evaluating the expressions
  * for them.
@@ -50,9 +124,8 @@ static TupleTableSlot *TidNext(TidScanState *node);
  * (The result is actually an array, not a list.)
  */
 static void
-TidListCreate(TidScanState *tidstate)
+TidListEval(TidScanState *tidstate)
 {
-       List       *evalList = tidstate->tss_tidquals;
        ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
        BlockNumber nblocks;
        ItemPointerData *tidList;
@@ -73,36 +146,21 @@ TidListCreate(TidScanState *tidstate)
         * are simple OpExprs or CurrentOfExprs.  If there are any
         * ScalarArrayOpExprs, we may have to enlarge the array.
         */
-       numAllocTids = list_length(evalList);
+       numAllocTids = list_length(tidstate->tss_tidexprs);
        tidList = (ItemPointerData *)
                palloc(numAllocTids * sizeof(ItemPointerData));
        numTids = 0;
-       tidstate->tss_isCurrentOf = false;
 
-       foreach(l, evalList)
+       foreach(l, tidstate->tss_tidexprs)
        {
-               ExprState  *exstate = (ExprState *) lfirst(l);
-               Expr       *expr = exstate->expr;
+               TidExpr    *tidexpr = (TidExpr *) lfirst(l);
                ItemPointer itemptr;
                bool            isNull;
 
-               if (is_opclause(expr))
+               if (tidexpr->exprstate && !tidexpr->isarray)
                {
-                       FuncExprState *fexstate = (FuncExprState *) exstate;
-                       Node       *arg1;
-                       Node       *arg2;
-
-                       arg1 = get_leftop(expr);
-                       arg2 = get_rightop(expr);
-                       if (IsCTIDVar(arg1))
-                               exstate = (ExprState *) lsecond(fexstate->args);
-                       else if (IsCTIDVar(arg2))
-                               exstate = (ExprState *) linitial(fexstate->args);
-                       else
-                               elog(ERROR, "could not identify CTID variable");
-
                        itemptr = (ItemPointer)
-                               DatumGetPointer(ExecEvalExprSwitchContext(exstate,
+                               DatumGetPointer(ExecEvalExprSwitchContext(tidexpr->exprstate,
                                                                                                                  econtext,
                                                                                                                  &isNull));
                        if (!isNull &&
@@ -119,9 +177,8 @@ TidListCreate(TidScanState *tidstate)
                                tidList[numTids++] = *itemptr;
                        }
                }
-               else if (expr && IsA(expr, ScalarArrayOpExpr))
+               else if (tidexpr->exprstate && tidexpr->isarray)
                {
-                       ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate;
                        Datum           arraydatum;
                        ArrayType  *itemarray;
                        Datum      *ipdatums;
@@ -129,8 +186,7 @@ TidListCreate(TidScanState *tidstate)
                        int                     ndatums;
                        int                     i;
 
-                       exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
-                       arraydatum = ExecEvalExprSwitchContext(exstate,
+                       arraydatum = ExecEvalExprSwitchContext(tidexpr->exprstate,
                                                                                                   econtext,
                                                                                                   &isNull);
                        if (isNull)
@@ -159,12 +215,12 @@ TidListCreate(TidScanState *tidstate)
                        pfree(ipdatums);
                        pfree(ipnulls);
                }
-               else if (expr && IsA(expr, CurrentOfExpr))
+               else
                {
-                       CurrentOfExpr *cexpr = (CurrentOfExpr *) expr;
                        ItemPointerData cursor_tid;
 
-                       if (execCurrentOf(cexpr, econtext,
+                       Assert(tidexpr->cexpr);
+                       if (execCurrentOf(tidexpr->cexpr, econtext,
                                                   RelationGetRelid(tidstate->ss.ss_currentRelation),
                                                          &cursor_tid))
                        {
@@ -176,11 +232,8 @@ TidListCreate(TidScanState *tidstate)
                                                                 numAllocTids * sizeof(ItemPointerData));
                                }
                                tidList[numTids++] = cursor_tid;
-                               tidstate->tss_isCurrentOf = true;
                        }
                }
-               else
-                       elog(ERROR, "could not identify CTID expression");
        }
 
        /*
@@ -272,11 +325,15 @@ TidNext(TidScanState *node)
         * First time through, compute the list of TIDs to be visited
         */
        if (node->tss_TidList == NULL)
-               TidListCreate(node);
+               TidListEval(node);
 
        tidList = node->tss_TidList;
        numTids = node->tss_NumTids;
 
+       /*
+        * We use node->tss_htup as the tuple pointer; note this can't just be a
+        * local variable here, as the scan tuple slot will keep a pointer to it.
+        */
        tuple = &(node->tss_htup);
 
        /*
@@ -470,16 +527,10 @@ 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);
+
+       TidExprListCreate(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 a129d1ecb3b2f94b912e3500428b782f3123c1b4..57229059bda29b53bce5bffd06e5724009c9ec54 100644 (file)
@@ -3501,7 +3501,7 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
                /*
                 * Aggref and WindowFunc nodes are (and should be) treated like Vars,
                 * ie, zero execution cost in the current model, because they behave
-                * essentially like Vars in execQual.c.  We disregard the costs of
+                * essentially like Vars at execution.  We disregard the costs of
                 * their input expressions for the same reason.  The actual execution
                 * costs of the aggregate/window functions and their arguments have to
                 * be factored into plan-node-specific costing of the Agg or WindowAgg
index 68d74cb4324ca3e172fce96de92034c2ef2e2bf7..90619509a2aa7d320a390929ea282a1e9b03fdf0 100644 (file)
@@ -5013,7 +5013,7 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
  * bloat the sort dataset, and because it might cause unexpected output order
  * if the sort isn't stable.  However there's a constraint on that: all SRFs
  * in the tlist should be evaluated at the same plan step, so that they can
- * run in sync in ExecTargetList.  So if any SRFs are in sort columns, we
+ * run in sync in nodeProjectSet.  So if any SRFs are in sort columns, we
  * mustn't postpone any SRFs.  (Note that in principle that policy should
  * probably get applied to the group/window input targetlists too, but we
  * have not done that historically.)  Lastly, expensive expressions are
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..73deaa7e1cf2d84b7db730d67a51b8abd67e7ce6 100644 (file)
@@ -107,7 +107,7 @@ domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
        fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, mcxt);
 
        /* Look up constraints for domain */
-       InitDomainConstraintRef(domainType, &my_extra->constraint_ref, mcxt);
+       InitDomainConstraintRef(domainType, &my_extra->constraint_ref, mcxt, true);
 
        /* We don't make an ExprContext until needed */
        my_extra->econtext = NULL;
@@ -122,7 +122,9 @@ 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 roughly similar to the handling of CoerceToDomain nodes in
+ * execExpr*.c, but we execute each constraint separately, rather than
+ * compiling them in-line within a larger expression.
  */
 static void
 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
@@ -149,9 +151,6 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
                                break;
                        case DOM_CONSTRAINT_CHECK:
                                {
-                                       Datum           conResult;
-                                       bool            conIsNull;
-
                                        /* Make the econtext if we didn't already */
                                        if (econtext == NULL)
                                        {
@@ -165,24 +164,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,
-                                                                                                                 econtext,
-                                                                                                                 &conIsNull);
-
-                                       if (!conIsNull &&
-                                               !DatumGetBool(conResult))
+                                       if (!ExecCheck(con->check_exprstate, econtext))
                                                ereport(ERROR,
                                                                (errcode(ERRCODE_CHECK_VIOLATION),
                                                                 errmsg("value for domain %s violates check constraint \"%s\"",
index 81c91039e40925a155c5b4794e5b61516a2523b4..d57d5568b2800c698c7e5d68a03bef32de4bd367 100644 (file)
@@ -7000,7 +7000,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;
 
@@ -7041,7 +7041,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..2f87151beccc7da1be09b077558303d860cac77a 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,11 @@ xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg)
 
 
 xmltype *
-xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
+xmlelement(XmlExpr *xexpr,
+                  Datum *named_argvalue, bool *named_argnull,
+                  Datum *argvalue, bool *argnull)
 {
 #ifdef USE_LIBXML
-       XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
        xmltype    *result;
        List       *named_arg_strings;
        List       *arg_strings;
@@ -635,48 +635,47 @@ 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, and their values are passed in the
+        * named_argvalue/named_argnull or argvalue/argnull arrays.  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.  We examine the
+        * original XmlExpr to identify the numbers and types of the arguments.
         */
        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 */
        xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
 
        PG_TRY();
index 6992634c397095d87d0f384b492d2ebbcd691264..0cf5001a758175a8f0dc86f64bbc5a1a1ed28aeb 100644 (file)
@@ -96,11 +96,11 @@ static TypeCacheEntry *firstDomainTypeEntry = NULL;
  * this struct for the common case of a constraint-less domain; we just set
  * domainData to NULL to indicate that.
  *
- * Within a DomainConstraintCache, we abuse the DomainConstraintState node
- * type a bit: check_expr fields point to expression plan trees, not plan
- * state trees.  When needed, expression state trees are built by flat-copying
- * the DomainConstraintState nodes and applying ExecInitExpr to check_expr.
- * Such a state tree is not part of the DomainConstraintCache, but is
+ * Within a DomainConstraintCache, we store expression plan trees, but the
+ * check_exprstate fields of the DomainConstraintState nodes are just NULL.
+ * When needed, expression evaluation nodes are built by flat-copying the
+ * DomainConstraintState nodes and applying ExecInitExpr to check_expr.
+ * Such a node tree is not part of the DomainConstraintCache, but is
  * considered to belong to a DomainConstraintRef.
  */
 struct DomainConstraintCache
@@ -779,8 +779,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 +859,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 +947,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);
        }
@@ -962,13 +963,18 @@ prep_domain_constraints(List *constraints, MemoryContext execctx)
  *
  * Caller must tell us the MemoryContext in which the DomainConstraintRef
  * lives.  The ref will be cleaned up when that context is reset/deleted.
+ *
+ * Caller must also tell us whether it wants check_exprstate fields to be
+ * computed in the DomainConstraintState nodes attached to this ref.
+ * If it doesn't, we need not make a copy of the DomainConstraintState list.
  */
 void
 InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref,
-                                               MemoryContext refctx)
+                                               MemoryContext refctx, bool need_exprstate)
 {
        /* Look up the typcache entry --- we assume it survives indefinitely */
        ref->tcache = lookup_type_cache(type_id, TYPECACHE_DOMAIN_INFO);
+       ref->need_exprstate = need_exprstate;
        /* For safety, establish the callback before acquiring a refcount */
        ref->refctx = refctx;
        ref->dcc = NULL;
@@ -980,8 +986,11 @@ InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref,
        {
                ref->dcc = ref->tcache->domainData;
                ref->dcc->dccRefCount++;
-               ref->constraints = prep_domain_constraints(ref->dcc->constraints,
-                                                                                                  ref->refctx);
+               if (ref->need_exprstate)
+                       ref->constraints = prep_domain_constraints(ref->dcc->constraints,
+                                                                                                          ref->refctx);
+               else
+                       ref->constraints = ref->dcc->constraints;
        }
        else
                ref->constraints = NIL;
@@ -1032,8 +1041,11 @@ UpdateDomainConstraintRef(DomainConstraintRef *ref)
                {
                        ref->dcc = dcc;
                        dcc->dccRefCount++;
-                       ref->constraints = prep_domain_constraints(dcc->constraints,
-                                                                                                          ref->refctx);
+                       if (ref->need_exprstate)
+                               ref->constraints = prep_domain_constraints(dcc->constraints,
+                                                                                                                  ref->refctx);
+                       else
+                               ref->constraints = dcc->constraints;
                }
        }
 }
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
new file mode 100644 (file)
index 0000000..a665388
--- /dev/null
@@ -0,0 +1,642 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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"
+
+/* forward reference to avoid circularity */
+struct ArrayRefState;
+
+/* Bits in ExprState->flags (see also execnodes.h for public flag bits): */
+/* expression's interpreter has been initialized */
+#define EEO_FLAG_INTERPRETER_INITIALIZED       (1 << 1)
+/* jump-threading is in use */
+#define EEO_FLAG_DIRECT_THREADED                       (1 << 2)
+
+/*
+ * Discriminator for ExprEvalSteps.
+ *
+ * Identifies the operation to be executed and which member in the
+ * ExprEvalStep->d union is valid.
+ *
+ * The order of entries needs to be kept in sync with the dispatch_table[]
+ * array in execExprInterp.c:ExecInterpExpr().
+ */
+typedef enum ExprEvalOp
+{
+       /* entire expression has been evaluated completely, return */
+       EEOP_DONE,
+
+       /* apply slot_getsomeattrs on corresponding tuple slot */
+       EEOP_INNER_FETCHSOME,
+       EEOP_OUTER_FETCHSOME,
+       EEOP_SCAN_FETCHSOME,
+
+       /* compute non-system Var value */
+       /* "FIRST" variants are used only the first time through */
+       EEOP_INNER_VAR_FIRST,
+       EEOP_INNER_VAR,
+       EEOP_OUTER_VAR_FIRST,
+       EEOP_OUTER_VAR,
+       EEOP_SCAN_VAR_FIRST,
+       EEOP_SCAN_VAR,
+
+       /* compute system Var value */
+       EEOP_INNER_SYSVAR,
+       EEOP_OUTER_SYSVAR,
+       EEOP_SCAN_SYSVAR,
+
+       /* compute wholerow Var */
+       EEOP_WHOLEROW,
+
+       /* compute non-system Var value, assign it into ExprState's resultslot */
+       /* (these are not used if _FIRST checks would be needed) */
+       EEOP_ASSIGN_INNER_VAR,
+       EEOP_ASSIGN_OUTER_VAR,
+       EEOP_ASSIGN_SCAN_VAR,
+
+       /* assign ExprState's resvalue/resnull to a column of its resultslot */
+       EEOP_ASSIGN_TMP,
+       /* ditto, applying MakeExpandedObjectReadOnly() */
+       EEOP_ASSIGN_TMP_MAKE_RO,
+
+       /* evaluate Const value */
+       EEOP_CONST,
+
+       /*
+        * Evaluate function call (including OpExprs etc).  For speed, we
+        * distinguish in the opcode whether the function is strict and/or
+        * requires usage stats tracking.
+        */
+       EEOP_FUNCEXPR,
+       EEOP_FUNCEXPR_STRICT,
+       EEOP_FUNCEXPR_FUSAGE,
+       EEOP_FUNCEXPR_STRICT_FUSAGE,
+
+       /*
+        * Evaluate boolean AND expression, one step per subexpression. FIRST/LAST
+        * subexpressions are special-cased for performance.  Since AND always has
+        * at least two subexpressions, FIRST and LAST never apply to the same
+        * subexpression.
+        */
+       EEOP_BOOL_AND_STEP_FIRST,
+       EEOP_BOOL_AND_STEP,
+       EEOP_BOOL_AND_STEP_LAST,
+
+       /* similarly for boolean OR expression */
+       EEOP_BOOL_OR_STEP_FIRST,
+       EEOP_BOOL_OR_STEP,
+       EEOP_BOOL_OR_STEP_LAST,
+
+       /* evaluate boolean NOT expression */
+       EEOP_BOOL_NOT_STEP,
+
+       /* simplified version of BOOL_AND_STEP for use by ExecQual() */
+       EEOP_QUAL,
+
+       /* unconditional jump to another step */
+       EEOP_JUMP,
+
+       /* conditional jumps based on current result value */
+       EEOP_JUMP_IF_NULL,
+       EEOP_JUMP_IF_NOT_NULL,
+       EEOP_JUMP_IF_NOT_TRUE,
+
+       /* perform NULL tests for scalar values */
+       EEOP_NULLTEST_ISNULL,
+       EEOP_NULLTEST_ISNOTNULL,
+
+       /* perform NULL tests for row values */
+       EEOP_NULLTEST_ROWISNULL,
+       EEOP_NULLTEST_ROWISNOTNULL,
+
+       /* evaluate a BooleanTest expression */
+       EEOP_BOOLTEST_IS_TRUE,
+       EEOP_BOOLTEST_IS_NOT_TRUE,
+       EEOP_BOOLTEST_IS_FALSE,
+       EEOP_BOOLTEST_IS_NOT_FALSE,
+
+       /* evaluate PARAM_EXEC/EXTERN parameters */
+       EEOP_PARAM_EXEC,
+       EEOP_PARAM_EXTERN,
+
+       /* return CaseTestExpr value */
+       EEOP_CASE_TESTVAL,
+
+       /* apply MakeExpandedObjectReadOnly() to target value */
+       EEOP_MAKE_READONLY,
+
+       /* evaluate assorted special-purpose expression types */
+       EEOP_IOCOERCE,
+       EEOP_DISTINCT,
+       EEOP_NULLIF,
+       EEOP_SQLVALUEFUNCTION,
+       EEOP_CURRENTOFEXPR,
+       EEOP_ARRAYEXPR,
+       EEOP_ARRAYCOERCE,
+       EEOP_ROW,
+
+       /*
+        * Compare two individual elements of each of two compared ROW()
+        * expressions.  Skip to ROWCOMPARE_FINAL if elements are not equal.
+        */
+       EEOP_ROWCOMPARE_STEP,
+
+       /* evaluate boolean value based on previous ROWCOMPARE_STEP operations */
+       EEOP_ROWCOMPARE_FINAL,
+
+       /* evaluate GREATEST() or LEAST() */
+       EEOP_MINMAX,
+
+       /* evaluate FieldSelect expression */
+       EEOP_FIELDSELECT,
+
+       /*
+        * Deform tuple before evaluating new values for individual fields in a
+        * FieldStore expression.
+        */
+       EEOP_FIELDSTORE_DEFORM,
+
+       /*
+        * Form the new tuple for a FieldStore expression.  Individual fields will
+        * have been evaluated into columns of the tuple deformed by the preceding
+        * DEFORM step.
+        */
+       EEOP_FIELDSTORE_FORM,
+
+       /* Process an array subscript; short-circuit expression to NULL if NULL */
+       EEOP_ARRAYREF_SUBSCRIPT,
+
+       /*
+        * Compute old array element/slice when an ArrayRef assignment expression
+        * contains ArrayRef/FieldStore subexpressions.  Value is accessed using
+        * the CaseTest mechanism.
+        */
+       EEOP_ARRAYREF_OLD,
+
+       /* compute new value for ArrayRef assignment expression */
+       EEOP_ARRAYREF_ASSIGN,
+
+       /* compute element/slice for ArrayRef fetch expression */
+       EEOP_ARRAYREF_FETCH,
+
+       /* evaluate value for CoerceToDomainValue */
+       EEOP_DOMAIN_TESTVAL,
+
+       /* evaluate a domain's NOT NULL constraint */
+       EEOP_DOMAIN_NOTNULL,
+
+       /* evaluate a single domain CHECK constraint */
+       EEOP_DOMAIN_CHECK,
+
+       /* evaluate assorted special-purpose expression types */
+       EEOP_CONVERT_ROWTYPE,
+       EEOP_SCALARARRAYOP,
+       EEOP_XMLEXPR,
+       EEOP_AGGREF,
+       EEOP_GROUPING_FUNC,
+       EEOP_WINDOW_FUNC,
+       EEOP_SUBPLAN,
+       EEOP_ALTERNATIVE_SUBPLAN,
+
+       /* non-existent operation, used e.g. to check array lengths */
+       EEOP_LAST
+} ExprEvalOp;
+
+
+typedef struct ExprEvalStep
+{
+       /*
+        * Instruction to be executed.  During instruction preparation this is an
+        * enum ExprEvalOp, but later it can be changed to some other type, e.g. a
+        * pointer for computed goto (that's why it's an intptr_t).
+        */
+       intptr_t        opcode;
+
+       /* where to store the result of this step */
+       Datum      *resvalue;
+       bool       *resnull;
+
+       /*
+        * Inline data for the operation.  Inline data is faster to access, but
+        * also bloats the size of all instructions.  The union should be kept to
+        * no more than 40 bytes on 64-bit systems (so that the entire struct is
+        * no more than 64 bytes, a single cacheline on common systems).
+        */
+       union
+       {
+               /* for EEOP_INNER/OUTER/SCAN_FETCHSOME */
+               struct
+               {
+                       /* attribute number up to which to fetch (inclusive) */
+                       int                     last_var;
+               }                       fetch;
+
+               /* for EEOP_INNER/OUTER/SCAN_[SYS]VAR[_FIRST] */
+               struct
+               {
+                       /* attnum is attr number - 1 for regular VAR ... */
+                       /* but it's just the normal (negative) attr number for SYSVAR */
+                       int                     attnum;
+                       Oid                     vartype;        /* type OID of variable */
+               }                       var;
+
+               /* for EEOP_WHOLEROW */
+               struct
+               {
+                       Var                *var;        /* original Var node in plan tree */
+                       bool            first;  /* first time through, need to initialize? */
+                       bool            slow;   /* need runtime check for nulls? */
+                       TupleDesc       tupdesc;        /* descriptor for resulting tuples */
+                       JunkFilter *junkFilter;         /* JunkFilter to remove resjunk cols */
+               }                       wholerow;
+
+               /* for EEOP_ASSIGN_*_VAR */
+               struct
+               {
+                       /* target index in ExprState->resultslot->tts_values/nulls */
+                       int                     resultnum;
+                       /* source attribute number - 1 */
+                       int                     attnum;
+               }                       assign_var;
+
+               /* for EEOP_ASSIGN_TMP[_MAKE_RO] */
+               struct
+               {
+                       /* target index in ExprState->resultslot->tts_values/nulls */
+                       int                     resultnum;
+               }                       assign_tmp;
+
+               /* for EEOP_CONST */
+               struct
+               {
+                       /* constant's value */
+                       Datum           value;
+                       bool            isnull;
+               }                       constval;
+
+               /* for EEOP_FUNCEXPR_* / NULLIF / DISTINCT */
+               struct
+               {
+                       FmgrInfo   *finfo;      /* function's lookup data */
+                       FunctionCallInfo fcinfo_data;           /* arguments etc */
+                       /* faster to access without additional indirection: */
+                       PGFunction      fn_addr;        /* actual call address */
+                       int                     nargs;  /* number of arguments */
+               }                       func;
+
+               /* for EEOP_BOOL_*_STEP */
+               struct
+               {
+                       bool       *anynull;    /* track if any input was NULL */
+                       int                     jumpdone;               /* jump here if result determined */
+               }                       boolexpr;
+
+               /* for EEOP_QUAL */
+               struct
+               {
+                       int                     jumpdone;               /* jump here on false or null */
+               }                       qualexpr;
+
+               /* for EEOP_JUMP[_CONDITION] */
+               struct
+               {
+                       int                     jumpdone;               /* target instruction's index */
+               }                       jump;
+
+               /* for EEOP_NULLTEST_ROWIS[NOT]NULL */
+               struct
+               {
+                       /* cached tupdesc pointer - filled at runtime */
+                       TupleDesc       argdesc;
+               }                       nulltest_row;
+
+               /* for EEOP_PARAM_EXEC/EXTERN */
+               struct
+               {
+                       int                     paramid;        /* numeric ID for parameter */
+                       Oid                     paramtype;              /* OID of parameter's datatype */
+               }                       param;
+
+               /* for EEOP_CASE_TESTVAL/DOMAIN_TESTVAL */
+               struct
+               {
+                       Datum      *value;      /* value to return */
+                       bool       *isnull;
+               }                       casetest;
+
+               /* for EEOP_MAKE_READONLY */
+               struct
+               {
+                       Datum      *value;      /* value to coerce to read-only */
+                       bool       *isnull;
+               }                       make_readonly;
+
+               /* for EEOP_IOCOERCE */
+               struct
+               {
+                       /* lookup and call info for source type's output function */
+                       FmgrInfo   *finfo_out;
+                       FunctionCallInfo fcinfo_data_out;
+                       /* lookup and call info for result type's input function */
+                       FmgrInfo   *finfo_in;
+                       FunctionCallInfo fcinfo_data_in;
+               }                       iocoerce;
+
+               /* for EEOP_SQLVALUEFUNCTION */
+               struct
+               {
+                       SQLValueFunction *svf;
+               }                       sqlvaluefunction;
+
+               /* for EEOP_ARRAYEXPR */
+               struct
+               {
+                       Datum      *elemvalues;         /* element values get stored here */
+                       bool       *elemnulls;
+                       int                     nelems; /* length of the above arrays */
+                       Oid                     elemtype;               /* array element type */
+                       int16           elemlength;             /* typlen of the array element type */
+                       bool            elembyval;              /* is the element type pass-by-value? */
+                       char            elemalign;              /* typalign of the element type */
+                       bool            multidims;              /* is array expression multi-D? */
+               }                       arrayexpr;
+
+               /* for EEOP_ARRAYCOERCE */
+               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;
+
+               /* for EEOP_ROW */
+               struct
+               {
+                       TupleDesc       tupdesc;        /* descriptor for result tuples */
+                       /* workspace for the values constituting the row: */
+                       Datum      *elemvalues;
+                       bool       *elemnulls;
+               }                       row;
+
+               /* for EEOP_ROWCOMPARE_STEP */
+               struct
+               {
+                       /* lookup and call data for column comparison function */
+                       FmgrInfo   *finfo;
+                       FunctionCallInfo fcinfo_data;
+                       PGFunction      fn_addr;
+                       /* target for comparison resulting in NULL */
+                       int                     jumpnull;
+                       /* target for comparison yielding inequality */
+                       int                     jumpdone;
+               }                       rowcompare_step;
+
+               /* for EEOP_ROWCOMPARE_FINAL */
+               struct
+               {
+                       RowCompareType rctype;
+               }                       rowcompare_final;
+
+               /* for EEOP_MINMAX */
+               struct
+               {
+                       /* workspace for argument values */
+                       Datum      *values;
+                       bool       *nulls;
+                       int                     nelems;
+                       /* is it GREATEST or LEAST? */
+                       MinMaxOp        op;
+                       /* lookup and call data for comparison function */
+                       FmgrInfo   *finfo;
+                       FunctionCallInfo fcinfo_data;
+               }                       minmax;
+
+               /* for EEOP_FIELDSELECT */
+               struct
+               {
+                       AttrNumber      fieldnum;               /* field number to extract */
+                       Oid                     resulttype;             /* field's type */
+                       /* cached tupdesc pointer - filled at runtime */
+                       TupleDesc       argdesc;
+               }                       fieldselect;
+
+               /* for EEOP_FIELDSTORE_DEFORM / FIELDSTORE_FORM */
+               struct
+               {
+                       /* original expression node */
+                       FieldStore *fstore;
+
+                       /* cached tupdesc pointer - filled at runtime */
+                       /* note that a DEFORM and FORM pair share the same tupdesc */
+                       TupleDesc  *argdesc;
+
+                       /* workspace for column values */
+                       Datum      *values;
+                       bool       *nulls;
+                       int                     ncolumns;
+               }                       fieldstore;
+
+               /* for EEOP_ARRAYREF_SUBSCRIPT */
+               struct
+               {
+                       /* too big to have inline */
+                       struct ArrayRefState *state;
+                       int                     off;    /* 0-based index of this subscript */
+                       bool            isupper;        /* is it upper or lower subscript? */
+                       int                     jumpdone;               /* jump here on null */
+               }                       arrayref_subscript;
+
+               /* for EEOP_ARRAYREF_OLD / ASSIGN / FETCH */
+               struct
+               {
+                       /* too big to have inline */
+                       struct ArrayRefState *state;
+               }                       arrayref;
+
+               /* for EEOP_DOMAIN_NOTNULL / DOMAIN_CHECK */
+               struct
+               {
+                       /* name of constraint */
+                       char       *constraintname;
+                       /* where the result of a CHECK constraint will be stored */
+                       Datum      *checkvalue;
+                       bool       *checknull;
+                       /* OID of domain type */
+                       Oid                     resulttype;
+               }                       domaincheck;
+
+               /* for EEOP_CONVERT_ROWTYPE */
+               struct
+               {
+                       ConvertRowtypeExpr *convert;            /* original expression */
+                       /* these three fields are filled at runtime: */
+                       TupleDesc       indesc; /* tupdesc for input type */
+                       TupleDesc       outdesc;        /* tupdesc for output type */
+                       TupleConversionMap *map;        /* column mapping */
+                       bool            initialized;    /* initialized for current types? */
+               }                       convert_rowtype;
+
+               /* for EEOP_SCALARARRAYOP */
+               struct
+               {
+                       /* element_type/typlen/typbyval/typalign are filled at runtime */
+                       Oid                     element_type;   /* InvalidOid if not yet filled */
+                       bool            useOr;  /* use OR or AND semantics? */
+                       int16           typlen; /* array element type storage info */
+                       bool            typbyval;
+                       char            typalign;
+                       FmgrInfo   *finfo;      /* function's lookup data */
+                       FunctionCallInfo fcinfo_data;           /* arguments etc */
+                       /* faster to access without additional indirection: */
+                       PGFunction      fn_addr;        /* actual call address */
+               }                       scalararrayop;
+
+               /* for EEOP_XMLEXPR */
+               struct
+               {
+                       XmlExpr    *xexpr;      /* original expression node */
+                       /* workspace for evaluating named args, if any */
+                       Datum      *named_argvalue;
+                       bool       *named_argnull;
+                       /* workspace for evaluating unnamed args, if any */
+                       Datum      *argvalue;
+                       bool       *argnull;
+               }                       xmlexpr;
+
+               /* for EEOP_AGGREF */
+               struct
+               {
+                       /* out-of-line state, modified by nodeAgg.c */
+                       AggrefExprState *astate;
+               }                       aggref;
+
+               /* for EEOP_GROUPING_FUNC */
+               struct
+               {
+                       AggState   *parent; /* parent Agg */
+                       List       *clauses;    /* integer list of column numbers */
+               }                       grouping_func;
+
+               /* for EEOP_WINDOW_FUNC */
+               struct
+               {
+                       /* out-of-line state, modified by nodeWindowFunc.c */
+                       WindowFuncExprState *wfstate;
+               }                       window_func;
+
+               /* for EEOP_SUBPLAN */
+               struct
+               {
+                       /* out-of-line state, created by nodeSubplan.c */
+                       SubPlanState *sstate;
+               }                       subplan;
+
+               /* for EEOP_ALTERNATIVE_SUBPLAN */
+               struct
+               {
+                       /* out-of-line state, created by nodeSubplan.c */
+                       AlternativeSubPlanState *asstate;
+               }                       alternative_subplan;
+       }                       d;
+} ExprEvalStep;
+
+
+/* Non-inline data for array operations */
+typedef struct ArrayRefState
+{
+       bool            isassignment;   /* is it assignment, or just fetch? */
+
+       Oid                     refelemtype;    /* OID of the array element type */
+       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 */
+
+       /* numupper and upperprovided[] are filled at compile time */
+       /* at runtime, extracted subscript datums get stored in upperindex[] */
+       int                     numupper;
+       bool            upperprovided[MAXDIM];
+       int                     upperindex[MAXDIM];
+
+       /* similarly for lower indexes, if any */
+       int                     numlower;
+       bool            lowerprovided[MAXDIM];
+       int                     lowerindex[MAXDIM];
+
+       /* subscript expressions get evaluated into here */
+       Datum           subscriptvalue;
+       bool            subscriptnull;
+
+       /* for assignment, new value to assign is evaluated into here */
+       Datum           replacevalue;
+       bool            replacenull;
+
+       /* if we have a nested assignment, ARRAYREF_OLD puts old value here */
+       Datum           prevvalue;
+       bool            prevnull;
+} ArrayRefState;
+
+
+extern void ExecReadyInterpretedExpr(ExprState *state);
+
+extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op);
+
+/*
+ * Non fast-path execution functions. These are externs instead of statics in
+ * execExprInterp.c, because that allows them to be used by other methods of
+ * expression evaluation, reducing code duplication.
+ */
+extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op,
+                                 ExprContext *econtext);
+extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,
+                                       ExprContext *econtext);
+extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRowNull(ExprState *state, ExprEvalStep *op,
+                               ExprContext *econtext);
+extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op,
+                                  ExprContext *econtext);
+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 ExecEvalArrayRefSubscript(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefAssign(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..8b61520e18960786d9d0240df493bed81be446b4 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
  * ----------------
 #define MJ_DEBUG_PROC_NODE(slot)
 #endif   /* EXEC_MERGEJOINDEBUG */
 
-#endif   /* ExecDebugIncluded */
+#endif   /* EXECDEBUG_H */
index a5c75e771f0374e1c585491e745dcb96aed3a326..d3849b93eb19c49226c48b3f0fbb0323db1846a6 100644 (file)
 #define EXEC_FLAG_WITH_NO_DATA 0x0080  /* rel scannability doesn't matter */
 
 
-/*
- * ExecEvalExpr was formerly a function containing a switch statement;
- * 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))
-
-
 /* Hook for plugins to get control in ExecutorStart() */
 typedef void (*ExecutorStart_hook_type) (QueryDesc *queryDesc, int eflags);
 extern PGDLLIMPORT ExecutorStart_hook_type ExecutorStart_hook;
@@ -242,29 +233,155 @@ extern void ExecEndNode(PlanState *node);
 extern bool ExecShutdownNode(PlanState *node);
 
 /*
- * prototypes from functions in execQual.c
+ * prototypes from functions in execExpr.c
  */
-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 ExprState *ExecInitExpr(Expr *node, PlanState *parent);
+extern ExprState *ExecInitQual(List *qual, PlanState *parent);
+extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
+extern List *ExecInitExprList(List *nodes, PlanState *parent);
+extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
+                                               ExprContext *econtext,
+                                               TupleTableSlot *slot,
+                                               PlanState *parent,
+                                               TupleDesc inputDesc);
+extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
+extern ExprState *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState *ExecPrepareCheck(List *qual, EState *estate);
+extern List *ExecPrepareExprList(List *nodes, EState *estate);
+
+/*
+ * ExecEvalExpr
+ *
+ * Evaluate expression identified by "state" in the execution context
+ * given by "econtext".  *isNull is set to the is-null flag for the result,
+ * and the Datum value is the function 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.
+ */
+#ifndef FRONTEND
+static inline Datum
+ExecEvalExpr(ExprState *state,
+                        ExprContext *econtext,
+                        bool *isNull)
+{
+       return (*state->evalfunc) (state, econtext, isNull);
+}
+#endif
+
+/*
+ * 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 = (*state->evalfunc) (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)
+{
+       ExprContext *econtext = projInfo->pi_exprContext;
+       ExprState  *state = &projInfo->pi_state;
+       TupleTableSlot *slot = state->resultslot;
+       bool            isnull;
+
+       /*
+        * 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);
+
+       /* Run the expression, discarding scalar result from the last column. */
+       (void) ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+       /*
+        * Successfully formed a result row.  Mark the result slot as containing a
+        * valid virtual tuple (inlined version of ExecStoreVirtualTuple()).
+        */
+       slot->tts_isempty = false;
+       slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
+
+       return slot;
+}
+#endif
+
+/*
+ * ExecQual - evaluate a qual prepared with ExecInitQual (possibly via
+ * ExecPrepareQual).  Returns true if qual is satisfied, else false.
+ *
+ * Note: ExecQual used to have a third argument "resultForNull".  The
+ * behavior of this function now corresponds to resultForNull == false.
+ * If you want the resultForNull == true behavior, see ExecCheck.
+ */
+#ifndef FRONTEND
+static inline bool
+ExecQual(ExprState *state, ExprContext *econtext)
+{
+       Datum           ret;
+       bool            isnull;
+
+       /* short-circuit (here and in ExecInitQual) for empty restriction list */
+       if (state == NULL)
+               return true;
+
+       /* verify that expression was compiled using ExecInitQual */
+       Assert(state->flags & EEO_FLAG_IS_QUAL);
+
+       ret = ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+       /* EEOP_QUAL should never return NULL */
+       Assert(!isnull);
+
+       return DatumGetBool(ret);
+}
+#endif
+
+extern bool ExecCheck(ExprState *state, ExprContext *context);
+
+/*
+ * prototypes from functions in execSRF.c
+ */
+extern SetExprState *ExecInitTableFunctionResult(Expr *expr,
+                                                       ExprContext *econtext, PlanState *parent);
+extern Tuplestorestate *ExecMakeTableFunctionResult(SetExprState *setexpr,
                                                        ExprContext *econtext,
                                                        MemoryContext argContext,
                                                        TupleDesc expectedDesc,
                                                        bool randomAccess);
-extern Datum ExecMakeFunctionResultSet(FuncExprState *fcache,
+extern SetExprState *ExecInitFunctionResultSet(Expr *expr,
+                                                 ExprContext *econtext, PlanState *parent);
+extern Datum ExecMakeFunctionResultSet(SetExprState *fcache,
                                                  ExprContext *econtext,
                                                  bool *isNull,
                                                  ExprDoneCond *isDone);
-extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,
-                                                 bool *isNull);
-extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
-extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
-extern int     ExecTargetListLength(List *targetlist);
-extern int     ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo);
 
 /*
  * prototypes from functions in execScan.c
@@ -355,10 +472,6 @@ extern void ExecAssignExprContext(EState *estate, PlanState *planstate);
 extern void ExecAssignResultType(PlanState *planstate, TupleDesc tupDesc);
 extern void ExecAssignResultTypeFromTL(PlanState *planstate);
 extern TupleDesc ExecGetResultType(PlanState *planstate);
-extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
-                                               ExprContext *econtext,
-                                               TupleTableSlot *slot,
-                                               TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
                                                 TupleDesc inputDesc);
 extern void ExecFreeExprContext(PlanState *planstate);
@@ -376,8 +489,17 @@ extern void RegisterExprContextCallback(ExprContext *econtext,
 extern void UnregisterExprContextCallback(ExprContext *econtext,
                                                          ExprContextCallbackFunction function,
                                                          Datum arg);
+
 extern void ExecLockNonLeafAppendTables(List *partitioned_rels, EState *estate);
 
+extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname,
+                                  bool *isNull);
+extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno,
+                                 bool *isNull);
+
+extern int     ExecTargetListLength(List *targetlist);
+extern int     ExecCleanTargetListLength(List *targetlist);
+
 /*
  * prototypes from functions in execIndexing.c
  */
index 0f821dc8f67b19f17d2bad42d41ddb27c8142e7d..0d3f52118b2b07791ce754c614828aa03c50df45 100644 (file)
@@ -20,6 +20,10 @@ 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 0a155acee6290a1bcfc936b70e01beba35637be2..6128752ab19e8f690adbdd3460b312ed4d79563d 100644 (file)
@@ -49,6 +49,9 @@ typedef Datum (*PGFunction) (FunctionCallInfo fcinfo);
  * arguments, rather than about the function itself.  But it's convenient
  * to store it here rather than in FunctionCallInfoData, where it might more
  * logically belong.
+ *
+ * fn_extra is available for use by the called function; all other fields
+ * should be treated as read-only after the struct is created.
  */
 typedef struct FmgrInfo
 {
@@ -65,6 +68,11 @@ typedef struct FmgrInfo
 
 /*
  * This struct is the data actually passed to an fmgr-called function.
+ *
+ * The called function is expected to set isnull, and possibly resultinfo or
+ * fields in whatever resultinfo points to.  It should not change any other
+ * fields.  (In particular, scribbling on the argument arrays is a bad idea,
+ * since some callers assume they can re-call with the same arguments.)
  */
 typedef struct FunctionCallInfoData
 {
index f856f6036f664f0b341106593a8d1eb77a6a3bf3..ff428951186007083f9bb35d4be6a57bdfb8e760 100644 (file)
 #include "storage/condition_variable.h"
 
 
+/* ----------------
+ *             ExprState node
+ *
+ * ExprState is the top-level node for expression evaluation.
+ * It contains instructions (in ->steps) to evaluate the expression.
+ * ----------------
+ */
+struct ExprState;                              /* forward references in this file */
+struct ExprContext;
+struct ExprEvalStep;                   /* avoid including execExpr.h everywhere */
+
+typedef Datum (*ExprStateEvalFunc) (struct ExprState *expression,
+                                                                                               struct ExprContext *econtext,
+                                                                                               bool *isNull);
+
+/* Bits in ExprState->flags (see also execExpr.h for private flag bits): */
+/* expression is for use with ExecQual() */
+#define EEO_FLAG_IS_QUAL                                       (1 << 0)
+
+typedef struct ExprState
+{
+       Node            tag;
+
+       uint8           flags;                  /* bitmask of EEO_FLAG_* bits, see above */
+
+       /*
+        * Storage for result value of a scalar expression, or for individual
+        * column results within expressions built by ExecBuildProjectionInfo().
+        */
+       bool            resnull;
+       Datum           resvalue;
+
+       /*
+        * If projecting a tuple result, this slot holds the result; else NULL.
+        */
+       TupleTableSlot *resultslot;
+
+       /*
+        * Instructions to compute expression's return value.
+        */
+       struct ExprEvalStep *steps;
+
+       /*
+        * Function that actually evaluates the expression.  This can be set to
+        * different values depending on the complexity of the expression.
+        */
+       ExprStateEvalFunc evalfunc;
+
+       /* original expression tree, for debugging only */
+       Expr       *expr;
+
+       /*
+        * XXX: following only needed during "compilation", could be thrown away.
+        */
+
+       int                     steps_len;              /* number of steps currently */
+       int                     steps_alloc;    /* allocated length of steps array */
+
+       Datum      *innermost_caseval;
+       bool       *innermost_casenull;
+
+       Datum      *innermost_domainval;
+       bool       *innermost_domainnull;
+} ExprState;
+
+
 /* ----------------
  *       IndexInfo information
  *
@@ -69,7 +135,7 @@ typedef struct IndexInfo
        List       *ii_Expressions; /* list of Expr */
        List       *ii_ExpressionsState;        /* list of ExprState */
        List       *ii_Predicate;       /* list of Expr */
-       List       *ii_PredicateState;          /* list of ExprState */
+       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 */
@@ -214,51 +280,21 @@ typedef struct ReturnSetInfo
  *             that is, form new tuples by evaluation of targetlist expressions.
  *             Nodes which need to do projections create one of these.
  *
+ *             The target tuple slot is kept in ProjectionInfo->pi_state.resultslot.
  *             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)
- *             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;
+       /* instructions to evaluate projection */
+       ExprState       pi_state;
+       /* expression context in which to evaluate expression */
        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;
 
 /* ----------------
@@ -340,20 +376,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;
 
@@ -564,139 +600,63 @@ typedef tuplehash_iterator TupleHashIterator;
 
 
 /* ----------------------------------------------------------------
- *                              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.
+ *                              Expression State Nodes
+ *
+ * Formerly, there was a separate executor expression state node corresponding
+ * to each node in a planned expression tree.  That's no longer the case; for
+ * common expression node types, all the execution info is embedded into
+ * step(s) in a single ExprState node.  But we still have a few executor state
+ * node types for selected expression node types, mostly those in which info
+ * has to be shared with other parts of the execution state tree.
  * ----------------------------------------------------------------
  */
 
-/* ----------------
- *             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;                     /* expression plan node */
        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;
-       List       *args;                       /* states of argument expressions */
+       NodeTag         type;
+       WindowFunc *wfunc;                      /* expression plan node */
+       List       *args;                       /* ExprStates for 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 nonetheless it uses the same
+ * machinery as SRFs; it will be treated as a SRF returning a single row.
  * ----------------
  */
-typedef struct FuncExprState
+typedef struct SetExprState
 {
-       ExprState       xprstate;
-       List       *args;                       /* states of argument expressions */
+       NodeTag         type;
+       Expr       *expr;                       /* expression plan node */
+       List       *args;                       /* ExprStates for argument expressions */
+
+       /*
+        * In ROWS FROM, functions can be inlined, removing the FuncExpr normally
+        * inside.  In such a case this is the compiled expression (which cannot
+        * return a set), which'll be evaluated using regular ExecEvalExpr().
+        */
+       ExprState  *elidedFuncState;
 
        /*
         * Function manager's lookup info for the target function.  If func.fn_oid
@@ -738,7 +698,7 @@ typedef struct FuncExprState
 
        /*
         * Flag to remember whether we have registered a shutdown callback for
-        * this FuncExprState.  We do so only if funcResultStore or setArgsValid
+        * this SetExprState.  We do so only if funcResultStore or setArgsValid
         * has been set at least once (since all the callback is for is to release
         * the tuplestore or clear setArgsValid).
         */
@@ -750,33 +710,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 +718,8 @@ typedef struct BoolExprState
  */
 typedef struct SubPlanState
 {
-       ExprState       xprstate;
+       NodeTag         type;
+       SubPlan    *subplan;            /* expression plan node */
        struct PlanState *planstate;    /* subselect plan's state tree */
        struct PlanState *parent;       /* parent plan node's state tree */
        ExprState  *testexpr;           /* state of combining expression */
@@ -814,203 +749,18 @@ typedef struct SubPlanState
  */
 typedef struct AlternativeSubPlanState
 {
-       ExprState       xprstate;
-       List       *subplans;           /* states of alternative subplans */
+       NodeTag         type;
+       AlternativeSubPlan *subplan;    /* expression plan node */
+       List       *subplans;           /* SubPlanStates 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
  *
- * Note: this is just a Node, and not an ExprState, because it has no
- * corresponding Expr to link to.  Nonetheless it is part of an ExprState
- * tree, so we give it a name following the xxxState convention.
+ * Note: we consider this to be part of an ExprState tree, so we give it
+ * a name following the xxxState convention.  But there's no directly
+ * associated plan-tree node.
  */
 typedef enum DomainConstraintType
 {
@@ -1023,7 +773,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 eval state, or NULL */
 } DomainConstraintState;
 
 
@@ -1060,8 +811,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;                       /* boolean qual condition */
        struct PlanState *lefttree; /* input plan tree(s) */
        struct PlanState *righttree;
        List       *initPlan;           /* Init SubPlanState nodes (un-correlated expr
@@ -1133,11 +883,15 @@ typedef struct ResultState
 
 /* ----------------
  *      ProjectSetState information
+ *
+ * Note: at least one of the "elems" will be a SetExprState; the rest are
+ * regular ExprStates.
  * ----------------
  */
 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 +1126,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 +1172,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 +1288,7 @@ typedef struct ParallelBitmapHeapState
 typedef struct BitmapHeapScanState
 {
        ScanState       ss;                             /* its first field is NodeTag */
-       List       *bitmapqualorig;
+       ExprState  *bitmapqualorig;
        TIDBitmap  *tbm;
        TBMIterator *tbmiterator;
        TBMIterateResult *tbmres;
@@ -1554,16 +1308,18 @@ typedef struct BitmapHeapScanState
 /* ----------------
  *      TidScanState information
  *
+ *             tidexprs           list of TidExpr structs (see nodeTidscan.c)
  *             isCurrentOf    scan has a CurrentOfExpr qual
  *             NumTids            number of tids in this scan
  *             TidPtr             index of currently fetched tid
  *             TidList            evaluated item pointers (array of size NumTids)
+ *             htup               currently-fetched tuple, if any
  * ----------------
  */
 typedef struct TidScanState
 {
        ScanState       ss;                             /* its first field is NodeTag */
-       List       *tss_tidquals;       /* list of ExprState nodes */
+       List       *tss_tidexprs;
        bool            tss_isCurrentOf;
        int                     tss_NumTids;
        int                     tss_TidPtr;
@@ -1712,7 +1468,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 +1515,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 +1613,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 fc883a6f3ec0744da50bebfb2eade049aa53a446..c83216943c1ab65554fd55106bf39a4c26e4e9fd 100644 (file)
@@ -192,36 +192,18 @@ 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 plan nodes do not have a corresponding expression state
+        * node, they're fully handled within execExpr* - but sometimes the state
+        * needs to be shared with other parts of the executor, as for example
+        * with 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 90a1f6347a4cad2016def153ca869164587cac49..1bf94e2548da0e6ac9b906dc48ed983534686b66 100644 (file)
@@ -132,6 +132,7 @@ typedef struct DomainConstraintRef
        List       *constraints;        /* list of DomainConstraintState nodes */
        MemoryContext refctx;           /* context holding DomainConstraintRef */
        TypeCacheEntry *tcache;         /* typcache entry for domain type */
+       bool            need_exprstate; /* does caller need check_exprstate? */
 
        /* Management data --- treat these fields as private to typcache.c */
        DomainConstraintCache *dcc; /* current constraints, or NULL if none */
@@ -142,7 +143,7 @@ typedef struct DomainConstraintRef
 extern TypeCacheEntry *lookup_type_cache(Oid type_id, int flags);
 
 extern void InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref,
-                                               MemoryContext refctx);
+                                               MemoryContext refctx, bool need_exprstate);
 
 extern void UpdateDomainConstraintRef(DomainConstraintRef *ref);
 
index e570b71c04f9da23fb96e9dbf8b016155fccb9d8..195b9b3a97a75dad01dce9762cd75477979174c0 100644 (file)
@@ -61,7 +61,9 @@ 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,
+                  Datum *named_argvalue, bool *named_argnull,
+                  Datum *argvalue, bool *argnull);
 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 664fc425143c886ec336deba4e5ae59751856375..84af81553751af794c7f977da774a20e34970444 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;
@@ -4710,7 +4710,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 720675032a566305264aeb6da0f11a9c27b53e73..f3499807596aee12babd30c2aa3907a0076929f1 100644 (file)
@@ -586,14 +586,8 @@ ERROR:  must be owner of function testfunc1
 DROP FUNCTION testfunc1(int); -- ok
 -- restore to sanity
 GRANT ALL PRIVILEGES ON LANGUAGE sql TO PUBLIC;
--- verify privilege checks on coercions
+-- verify privilege checks on array-element coercions
 BEGIN;
-SELECT NULL::int4[]::int8[];
- int8 
-------
-(1 row)
-
 SELECT '{1}'::int4[]::int8[];
  int8 
 ------
@@ -601,12 +595,6 @@ SELECT '{1}'::int4[]::int8[];
 (1 row)
 
 REVOKE ALL ON FUNCTION int8(integer) FROM PUBLIC;
-SELECT NULL::int4[]::int8[];
- int8 
-------
-(1 row)
-
 SELECT '{1}'::int4[]::int8[]; --superuser, suceed
  int8 
 ------
@@ -614,12 +602,6 @@ SELECT '{1}'::int4[]::int8[]; --superuser, suceed
 (1 row)
 
 SET SESSION AUTHORIZATION regress_user4;
-SELECT NULL::int4[]::int8[];  --other user, no elements to convert
- int8 
-------
-(1 row)
-
 SELECT '{1}'::int4[]::int8[]; --other user, fail
 ERROR:  permission denied for function int8
 ROLLBACK;
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 e3275febea72f7b25f25da1c2c335db40fad608a..166e903012b2a71a367e0ee1c65f040840e95188 100644 (file)
@@ -398,15 +398,12 @@ DROP FUNCTION testfunc1(int); -- ok
 -- restore to sanity
 GRANT ALL PRIVILEGES ON LANGUAGE sql TO PUBLIC;
 
--- verify privilege checks on coercions
+-- verify privilege checks on array-element coercions
 BEGIN;
-SELECT NULL::int4[]::int8[];
 SELECT '{1}'::int4[]::int8[];
 REVOKE ALL ON FUNCTION int8(integer) FROM PUBLIC;
-SELECT NULL::int4[]::int8[];
 SELECT '{1}'::int4[]::int8[]; --superuser, suceed
 SET SESSION AUTHORIZATION regress_user4;
-SELECT NULL::int4[]::int8[];  --other user, no elements to convert
 SELECT '{1}'::int4[]::int8[]; --other user, fail
 ROLLBACK;
 
index 3487f7becb72d017785dfb209a1330e5fc2be302..15c72f57651aa326548691b9e9e43955d7ad6b6d 100644 (file)
@@ -120,7 +120,7 @@ ArrayMapState
 ArrayMetaState
 ArrayParseState
 ArrayRef
-ArrayRefExprState
+ArrayRefState
 ArrayRemapInfo
 ArrayType
 AsyncQueueControl
@@ -578,6 +578,8 @@ ExprContext_CB
 ExprDoneCond
 ExprState
 ExprStateEvalFunc
+ExprEvalOp
+ExprEvalStep
 ExtensibleNode
 ExtensibleNodeEntry
 ExtensibleNodeMethods
@@ -1073,6 +1075,7 @@ LWLockPadded
 LWLockTranche
 LabelProvider
 LargeObjectDesc
+LastAttnumInfo
 Latch
 LerpFunc
 LexDescr
@@ -1908,6 +1911,7 @@ Session
 SetConstraintState
 SetConstraintStateData
 SetConstraintTriggerData
+SetExprState
 SetFunctionReturnMode
 SetOp
 SetOpCmd