From 16f2dea62bcff534a41f3185c349248498d01383 Mon Sep 17 00:00:00 2001 From: Amit Langote Date: Wed, 6 Sep 2023 17:53:34 +0900 Subject: [PATCH v47 3/8] Support for ExecInitNode() to detect CachedPlan invalidation This commit adds checks to determine if a CachedPlan remains valid during ExecInitNode() traversal of the plan from the CachedPlan. This includes points right after opening/locking tables and during recursive ExecInitNode() calls to initialize child plans. Depending on the situation, specific ExecInit*() routines will: * Return NULL if invalidation is spotted right after opening a table or after a function that opens one, but before initializing child nodes. * Return the partially initialized PlanState node if invalidation is found after recursively initializing a child node via ExecInitNode(). A prior commit already fortified ExecEnd*() to manage these partially initialized nodes. Importantly, this commit doesn't alter functionality. The CachedPlan isn't fed to the executor as of now, and the executor doesn't lock tables. Reviewed-by: Robert Haas --- contrib/postgres_fdw/postgres_fdw.c | 4 ++++ src/backend/executor/execMain.c | 24 ++++++++++++++++++++-- src/backend/executor/execPartition.c | 4 ++++ src/backend/executor/execProcnode.c | 19 ++++++++++++++++- src/backend/executor/execUtils.c | 2 ++ src/backend/executor/nodeAgg.c | 2 ++ src/backend/executor/nodeAppend.c | 14 ++++++++++--- src/backend/executor/nodeBitmapAnd.c | 11 +++++++--- src/backend/executor/nodeBitmapHeapscan.c | 4 ++++ src/backend/executor/nodeBitmapIndexscan.c | 2 ++ src/backend/executor/nodeBitmapOr.c | 11 +++++++--- src/backend/executor/nodeCustom.c | 2 ++ src/backend/executor/nodeForeignscan.c | 4 ++++ src/backend/executor/nodeGather.c | 3 +++ src/backend/executor/nodeGatherMerge.c | 2 ++ src/backend/executor/nodeGroup.c | 2 ++ src/backend/executor/nodeHash.c | 2 ++ src/backend/executor/nodeHashjoin.c | 4 ++++ src/backend/executor/nodeIncrementalSort.c | 2 ++ src/backend/executor/nodeIndexonlyscan.c | 4 ++++ src/backend/executor/nodeIndexscan.c | 4 ++++ src/backend/executor/nodeLimit.c | 2 ++ src/backend/executor/nodeLockRows.c | 2 ++ src/backend/executor/nodeMaterial.c | 2 ++ src/backend/executor/nodeMemoize.c | 2 ++ src/backend/executor/nodeMergeAppend.c | 10 ++++++++- src/backend/executor/nodeMergejoin.c | 4 ++++ src/backend/executor/nodeModifyTable.c | 7 +++++++ src/backend/executor/nodeNestloop.c | 4 ++++ src/backend/executor/nodeProjectSet.c | 2 ++ src/backend/executor/nodeRecursiveunion.c | 4 ++++ src/backend/executor/nodeResult.c | 2 ++ src/backend/executor/nodeSamplescan.c | 2 ++ src/backend/executor/nodeSeqscan.c | 2 ++ src/backend/executor/nodeSetOp.c | 2 ++ src/backend/executor/nodeSort.c | 2 ++ src/backend/executor/nodeSubqueryscan.c | 2 ++ src/backend/executor/nodeTidrangescan.c | 2 ++ src/backend/executor/nodeTidscan.c | 2 ++ src/backend/executor/nodeUnique.c | 2 ++ src/backend/executor/nodeWindowAgg.c | 2 ++ src/include/executor/executor.h | 10 +++++++++ src/include/nodes/execnodes.h | 2 ++ src/include/utils/plancache.h | 14 +++++++++++++ 44 files changed, 198 insertions(+), 13 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 1393716587..ab7ecb925c 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -2660,7 +2660,11 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* Get info about foreign table. */ rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) + { dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); + if (!ExecPlanStillValid(estate)) + return; + } else dmstate->rel = node->ss.ss_currentRelation; table = GetForeignTable(RelationGetRelid(dmstate->rel)); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 4c5a7bbf62..66f1b7398d 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -839,8 +839,8 @@ InitPlan(QueryDesc *queryDesc, int eflags) Plan *plan = plannedstmt->planTree; List *rangeTable = plannedstmt->rtable; EState *estate = queryDesc->estate; - PlanState *planstate; - TupleDesc tupType; + PlanState *planstate = NULL; + TupleDesc tupType = NULL; ListCell *l; int i; @@ -855,6 +855,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) ExecInitRangeTable(estate, rangeTable, plannedstmt->permInfos); estate->es_plannedstmt = plannedstmt; + estate->es_cachedplan = NULL; /* * Next, build the ExecRowMark array from the PlanRowMark(s), if any. @@ -886,6 +887,8 @@ InitPlan(QueryDesc *queryDesc, int eflags) case ROW_MARK_KEYSHARE: case ROW_MARK_REFERENCE: relation = ExecGetRangeTableRelation(estate, rc->rti); + if (!ExecPlanStillValid(estate)) + goto plan_init_suspended; break; case ROW_MARK_COPY: /* no physical table access is required */ @@ -956,6 +959,8 @@ InitPlan(QueryDesc *queryDesc, int eflags) estate->es_subplanstates = lappend(estate->es_subplanstates, subplanstate); + if (!ExecPlanStillValid(estate)) + goto plan_init_suspended; i++; } @@ -966,6 +971,8 @@ InitPlan(QueryDesc *queryDesc, int eflags) * processing tuples. */ planstate = ExecInitNode(plan, estate, eflags); + if (!ExecPlanStillValid(estate)) + goto plan_init_suspended; /* * Get the tuple descriptor describing the type of tuples to return. @@ -1007,6 +1014,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) } } +plan_init_suspended: queryDesc->tupDesc = tupType; queryDesc->planstate = planstate; } @@ -2945,6 +2953,12 @@ EvalPlanQualStart(EPQState *epqstate, Plan *planTree) PlanState *subplanstate; subplanstate = ExecInitNode(subplan, rcestate, 0); + + /* + * At this point, we shouldn't have received any new invalidation + * messages that would make the plan tree stale. + */ + Assert(ExecPlanStillValid(rcestate)); rcestate->es_subplanstates = lappend(rcestate->es_subplanstates, subplanstate); } @@ -2988,6 +3002,12 @@ EvalPlanQualStart(EPQState *epqstate, Plan *planTree) */ epqstate->recheckplanstate = ExecInitNode(planTree, rcestate, 0); + /* + * At this point, we shouldn't have received any new invalidation messages + * that would make the plan tree stale. + */ + Assert(ExecPlanStillValid(rcestate)); + MemoryContextSwitchTo(oldcontext); } diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index eb8a87fd63..e88455368c 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -1801,6 +1801,8 @@ ExecInitPartitionPruning(PlanState *planstate, /* Create the working data structure for pruning */ prunestate = CreatePartitionPruneState(planstate, pruneinfo); + if (!ExecPlanStillValid(estate)) + return NULL; /* * Perform an initial partition prune pass, if required. @@ -1927,6 +1929,8 @@ CreatePartitionPruneState(PlanState *planstate, PartitionPruneInfo *pruneinfo) * duration of this executor run. */ partrel = ExecGetRangeTableRelation(estate, pinfo->rtindex); + if (!ExecPlanStillValid(estate)) + return NULL; partkey = RelationGetPartitionKey(partrel); partdesc = PartitionDirectoryLookup(estate->es_partition_directory, partrel); diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c index 6098cdca69..169a52b038 100644 --- a/src/backend/executor/execProcnode.c +++ b/src/backend/executor/execProcnode.c @@ -135,7 +135,20 @@ static bool ExecShutdownNode_walker(PlanState *node, void *context); * 'estate' is the shared execution state for the plan tree * 'eflags' is a bitwise OR of flag bits described in executor.h * - * Returns a PlanState node corresponding to the given Plan node. + * Returns a PlanState node corresponding to the given Plan node or NULL. + * + * The node type-specific ExecInit* routines listed in this function can + * either return NULL or a partially initialized PlanState tree when they + * detect that the CachedPlan has been invalidated. This is determined by + * invoking ExecPlanStillValid() at key intervals, for instance, right + * after opening/locking a relation, or following the call to a function + * that might open/lock a relation. The latter involves recursive calls + * to ExecInitNode() for child node initialization. If an ExecInit* + * routine gets a false from ExecPlanStillValid(), it should: + * - Return NULL if no child node was initialized at the time of + * checking. + * - Provide the partially initialized PlanState node if any child node + * was set up recursively by then. * ------------------------------------------------------------------------ */ PlanState * @@ -388,6 +401,10 @@ ExecInitNode(Plan *node, EState *estate, int eflags) break; } + if (!ExecPlanStillValid(estate)) + return result; + + Assert(result != NULL); ExecSetExecProcNode(result, result->ExecProcNode); /* diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 16704c0c2f..c3f7279b06 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -822,6 +822,8 @@ ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo, Relation resultRelationDesc; resultRelationDesc = ExecGetRangeTableRelation(estate, rti); + if (!ExecPlanStillValid(estate)) + return; InitResultRelInfo(resultRelInfo, resultRelationDesc, rti, diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index f154f28902..b70e8c2cd6 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -3304,6 +3304,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) eflags &= ~EXEC_FLAG_REWIND; outerPlan = outerPlan(node); outerPlanState(aggstate) = ExecInitNode(outerPlan, estate, eflags); + if (!ExecPlanStillValid(estate)) + return aggstate; /* * initialize source tuple type. diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 609df6b9e6..588f5388c7 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -147,6 +147,8 @@ ExecInitAppend(Append *node, EState *estate, int eflags) list_length(node->appendplans), node->part_prune_info, &validsubplans); + if (!ExecPlanStillValid(estate)) + return NULL; appendstate->as_prune_state = prunestate; nplans = bms_num_members(validsubplans); @@ -185,8 +187,13 @@ ExecInitAppend(Append *node, EState *estate, int eflags) appendstate->ps.resultopsset = true; appendstate->ps.resultopsfixed = false; - appendplanstates = (PlanState **) palloc(nplans * - sizeof(PlanState *)); + /* + * Any uninitialized sunbodes will have NULL in appendplans in the case of + * an early return. + */ + appendstate->appendplans = appendplanstates = + (PlanState **) palloc0(nplans * sizeof(PlanState *)); + appendstate->as_nplans = nplans; /* * call ExecInitNode on each of the valid plans to be executed and save @@ -221,11 +228,12 @@ ExecInitAppend(Append *node, EState *estate, int eflags) firstvalid = j; appendplanstates[j++] = ExecInitNode(initNode, estate, eflags); + if (!ExecPlanStillValid(estate)) + return appendstate; } appendstate->as_first_partial_plan = firstvalid; appendstate->appendplans = appendplanstates; - appendstate->as_nplans = nplans; /* Initialize async state */ appendstate->as_asyncplans = asyncplans; diff --git a/src/backend/executor/nodeBitmapAnd.c b/src/backend/executor/nodeBitmapAnd.c index 4c5eb2b23b..c0495ec90f 100644 --- a/src/backend/executor/nodeBitmapAnd.c +++ b/src/backend/executor/nodeBitmapAnd.c @@ -69,6 +69,10 @@ ExecInitBitmapAnd(BitmapAnd *node, EState *estate, int eflags) */ nplans = list_length(node->bitmapplans); + /* + * Any uninitialized sunbodes will have NULL in bitmapplans in the case of + * an early return. + */ bitmapplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); /* @@ -78,7 +82,6 @@ ExecInitBitmapAnd(BitmapAnd *node, EState *estate, int eflags) bitmapandstate->ps.state = estate; bitmapandstate->ps.ExecProcNode = ExecBitmapAnd; bitmapandstate->bitmapplans = bitmapplanstates; - bitmapandstate->nplans = nplans; /* * call ExecInitNode on each of the plans to be executed and save the @@ -88,8 +91,10 @@ ExecInitBitmapAnd(BitmapAnd *node, EState *estate, int eflags) foreach(l, node->bitmapplans) { initNode = (Plan *) lfirst(l); - bitmapplanstates[i] = ExecInitNode(initNode, estate, eflags); - i++; + bitmapplanstates[i++] = ExecInitNode(initNode, estate, eflags); + if (!ExecPlanStillValid(estate)) + return bitmapandstate; + bitmapandstate->nplans = i; } /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index c8c1c9d88e..715c111be9 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -752,11 +752,15 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) * open the scan relation */ currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags); + if (!ExecPlanStillValid(estate)) + return scanstate; /* * initialize child nodes */ outerPlanState(scanstate) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return scanstate; /* * get the scan type from the relation descriptor. diff --git a/src/backend/executor/nodeBitmapIndexscan.c b/src/backend/executor/nodeBitmapIndexscan.c index 7cf8532bc9..4200472d02 100644 --- a/src/backend/executor/nodeBitmapIndexscan.c +++ b/src/backend/executor/nodeBitmapIndexscan.c @@ -255,6 +255,8 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags) /* Open the index relation. */ lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode; indexstate->biss_RelationDesc = index_open(node->indexid, lockmode); + if (!ExecPlanStillValid(estate)) + return indexstate; /* * Initialize index-specific scan state diff --git a/src/backend/executor/nodeBitmapOr.c b/src/backend/executor/nodeBitmapOr.c index 0bf8af9652..00120669a5 100644 --- a/src/backend/executor/nodeBitmapOr.c +++ b/src/backend/executor/nodeBitmapOr.c @@ -70,6 +70,10 @@ ExecInitBitmapOr(BitmapOr *node, EState *estate, int eflags) */ nplans = list_length(node->bitmapplans); + /* + * Any uninitialized sunbodes will have NULL in bitmapplans in the case of + * an early return. + */ bitmapplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); /* @@ -79,7 +83,6 @@ ExecInitBitmapOr(BitmapOr *node, EState *estate, int eflags) bitmaporstate->ps.state = estate; bitmaporstate->ps.ExecProcNode = ExecBitmapOr; bitmaporstate->bitmapplans = bitmapplanstates; - bitmaporstate->nplans = nplans; /* * call ExecInitNode on each of the plans to be executed and save the @@ -89,8 +92,10 @@ ExecInitBitmapOr(BitmapOr *node, EState *estate, int eflags) foreach(l, node->bitmapplans) { initNode = (Plan *) lfirst(l); - bitmapplanstates[i] = ExecInitNode(initNode, estate, eflags); - i++; + bitmapplanstates[i++] = ExecInitNode(initNode, estate, eflags); + if (!ExecPlanStillValid(estate)) + return bitmaporstate; + bitmaporstate->nplans = i; } /* diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 28b5bb9353..160eeee071 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -61,6 +61,8 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) if (scanrelid > 0) { scan_rel = ExecOpenScanRelation(estate, scanrelid, eflags); + if (!ExecPlanStillValid(estate)) + return NULL; css->ss.ss_currentRelation = scan_rel; } diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index 298ea59a1e..0f29b03977 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -173,6 +173,8 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) if (scanrelid > 0) { currentRelation = ExecOpenScanRelation(estate, scanrelid, eflags); + if (!ExecPlanStillValid(estate)) + return NULL; scanstate->ss.ss_currentRelation = currentRelation; fdwroutine = GetFdwRoutineForRelation(currentRelation, true); } @@ -264,6 +266,8 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) if (outerPlan(node)) outerPlanState(scanstate) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return scanstate; /* * Tell the FDW to initialize the scan. diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index bb2500a469..6b26e03f74 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -89,6 +89,9 @@ ExecInitGather(Gather *node, EState *estate, int eflags) */ outerNode = outerPlan(node); outerPlanState(gatherstate) = ExecInitNode(outerNode, estate, eflags); + if (!ExecPlanStillValid(estate)) + return gatherstate; + tupDesc = ExecGetResultType(outerPlanState(gatherstate)); /* diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c index 7a71a58509..84412f94bb 100644 --- a/src/backend/executor/nodeGatherMerge.c +++ b/src/backend/executor/nodeGatherMerge.c @@ -108,6 +108,8 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags) */ outerNode = outerPlan(node); outerPlanState(gm_state) = ExecInitNode(outerNode, estate, eflags); + if (!ExecPlanStillValid(estate)) + return gm_state; /* * Leader may access ExecProcNode result directly (if diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index 8c650f0e46..b6068887f6 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -185,6 +185,8 @@ ExecInitGroup(Group *node, EState *estate, int eflags) * initialize child nodes */ outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return grpstate; /* * Initialize scan slot and type. diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index e72f0986c2..030bf0ed43 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -386,6 +386,8 @@ ExecInitHash(Hash *node, EState *estate, int eflags) * initialize child nodes */ outerPlanState(hashstate) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return hashstate; /* * initialize our result slot and type. No need to build projection diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index aea44a9d56..49a6ba4276 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -752,8 +752,12 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) hashNode = (Hash *) innerPlan(node); outerPlanState(hjstate) = ExecInitNode(outerNode, estate, eflags); + if (!ExecPlanStillValid(estate)) + return hjstate; outerDesc = ExecGetResultType(outerPlanState(hjstate)); innerPlanState(hjstate) = ExecInitNode((Plan *) hashNode, estate, eflags); + if (!ExecPlanStillValid(estate)) + return hjstate; innerDesc = ExecGetResultType(innerPlanState(hjstate)); /* diff --git a/src/backend/executor/nodeIncrementalSort.c b/src/backend/executor/nodeIncrementalSort.c index 544e64dfab..e83feae353 100644 --- a/src/backend/executor/nodeIncrementalSort.c +++ b/src/backend/executor/nodeIncrementalSort.c @@ -1041,6 +1041,8 @@ ExecInitIncrementalSort(IncrementalSort *node, EState *estate, int eflags) * nodes may be able to do something more useful. */ outerPlanState(incrsortstate) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return incrsortstate; /* * Initialize scan slot and type. diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index f1db35665c..ea7fd89c0c 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -496,6 +496,8 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) * open the scan relation */ currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags); + if (!ExecPlanStillValid(estate)) + return NULL; indexstate->ss.ss_currentRelation = currentRelation; indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */ @@ -549,6 +551,8 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) /* Open the index relation. */ lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode; indexstate->ioss_RelationDesc = index_open(node->indexid, lockmode); + if (!ExecPlanStillValid(estate)) + return NULL; /* * Initialize index-specific scan state diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 14b9c00217..906358011a 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -909,6 +909,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) * open the scan relation */ currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags); + if (!ExecPlanStillValid(estate)) + return NULL; indexstate->ss.ss_currentRelation = currentRelation; indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */ @@ -954,6 +956,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) /* Open the index relation. */ lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode; indexstate->iss_RelationDesc = index_open(node->indexid, lockmode); + if (!ExecPlanStillValid(estate)) + return NULL; /* * Initialize index-specific scan state diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c index 5654158e3e..6760de0f25 100644 --- a/src/backend/executor/nodeLimit.c +++ b/src/backend/executor/nodeLimit.c @@ -476,6 +476,8 @@ ExecInitLimit(Limit *node, EState *estate, int eflags) */ outerPlan = outerPlan(node); outerPlanState(limitstate) = ExecInitNode(outerPlan, estate, eflags); + if (!ExecPlanStillValid(estate)) + return limitstate; /* * initialize child expressions diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c index e459971d32..2599332f01 100644 --- a/src/backend/executor/nodeLockRows.c +++ b/src/backend/executor/nodeLockRows.c @@ -322,6 +322,8 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags) * then initialize outer plan */ outerPlanState(lrstate) = ExecInitNode(outerPlan, estate, eflags); + if (!ExecPlanStillValid(estate)) + return lrstate; /* node returns unmodified slots from the outer plan */ lrstate->ps.resultopsset = true; diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c index 753ea28915..b974ebdc8a 100644 --- a/src/backend/executor/nodeMaterial.c +++ b/src/backend/executor/nodeMaterial.c @@ -214,6 +214,8 @@ ExecInitMaterial(Material *node, EState *estate, int eflags) outerPlan = outerPlan(node); outerPlanState(matstate) = ExecInitNode(outerPlan, estate, eflags); + if (!ExecPlanStillValid(estate)) + return matstate; /* * Initialize result type and slot. No need to initialize projection info diff --git a/src/backend/executor/nodeMemoize.c b/src/backend/executor/nodeMemoize.c index 81f2acde5e..ac0a8a0ae4 100644 --- a/src/backend/executor/nodeMemoize.c +++ b/src/backend/executor/nodeMemoize.c @@ -938,6 +938,8 @@ ExecInitMemoize(Memoize *node, EState *estate, int eflags) outerNode = outerPlan(node); outerPlanState(mstate) = ExecInitNode(outerNode, estate, eflags); + if (!ExecPlanStillValid(estate)) + return mstate; /* * Initialize return slot and type. No need to initialize projection info diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c index 21b5726e6e..c9d406c230 100644 --- a/src/backend/executor/nodeMergeAppend.c +++ b/src/backend/executor/nodeMergeAppend.c @@ -95,6 +95,8 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags) list_length(node->mergeplans), node->part_prune_info, &validsubplans); + if (!ExecPlanStillValid(estate)) + return NULL; mergestate->ms_prune_state = prunestate; nplans = bms_num_members(validsubplans); @@ -120,7 +122,11 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags) mergestate->ms_prune_state = NULL; } - mergeplanstates = (PlanState **) palloc(nplans * sizeof(PlanState *)); + /* + * Any uninitialized sunbodes will have NULL in mergeplans in the case of + * an early return. + */ + mergeplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); mergestate->mergeplans = mergeplanstates; mergestate->ms_nplans = nplans; @@ -151,6 +157,8 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags) Plan *initNode = (Plan *) list_nth(node->mergeplans, i); mergeplanstates[j++] = ExecInitNode(initNode, estate, eflags); + if (!ExecPlanStillValid(estate)) + return mergestate; } mergestate->ps.ps_ProjInfo = NULL; diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 648fdd9a5f..e7f4512419 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -1490,11 +1490,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) mergestate->mj_SkipMarkRestore = node->skip_mark_restore; outerPlanState(mergestate) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return mergestate; outerDesc = ExecGetResultType(outerPlanState(mergestate)); innerPlanState(mergestate) = ExecInitNode(innerPlan(node), estate, mergestate->mj_SkipMarkRestore ? eflags : (eflags | EXEC_FLAG_MARK)); + if (!ExecPlanStillValid(estate)) + return mergestate; innerDesc = ExecGetResultType(innerPlanState(mergestate)); /* diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index d21a178ad5..c28d5058e9 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -3985,6 +3985,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) linitial_int(node->resultRelations)); } + if (!ExecPlanStillValid(estate)) + return NULL; + /* set up epqstate with dummy subplan data for the moment */ EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam, node->resultRelations); @@ -4012,6 +4015,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) if (resultRelInfo != mtstate->rootResultRelInfo) { ExecInitResultRelation(estate, resultRelInfo, resultRelation); + if (!ExecPlanStillValid(estate)) + return NULL; /* * For child result relations, store the root result relation @@ -4039,6 +4044,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * Now we may initialize the subplan. */ outerPlanState(mtstate) = ExecInitNode(subplan, estate, eflags); + if (!ExecPlanStillValid(estate)) + return mtstate; /* * Do additional per-result-relation initialization. diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index fc8f833d8b..0158a3e592 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -295,11 +295,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) * values. */ outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return nlstate; if (node->nestParams == NIL) eflags |= EXEC_FLAG_REWIND; else eflags &= ~EXEC_FLAG_REWIND; innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return nlstate; /* * Initialize result slot, type and projection. diff --git a/src/backend/executor/nodeProjectSet.c b/src/backend/executor/nodeProjectSet.c index b4bbdc89b1..1b4774d4f7 100644 --- a/src/backend/executor/nodeProjectSet.c +++ b/src/backend/executor/nodeProjectSet.c @@ -247,6 +247,8 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags) * initialize child nodes */ outerPlanState(state) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return state; /* * we don't use inner plan diff --git a/src/backend/executor/nodeRecursiveunion.c b/src/backend/executor/nodeRecursiveunion.c index 54cd6f2347..6398475c62 100644 --- a/src/backend/executor/nodeRecursiveunion.c +++ b/src/backend/executor/nodeRecursiveunion.c @@ -244,7 +244,11 @@ ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags) * initialize child nodes */ outerPlanState(rustate) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return rustate; innerPlanState(rustate) = ExecInitNode(innerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return rustate; /* * If hashing, precompute fmgr lookup data for inner loop, and create the diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index e9f5732f33..d4ea101cbe 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -208,6 +208,8 @@ ExecInitResult(Result *node, EState *estate, int eflags) * initialize child nodes */ outerPlanState(resstate) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return resstate; /* * we don't use inner plan diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 41c1ea37ad..5bec5c1f64 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -125,6 +125,8 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) ExecOpenScanRelation(estate, node->scan.scanrelid, eflags); + if (!ExecPlanStillValid(estate)) + return NULL; /* we won't set up the HeapScanDesc till later */ scanstate->ss.ss_currentScanDesc = NULL; diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 49a5933aff..48e20aa735 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -153,6 +153,8 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) ExecOpenScanRelation(estate, node->scan.scanrelid, eflags); + if (!ExecPlanStillValid(estate)) + return NULL; /* and create slot with the appropriate rowtype */ ExecInitScanTupleSlot(estate, &scanstate->ss, diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c index 98c1b84d43..7a3a142204 100644 --- a/src/backend/executor/nodeSetOp.c +++ b/src/backend/executor/nodeSetOp.c @@ -528,6 +528,8 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags) if (node->strategy == SETOP_HASHED) eflags &= ~EXEC_FLAG_REWIND; outerPlanState(setopstate) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return setopstate; outerDesc = ExecGetResultType(outerPlanState(setopstate)); /* diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c index eea7f2ae15..3ebbc46604 100644 --- a/src/backend/executor/nodeSort.c +++ b/src/backend/executor/nodeSort.c @@ -263,6 +263,8 @@ ExecInitSort(Sort *node, EState *estate, int eflags) eflags &= ~(EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK); outerPlanState(sortstate) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return sortstate; /* * Initialize scan slot and type. diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 1ee6295660..3c5c7c2ebb 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -124,6 +124,8 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) * initialize subquery */ subquerystate->subplan = ExecInitNode(node->subplan, estate, eflags); + if (!ExecPlanStillValid(estate)) + return subquerystate; /* * Initialize scan slot and type (needed by ExecAssignScanProjectionInfo) diff --git a/src/backend/executor/nodeTidrangescan.c b/src/backend/executor/nodeTidrangescan.c index da622d3f5f..d337f3d54a 100644 --- a/src/backend/executor/nodeTidrangescan.c +++ b/src/backend/executor/nodeTidrangescan.c @@ -374,6 +374,8 @@ ExecInitTidRangeScan(TidRangeScan *node, EState *estate, int eflags) * open the scan relation */ currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags); + if (!ExecPlanStillValid(estate)) + return NULL; tidrangestate->ss.ss_currentRelation = currentRelation; tidrangestate->ss.ss_currentScanDesc = NULL; /* no table scan here */ diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 15055077d0..9637f354b2 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -517,6 +517,8 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) * open the scan relation */ currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags); + if (!ExecPlanStillValid(estate)) + return NULL; tidstate->ss.ss_currentRelation = currentRelation; tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */ diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c index 01f951197c..28630e380e 100644 --- a/src/backend/executor/nodeUnique.c +++ b/src/backend/executor/nodeUnique.c @@ -136,6 +136,8 @@ ExecInitUnique(Unique *node, EState *estate, int eflags) * then initialize outer plan */ outerPlanState(uniquestate) = ExecInitNode(outerPlan(node), estate, eflags); + if (!ExecPlanStillValid(estate)) + return uniquestate; /* * Initialize result slot and type. Unique nodes do no projections, so diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index f5170799e4..29151fe44b 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -2461,6 +2461,8 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) */ outerPlan = outerPlan(node); outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags); + if (!ExecPlanStillValid(estate)) + return winstate; /* * initialize source tuple type (which is also the tuple type that we'll diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index aeebe0e0ff..72cbf120c5 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -19,6 +19,7 @@ #include "nodes/lockoptions.h" #include "nodes/parsenodes.h" #include "utils/memutils.h" +#include "utils/plancache.h" /* @@ -256,6 +257,15 @@ extern void ExecEndNode(PlanState *node); extern void ExecShutdownNode(PlanState *node); extern void ExecSetTupleBound(int64 tuples_needed, PlanState *child_node); +/* + * Is the CachedPlan in es_cachedplan still valid? + */ +static inline bool +ExecPlanStillValid(EState *estate) +{ + return estate->es_cachedplan == NULL ? true : + CachedPlanStillValid(estate->es_cachedplan); +} /* ---------------------------------------------------------------- * ExecProcNode diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index cb714f4a19..b2a576b76d 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -623,6 +623,8 @@ typedef struct EState * ExecRowMarks, or NULL if none */ List *es_rteperminfos; /* List of RTEPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ + struct CachedPlan *es_cachedplan; /* CachedPlan if plannedstmt is from + * one or NULL if not */ const char *es_sourceText; /* Source text from QueryDesc */ JunkFilter *es_junkFilter; /* top-level junk filter, if any */ diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h index 916e59d9fe..0a9e041d51 100644 --- a/src/include/utils/plancache.h +++ b/src/include/utils/plancache.h @@ -221,6 +221,20 @@ extern CachedPlan *GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams, ResourceOwner owner, QueryEnvironment *queryEnv); + +/* + * CachedPlanStillValid + * Returns if a cached generic plan is still valid + * + * Invoked by the executor for each relation lock acquired during the + * initialization of the plan tree within the CachedPlan. + */ +static inline bool +CachedPlanStillValid(CachedPlan *cplan) +{ + return cplan->is_valid; +} + extern void ReleaseCachedPlan(CachedPlan *plan, ResourceOwner owner); extern bool CachedPlanAllowsSimpleValidityCheck(CachedPlanSource *plansource, -- 2.35.3