1717 *-------------------------------------------------------------------------
1818 */
1919/*
20- * INTERFACE ROUTINES
21- * ExecInitNode - initialize a plan node and its subplans
22- * ExecProcNode - get a tuple by executing the plan node
23- * ExecEndNode - shut down a plan node and its subplans
24- *
2520 * NOTES
2621 * This used to be three files. It is now all combined into
27- * one file so that it is easier to keep ExecInitNode, ExecProcNode,
28- * and ExecEndNode in sync when new nodes are added.
22+ * one file so that it is easier to keep the dispatch routines
23+ * in sync when new nodes are added.
2924 *
3025 * EXAMPLE
3126 * Suppose we want the age of the manager of the shoe department and
122117#include "miscadmin.h"
123118
124119
120+ static TupleTableSlot * ExecProcNodeFirst (PlanState * node );
121+ static TupleTableSlot * ExecProcNodeInstr (PlanState * node );
122+
123+
125124/* ------------------------------------------------------------------------
126125 * ExecInitNode
127126 *
@@ -149,6 +148,13 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
149148 if (node == NULL )
150149 return NULL ;
151150
151+ /*
152+ * Make sure there's enough stack available. Need to check here, in
153+ * addition to ExecProcNode() (via ExecProcNodeFirst()), to ensure the
154+ * stack isn't overrun while initializing the node tree.
155+ */
156+ check_stack_depth ();
157+
152158 switch (nodeTag (node ))
153159 {
154160 /*
@@ -364,6 +370,13 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
364370 break ;
365371 }
366372
373+ /*
374+ * Add a wrapper around the ExecProcNode callback that checks stack depth
375+ * during the first execution.
376+ */
377+ result -> ExecProcNodeReal = result -> ExecProcNode ;
378+ result -> ExecProcNode = ExecProcNodeFirst ;
379+
367380 /*
368381 * Initialize any initPlans present in this node. The planner put them in
369382 * a separate list for us.
@@ -388,195 +401,51 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
388401}
389402
390403
391- /* ----------------------------------------------------------------
392- * ExecProcNode
393- *
394- * Execute the given node to return a(nother) tuple.
395- * ----------------------------------------------------------------
404+ /*
405+ * ExecProcNode wrapper that performs some one-time checks, before calling
406+ * the relevant node method (possibly via an instrumentation wrapper).
396407 */
397- TupleTableSlot *
398- ExecProcNode (PlanState * node )
408+ static TupleTableSlot *
409+ ExecProcNodeFirst (PlanState * node )
399410{
400- TupleTableSlot * result ;
401-
402- if (node -> chgParam != NULL ) /* something changed */
403- ExecReScan (node ); /* let ReScan handle this */
411+ /*
412+ * Perform stack depth check during the first execution of the node. We
413+ * only do so the first time round because it turns out to not be cheap on
414+ * some common architectures (eg. x86). This relies on the assumption that
415+ * ExecProcNode calls for a given plan node will always be made at roughly
416+ * the same stack depth.
417+ */
418+ check_stack_depth ();
404419
420+ /*
421+ * If instrumentation is required, change the wrapper to one that just
422+ * does instrumentation. Otherwise we can dispense with all wrappers and
423+ * have ExecProcNode() directly call the relevant function from now on.
424+ */
405425 if (node -> instrument )
406- InstrStartNode (node -> instrument );
407-
408- switch (nodeTag (node ))
409- {
410- /*
411- * control nodes
412- */
413- case T_ResultState :
414- result = ExecResult ((ResultState * ) node );
415- break ;
416-
417- case T_ProjectSetState :
418- result = ExecProjectSet ((ProjectSetState * ) node );
419- break ;
420-
421- case T_ModifyTableState :
422- result = ExecModifyTable ((ModifyTableState * ) node );
423- break ;
424-
425- case T_AppendState :
426- result = ExecAppend ((AppendState * ) node );
427- break ;
428-
429- case T_MergeAppendState :
430- result = ExecMergeAppend ((MergeAppendState * ) node );
431- break ;
432-
433- case T_RecursiveUnionState :
434- result = ExecRecursiveUnion ((RecursiveUnionState * ) node );
435- break ;
436-
437- /* BitmapAndState does not yield tuples */
438-
439- /* BitmapOrState does not yield tuples */
440-
441- /*
442- * scan nodes
443- */
444- case T_SeqScanState :
445- result = ExecSeqScan ((SeqScanState * ) node );
446- break ;
447-
448- case T_SampleScanState :
449- result = ExecSampleScan ((SampleScanState * ) node );
450- break ;
451-
452- case T_IndexScanState :
453- result = ExecIndexScan ((IndexScanState * ) node );
454- break ;
455-
456- case T_IndexOnlyScanState :
457- result = ExecIndexOnlyScan ((IndexOnlyScanState * ) node );
458- break ;
459-
460- /* BitmapIndexScanState does not yield tuples */
461-
462- case T_BitmapHeapScanState :
463- result = ExecBitmapHeapScan ((BitmapHeapScanState * ) node );
464- break ;
465-
466- case T_TidScanState :
467- result = ExecTidScan ((TidScanState * ) node );
468- break ;
469-
470- case T_SubqueryScanState :
471- result = ExecSubqueryScan ((SubqueryScanState * ) node );
472- break ;
473-
474- case T_FunctionScanState :
475- result = ExecFunctionScan ((FunctionScanState * ) node );
476- break ;
477-
478- case T_TableFuncScanState :
479- result = ExecTableFuncScan ((TableFuncScanState * ) node );
480- break ;
481-
482- case T_ValuesScanState :
483- result = ExecValuesScan ((ValuesScanState * ) node );
484- break ;
485-
486- case T_CteScanState :
487- result = ExecCteScan ((CteScanState * ) node );
488- break ;
489-
490- case T_NamedTuplestoreScanState :
491- result = ExecNamedTuplestoreScan ((NamedTuplestoreScanState * ) node );
492- break ;
493-
494- case T_WorkTableScanState :
495- result = ExecWorkTableScan ((WorkTableScanState * ) node );
496- break ;
497-
498- case T_ForeignScanState :
499- result = ExecForeignScan ((ForeignScanState * ) node );
500- break ;
501-
502- case T_CustomScanState :
503- result = ExecCustomScan ((CustomScanState * ) node );
504- break ;
505-
506- /*
507- * join nodes
508- */
509- case T_NestLoopState :
510- result = ExecNestLoop ((NestLoopState * ) node );
511- break ;
512-
513- case T_MergeJoinState :
514- result = ExecMergeJoin ((MergeJoinState * ) node );
515- break ;
516-
517- case T_HashJoinState :
518- result = ExecHashJoin ((HashJoinState * ) node );
519- break ;
520-
521- /*
522- * materialization nodes
523- */
524- case T_MaterialState :
525- result = ExecMaterial ((MaterialState * ) node );
526- break ;
527-
528- case T_SortState :
529- result = ExecSort ((SortState * ) node );
530- break ;
531-
532- case T_GroupState :
533- result = ExecGroup ((GroupState * ) node );
534- break ;
426+ node -> ExecProcNode = ExecProcNodeInstr ;
427+ else
428+ node -> ExecProcNode = node -> ExecProcNodeReal ;
535429
536- case T_AggState :
537- result = ExecAgg ((AggState * ) node );
538- break ;
539-
540- case T_WindowAggState :
541- result = ExecWindowAgg ((WindowAggState * ) node );
542- break ;
543-
544- case T_UniqueState :
545- result = ExecUnique ((UniqueState * ) node );
546- break ;
547-
548- case T_GatherState :
549- result = ExecGather ((GatherState * ) node );
550- break ;
551-
552- case T_GatherMergeState :
553- result = ExecGatherMerge ((GatherMergeState * ) node );
554- break ;
555-
556- case T_HashState :
557- result = ExecHash ((HashState * ) node );
558- break ;
430+ return node -> ExecProcNode (node );
431+ }
559432
560- case T_SetOpState :
561- result = ExecSetOp ((SetOpState * ) node );
562- break ;
563433
564- case T_LockRowsState :
565- result = ExecLockRows ((LockRowsState * ) node );
566- break ;
434+ /*
435+ * ExecProcNode wrapper that performs instrumentation calls. By keeping
436+ * this a separate function, we avoid overhead in the normal case where
437+ * no instrumentation is wanted.
438+ */
439+ static TupleTableSlot *
440+ ExecProcNodeInstr (PlanState * node )
441+ {
442+ TupleTableSlot * result ;
567443
568- case T_LimitState :
569- result = ExecLimit ((LimitState * ) node );
570- break ;
444+ InstrStartNode (node -> instrument );
571445
572- default :
573- elog (ERROR , "unrecognized node type: %d" , (int ) nodeTag (node ));
574- result = NULL ;
575- break ;
576- }
446+ result = node -> ExecProcNodeReal (node );
577447
578- if (node -> instrument )
579- InstrStopNode (node -> instrument , TupIsNull (result ) ? 0.0 : 1.0 );
448+ InstrStopNode (node -> instrument , TupIsNull (result ) ? 0.0 : 1.0 );
580449
581450 return result ;
582451}
@@ -600,6 +469,8 @@ MultiExecProcNode(PlanState *node)
600469{
601470 Node * result ;
602471
472+ check_stack_depth ();
473+
603474 CHECK_FOR_INTERRUPTS ();
604475
605476 if (node -> chgParam != NULL ) /* something changed */
@@ -657,6 +528,13 @@ ExecEndNode(PlanState *node)
657528 if (node == NULL )
658529 return ;
659530
531+ /*
532+ * Make sure there's enough stack available. Need to check here, in
533+ * addition to ExecProcNode() (via ExecProcNodeFirst()), because it's not
534+ * guaranteed that ExecProcNode() is reached for all nodes.
535+ */
536+ check_stack_depth ();
537+
660538 if (node -> chgParam != NULL )
661539 {
662540 bms_free (node -> chgParam );
@@ -855,6 +733,8 @@ ExecShutdownNode(PlanState *node)
855733 if (node == NULL )
856734 return false;
857735
736+ check_stack_depth ();
737+
858738 planstate_tree_walker (node , ExecShutdownNode , NULL );
859739
860740 switch (nodeTag (node ))
0 commit comments