diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c index 8b5eaf3ef3..623a674f99 100644 --- a/contrib/auto_explain/auto_explain.c +++ b/contrib/auto_explain/auto_explain.c @@ -298,10 +298,6 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags) else standard_ExecutorStart(queryDesc, eflags); - /* The plan may have become invalid during standard_ExecutorStart() */ - if (!ExecPlanStillValid(queryDesc->estate)) - return; - if (auto_explain_enabled()) { /* diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index b11691ae26..49c657b3e0 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -994,10 +994,6 @@ pgss_ExecutorStart(QueryDesc *queryDesc, int eflags) else standard_ExecutorStart(queryDesc, eflags); - /* The plan may have become invalid during standard_ExecutorStart() */ - if (!ExecPlanStillValid(queryDesc->estate)) - return; - /* * If query has queryId zero, don't track it. This prevents double * counting of optimizable statements that are directly contained in diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 9543d9490c..18758287bf 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -123,6 +123,16 @@ static void EvalPlanQualStart(EPQState *epqstate, Plan *planTree); void ExecutorStart(QueryDesc *queryDesc, int eflags) { + EState *estate; + MemoryContext oldcontext; + PlannedStmt *plannedstmt = queryDesc->plannedstmt; + List *rangeTable = plannedstmt->rtable; + CachedPlan *cachedplan = queryDesc->cplan; + + /* sanity checks: queryDesc must not be started already */ + Assert(queryDesc != NULL); + Assert(queryDesc->estate == NULL); + /* * In some cases (e.g. an EXECUTE statement or an execute message with the * extended query protocol) the query_id won't be reported, so do it now. @@ -133,6 +143,117 @@ ExecutorStart(QueryDesc *queryDesc, int eflags) */ pgstat_report_query_id(queryDesc->plannedstmt->queryId, false); + /* + * Build EState, switch into per-query memory context for startup. + */ + estate = CreateExecutorState(); + queryDesc->estate = estate; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + /* + * Fill in external parameters, if any, from queryDesc; and allocate + * workspace for internal parameters + */ + estate->es_param_list_info = queryDesc->params; + + if (queryDesc->plannedstmt->paramExecTypes != NIL) + { + int nParamExec; + + nParamExec = list_length(queryDesc->plannedstmt->paramExecTypes); + estate->es_param_exec_vals = (ParamExecData *) + palloc0(nParamExec * sizeof(ParamExecData)); + } + + /* We now require all callers to provide sourceText */ + Assert(queryDesc->sourceText != NULL); + estate->es_sourceText = queryDesc->sourceText; + + /* + * Fill in the query environment, if any, from queryDesc. + */ + estate->es_queryEnv = queryDesc->queryEnv; + + /* + * If non-read-only query, set the command ID to mark output tuples with + */ + switch (queryDesc->operation) + { + case CMD_SELECT: + + /* + * SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark + * tuples + */ + if (queryDesc->plannedstmt->rowMarks != NIL || + queryDesc->plannedstmt->hasModifyingCTE) + estate->es_output_cid = GetCurrentCommandId(true); + + /* + * A SELECT without modifying CTEs can't possibly queue triggers, + * so force skip-triggers mode. This is just a marginal efficiency + * hack, since AfterTriggerBeginQuery/AfterTriggerEndQuery aren't + * all that expensive, but we might as well do it. + */ + if (!queryDesc->plannedstmt->hasModifyingCTE) + eflags |= EXEC_FLAG_SKIP_TRIGGERS; + break; + + case CMD_INSERT: + case CMD_DELETE: + case CMD_UPDATE: + case CMD_MERGE: + estate->es_output_cid = GetCurrentCommandId(true); + break; + + default: + elog(ERROR, "unrecognized operation code: %d", + (int) queryDesc->operation); + break; + } + + /* + * Copy other important information into the EState + */ + estate->es_snapshot = RegisterSnapshot(queryDesc->snapshot); + estate->es_crosscheck_snapshot = RegisterSnapshot(queryDesc->crosscheck_snapshot); + estate->es_top_eflags = eflags; + estate->es_instrument = queryDesc->instrument_options; + estate->es_jit_flags = queryDesc->plannedstmt->jitFlags; + + estate->es_part_prune_infos = plannedstmt->partPruneInfos; + estate->es_unpruned_relids = bms_copy(plannedstmt->unprunableRelids); + + /* + * Do permissions checks + */ + ExecCheckPermissions(rangeTable, plannedstmt->permInfos, true); + + /* + * initialize the node's execution state + */ + ExecInitRangeTable(estate, rangeTable, plannedstmt->permInfos); + + /* + * Perform runtime "initial" pruning to identify which child subplans, + * corresponding to the children of plan nodes that contain + * PartitionPruneInfo such as Append, will not be executed. The results, + * which are bitmapsets of indexes of the child subplans that will be + * executed, are saved in es_part_prune_results. These results correspond + * to each PartitionPruneInfo entry, and the es_part_prune_results list is + * parallel to es_part_prune_infos. + * + * This will also add the RT indexes of surviving leaf partitions to + * es_unpruned_relids. + */ + ExecDoInitialPruning(estate, cachedplan); + + MemoryContextSwitchTo(oldcontext); + + if (cachedplan && !CachedPlanValid(cachedplan)) + return; + if (ExecutorStart_hook) (*ExecutorStart_hook) (queryDesc, eflags); else @@ -198,12 +319,12 @@ ExecutorStartCachedPlan(QueryDesc *queryDesc, int eflags, void standard_ExecutorStart(QueryDesc *queryDesc, int eflags) { - EState *estate; MemoryContext oldcontext; + EState *estate; /* sanity checks: queryDesc must not be started already */ Assert(queryDesc != NULL); - Assert(queryDesc->estate == NULL); + Assert(queryDesc->estate != NULL); /* caller must ensure the query's snapshot is active */ Assert(GetActiveSnapshot() == queryDesc->snapshot); @@ -227,85 +348,6 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags) !(eflags & EXEC_FLAG_EXPLAIN_ONLY)) ExecCheckXactReadOnly(queryDesc->plannedstmt); - /* - * Build EState, switch into per-query memory context for startup. - */ - estate = CreateExecutorState(); - queryDesc->estate = estate; - - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * Fill in external parameters, if any, from queryDesc; and allocate - * workspace for internal parameters - */ - estate->es_param_list_info = queryDesc->params; - - if (queryDesc->plannedstmt->paramExecTypes != NIL) - { - int nParamExec; - - nParamExec = list_length(queryDesc->plannedstmt->paramExecTypes); - estate->es_param_exec_vals = (ParamExecData *) - palloc0(nParamExec * sizeof(ParamExecData)); - } - - /* We now require all callers to provide sourceText */ - Assert(queryDesc->sourceText != NULL); - estate->es_sourceText = queryDesc->sourceText; - - /* - * Fill in the query environment, if any, from queryDesc. - */ - estate->es_queryEnv = queryDesc->queryEnv; - - /* - * If non-read-only query, set the command ID to mark output tuples with - */ - switch (queryDesc->operation) - { - case CMD_SELECT: - - /* - * SELECT FOR [KEY] UPDATE/SHARE and modifying CTEs need to mark - * tuples - */ - if (queryDesc->plannedstmt->rowMarks != NIL || - queryDesc->plannedstmt->hasModifyingCTE) - estate->es_output_cid = GetCurrentCommandId(true); - - /* - * A SELECT without modifying CTEs can't possibly queue triggers, - * so force skip-triggers mode. This is just a marginal efficiency - * hack, since AfterTriggerBeginQuery/AfterTriggerEndQuery aren't - * all that expensive, but we might as well do it. - */ - if (!queryDesc->plannedstmt->hasModifyingCTE) - eflags |= EXEC_FLAG_SKIP_TRIGGERS; - break; - - case CMD_INSERT: - case CMD_DELETE: - case CMD_UPDATE: - case CMD_MERGE: - estate->es_output_cid = GetCurrentCommandId(true); - break; - - default: - elog(ERROR, "unrecognized operation code: %d", - (int) queryDesc->operation); - break; - } - - /* - * Copy other important information into the EState - */ - estate->es_snapshot = RegisterSnapshot(queryDesc->snapshot); - estate->es_crosscheck_snapshot = RegisterSnapshot(queryDesc->crosscheck_snapshot); - estate->es_top_eflags = eflags; - estate->es_instrument = queryDesc->instrument_options; - estate->es_jit_flags = queryDesc->plannedstmt->jitFlags; - /* * Set up an AFTER-trigger statement context, unless told not to, or * unless it's EXPLAIN-only mode (when ExecutorFinish won't be called). @@ -313,6 +355,9 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags) if (!(eflags & (EXEC_FLAG_SKIP_TRIGGERS | EXEC_FLAG_EXPLAIN_ONLY))) AfterTriggerBeginQuery(); + estate = queryDesc->estate; + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + /* * Initialize the plan state tree */ @@ -922,46 +967,14 @@ InitPlan(QueryDesc *queryDesc, int eflags) { CmdType operation = queryDesc->operation; PlannedStmt *plannedstmt = queryDesc->plannedstmt; - CachedPlan *cachedplan = queryDesc->cplan; Plan *plan = plannedstmt->planTree; - List *rangeTable = plannedstmt->rtable; EState *estate = queryDesc->estate; PlanState *planstate; TupleDesc tupType; ListCell *l; int i; - /* - * Do permissions checks - */ - ExecCheckPermissions(rangeTable, plannedstmt->permInfos, true); - - /* - * initialize the node's execution state - */ - ExecInitRangeTable(estate, rangeTable, plannedstmt->permInfos); - estate->es_plannedstmt = plannedstmt; - estate->es_cachedplan = cachedplan; - estate->es_part_prune_infos = plannedstmt->partPruneInfos; - estate->es_unpruned_relids = bms_copy(plannedstmt->unprunableRelids); - - /* - * Perform runtime "initial" pruning to identify which child subplans, - * corresponding to the children of plan nodes that contain - * PartitionPruneInfo such as Append, will not be executed. The results, - * which are bitmapsets of indexes of the child subplans that will be - * executed, are saved in es_part_prune_results. These results correspond - * to each PartitionPruneInfo entry, and the es_part_prune_results list is - * parallel to es_part_prune_infos. - * - * This will also add the RT indexes of surviving leaf partitions to - * es_unpruned_relids. - */ - ExecDoInitialPruning(estate); - - if (!ExecPlanStillValid(estate)) - return; /* * Next, build the ExecRowMark array from the PlanRowMark(s), if any. diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index b349ccb211..126f330ef8 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -1812,7 +1812,7 @@ adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap) * use the same index to retrieve the pruning results. */ void -ExecDoInitialPruning(EState *estate) +ExecDoInitialPruning(EState *estate, CachedPlan *cplan) { ListCell *lc; List *locked_relids = NIL; @@ -1838,7 +1838,7 @@ ExecDoInitialPruning(EState *estate) validsubplans = ExecFindMatchingSubPlans(prunestate, true, &validsubplan_rtis); - if (ExecShouldLockRelations(estate)) + if (cplan && CachedPlanRequiresLocking(cplan)) { int rtindex = -1; @@ -1866,7 +1866,7 @@ ExecDoInitialPruning(EState *estate) * Release the useless locks if the plan won't be executed. This is the * same as what CheckCachedPlan() in plancache.c does. */ - if (!ExecPlanStillValid(estate)) + if (cplan && !CachedPlanValid(cplan)) { foreach(lc, locked_relids) { diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h index a0843481f7..95d886884c 100644 --- a/src/include/executor/execPartition.h +++ b/src/include/executor/execPartition.h @@ -17,6 +17,7 @@ #include "nodes/parsenodes.h" #include "nodes/plannodes.h" #include "partitioning/partprune.h" +#include "utils/plancache.h" /* See execPartition.c for the definitions. */ typedef struct PartitionDispatchData *PartitionDispatch; @@ -136,7 +137,7 @@ typedef struct PartitionPruneState PartitionPruningData *partprunedata[FLEXIBLE_ARRAY_MEMBER]; } PartitionPruneState; -void ExecDoInitialPruning(EState *estate); +void ExecDoInitialPruning(EState *estate, CachedPlan *cplan); extern PartitionPruneState *ExecInitPartitionExecPruning(PlanState *planstate, int n_total_subplans, int part_prune_index, diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 6d72f7d9d6..6a7ca37753 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -265,31 +265,6 @@ 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? - * - * Called from InitPlan() because invalidation messages that affect the plan - * might be received after locks have been taken on runtime-prunable relations. - * The caller should take appropriate action if the plan has become invalid. - */ -static inline bool -ExecPlanStillValid(EState *estate) -{ - return estate->es_cachedplan == NULL ? true : - CachedPlanValid(estate->es_cachedplan); -} - -/* - * Locks are needed only if running a cached plan that might contain unlocked - * relations, such as a reused generic plan. - */ -static inline bool -ExecShouldLockRelations(EState *estate) -{ - return estate->es_cachedplan == NULL ? false : - CachedPlanRequiresLocking(estate->es_cachedplan); -} - /* ---------------------------------------------------------------- * ExecProcNode *