Hi,

On 2017-07-17 00:26:29 -0700, Andres Freund wrote:
> I'm less convinced of that, due to the overhead argument.  I think
> there's a couple ways around that however:
> 
> 1) We could move ExecProcNode() to be callback based. The first time a
>    node is executed a "wrapper" function is called that does the stack
>    and potentially other checks. That also makes ExecProcNode() small
>    enough to be inlined, which ends up being good for jump target
>    prediction.   I've done something similar for v11 for expression
>    evaluation, getting rid of EEOP_*_FIRST duplication etc, and it seems
>    to work well.  The big disadvantage to that is that it's a bit
>    invasive for v10, and very likely too invasive to backpatch.
> 2) I think there's some fair argument to be made that ExecInitNode()'s
>    stack-space needs are similar enough to ExecProcNode()'s allowing us
>    to put a check_stack_depth() into the former.  That seems like it's
>    required anyway, since in many cases that's going to trigger
>    stack-depth exhaustion first anyway (unless we hit it in parse
>    analysis, which also seems quite common).

Attached is a trivial patch implementing 1) and a proof-of-concept hack
for 2).

The latter obviously isn't ready, but might make clearer what I'm
thinking about. If we were to go this route we'd have to probably move
the callback assignment into the ExecInit* routines, and possibly
replace the switch in ExecInitNode() with another callback, assigned in
make_*, and implement callbacks for ExecShutdown, MultiExecProcNode etc.

This results in a good speedup in tpc-h btw:
master total min: 46434.897 cb min: 44778.228 [diff -3.70]

- Andres
>From 06b88ddf63427e1f6aeb49feb48916c3351e3380 Mon Sep 17 00:00:00 2001
From: Andres Freund <and...@anarazel.de>
Date: Mon, 17 Jul 2017 00:33:49 -0700
Subject: [PATCH 1/2] Check stack-depth when initializing executor nodes.

Previously we only checked stack-depth during parse-analysis, but
that's not necessarily sufficient. In v10, where targetlist SRF
evaluation now is executor node based, this can indeed cause
problems. It's quite possible that earlier versions are also affected
in a bit different manner, or might become vulnerable due to future
changes.

To shore this up, add a stack-depth check to ExecInitNode(). As that's
called in the same recursive manner as ExecProcNode() /
MultiExecProcNode(), it should have similar stack usage as those,
without incurring noticeable overhead in queries processing many rows.

Author: Andres Freund
Reported-By: Julien Rouhaud
Discussion:
    https://postgr.es/m/22833.1490390...@sss.pgh.pa.us
    https://postgr.es/m/b0af9eaa-130c-60d0-9e4e-7a135b1e0...@dalibo.com
Backpatch: 9.2-, issue has existed for a long while
---
 src/backend/executor/execProcnode.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 294ad2cff9..360479d081 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -143,6 +143,15 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
 	List	   *subps;
 	ListCell   *l;
 
+	/*
+	 * Make sure there's enough stack available. Check that here, rather than
+	 * ExecProcNode() / MultiExecProcNode(), to avoid incurring overhead for
+	 * every single tuple. The assumption making this valid is that the
+	 * difference in stack use between node initialization and execution
+	 * should be far less than the safety margin from check_stack_depth().
+	 */
+	check_stack_depth();
+
 	/*
 	 * do nothing when we get to the end of a leaf on tree.
 	 */
-- 
2.13.1.392.g8d1b10321b.dirty

>From 2321fb83a8973c970ec934d3c1f121e739a7a20a Mon Sep 17 00:00:00 2001
From: Andres Freund <and...@anarazel.de>
Date: Mon, 17 Jul 2017 07:50:40 -0700
Subject: [PATCH 2/2] WIP: Move to callback based executor nodes.

---
 src/backend/executor/execProcnode.c | 436 +++++++-----------------------------
 src/include/executor/executor.h     |  30 ++-
 src/include/nodes/execnodes.h       |  13 ++
 3 files changed, 118 insertions(+), 361 deletions(-)

diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 360479d081..9d8727f41c 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -122,6 +122,15 @@
 #include "miscadmin.h"
 
 
