pruning-in-ExecutorStart.diff

application/octet-stream

Filename: pruning-in-ExecutorStart.diff
Type: application/octet-stream
Part: 0
Message: Re: generic plans and "initial" pruning

Patch

Format: unified
File+
contrib/auto_explain/auto_explain.c 0 4
contrib/pg_stat_statements/pg_stat_statements.c 0 4
src/backend/executor/execMain.c 126 113
src/backend/executor/execPartition.c 3 3
src/include/executor/execPartition.h 2 1
src/include/executor/executor.h 0 25
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
  *