Re: [HACKERS] segfault in HEAD when too many nested functions call

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: [HACKERS] segfault in HEAD when too many nested functions call
Дата
Msg-id 13433.1501359248@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: [HACKERS] segfault in HEAD when too many nested functions call  (Andres Freund <andres@anarazel.de>)
Ответы Re: [HACKERS] segfault in HEAD when too many nested functions call  (Andres Freund <andres@anarazel.de>)
Список pgsql-hackers
Andres Freund <andres@anarazel.de> writes:
> [ 0002-Move-ExecProcNode-from-dispatch-to-function-pointer-.patch ]

Here's a reviewed version of this patch.

I added dummy ExecProcNodeMtd functions to the various node types that
lacked them because they expect to be called through MultiExecProcNode
instead.  In the old coding, trying to call ExecProcNode on one of those
node types would have led to a useful error message; as you had it,
it'd have dumped core, which is not an improvement.

Also, I removed the ExecReScan stanza from ExecProcNodeFirst; that
should surely be redundant, because we should only get to that function
through ExecProcNode().  If somehow it's not redundant, please add a
comment explaining why not.

Some other minor cosmetic changes, mostly comment wordsmithing.

I think this (and the previous one) are committable.

            regards, tom lane

diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 20fd9f8..d338cfe 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -17,15 +17,10 @@
  *-------------------------------------------------------------------------
  */
 /*
- *     INTERFACE ROUTINES
- *        ExecInitNode    -        initialize a plan node and its subplans
- *        ExecProcNode    -        get a tuple by executing the plan node
- *        ExecEndNode        -        shut down a plan node and its subplans
- *
  *     NOTES
  *        This used to be three files.  It is now all combined into
- *        one file so that it is easier to keep ExecInitNode, ExecProcNode,
- *        and ExecEndNode in sync when new nodes are added.
+ *        one file so that it is easier to keep the dispatch routines
+ *        in sync when new nodes are added.
  *
  *     EXAMPLE
  *        Suppose we want the age of the manager of the shoe department and
@@ -122,6 +117,10 @@
 #include "miscadmin.h"


+static TupleTableSlot *ExecProcNodeFirst(PlanState *node);
+static TupleTableSlot *ExecProcNodeInstr(PlanState *node);
+
+
 /* ------------------------------------------------------------------------
  *        ExecInitNode
  *
@@ -149,6 +148,13 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
     if (node == NULL)
         return NULL;

+    /*
+     * Make sure there's enough stack available. Need to check here, in
+     * addition to ExecProcNode() (via ExecProcNodeFirst()), to ensure the
+     * stack isn't overrun while initializing the node tree.
+     */
+    check_stack_depth();
+
     switch (nodeTag(node))
     {
             /*
@@ -365,6 +371,13 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
     }

     /*
+     * Add a wrapper around the ExecProcNode callback that checks stack depth
+     * during the first execution.
+     */
+    result->ExecProcNodeReal = result->ExecProcNode;
+    result->ExecProcNode = ExecProcNodeFirst;
+
+    /*
      * Initialize any initPlans present in this node.  The planner put them in
      * a separate list for us.
      */
@@ -388,195 +401,51 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
 }


-/* ----------------------------------------------------------------
- *        ExecProcNode
- *
- *        Execute the given node to return a(nother) tuple.
- * ----------------------------------------------------------------
+/*
+ * ExecProcNode wrapper that performs some one-time checks, before calling
+ * the relevant node method (possibly via an instrumentation wrapper).
  */
-TupleTableSlot *
-ExecProcNode(PlanState *node)
+static TupleTableSlot *
+ExecProcNodeFirst(PlanState *node)
 {
-    TupleTableSlot *result;
-
-    if (node->chgParam != NULL) /* something changed */
-        ExecReScan(node);        /* let ReScan handle this */
+    /*
+     * Perform stack depth check during the first execution of the node.  We
+     * only do so the first time round because it turns out to not be cheap on
+     * some common architectures (eg. x86).  This relies on an assumption that
+     * ExecProcNode calls for a given plan node will always be made at roughly
+     * the same stack depth.
+     */
+    check_stack_depth();

+    /*
+     * If instrumentation is required, change the wrapper to one that just
+     * does instrumentation.  Otherwise we can dispense with all wrappers and
+     * have ExecProcNode() directly call the relevant function from now on.
+     */
     if (node->instrument)
-        InstrStartNode(node->instrument);
-
-    switch (nodeTag(node))
-    {
-            /*
-             * control nodes
-             */
-        case T_ResultState:
-            result = ExecResult((ResultState *) node);
-            break;
-
-        case T_ProjectSetState:
-            result = ExecProjectSet((ProjectSetState *) node);
-            break;
-
-        case T_ModifyTableState:
-            result = ExecModifyTable((ModifyTableState *) node);
-            break;
-
-        case T_AppendState:
-            result = ExecAppend((AppendState *) node);
-            break;
-
-        case T_MergeAppendState:
-            result = ExecMergeAppend((MergeAppendState *) node);
-            break;
-
-        case T_RecursiveUnionState:
-            result = ExecRecursiveUnion((RecursiveUnionState *) node);
-            break;
-
-            /* BitmapAndState does not yield tuples */
-
-            /* BitmapOrState does not yield tuples */
-
-            /*
-             * scan nodes
-             */
-        case T_SeqScanState:
-            result = ExecSeqScan((SeqScanState *) node);
-            break;
-
-        case T_SampleScanState:
-            result = ExecSampleScan((SampleScanState *) node);
-            break;
-
-        case T_IndexScanState:
-            result = ExecIndexScan((IndexScanState *) node);
-            break;
-
-        case T_IndexOnlyScanState:
-            result = ExecIndexOnlyScan((IndexOnlyScanState *) node);
-            break;
-
-            /* BitmapIndexScanState does not yield tuples */
-
-        case T_BitmapHeapScanState:
-            result = ExecBitmapHeapScan((BitmapHeapScanState *) node);
-            break;
-
-        case T_TidScanState:
-            result = ExecTidScan((TidScanState *) node);
-            break;
-
-        case T_SubqueryScanState:
-            result = ExecSubqueryScan((SubqueryScanState *) node);
-            break;
-
-        case T_FunctionScanState:
-            result = ExecFunctionScan((FunctionScanState *) node);
-            break;
-
-        case T_TableFuncScanState:
-            result = ExecTableFuncScan((TableFuncScanState *) node);
-            break;
-
-        case T_ValuesScanState:
-            result = ExecValuesScan((ValuesScanState *) node);
-            break;
-
-        case T_CteScanState:
-            result = ExecCteScan((CteScanState *) node);
-            break;
-
-        case T_NamedTuplestoreScanState:
-            result = ExecNamedTuplestoreScan((NamedTuplestoreScanState *) node);
-            break;
-
-        case T_WorkTableScanState:
-            result = ExecWorkTableScan((WorkTableScanState *) node);
-            break;
-
-        case T_ForeignScanState:
-            result = ExecForeignScan((ForeignScanState *) node);
-            break;
-
-        case T_CustomScanState:
-            result = ExecCustomScan((CustomScanState *) node);
-            break;
-
-            /*
-             * join nodes
-             */
-        case T_NestLoopState:
-            result = ExecNestLoop((NestLoopState *) node);
-            break;
-
-        case T_MergeJoinState:
-            result = ExecMergeJoin((MergeJoinState *) node);
-            break;
-
-        case T_HashJoinState:
-            result = ExecHashJoin((HashJoinState *) node);
-            break;
-
-            /*
-             * materialization nodes
-             */
-        case T_MaterialState:
-            result = ExecMaterial((MaterialState *) node);
-            break;
-
-        case T_SortState:
-            result = ExecSort((SortState *) node);
-            break;
-
-        case T_GroupState:
-            result = ExecGroup((GroupState *) node);
-            break;
+        node->ExecProcNode = ExecProcNodeInstr;
+    else
+        node->ExecProcNode = node->ExecProcNodeReal;

-        case T_AggState:
-            result = ExecAgg((AggState *) node);
-            break;
-
-        case T_WindowAggState:
-            result = ExecWindowAgg((WindowAggState *) node);
-            break;
-
-        case T_UniqueState:
-            result = ExecUnique((UniqueState *) node);
-            break;
-
-        case T_GatherState:
-            result = ExecGather((GatherState *) node);
-            break;
-
-        case T_GatherMergeState:
-            result = ExecGatherMerge((GatherMergeState *) node);
-            break;
-
-        case T_HashState:
-            result = ExecHash((HashState *) node);
-            break;
+    return node->ExecProcNode(node);
+}

-        case T_SetOpState:
-            result = ExecSetOp((SetOpState *) node);
-            break;

-        case T_LockRowsState:
-            result = ExecLockRows((LockRowsState *) node);
-            break;
+/*
+ * ExecProcNode wrapper that performs instrumentation calls.  By keeping
+ * this a separate function, we avoid overhead in the normal case where
+ * no instrumentation is wanted.
+ */
+static TupleTableSlot *
+ExecProcNodeInstr(PlanState *node)
+{
+    TupleTableSlot *result;

-        case T_LimitState:
-            result = ExecLimit((LimitState *) node);
-            break;
+    InstrStartNode(node->instrument);

-        default:
-            elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
-            result = NULL;
-            break;
-    }
+    result = node->ExecProcNodeReal(node);

-    if (node->instrument)
-        InstrStopNode(node->instrument, TupIsNull(result) ? 0.0 : 1.0);
+    InstrStopNode(node->instrument, TupIsNull(result) ? 0.0 : 1.0);

     return result;
 }