+
+static TupleTableSlot *ExecProcNodeFirst(PlanState *node);
+static TupleTableSlot *ExecProcNodeInstr(PlanState *node);
+
+#define INIT_CB(node, name) \
+	(node)->ExecProcNode = ExecProcNodeFirst; \
+	(node)->ExecProcNodeReal = (ExecProcNodeCB) Exec##name; \
+	(node)->ExecEndNode = (ExecEndNodeCB) ExecEnd##name
+
 /* ------------------------------------------------------------------------
  *		ExecInitNode
  *
@@ -166,41 +175,49 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
 		case T_Result:
 			result = (PlanState *) ExecInitResult((Result *) node,
 												  estate, eflags);
+			INIT_CB(result, Result);
 			break;
 
 		case T_ProjectSet:
 			result = (PlanState *) ExecInitProjectSet((ProjectSet *) node,
 													  estate, eflags);
+			INIT_CB(result, ProjectSet);
 			break;
 
 		case T_ModifyTable:
 			result = (PlanState *) ExecInitModifyTable((ModifyTable *) node,
 													   estate, eflags);
+			INIT_CB(result, ModifyTable);
 			break;
 
 		case T_Append:
 			result = (PlanState *) ExecInitAppend((Append *) node,
 												  estate, eflags);
+			INIT_CB(result, Append);
 			break;
 
 		case T_MergeAppend:
 			result = (PlanState *) ExecInitMergeAppend((MergeAppend *) node,
 													   estate, eflags);
+			INIT_CB(result, MergeAppend);
 			break;
 
 		case T_RecursiveUnion:
 			result = (PlanState *) ExecInitRecursiveUnion((RecursiveUnion *) node,
 														  estate, eflags);
+			INIT_CB(result, RecursiveUnion);
 			break;
 
 		case T_BitmapAnd:
 			result = (PlanState *) ExecInitBitmapAnd((BitmapAnd *) node,
 													 estate, eflags);
+			result->ExecEndNode = (ExecEndNodeCB) ExecEndBitmapAnd;
 			break;
 
 		case T_BitmapOr:
 			result = (PlanState *) ExecInitBitmapOr((BitmapOr *) node,
 													estate, eflags);
+			result->ExecEndNode = (ExecEndNodeCB) ExecEndBitmapOr;
 			break;
 
 			/*
@@ -209,81 +226,97 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
 		case T_SeqScan:
 			result = (PlanState *) ExecInitSeqScan((SeqScan *) node,
 												   estate, eflags);
+			INIT_CB(result, SeqScan);
 			break;
 
 		case T_SampleScan:
 			result = (PlanState *) ExecInitSampleScan((SampleScan *) node,
 													  estate, eflags);
+			INIT_CB(result, SampleScan);
 			break;
 
 		case T_IndexScan:
 			result = (PlanState *) ExecInitIndexScan((IndexScan *) node,
 													 estate, eflags);
+			INIT_CB(result, IndexScan);
 			break;
 
 		case T_IndexOnlyScan:
 			result = (PlanState *) ExecInitIndexOnlyScan((IndexOnlyScan *) node,
 														 estate, eflags);
+			INIT_CB(result, IndexOnlyScan);
 			break;
 
 		case T_BitmapIndexScan:
 			result = (PlanState *) ExecInitBitmapIndexScan((BitmapIndexScan *) node,
 														   estate, eflags);
+			result->ExecEndNode =  (ExecEndNodeCB) ExecEndBitmapIndexScan;
 			break;
 
 		case T_BitmapHeapScan:
 			result = (PlanState *) ExecInitBitmapHeapScan((BitmapHeapScan *) node,
 														  estate, eflags);
+			INIT_CB(result, BitmapHeapScan);
 			break;
 
 		case T_TidScan:
 			result = (PlanState *) ExecInitTidScan((TidScan *) node,
 												   estate, eflags);
+			INIT_CB(result, TidScan);
 			break;
 
 		case T_SubqueryScan:
 			result = (PlanState *) ExecInitSubqueryScan((SubqueryScan *) node,
 														estate, eflags);
+			INIT_CB(result, SubqueryScan);
 			break;
 
 		case T_FunctionScan:
 			result = (PlanState *) ExecInitFunctionScan((FunctionScan *) node,
 														estate, eflags);
+			INIT_CB(result, FunctionScan);
 			break;
 
 		case T_TableFuncScan:
 			result = (PlanState *) ExecInitTableFuncScan((TableFuncScan *) node,
 														 estate, eflags);
+			INIT_CB(result, TableFuncScan);
 			break;
 
 		case T_ValuesScan:
 			result = (PlanState *) ExecInitValuesScan((ValuesScan *) node,
 													  estate, eflags);
+			INIT_CB(result, ValuesScan);
 			break;
 
 		case T_CteScan:
 			result = (PlanState *) ExecInitCteScan((CteScan *) node,
 												   estate, eflags);
+			INIT_CB(result, CteScan);
 			break;
 
 		case T_NamedTuplestoreScan:
 			result = (PlanState *) ExecInitNamedTuplestoreScan((NamedTuplestoreScan *) node,
 															   estate, eflags);
+			INIT_CB(result, NamedTuplestoreScan);
 			break;
 
 		case T_WorkTableScan:
 			result = (PlanState *) ExecInitWorkTableScan((WorkTableScan *) node,
 														 estate, eflags);
+			INIT_CB(result, WorkTableScan);
 			break;
 
 		case T_ForeignScan:
 			result = (PlanState *) ExecInitForeignScan((ForeignScan *) node,
 													   estate, eflags);
+			INIT_CB(result, ForeignScan);
 			break;
 
 		case T_CustomScan:
 			result = (PlanState *) ExecInitCustomScan((CustomScan *) node,
 													  estate, eflags);
+			INIT_CB(result, CustomScan);
 			break;
 
 			/*
@@ -292,16 +325,19 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
 		case T_NestLoop:
 			result = (PlanState *) ExecInitNestLoop((NestLoop *) node,
 													estate, eflags);
+			INIT_CB(result, NestLoop);
 			break;
 
 		case T_MergeJoin:
 			result = (PlanState *) ExecInitMergeJoin((MergeJoin *) node,
 													 estate, eflags);
+			INIT_CB(result, MergeJoin);
 			break;
 
 		case T_HashJoin:
 			result = (PlanState *) ExecInitHashJoin((HashJoin *) node,
 													estate, eflags);
+			INIT_CB(result, HashJoin);
 			break;
 
 			/*
@@ -310,61 +346,73 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
 		case T_Material:
 			result = (PlanState *) ExecInitMaterial((Material *) node,
 													estate, eflags);
+			INIT_CB(result, Material);
 			break;
 
 		case T_Sort:
 			result = (PlanState *) ExecInitSort((Sort *) node,
 												estate, eflags);
+			INIT_CB(result, Sort);
 			break;
 
 		case T_Group:
 			result = (PlanState *) ExecInitGroup((Group *) node,
 												 estate, eflags);
+			INIT_CB(result, Group);
 			break;
 
 		case T_Agg:
 			result = (PlanState *) ExecInitAgg((Agg *) node,
 											   estate, eflags);
+			INIT_CB(result, Agg);
 			break;
 
 		case T_WindowAgg:
 			result = (PlanState *) ExecInitWindowAgg((WindowAgg *) node,
 													 estate, eflags);
+			INIT_CB(result, WindowAgg);
 			break;
 
 		case T_Unique:
 			result = (PlanState *) ExecInitUnique((Unique *) node,
 												  estate, eflags);
+			INIT_CB(result, Unique);
 			break;
 
 		case T_Gather:
 			result = (PlanState *) ExecInitGather((Gather *) node,
 												  estate, eflags);
+			INIT_CB(result, Gather);
 			break;
 
 		case T_GatherMerge:
 			result = (PlanState *) ExecInitGatherMerge((GatherMerge *) node,
 													   estate, eflags);
+			INIT_CB(result, GatherMerge);
 			break;
 
 		case T_Hash:
 			result = (PlanState *) ExecInitHash((Hash *) node,
 												estate, eflags);
+			INIT_CB(result, Hash);
 			break;
 
 		case T_SetOp:
 			result = (PlanState *) ExecInitSetOp((SetOp *) node,
 												 estate, eflags);
+			INIT_CB(result, SetOp);
 			break;
 
 		case T_LockRows:
 			result = (PlanState *) ExecInitLockRows((LockRows *) node,
 													estate, eflags);
+			INIT_CB(result, LockRows);
 			break;
 
 		case T_Limit:
 			result = (PlanState *) ExecInitLimit((Limit *) node,
 												 estate, eflags);
+			INIT_CB(result, Limit);
 			break;
 
 		default:
@@ -397,202 +445,45 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
 }
 
 
-/* ----------------------------------------------------------------
- *		ExecProcNode
- *
- *		Execute the given node to return a(nother) tuple.
- * ----------------------------------------------------------------
+/*
+ * ExecProcNode helper that performs some additional checks, before calling
+ * the relevant executor node (possibly via an instrumentation wrapper).
  */
-TupleTableSlot *
-ExecProcNode(PlanState *node)
+static TupleTableSlot *
+ExecProcNodeFirst(PlanState *node)
+{
+	check_stack_depth();
+
+	if (node->instrument)
+		node->ExecProcNode = ExecProcNodeInstr;
+	else
+		node->ExecProcNode = node->ExecProcNodeReal;
+
+	return node->ExecProcNode(node);
+}
+
+
+/*
+ * Wrapper for the proper ExecProcNode invocation that performs
+ * instrumentation.  Keeping this in a separate function allows to avoid the
+ * overhead.
+ */
+static TupleTableSlot *
+ExecProcNodeInstr(PlanState *node)
 {
 	TupleTableSlot *result;
 
-	CHECK_FOR_INTERRUPTS();
+	InstrStartNode(node->instrument);
 
-	if (node->chgParam != NULL) /* something changed */
-		ExecReScan(node);		/* let ReScan handle this */
+	result = node->ExecProcNodeReal(node);
 
-	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;
-
-		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;
-
-		case T_SetOpState:
-			result = ExecSetOp((SetOpState *) node);
-			break;
-
-		case T_LockRowsState:
-			result = ExecLockRows((LockRowsState *) node);
-			break;
-
-		case T_LimitState:
-			result = ExecLimit((LimitState *) node);
-			break;
-
-		default:
-			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
-			result = NULL;
-			break;
-	}
-
-	if (node->instrument)
-		InstrStopNode(node->instrument, TupIsNull(result) ? 0.0 : 1.0);
+	InstrStopNode(node->instrument, TupIsNull(result) ? 0.0 : 1.0);
 
 	return result;
 }
 
 