@@ -600,6 +469,8 @@ MultiExecProcNode(PlanState *node)
 {
     Node       *result;

+    check_stack_depth();
+
     CHECK_FOR_INTERRUPTS();

     if (node->chgParam != NULL) /* something changed */
@@ -657,6 +528,13 @@ ExecEndNode(PlanState *node)
     if (node == NULL)
         return;

+    /*
+     * Make sure there's enough stack available. Need to check here, in
+     * addition to ExecProcNode() (via ExecProcNodeFirst()), because it's not
+     * guaranteed that ExecProcNode() is reached for all nodes.
+     */
+    check_stack_depth();
+
     if (node->chgParam != NULL)
     {
         bms_free(node->chgParam);
@@ -855,6 +733,8 @@ ExecShutdownNode(PlanState *node)
     if (node == NULL)
         return false;

+    check_stack_depth();
+
     planstate_tree_walker(node, ExecShutdownNode, NULL);

     switch (nodeTag(node))
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 377916d..6a26773 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -2099,9 +2099,10 @@ lookup_hash_entries(AggState *aggstate)
  *      stored in the expression context to be used when ExecProject evaluates
  *      the result tuple.
  */
-TupleTableSlot *
-ExecAgg(AggState *node)
+static TupleTableSlot *
+ExecAgg(PlanState *pstate)
 {
+    AggState   *node = castNode(AggState, pstate);
     TupleTableSlot *result = NULL;

     CHECK_FOR_INTERRUPTS();
@@ -2695,6 +2696,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
     aggstate = makeNode(AggState);
     aggstate->ss.ps.plan = (Plan *) node;
     aggstate->ss.ps.state = estate;
+    aggstate->ss.ps.ExecProcNode = ExecAgg;

     aggstate->aggs = NIL;
     aggstate->numaggs = 0;
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 58045e0..bed9bb8 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -61,6 +61,7 @@
 #include "executor/nodeAppend.h"
 #include "miscadmin.h"

+static TupleTableSlot *ExecAppend(PlanState *pstate);
 static bool exec_append_initialize_next(AppendState *appendstate);


@@ -147,6 +148,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
      */
     appendstate->ps.plan = (Plan *) node;
     appendstate->ps.state = estate;
+    appendstate->ps.ExecProcNode = ExecAppend;
     appendstate->appendplans = appendplanstates;
     appendstate->as_nplans = nplans;

@@ -197,9 +199,11 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
  *        Handles iteration over multiple subplans.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecAppend(AppendState *node)
+static TupleTableSlot *
+ExecAppend(PlanState *pstate)
 {
+    AppendState *node = castNode(AppendState, pstate);
+
     for (;;)
     {
         PlanState  *subnode;
diff --git a/src/backend/executor/nodeBitmapAnd.c b/src/backend/executor/nodeBitmapAnd.c
index e4eb028..1c5c312 100644
--- a/src/backend/executor/nodeBitmapAnd.c
+++ b/src/backend/executor/nodeBitmapAnd.c
@@ -33,6 +33,19 @@


 /* ----------------------------------------------------------------
+ *        ExecBitmapAnd
+ *
+ *        stub for pro forma compliance
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+ExecBitmapAnd(PlanState *pstate)
+{
+    elog(ERROR, "BitmapAnd node does not support ExecProcNode call convention");
+    return NULL;
+}
+
+/* ----------------------------------------------------------------
  *        ExecInitBitmapAnd
  *
  *        Begin all of the subscans of the BitmapAnd node.
@@ -63,6 +76,7 @@ ExecInitBitmapAnd(BitmapAnd *node, EState *estate, int eflags)
      */
     bitmapandstate->ps.plan = (Plan *) node;
     bitmapandstate->ps.state = estate;
+    bitmapandstate->ps.ExecProcNode = ExecBitmapAnd;
     bitmapandstate->bitmapplans = bitmapplanstates;
     bitmapandstate->nplans = nplans;

diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index cf109d5..79f534e 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -665,9 +665,11 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
  *        ExecBitmapHeapScan(node)
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecBitmapHeapScan(BitmapHeapScanState *node)
+static TupleTableSlot *
+ExecBitmapHeapScan(PlanState *pstate)
 {
+    BitmapHeapScanState *node = castNode(BitmapHeapScanState, pstate);
+
     return ExecScan(&node->ss,
                     (ExecScanAccessMtd) BitmapHeapNext,
                     (ExecScanRecheckMtd) BitmapHeapRecheck);
@@ -815,6 +817,7 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
     scanstate = makeNode(BitmapHeapScanState);
     scanstate->ss.ps.plan = (Plan *) node;
     scanstate->ss.ps.state = estate;
+    scanstate->ss.ps.ExecProcNode = ExecBitmapHeapScan;

     scanstate->tbm = NULL;
     scanstate->tbmiterator = NULL;
diff --git a/src/backend/executor/nodeBitmapIndexscan.c b/src/backend/executor/nodeBitmapIndexscan.c
index 2411a2e..6feb70f 100644
--- a/src/backend/executor/nodeBitmapIndexscan.c
+++ b/src/backend/executor/nodeBitmapIndexscan.c
@@ -29,6 +29,19 @@


 /* ----------------------------------------------------------------
+ *        ExecBitmapIndexScan
+ *
+ *        stub for pro forma compliance
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+ExecBitmapIndexScan(PlanState *pstate)
+{
+    elog(ERROR, "BitmapIndexScan node does not support ExecProcNode call convention");
+    return NULL;
+}
+
+/* ----------------------------------------------------------------
  *        MultiExecBitmapIndexScan(node)
  * ----------------------------------------------------------------
  */
@@ -208,6 +221,7 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
     indexstate = makeNode(BitmapIndexScanState);
     indexstate->ss.ps.plan = (Plan *) node;
     indexstate->ss.ps.state = estate;
+    indexstate->ss.ps.ExecProcNode = ExecBitmapIndexScan;

     /* normally we don't make the result bitmap till runtime */
     indexstate->biss_result = NULL;
diff --git a/src/backend/executor/nodeBitmapOr.c b/src/backend/executor/nodeBitmapOr.c
index 4f0ddc6..66a7a89 100644
--- a/src/backend/executor/nodeBitmapOr.c
+++ b/src/backend/executor/nodeBitmapOr.c
@@ -34,6 +34,19 @@


 /* ----------------------------------------------------------------
+ *        ExecBitmapOr
+ *
+ *        stub for pro forma compliance
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+ExecBitmapOr(PlanState *pstate)
+{
+    elog(ERROR, "BitmapOr node does not support ExecProcNode call convention");
+    return NULL;
+}
+
+/* ----------------------------------------------------------------
  *        ExecInitBitmapOr
  *
  *        Begin all of the subscans of the BitmapOr node.
@@ -64,6 +77,7 @@ ExecInitBitmapOr(BitmapOr *node, EState *estate, int eflags)
      */
     bitmaporstate->ps.plan = (Plan *) node;
     bitmaporstate->ps.state = estate;
+    bitmaporstate->ps.ExecProcNode = ExecBitmapOr;
     bitmaporstate->bitmapplans = bitmapplanstates;
     bitmaporstate->nplans = nplans;

diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index bed7949..79676ca 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -149,9 +149,11 @@ CteScanRecheck(CteScanState *node, TupleTableSlot *slot)
  *        access method functions.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecCteScan(CteScanState *node)
+static TupleTableSlot *
+ExecCteScan(PlanState *pstate)
 {
+    CteScanState *node = castNode(CteScanState, pstate);
+
     return ExecScan(&node->ss,
                     (ExecScanAccessMtd) CteScanNext,
                     (ExecScanRecheckMtd) CteScanRecheck);
@@ -191,6 +193,7 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
     scanstate = makeNode(CteScanState);
     scanstate->ss.ps.plan = (Plan *) node;
     scanstate->ss.ps.state = estate;
+    scanstate->ss.ps.ExecProcNode = ExecCteScan;
     scanstate->eflags = eflags;
     scanstate->cte_table = NULL;
     scanstate->eof_cte = false;
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index fc15974..fb7645b 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -21,6 +21,10 @@
 #include "utils/memutils.h"
 #include "utils/rel.h"

+
+static TupleTableSlot *ExecCustomScan(PlanState *pstate);
+
+
 CustomScanState *
 ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 {
@@ -45,6 +49,7 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
     /* fill up fields of ScanState */
     css->ss.ps.plan = &cscan->scan.plan;
     css->ss.ps.state = estate;
+    css->ss.ps.ExecProcNode = ExecCustomScan;

     /* create expression context for node */
     ExecAssignExprContext(estate, &css->ss.ps);
@@ -102,9 +107,11 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
     return css;
 }

-TupleTableSlot *
-ExecCustomScan(CustomScanState *node)
+static TupleTableSlot *
+ExecCustomScan(PlanState *pstate)
 {
+    CustomScanState *node = castNode(CustomScanState, pstate);
+
     CHECK_FOR_INTERRUPTS();

     Assert(node->methods->ExecCustomScan != NULL);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 9cde112..140e82e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -113,10 +113,12 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
  *        access method functions.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecForeignScan(ForeignScanState *node)
+static TupleTableSlot *
+ExecForeignScan(PlanState *pstate)
 {
-    return ExecScan((ScanState *) node,
+    ForeignScanState *node = castNode(ForeignScanState, pstate);
+
+    return ExecScan(&node->ss,
                     (ExecScanAccessMtd) ForeignNext,
                     (ExecScanRecheckMtd) ForeignRecheck);
 }
@@ -144,6 +146,7 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
     scanstate = makeNode(ForeignScanState);
     scanstate->ss.ps.plan = (Plan *) node;
     scanstate->ss.ps.state = estate;
+    scanstate->ss.ps.ExecProcNode = ExecForeignScan;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 3217d64..9f87a7e 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -262,9 +262,11 @@ FunctionRecheck(FunctionScanState *node, TupleTableSlot *slot)
  *        access method functions.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecFunctionScan(FunctionScanState *node)
+static TupleTableSlot *
+ExecFunctionScan(PlanState *pstate)
 {
+    FunctionScanState *node = castNode(FunctionScanState, pstate);
+
     return ExecScan(&node->ss,
                     (ExecScanAccessMtd) FunctionNext,
                     (ExecScanRecheckMtd) FunctionRecheck);
@@ -299,6 +301,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
     scanstate = makeNode(FunctionScanState);
     scanstate->ss.ps.plan = (Plan *) node;
     scanstate->ss.ps.state = estate;
+    scanstate->ss.ps.ExecProcNode = ExecFunctionScan;
     scanstate->eflags = eflags;

     /*
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 5dbe19c..e8d94ee 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -43,6 +43,7 @@
 #include "utils/rel.h"


+static TupleTableSlot *ExecGather(PlanState *pstate);
 static TupleTableSlot *gather_getnext(GatherState *gatherstate);
 static HeapTuple gather_readnext(GatherState *gatherstate);
 static void ExecShutdownGatherWorkers(GatherState *node);
@@ -69,6 +70,7 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
     gatherstate = makeNode(GatherState);
     gatherstate->ps.plan = (Plan *) node;
     gatherstate->ps.state = estate;
+    gatherstate->ps.ExecProcNode = ExecGather;
     gatherstate->need_to_scan_locally = !node->single_copy;

     /*
@@ -120,9 +122,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
  *        the next qualifying tuple.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecGather(GatherState *node)
+static TupleTableSlot *
+ExecGather(PlanState *pstate)
 {
+    GatherState *node = castNode(GatherState, pstate);
     TupleTableSlot *fslot = node->funnel_slot;
     int            i;
     TupleTableSlot *slot;
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 0aff379..9a81e22 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -44,6 +44,7 @@ typedef struct GMReaderTupleBuffer
  */
 #define MAX_TUPLE_STORE 10

+static TupleTableSlot *ExecGatherMerge(PlanState *pstate);
 static int32 heap_compare_slots(Datum a, Datum b, void *arg);
 static TupleTableSlot *gather_merge_getnext(GatherMergeState *gm_state);
 static HeapTuple gm_readnext_tuple(GatherMergeState *gm_state, int nreader,
@@ -75,6 +76,7 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags)
     gm_state = makeNode(GatherMergeState);
     gm_state->ps.plan = (Plan *) node;
     gm_state->ps.state = estate;
+    gm_state->ps.ExecProcNode = ExecGatherMerge;

     /*
      * Miscellaneous initialization
@@ -157,9 +159,10 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags)
  *        the next qualifying tuple.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecGatherMerge(GatherMergeState *node)
+static TupleTableSlot *
+ExecGatherMerge(PlanState *pstate)
 {
+    GatherMergeState *node = castNode(GatherMergeState, pstate);
     TupleTableSlot *slot;
     ExprContext *econtext;
     int            i;
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index fc5e0e5..ab4ae24 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -32,9 +32,10 @@
  *
  *        Return one tuple for each group of matching input tuples.
  */
-TupleTableSlot *
-ExecGroup(GroupState *node)
+static TupleTableSlot *
+ExecGroup(PlanState *pstate)
 {
+    GroupState *node = castNode(GroupState, pstate);
     ExprContext *econtext;
     int            numCols;
     AttrNumber *grpColIdx;
@@ -175,6 +176,7 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
     grpstate = makeNode(GroupState);
     grpstate->ss.ps.plan = (Plan *) node;
     grpstate->ss.ps.state = estate;
+    grpstate->ss.ps.ExecProcNode = ExecGroup;
     grpstate->grp_done = FALSE;

     /*
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index fbeb562..d10d94c 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -56,8 +56,8 @@ static void *dense_alloc(HashJoinTable hashtable, Size size);
  *        stub for pro forma compliance
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecHash(HashState *node)
+static TupleTableSlot *
+ExecHash(PlanState *pstate)
 {
     elog(ERROR, "Hash node does not support ExecProcNode call convention");
     return NULL;
@@ -172,6 +172,7 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
     hashstate = makeNode(HashState);
     hashstate->ps.plan = (Plan *) node;
     hashstate->ps.state = estate;
+    hashstate->ps.ExecProcNode = ExecHash;
     hashstate->hashtable = NULL;
     hashstate->hashkeys = NIL;    /* will be set by parent HashJoin */

diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 252960c..ab1632c 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -58,9 +58,10 @@ static bool ExecHashJoinNewBatch(HashJoinState *hjstate);
  *              the other one is "outer".
  * ----------------------------------------------------------------
  */
-TupleTableSlot *                /* return: a tuple or NULL */
-ExecHashJoin(HashJoinState *node)
+static TupleTableSlot *            /* return: a tuple or NULL */
+ExecHashJoin(PlanState *pstate)
 {
+    HashJoinState *node = castNode(HashJoinState, pstate);
     PlanState  *outerNode;
     HashState  *hashNode;
     ExprState  *joinqual;
@@ -399,6 +400,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
     hjstate = makeNode(HashJoinState);
     hjstate->js.ps.plan = (Plan *) node;
     hjstate->js.ps.state = estate;
+    hjstate->js.ps.ExecProcNode = ExecHashJoin;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index e2000764..fe7ba3f 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -306,9 +306,11 @@ IndexOnlyRecheck(IndexOnlyScanState *node, TupleTableSlot *slot)
  *        ExecIndexOnlyScan(node)
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecIndexOnlyScan(IndexOnlyScanState *node)
+static TupleTableSlot *
+ExecIndexOnlyScan(PlanState *pstate)
 {
+    IndexOnlyScanState *node = castNode(IndexOnlyScanState, pstate);
+
     /*
      * If we have runtime keys and they've not already been set up, do it now.
      */
@@ -476,6 +478,7 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
     indexstate = makeNode(IndexOnlyScanState);
     indexstate->ss.ps.plan = (Plan *) node;
     indexstate->ss.ps.state = estate;
+    indexstate->ss.ps.ExecProcNode = ExecIndexOnlyScan;
     indexstate->ioss_HeapFetches = 0;

     /*
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 6704ede..404076d 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -542,9 +542,11 @@ reorderqueue_pop(IndexScanState *node)
  *        ExecIndexScan(node)
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecIndexScan(IndexScanState *node)
+static TupleTableSlot *
+ExecIndexScan(PlanState *pstate)
 {
+    IndexScanState *node = castNode(IndexScanState, pstate);
+
     /*
      * If we have runtime keys and they've not already been set up, do it now.
      */
@@ -910,6 +912,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
     indexstate = makeNode(IndexScanState);
     indexstate->ss.ps.plan = (Plan *) node;
     indexstate->ss.ps.state = estate;
+    indexstate->ss.ps.ExecProcNode = ExecIndexScan;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
index 2ed3523..ac5a2ff 100644
--- a/src/backend/executor/nodeLimit.c
+++ b/src/backend/executor/nodeLimit.c
@@ -37,9 +37,10 @@ static void pass_down_bound(LimitState *node, PlanState *child_node);
  *        filtering on the stream of tuples returned by a subplan.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *                /* return: a tuple or NULL */
-ExecLimit(LimitState *node)
+static TupleTableSlot *            /* return: a tuple or NULL */
+ExecLimit(PlanState *pstate)
 {
+    LimitState *node = castNode(LimitState, pstate);
     ScanDirection direction;
     TupleTableSlot *slot;
     PlanState  *outerPlan;
@@ -378,6 +379,7 @@ ExecInitLimit(Limit *node, EState *estate, int eflags)
     limitstate = makeNode(LimitState);
     limitstate->ps.plan = (Plan *) node;
     limitstate->ps.state = estate;
+    limitstate->ps.ExecProcNode = ExecLimit;

     limitstate->lstate = LIMIT_INITIAL;

diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index dd4e2c5..9389560 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -36,9 +36,10 @@
  *        ExecLockRows
  * ----------------------------------------------------------------
  */
-TupleTableSlot *                /* return: a tuple or NULL */
-ExecLockRows(LockRowsState *node)
+static TupleTableSlot *            /* return: a tuple or NULL */
+ExecLockRows(PlanState *pstate)
 {
+    LockRowsState *node = castNode(LockRowsState, pstate);
     TupleTableSlot *slot;
     EState       *estate;
     PlanState  *outerPlan;
@@ -364,6 +365,7 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
     lrstate = makeNode(LockRowsState);
     lrstate->ps.plan = (Plan *) node;
     lrstate->ps.state = estate;
+    lrstate->ps.ExecProcNode = ExecLockRows;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index 3342949..91178f1 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -35,9 +35,10 @@
  *
  * ----------------------------------------------------------------
  */
-TupleTableSlot *                /* result tuple from subplan */
-ExecMaterial(MaterialState *node)
+static TupleTableSlot *            /* result tuple from subplan */
+ExecMaterial(PlanState *pstate)
 {
+    MaterialState *node = castNode(MaterialState, pstate);
     EState       *estate;
     ScanDirection dir;
     bool        forward;
@@ -173,6 +174,7 @@ ExecInitMaterial(Material *node, EState *estate, int eflags)
     matstate = makeNode(MaterialState);
     matstate->ss.ps.plan = (Plan *) node;
     matstate->ss.ps.state = estate;
+    matstate->ss.ps.ExecProcNode = ExecMaterial;

     /*
      * We must have a tuplestore buffering the subplan output to do backward
diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c
index d41def1..6bf490b 100644
--- a/src/backend/executor/nodeMergeAppend.c
+++ b/src/backend/executor/nodeMergeAppend.c
@@ -50,6 +50,7 @@
  */
 typedef int32 SlotNumber;

+static TupleTableSlot *ExecMergeAppend(PlanState *pstate);
 static int    heap_compare_slots(Datum a, Datum b, void *arg);


@@ -89,6 +90,7 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
      */
     mergestate->ps.plan = (Plan *) node;
     mergestate->ps.state = estate;
+    mergestate->ps.ExecProcNode = ExecMergeAppend;
     mergestate->mergeplans = mergeplanstates;
     mergestate->ms_nplans = nplans;

@@ -169,9 +171,10 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
  *        Handles iteration over multiple subplans.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecMergeAppend(MergeAppendState *node)
+static TupleTableSlot *
+ExecMergeAppend(PlanState *pstate)
 {
+    MergeAppendState *node = castNode(MergeAppendState, pstate);
     TupleTableSlot *result;
     SlotNumber    i;

diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 324b61b..925b4cf 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -596,9 +596,10 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
  *        ExecMergeJoin
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecMergeJoin(MergeJoinState *node)
+static TupleTableSlot *
+ExecMergeJoin(PlanState *pstate)
 {
+    MergeJoinState *node = castNode(MergeJoinState, pstate);
     ExprState  *joinqual;
     ExprState  *otherqual;
     bool        qualResult;
@@ -1448,6 +1449,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
     mergestate = makeNode(MergeJoinState);
     mergestate->js.ps.plan = (Plan *) node;
     mergestate->js.ps.state = estate;
+    mergestate->js.ps.ExecProcNode = ExecMergeJoin;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 637a582..0dde0ed 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1535,9 +1535,10 @@ ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
  *        if needed.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecModifyTable(ModifyTableState *node)
+static TupleTableSlot *
+ExecModifyTable(PlanState *pstate)
 {
+    ModifyTableState *node = castNode(ModifyTableState, pstate);
     EState       *estate = node->ps.state;
     CmdType        operation = node->operation;
     ResultRelInfo *saved_resultRelInfo;
@@ -1806,6 +1807,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
     mtstate = makeNode(ModifyTableState);
     mtstate->ps.plan = (Plan *) node;
     mtstate->ps.state = estate;
+    mtstate->ps.ExecProcNode = ExecModifyTable;

     mtstate->operation = operation;
     mtstate->canSetTag = node->canSetTag;
diff --git a/src/backend/executor/nodeNamedtuplestorescan.c b/src/backend/executor/nodeNamedtuplestorescan.c
index 6223486..3a65b9f5 100644
--- a/src/backend/executor/nodeNamedtuplestorescan.c
+++ b/src/backend/executor/nodeNamedtuplestorescan.c
@@ -63,9 +63,11 @@ NamedTuplestoreScanRecheck(NamedTuplestoreScanState *node, TupleTableSlot *slot)
  *        access method functions.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecNamedTuplestoreScan(NamedTuplestoreScanState *node)
+static TupleTableSlot *
+ExecNamedTuplestoreScan(PlanState *pstate)
 {
+    NamedTuplestoreScanState *node = castNode(NamedTuplestoreScanState, pstate);
+
     return ExecScan(&node->ss,
                     (ExecScanAccessMtd) NamedTuplestoreScanNext,
                     (ExecScanRecheckMtd) NamedTuplestoreScanRecheck);
@@ -97,6 +99,7 @@ ExecInitNamedTuplestoreScan(NamedTuplestoreScan *node, EState *estate, int eflag
     scanstate = makeNode(NamedTuplestoreScanState);
     scanstate->ss.ps.plan = (Plan *) node;
     scanstate->ss.ps.state = estate;
+    scanstate->ss.ps.ExecProcNode = ExecNamedTuplestoreScan;

     enr = get_ENR(estate->es_queryEnv, node->enrname);
     if (!enr)
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index bedc374..4447b7c 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -57,9 +57,10 @@
  *               are prepared to return the first tuple.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecNestLoop(NestLoopState *node)
+static TupleTableSlot *
+ExecNestLoop(PlanState *pstate)
 {
+    NestLoopState *node = castNode(NestLoopState, pstate);
     NestLoop   *nl;
     PlanState  *innerPlan;
     PlanState  *outerPlan;
@@ -275,6 +276,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
     nlstate = makeNode(NestLoopState);
     nlstate->js.ps.plan = (Plan *) node;
     nlstate->js.ps.state = estate;
+    nlstate->js.ps.ExecProcNode = ExecNestLoop;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeProjectSet.c b/src/backend/executor/nodeProjectSet.c
index 3b69c7a..d93462c 100644
--- a/src/backend/executor/nodeProjectSet.c
+++ b/src/backend/executor/nodeProjectSet.c
@@ -39,9 +39,10 @@ static TupleTableSlot *ExecProjectSRF(ProjectSetState *node, bool continuing);
  *        returning functions).
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecProjectSet(ProjectSetState *node)
+static TupleTableSlot *
+ExecProjectSet(PlanState *pstate)
 {
+    ProjectSetState *node = castNode(ProjectSetState, pstate);
     TupleTableSlot *outerTupleSlot;
     TupleTableSlot *resultSlot;
     PlanState  *outerPlan;
@@ -215,6 +216,7 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
     state = makeNode(ProjectSetState);
     state->ps.plan = (Plan *) node;
     state->ps.state = estate;
+    state->ps.ExecProcNode = ExecProjectSet;

     state->pending_srf_tuples = false;

diff --git a/src/backend/executor/nodeRecursiveunion.c b/src/backend/executor/nodeRecursiveunion.c
index 2802fff..a64dd13 100644
--- a/src/backend/executor/nodeRecursiveunion.c
+++ b/src/backend/executor/nodeRecursiveunion.c
@@ -66,9 +66,10 @@ build_hash_table(RecursiveUnionState *rustate)
  * 2.6 go back to 2.2
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecRecursiveUnion(RecursiveUnionState *node)
+static TupleTableSlot *
+ExecRecursiveUnion(PlanState *pstate)
 {
+    RecursiveUnionState *node = castNode(RecursiveUnionState, pstate);
     PlanState  *outerPlan = outerPlanState(node);
     PlanState  *innerPlan = innerPlanState(node);
     RecursiveUnion *plan = (RecursiveUnion *) node->ps.plan;
@@ -172,6 +173,7 @@ ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
     rustate = makeNode(RecursiveUnionState);
     rustate->ps.plan = (Plan *) node;
     rustate->ps.state = estate;
+    rustate->ps.ExecProcNode = ExecRecursiveUnion;

     rustate->eqfunctions = NULL;
     rustate->hashfunctions = NULL;
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index f007f46..4c879d8 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -64,9 +64,10 @@
  *        'nil' if the constant qualification is not satisfied.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecResult(ResultState *node)
+static TupleTableSlot *
+ExecResult(PlanState *pstate)
 {
+    ResultState *node = castNode(ResultState, pstate);
     TupleTableSlot *outerTupleSlot;
     PlanState  *outerPlan;
     ExprContext *econtext;
@@ -191,6 +192,7 @@ ExecInitResult(Result *node, EState *estate, int eflags)
     resstate = makeNode(ResultState);
     resstate->ps.plan = (Plan *) node;
     resstate->ps.state = estate;
+    resstate->ps.ExecProcNode = ExecResult;

     resstate->rs_done = false;
     resstate->rs_checkqual = (node->resconstantqual == NULL) ? false : true;
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index b710ef7..9c74a83 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -96,10 +96,12 @@ SampleRecheck(SampleScanState *node, TupleTableSlot *slot)
  *        access method functions.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecSampleScan(SampleScanState *node)
+static TupleTableSlot *
+ExecSampleScan(PlanState *pstate)
 {
-    return ExecScan((ScanState *) node,
+    SampleScanState *node = castNode(SampleScanState, pstate);
+
+    return ExecScan(&node->ss,
                     (ExecScanAccessMtd) SampleNext,
                     (ExecScanRecheckMtd) SampleRecheck);
 }
@@ -153,6 +155,7 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
     scanstate = makeNode(SampleScanState);
     scanstate->ss.ps.plan = (Plan *) node;
     scanstate->ss.ps.state = estate;
+    scanstate->ss.ps.ExecProcNode = ExecSampleScan;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 307df87..5c49d4c 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -121,10 +121,12 @@ SeqRecheck(SeqScanState *node, TupleTableSlot *slot)
  *        access method functions.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecSeqScan(SeqScanState *node)
+static TupleTableSlot *
+ExecSeqScan(PlanState *pstate)
 {
-    return ExecScan((ScanState *) node,
+    SeqScanState *node = castNode(SeqScanState, pstate);
+
+    return ExecScan(&node->ss,
                     (ExecScanAccessMtd) SeqNext,
                     (ExecScanRecheckMtd) SeqRecheck);
 }
@@ -177,6 +179,7 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
     scanstate = makeNode(SeqScanState);
     scanstate->ss.ps.plan = (Plan *) node;
     scanstate->ss.ps.state = estate;
+    scanstate->ss.ps.ExecProcNode = ExecSeqScan;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c
index 56c5643..571cbf8 100644
--- a/src/backend/executor/nodeSetOp.c
+++ b/src/backend/executor/nodeSetOp.c
@@ -180,9 +180,10 @@ set_output_count(SetOpState *setopstate, SetOpStatePerGroup pergroup)
  *        ExecSetOp
  * ----------------------------------------------------------------
  */
-TupleTableSlot *                /* return: a tuple or NULL */
-ExecSetOp(SetOpState *node)
+static TupleTableSlot *            /* return: a tuple or NULL */
+ExecSetOp(PlanState *pstate)
 {
+    SetOpState *node = castNode(SetOpState, pstate);
     SetOp       *plannode = (SetOp *) node->ps.plan;
     TupleTableSlot *resultTupleSlot = node->ps.ps_ResultTupleSlot;

@@ -485,6 +486,7 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags)
     setopstate = makeNode(SetOpState);
     setopstate->ps.plan = (Plan *) node;
     setopstate->ps.state = estate;
+    setopstate->ps.ExecProcNode = ExecSetOp;

     setopstate->eqfunctions = NULL;
     setopstate->hashfunctions = NULL;
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index 799a4e9..aae4150 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -35,9 +35,10 @@
  *          -- the outer child is prepared to return the first tuple.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecSort(SortState *node)
+static TupleTableSlot *
+ExecSort(PlanState *pstate)
 {
+    SortState  *node = castNode(SortState, pstate);
     EState       *estate;
     ScanDirection dir;
     Tuplesortstate *tuplesortstate;
@@ -165,6 +166,7 @@ ExecInitSort(Sort *node, EState *estate, int eflags)
     sortstate = makeNode(SortState);
     sortstate->ss.ps.plan = (Plan *) node;
     sortstate->ss.ps.state = estate;
+    sortstate->ss.ps.ExecProcNode = ExecSort;

     /*
      * We must have random access to the sort output to do backward scan or
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index ae18470..088c929 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -79,9 +79,11 @@ SubqueryRecheck(SubqueryScanState *node, TupleTableSlot *slot)
  *        access method functions.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecSubqueryScan(SubqueryScanState *node)
+static TupleTableSlot *
+ExecSubqueryScan(PlanState *pstate)
 {
+    SubqueryScanState *node = castNode(SubqueryScanState, pstate);
+
     return ExecScan(&node->ss,
                     (ExecScanAccessMtd) SubqueryNext,
                     (ExecScanRecheckMtd) SubqueryRecheck);
@@ -109,6 +111,7 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
     subquerystate = makeNode(SubqueryScanState);
     subquerystate->ss.ps.plan = (Plan *) node;
     subquerystate->ss.ps.state = estate;
+    subquerystate->ss.ps.ExecProcNode = ExecSubqueryScan;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeTableFuncscan.c b/src/backend/executor/nodeTableFuncscan.c
index 2859363..b03d2ef 100644
--- a/src/backend/executor/nodeTableFuncscan.c
+++ b/src/backend/executor/nodeTableFuncscan.c
@@ -93,9 +93,11 @@ TableFuncRecheck(TableFuncScanState *node, TupleTableSlot *slot)
  *        access method functions.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecTableFuncScan(TableFuncScanState *node)
+static TupleTableSlot *
+ExecTableFuncScan(PlanState *pstate)
 {
+    TableFuncScanState *node = castNode(TableFuncScanState, pstate);
+
     return ExecScan(&node->ss,
                     (ExecScanAccessMtd) TableFuncNext,
                     (ExecScanRecheckMtd) TableFuncRecheck);
@@ -128,6 +130,7 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
     scanstate = makeNode(TableFuncScanState);
     scanstate->ss.ps.plan = (Plan *) node;
     scanstate->ss.ps.state = estate;
+    scanstate->ss.ps.ExecProcNode = ExecTableFuncScan;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index c122473..0ee76e7 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -445,9 +445,11 @@ TidRecheck(TidScanState *node, TupleTableSlot *slot)
  *          -- tidPtr is -1.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecTidScan(TidScanState *node)
+static TupleTableSlot *
+ExecTidScan(PlanState *pstate)
 {
+    TidScanState *node = castNode(TidScanState, pstate);
+
     return ExecScan(&node->ss,
                     (ExecScanAccessMtd) TidNext,
                     (ExecScanRecheckMtd) TidRecheck);
@@ -519,6 +521,7 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
     tidstate = makeNode(TidScanState);
     tidstate->ss.ps.plan = (Plan *) node;
     tidstate->ss.ps.state = estate;
+    tidstate->ss.ps.ExecProcNode = ExecTidScan;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c
index db78c88..621fdd9 100644
--- a/src/backend/executor/nodeUnique.c
+++ b/src/backend/executor/nodeUnique.c
@@ -43,9 +43,10 @@
  *        ExecUnique
  * ----------------------------------------------------------------
  */
-TupleTableSlot *                /* return: a tuple or NULL */
-ExecUnique(UniqueState *node)
+static TupleTableSlot *            /* return: a tuple or NULL */
+ExecUnique(PlanState *pstate)
 {
+    UniqueState *node = castNode(UniqueState, pstate);
     Unique       *plannode = (Unique *) node->ps.plan;
     TupleTableSlot *resultTupleSlot;
     TupleTableSlot *slot;
@@ -125,6 +126,7 @@ ExecInitUnique(Unique *node, EState *estate, int eflags)
     uniquestate = makeNode(UniqueState);
     uniquestate->ps.plan = (Plan *) node;
     uniquestate->ps.state = estate;
+    uniquestate->ps.ExecProcNode = ExecUnique;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9ee776c..6eacaed 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -185,9 +185,11 @@ ValuesRecheck(ValuesScanState *node, TupleTableSlot *slot)
  *        access method functions.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecValuesScan(ValuesScanState *node)
+static TupleTableSlot *
+ExecValuesScan(PlanState *pstate)
 {
+    ValuesScanState *node = castNode(ValuesScanState, pstate);
+
     return ExecScan(&node->ss,
                     (ExecScanAccessMtd) ValuesNext,
                     (ExecScanRecheckMtd) ValuesRecheck);
@@ -218,6 +220,7 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
     scanstate = makeNode(ValuesScanState);
     scanstate->ss.ps.plan = (Plan *) node;
     scanstate->ss.ps.state = estate;
+    scanstate->ss.ps.ExecProcNode = ExecValuesScan;

     /*
      * Miscellaneous initialization
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 9da35ac..80be460 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1587,9 +1587,10 @@ update_frametailpos(WindowObject winobj, TupleTableSlot *slot)
  *    returned rows is exactly the same as its outer subplan's result.
  * -----------------
  */
-TupleTableSlot *
-ExecWindowAgg(WindowAggState *winstate)
+static TupleTableSlot *
+ExecWindowAgg(PlanState *pstate)
 {
+    WindowAggState *winstate = castNode(WindowAggState, pstate);
     ExprContext *econtext;
     int            i;
     int            numfuncs;
@@ -1790,6 +1791,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
     winstate = makeNode(WindowAggState);
     winstate->ss.ps.plan = (Plan *) node;
     winstate->ss.ps.state = estate;
+    winstate->ss.ps.ExecProcNode = ExecWindowAgg;

     /*
      * Create expression contexts.  We need two, one for per-input-tuple
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index d7616be..d5ffadd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -77,9 +77,11 @@ WorkTableScanRecheck(WorkTableScanState *node, TupleTableSlot *slot)
  *        access method functions.
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
-ExecWorkTableScan(WorkTableScanState *node)
+static TupleTableSlot *
+ExecWorkTableScan(PlanState *pstate)
 {
+    WorkTableScanState *node = castNode(WorkTableScanState, pstate);
+
     /*
      * On the first call, find the ancestor RecursiveUnion's state via the
      * Param slot reserved for it.  (We can't do this during node init because
@@ -144,6 +146,7 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
     scanstate = makeNode(WorkTableScanState);
     scanstate->ss.ps.plan = (Plan *) node;
     scanstate->ss.ps.state = estate;
+    scanstate->ss.ps.ExecProcNode = ExecWorkTableScan;
     scanstate->rustate = NULL;    /* we'll set this later */

     /*
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 59c28b7..60326f9 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -225,14 +225,31 @@ extern void EvalPlanQualBegin(EPQState *epqstate, EState *parentestate);
 extern void EvalPlanQualEnd(EPQState *epqstate);

 /*
- * prototypes from functions in execProcnode.c
+ * functions in execProcnode.c
  */
 extern PlanState *ExecInitNode(Plan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecProcNode(PlanState *node);
 extern Node *MultiExecProcNode(PlanState *node);
 extern void ExecEndNode(PlanState *node);
 extern bool ExecShutdownNode(PlanState *node);

+
+/* ----------------------------------------------------------------
+ *        ExecProcNode
+ *
+ *        Execute the given node to return a(nother) tuple.
+ * ----------------------------------------------------------------
+ */
+#ifndef FRONTEND
+static inline TupleTableSlot *
+ExecProcNode(PlanState *node)
+{
+    if (node->chgParam != NULL) /* something changed? */
+        ExecReScan(node);        /* let ReScan handle this */
+
+    return node->ExecProcNode(node);
+}
+#endif
+
 /*
  * prototypes from functions in execExpr.c
  */
diff --git a/src/include/executor/nodeAgg.h b/src/include/executor/nodeAgg.h
index fa11ba9..eff5af9 100644
--- a/src/include/executor/nodeAgg.h
+++ b/src/include/executor/nodeAgg.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern AggState *ExecInitAgg(Agg *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecAgg(AggState *node);
 extern void ExecEndAgg(AggState *node);
 extern void ExecReScanAgg(AggState *node);

diff --git a/src/include/executor/nodeAppend.h b/src/include/executor/nodeAppend.h
index ee0b6ad..4e38a13 100644
--- a/src/include/executor/nodeAppend.h
+++ b/src/include/executor/nodeAppend.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern AppendState *ExecInitAppend(Append *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecAppend(AppendState *node);
 extern void ExecEndAppend(AppendState *node);
 extern void ExecReScanAppend(AppendState *node);

diff --git a/src/include/executor/nodeBitmapHeapscan.h b/src/include/executor/nodeBitmapHeapscan.h
index f477d1c..c77694c 100644
--- a/src/include/executor/nodeBitmapHeapscan.h
+++ b/src/include/executor/nodeBitmapHeapscan.h
@@ -18,7 +18,6 @@
 #include "access/parallel.h"

 extern BitmapHeapScanState *ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecBitmapHeapScan(BitmapHeapScanState *node);
 extern void ExecEndBitmapHeapScan(BitmapHeapScanState *node);
 extern void ExecReScanBitmapHeapScan(BitmapHeapScanState *node);
 extern void ExecBitmapHeapEstimate(BitmapHeapScanState *node,
diff --git a/src/include/executor/nodeCtescan.h b/src/include/executor/nodeCtescan.h
index 397bdfd..d2fbcbd 100644
--- a/src/include/executor/nodeCtescan.h
+++ b/src/include/executor/nodeCtescan.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern CteScanState *ExecInitCteScan(CteScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecCteScan(CteScanState *node);
 extern void ExecEndCteScan(CteScanState *node);
 extern void ExecReScanCteScan(CteScanState *node);

diff --git a/src/include/executor/nodeCustom.h b/src/include/executor/nodeCustom.h
index e81bcf7..a1cc63a 100644
--- a/src/include/executor/nodeCustom.h
+++ b/src/include/executor/nodeCustom.h
@@ -21,7 +21,6 @@
  */
 extern CustomScanState *ExecInitCustomScan(CustomScan *custom_scan,
                    EState *estate, int eflags);
-extern TupleTableSlot *ExecCustomScan(CustomScanState *node);
 extern void ExecEndCustomScan(CustomScanState *node);

 extern void ExecReScanCustomScan(CustomScanState *node);
diff --git a/src/include/executor/nodeForeignscan.h b/src/include/executor/nodeForeignscan.h
index 3ff4ecd..0b66259 100644
--- a/src/include/executor/nodeForeignscan.h
+++ b/src/include/executor/nodeForeignscan.h
@@ -18,7 +18,6 @@
 #include "nodes/execnodes.h"

 extern ForeignScanState *ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecForeignScan(ForeignScanState *node);
 extern void ExecEndForeignScan(ForeignScanState *node);
 extern void ExecReScanForeignScan(ForeignScanState *node);

diff --git a/src/include/executor/nodeFunctionscan.h b/src/include/executor/nodeFunctionscan.h
index 5e830eb..aaa9d8c 100644
--- a/src/include/executor/nodeFunctionscan.h
+++ b/src/include/executor/nodeFunctionscan.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern FunctionScanState *ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecFunctionScan(FunctionScanState *node);
 extern void ExecEndFunctionScan(FunctionScanState *node);
 extern void ExecReScanFunctionScan(FunctionScanState *node);

diff --git a/src/include/executor/nodeGather.h b/src/include/executor/nodeGather.h
index b000693..189bd70 100644
--- a/src/include/executor/nodeGather.h
+++ b/src/include/executor/nodeGather.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern GatherState *ExecInitGather(Gather *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecGather(GatherState *node);
 extern void ExecEndGather(GatherState *node);
 extern void ExecShutdownGather(GatherState *node);
 extern void ExecReScanGather(GatherState *node);
diff --git a/src/include/executor/nodeGatherMerge.h b/src/include/executor/nodeGatherMerge.h
index 14b31a0..0154d73 100644
--- a/src/include/executor/nodeGatherMerge.h
+++ b/src/include/executor/nodeGatherMerge.h
@@ -19,7 +19,6 @@
 extern GatherMergeState *ExecInitGatherMerge(GatherMerge *node,
                     EState *estate,
                     int eflags);
-extern TupleTableSlot *ExecGatherMerge(GatherMergeState *node);
 extern void ExecEndGatherMerge(GatherMergeState *node);
 extern void ExecReScanGatherMerge(GatherMergeState *node);
 extern void ExecShutdownGatherMerge(GatherMergeState *node);
diff --git a/src/include/executor/nodeGroup.h b/src/include/executor/nodeGroup.h
index 7358b61..b0d7e31 100644
--- a/src/include/executor/nodeGroup.h
+++ b/src/include/executor/nodeGroup.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern GroupState *ExecInitGroup(Group *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecGroup(GroupState *node);
 extern void ExecEndGroup(GroupState *node);
 extern void ExecReScanGroup(GroupState *node);

diff --git a/src/include/executor/nodeHash.h b/src/include/executor/nodeHash.h
index 8052f27..3ae556f 100644
--- a/src/include/executor/nodeHash.h
+++ b/src/include/executor/nodeHash.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern HashState *ExecInitHash(Hash *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecHash(HashState *node);
 extern Node *MultiExecHash(HashState *node);
 extern void ExecEndHash(HashState *node);
 extern void ExecReScanHash(HashState *node);
diff --git a/src/include/executor/nodeHashjoin.h b/src/include/executor/nodeHashjoin.h
index 541c81e..7469bfb 100644
--- a/src/include/executor/nodeHashjoin.h
+++ b/src/include/executor/nodeHashjoin.h
@@ -18,7 +18,6 @@
 #include "storage/buffile.h"

 extern HashJoinState *ExecInitHashJoin(HashJoin *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecHashJoin(HashJoinState *node);
 extern void ExecEndHashJoin(HashJoinState *node);
 extern void ExecReScanHashJoin(HashJoinState *node);

diff --git a/src/include/executor/nodeIndexonlyscan.h b/src/include/executor/nodeIndexonlyscan.h
index cf227da..c8a709c 100644
--- a/src/include/executor/nodeIndexonlyscan.h
+++ b/src/include/executor/nodeIndexonlyscan.h
@@ -18,7 +18,6 @@
 #include "access/parallel.h"

 extern IndexOnlyScanState *ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecIndexOnlyScan(IndexOnlyScanState *node);
 extern void ExecEndIndexOnlyScan(IndexOnlyScanState *node);
 extern void ExecIndexOnlyMarkPos(IndexOnlyScanState *node);
 extern void ExecIndexOnlyRestrPos(IndexOnlyScanState *node);
diff --git a/src/include/executor/nodeIndexscan.h b/src/include/executor/nodeIndexscan.h
index 0118234..1668e34 100644
--- a/src/include/executor/nodeIndexscan.h
+++ b/src/include/executor/nodeIndexscan.h
@@ -18,7 +18,6 @@
 #include "nodes/execnodes.h"

 extern IndexScanState *ExecInitIndexScan(IndexScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecIndexScan(IndexScanState *node);
 extern void ExecEndIndexScan(IndexScanState *node);
 extern void ExecIndexMarkPos(IndexScanState *node);
 extern void ExecIndexRestrPos(IndexScanState *node);
diff --git a/src/include/executor/nodeLimit.h b/src/include/executor/nodeLimit.h
index 7bb20d9..db65b55 100644
--- a/src/include/executor/nodeLimit.h
+++ b/src/include/executor/nodeLimit.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern LimitState *ExecInitLimit(Limit *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecLimit(LimitState *node);
 extern void ExecEndLimit(LimitState *node);
 extern void ExecReScanLimit(LimitState *node);

diff --git a/src/include/executor/nodeLockRows.h b/src/include/executor/nodeLockRows.h
index 6b90756..c9d05b8 100644
--- a/src/include/executor/nodeLockRows.h
+++ b/src/include/executor/nodeLockRows.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern LockRowsState *ExecInitLockRows(LockRows *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecLockRows(LockRowsState *node);
 extern void ExecEndLockRows(LockRowsState *node);
 extern void ExecReScanLockRows(LockRowsState *node);

diff --git a/src/include/executor/nodeMaterial.h b/src/include/executor/nodeMaterial.h
index f69abbc..4b3c257 100644
--- a/src/include/executor/nodeMaterial.h
+++ b/src/include/executor/nodeMaterial.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern MaterialState *ExecInitMaterial(Material *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecMaterial(MaterialState *node);
 extern void ExecEndMaterial(MaterialState *node);
 extern void ExecMaterialMarkPos(MaterialState *node);
 extern void ExecMaterialRestrPos(MaterialState *node);
diff --git a/src/include/executor/nodeMergeAppend.h b/src/include/executor/nodeMergeAppend.h
index 3cc6ef5..a0ccbae 100644
--- a/src/include/executor/nodeMergeAppend.h
+++ b/src/include/executor/nodeMergeAppend.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern MergeAppendState *ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecMergeAppend(MergeAppendState *node);
 extern void ExecEndMergeAppend(MergeAppendState *node);
 extern void ExecReScanMergeAppend(MergeAppendState *node);

diff --git a/src/include/executor/nodeMergejoin.h b/src/include/executor/nodeMergejoin.h
index 32df25a..d20e415 100644
--- a/src/include/executor/nodeMergejoin.h
+++ b/src/include/executor/nodeMergejoin.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern MergeJoinState *ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecMergeJoin(MergeJoinState *node);
 extern void ExecEndMergeJoin(MergeJoinState *node);
 extern void ExecReScanMergeJoin(MergeJoinState *node);

diff --git a/src/include/executor/nodeModifyTable.h b/src/include/executor/nodeModifyTable.h
index 5a406f2..a2e7af9 100644
--- a/src/include/executor/nodeModifyTable.h
+++ b/src/include/executor/nodeModifyTable.h
@@ -16,7 +16,6 @@
 #include "nodes/execnodes.h"

 extern ModifyTableState *ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecModifyTable(ModifyTableState *node);
 extern void ExecEndModifyTable(ModifyTableState *node);
 extern void ExecReScanModifyTable(ModifyTableState *node);

diff --git a/src/include/executor/nodeNamedtuplestorescan.h b/src/include/executor/nodeNamedtuplestorescan.h
index 7f72fbe..395d978 100644
--- a/src/include/executor/nodeNamedtuplestorescan.h
+++ b/src/include/executor/nodeNamedtuplestorescan.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern NamedTuplestoreScanState *ExecInitNamedTuplestoreScan(NamedTuplestoreScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecNamedTuplestoreScan(NamedTuplestoreScanState *node);
 extern void ExecEndNamedTuplestoreScan(NamedTuplestoreScanState *node);
 extern void ExecReScanNamedTuplestoreScan(NamedTuplestoreScanState *node);

diff --git a/src/include/executor/nodeNestloop.h b/src/include/executor/nodeNestloop.h
index 8e0fcc1..0d6486c 100644
--- a/src/include/executor/nodeNestloop.h
+++ b/src/include/executor/nodeNestloop.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern NestLoopState *ExecInitNestLoop(NestLoop *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecNestLoop(NestLoopState *node);
 extern void ExecEndNestLoop(NestLoopState *node);
 extern void ExecReScanNestLoop(NestLoopState *node);

diff --git a/src/include/executor/nodeProjectSet.h b/src/include/executor/nodeProjectSet.h
index 2f6999e..a0b0521 100644
--- a/src/include/executor/nodeProjectSet.h
+++ b/src/include/executor/nodeProjectSet.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern ProjectSetState *ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecProjectSet(ProjectSetState *node);
 extern void ExecEndProjectSet(ProjectSetState *node);
 extern void ExecReScanProjectSet(ProjectSetState *node);

diff --git a/src/include/executor/nodeRecursiveunion.h b/src/include/executor/nodeRecursiveunion.h
index f0eba05..e6ce1b4 100644
--- a/src/include/executor/nodeRecursiveunion.h
+++ b/src/include/executor/nodeRecursiveunion.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern RecursiveUnionState *ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecRecursiveUnion(RecursiveUnionState *node);
 extern void ExecEndRecursiveUnion(RecursiveUnionState *node);
 extern void ExecReScanRecursiveUnion(RecursiveUnionState *node);

diff --git a/src/include/executor/nodeResult.h b/src/include/executor/nodeResult.h
index 61d3cb2..20e0063 100644
--- a/src/include/executor/nodeResult.h
+++ b/src/include/executor/nodeResult.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern ResultState *ExecInitResult(Result *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecResult(ResultState *node);
 extern void ExecEndResult(ResultState *node);
 extern void ExecResultMarkPos(ResultState *node);
 extern void ExecResultRestrPos(ResultState *node);
diff --git a/src/include/executor/nodeSamplescan.h b/src/include/executor/nodeSamplescan.h
index ed06e77..607bbd9 100644
--- a/src/include/executor/nodeSamplescan.h
+++ b/src/include/executor/nodeSamplescan.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern SampleScanState *ExecInitSampleScan(SampleScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecSampleScan(SampleScanState *node);
 extern void ExecEndSampleScan(SampleScanState *node);
 extern void ExecReScanSampleScan(SampleScanState *node);

diff --git a/src/include/executor/nodeSeqscan.h b/src/include/executor/nodeSeqscan.h
index 06e0686..0fba79f 100644
--- a/src/include/executor/nodeSeqscan.h
+++ b/src/include/executor/nodeSeqscan.h
@@ -18,7 +18,6 @@
 #include "nodes/execnodes.h"

 extern SeqScanState *ExecInitSeqScan(SeqScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecSeqScan(SeqScanState *node);
 extern void ExecEndSeqScan(SeqScanState *node);
 extern void ExecReScanSeqScan(SeqScanState *node);

diff --git a/src/include/executor/nodeSetOp.h b/src/include/executor/nodeSetOp.h
index af85977..c15f945 100644
--- a/src/include/executor/nodeSetOp.h
+++ b/src/include/executor/nodeSetOp.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern SetOpState *ExecInitSetOp(SetOp *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecSetOp(SetOpState *node);
 extern void ExecEndSetOp(SetOpState *node);
 extern void ExecReScanSetOp(SetOpState *node);

diff --git a/src/include/executor/nodeSort.h b/src/include/executor/nodeSort.h
index 1d2b713..ed0e9db 100644
--- a/src/include/executor/nodeSort.h
+++ b/src/include/executor/nodeSort.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern SortState *ExecInitSort(Sort *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecSort(SortState *node);
 extern void ExecEndSort(SortState *node);
 extern void ExecSortMarkPos(SortState *node);
 extern void ExecSortRestrPos(SortState *node);
diff --git a/src/include/executor/nodeSubqueryscan.h b/src/include/executor/nodeSubqueryscan.h
index c852e29..710e050 100644
--- a/src/include/executor/nodeSubqueryscan.h
+++ b/src/include/executor/nodeSubqueryscan.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern SubqueryScanState *ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecSubqueryScan(SubqueryScanState *node);
 extern void ExecEndSubqueryScan(SubqueryScanState *node);
 extern void ExecReScanSubqueryScan(SubqueryScanState *node);

diff --git a/src/include/executor/nodeTableFuncscan.h b/src/include/executor/nodeTableFuncscan.h
index c58156e..c4672c0 100644
--- a/src/include/executor/nodeTableFuncscan.h
+++ b/src/include/executor/nodeTableFuncscan.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern TableFuncScanState *ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecTableFuncScan(TableFuncScanState *node);
 extern void ExecEndTableFuncScan(TableFuncScanState *node);
 extern void ExecReScanTableFuncScan(TableFuncScanState *node);

diff --git a/src/include/executor/nodeTidscan.h b/src/include/executor/nodeTidscan.h
index d07ed7c..e68aaf3 100644
--- a/src/include/executor/nodeTidscan.h
+++ b/src/include/executor/nodeTidscan.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern TidScanState *ExecInitTidScan(TidScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecTidScan(TidScanState *node);
 extern void ExecEndTidScan(TidScanState *node);
 extern void ExecReScanTidScan(TidScanState *node);

diff --git a/src/include/executor/nodeUnique.h b/src/include/executor/nodeUnique.h
index 3d0ac9d..008774a 100644
--- a/src/include/executor/nodeUnique.h
+++ b/src/include/executor/nodeUnique.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern UniqueState *ExecInitUnique(Unique *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecUnique(UniqueState *node);
 extern void ExecEndUnique(UniqueState *node);
 extern void ExecReScanUnique(UniqueState *node);

diff --git a/src/include/executor/nodeValuesscan.h b/src/include/executor/nodeValuesscan.h
index c28bb1a..772a5e9 100644
--- a/src/include/executor/nodeValuesscan.h
+++ b/src/include/executor/nodeValuesscan.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern ValuesScanState *ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecValuesScan(ValuesScanState *node);
 extern void ExecEndValuesScan(ValuesScanState *node);
 extern void ExecReScanValuesScan(ValuesScanState *node);

diff --git a/src/include/executor/nodeWindowAgg.h b/src/include/executor/nodeWindowAgg.h
index db1ad60..1c17730 100644
--- a/src/include/executor/nodeWindowAgg.h
+++ b/src/include/executor/nodeWindowAgg.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern WindowAggState *ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecWindowAgg(WindowAggState *node);
 extern void ExecEndWindowAgg(WindowAggState *node);
 extern void ExecReScanWindowAgg(WindowAggState *node);

diff --git a/src/include/executor/nodeWorktablescan.h b/src/include/executor/nodeWorktablescan.h
index c222d9f..df05e75 100644
--- a/src/include/executor/nodeWorktablescan.h
+++ b/src/include/executor/nodeWorktablescan.h
@@ -17,7 +17,6 @@
 #include "nodes/execnodes.h"

 extern WorkTableScanState *ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags);
-extern TupleTableSlot *ExecWorkTableScan(WorkTableScanState *node);
 extern void ExecEndWorkTableScan(WorkTableScanState *node);
 extern void ExecReScanWorkTableScan(WorkTableScanState *node);

diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 85fac8a..35c28a6 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -818,6 +818,18 @@ typedef struct DomainConstraintState
  * ----------------------------------------------------------------
  */

+struct PlanState;
+
+/* ----------------
+ *     ExecProcNodeMtd
+ *
+ * This is the method called by ExecProcNode to return the next tuple
+ * from an executor node.  It returns NULL, or an empty TupleTableSlot,
+ * if no more tuples are available.
+ * ----------------
+ */
+typedef TupleTableSlot *(*ExecProcNodeMtd) (struct PlanState *pstate);
+
 /* ----------------
  *        PlanState node
  *
@@ -835,6 +847,10 @@ typedef struct PlanState
                                  * nodes point to one EState for the whole
                                  * top-level plan */

+    ExecProcNodeMtd ExecProcNode;    /* function to return next tuple */
+    ExecProcNodeMtd ExecProcNodeReal;    /* actual function, if above is a
+                                         * wrapper */
+
     Instrumentation *instrument;    /* Optional runtime stats for this node */
     WorkerInstrumentation *worker_instrument;    /* per-worker instrumentation */


-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Mark Rofail
Дата:
Сообщение: Re: [HACKERS] GSoC 2017: Foreign Key Arrays
Следующее
От: Mark Rofail
Дата:
Сообщение: Re: [HACKERS] GSoC 2017: Foreign Key Arrays