+
 /* ----------------------------------------------------------------
  *		MultiExecProcNode
  *
@@ -674,180 +565,7 @@ ExecEndNode(PlanState *node)
 		node->chgParam = NULL;
 	}
 
-	switch (nodeTag(node))
-	{
-			/*
-			 * control nodes
-			 */
-		case T_ResultState:
-			ExecEndResult((ResultState *) node);
-			break;
-
-		case T_ProjectSetState:
-			ExecEndProjectSet((ProjectSetState *) node);
-			break;
-
-		case T_ModifyTableState:
-			ExecEndModifyTable((ModifyTableState *) node);
-			break;
-
-		case T_AppendState:
-			ExecEndAppend((AppendState *) node);
-			break;
-
-		case T_MergeAppendState:
-			ExecEndMergeAppend((MergeAppendState *) node);
-			break;
-
-		case T_RecursiveUnionState:
-			ExecEndRecursiveUnion((RecursiveUnionState *) node);
-			break;
-
-		case T_BitmapAndState:
-			ExecEndBitmapAnd((BitmapAndState *) node);
-			break;
-
-		case T_BitmapOrState:
-			ExecEndBitmapOr((BitmapOrState *) node);
-			break;
-
-			/*
-			 * scan nodes
-			 */
-		case T_SeqScanState:
-			ExecEndSeqScan((SeqScanState *) node);
-			break;
-
-		case T_SampleScanState:
-			ExecEndSampleScan((SampleScanState *) node);
-			break;
-
-		case T_GatherState:
-			ExecEndGather((GatherState *) node);
-			break;
-
-		case T_GatherMergeState:
-			ExecEndGatherMerge((GatherMergeState *) node);
-			break;
-
-		case T_IndexScanState:
-			ExecEndIndexScan((IndexScanState *) node);
-			break;
-
-		case T_IndexOnlyScanState:
-			ExecEndIndexOnlyScan((IndexOnlyScanState *) node);
-			break;
-
-		case T_BitmapIndexScanState:
-			ExecEndBitmapIndexScan((BitmapIndexScanState *) node);
-			break;
-
-		case T_BitmapHeapScanState:
-			ExecEndBitmapHeapScan((BitmapHeapScanState *) node);
-			break;
-
-		case T_TidScanState:
-			ExecEndTidScan((TidScanState *) node);
-			break;
-
-		case T_SubqueryScanState:
-			ExecEndSubqueryScan((SubqueryScanState *) node);
-			break;
-
-		case T_FunctionScanState:
-			ExecEndFunctionScan((FunctionScanState *) node);
-			break;
-
-		case T_TableFuncScanState:
-			ExecEndTableFuncScan((TableFuncScanState *) node);
-			break;
-
-		case T_ValuesScanState:
-			ExecEndValuesScan((ValuesScanState *) node);
-			break;
-
-		case T_CteScanState:
-			ExecEndCteScan((CteScanState *) node);
-			break;
-
-		case T_NamedTuplestoreScanState:
-			ExecEndNamedTuplestoreScan((NamedTuplestoreScanState *) node);
-			break;
-
-		case T_WorkTableScanState:
-			ExecEndWorkTableScan((WorkTableScanState *) node);
-			break;
-
-		case T_ForeignScanState:
-			ExecEndForeignScan((ForeignScanState *) node);
-			break;
-
-		case T_CustomScanState:
-			ExecEndCustomScan((CustomScanState *) node);
-			break;
-
-			/*
-			 * join nodes
-			 */
-		case T_NestLoopState:
-			ExecEndNestLoop((NestLoopState *) node);
-			break;
-
-		case T_MergeJoinState:
-			ExecEndMergeJoin((MergeJoinState *) node);
-			break;
-
-		case T_HashJoinState:
-			ExecEndHashJoin((HashJoinState *) node);
-			break;
-
-			/*
-			 * materialization nodes
-			 */
-		case T_MaterialState:
-			ExecEndMaterial((MaterialState *) node);
-			break;
-
-		case T_SortState:
-			ExecEndSort((SortState *) node);
-			break;
-
-		case T_GroupState:
-			ExecEndGroup((GroupState *) node);
-			break;
-
-		case T_AggState:
-			ExecEndAgg((AggState *) node);
-			break;
-
-		case T_WindowAggState:
-			ExecEndWindowAgg((WindowAggState *) node);
-			break;
-
-		case T_UniqueState:
-			ExecEndUnique((UniqueState *) node);
-			break;
-
-		case T_HashState:
-			ExecEndHash((HashState *) node);
-			break;
-
-		case T_SetOpState:
-			ExecEndSetOp((SetOpState *) node);
-			break;
-
-		case T_LockRowsState:
-			ExecEndLockRows((LockRowsState *) node);
-			break;
-
-		case T_LimitState:
-			ExecEndLimit((LimitState *) node);
-			break;
-
-		default:
-			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
-			break;
-	}
+	node->ExecEndNode(node);
 }
 
 /*
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e25cfa3aba..b79c8c07d7 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -18,6 +18,9 @@
 #include "executor/execdesc.h"
 #include "nodes/parsenodes.h"
 
+/* FIXME: NOT OK */
+#include "miscadmin.h"
+
 
 /*
  * The "eflags" argument to ExecutorStart and the various ExecInitNode
@@ -224,14 +227,37 @@ 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)
+{
+	TupleTableSlot *result;
+
+	CHECK_FOR_INTERRUPTS();
+
+	if (node->chgParam != NULL) /* something changed */
+		ExecReScan(node);		/* let ReScan handle this */
+
+	result = node->ExecProcNode(node);
+
+	return result;
+}
+#endif
+
 /*
  * prototypes from functions in execExpr.c
  */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 85fac8ab91..5b3936f622 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -818,6 +818,12 @@ typedef struct DomainConstraintState
  * ----------------------------------------------------------------
  */
 
+struct PlanState;
+
+typedef TupleTableSlot* (*ExecProcNodeCB) (struct PlanState *node);
+typedef void (*ExecEndNodeCB) (struct PlanState *node);
+
+
 /* ----------------
  *		PlanState node
  *
@@ -829,6 +835,13 @@ typedef struct PlanState
 {
 	NodeTag		type;
 
+	/* callback to execute node */
+	ExecProcNodeCB ExecProcNode;
+	/* actual callback, if above is wrapper */
+	ExecProcNodeCB ExecProcNodeReal;
+	/* shutdown node */
+	ExecEndNodeCB ExecEndNode;
+
 	Plan	   *plan;			/* associated Plan node */
 
 	EState	   *state;			/* at execution time, states of individual
-- 
2.13.1.392.g8d1b10321b.dirty

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

Reply via email to