From 638c7297f63d530ce29370fffa20fcd34ee8b450 Mon Sep 17 00:00:00 2001 From: Amit Langote Date: Wed, 4 Dec 2024 16:16:49 +0900 Subject: [PATCH v59 3/5] Perform runtime initial pruning outside ExecInitNode() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit follows up on the previous change that moved PartitionPruneInfos out of individual plan nodes into a list in PlannedStmt. It moves the initialization of PartitionPruneStates and runtime initial pruning out of ExecInitNode() and into a new routine, ExecDoInitialPruning(), which is called by InitPlan() before ExecInitNode() is invoked on the main plan tree and subplans. ExecDoInitialPruning() performs the initial pruning and saves the result—a bitmapset of indexes for the surviving child subnodes—in es_part_prune_results, a list in EState. The PartitionPruneStates created for initial pruning are also saved in es_part_prune_states, another list in EState, for later use during exec pruning. Both lists are parallel to es_part_prune_infos (which holds the PartitionPruneInfos from PlannedStmt), allowing them to share the same index. Reviewed-by: Robert Haas Reviewed-by: Tomas Vondra Discussion: https://postgr.es/m/CA+HiwqFGkMSge6TgC9KQzde0ohpAycLQuV7ooitEEpbKB0O_mg@mail.gmail.com --- src/backend/executor/execMain.c | 12 +++ src/backend/executor/execPartition.c | 130 ++++++++++++++++++------- src/backend/executor/nodeAppend.c | 10 +- src/backend/executor/nodeMergeAppend.c | 10 +- src/include/executor/execPartition.h | 11 ++- src/include/nodes/execnodes.h | 2 + 6 files changed, 125 insertions(+), 50 deletions(-) diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index b40fe38178..5dc46f2e95 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -46,6 +46,7 @@ #include "commands/matview.h" #include "commands/trigger.h" #include "executor/executor.h" +#include "executor/execPartition.h" #include "executor/nodeSubplan.h" #include "foreign/fdwapi.h" #include "mb/pg_wchar.h" @@ -858,6 +859,17 @@ InitPlan(QueryDesc *queryDesc, int eflags) estate->es_plannedstmt = plannedstmt; estate->es_part_prune_infos = plannedstmt->partPruneInfos; + /* + * 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. + */ + ExecDoInitialPruning(estate); + /* * 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 f4d425cd45..46dd1c77a3 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -1761,48 +1761,105 @@ adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap) * * Functions: * - * ExecInitPartitionPruning: - * Creates the PartitionPruneState required by ExecFindMatchingSubPlans. - * Details stored include how to map the partition index returned by the - * partition pruning code into subplan indexes. Also determines the set - * of subplans to initialize considering the result of performing initial - * pruning steps if any. Maps in PartitionPruneState are updated to + * ExecDoInitialPruning: + * Perform runtime "initial" pruning, if necessary, to determine the set + * of child subnodes that need to be initialized during ExecInitNode() for + * all plan nodes that contain a PartitionPruneInfo. + * + * ExecInitPartitionExecPruning: + * Updates the PartitionPruneState found at given part_prune_index in + * EState.es_part_prune_states for use during "exec" pruning if required. + * Also returns the set of subplans to initialize that would be stored at + * part_prune_index in EState.es_part_prune_result by + * ExecDoInitialPruning(). Maps in PartitionPruneState are updated to * account for initial pruning possibly having eliminated some of the * subplans. * * ExecFindMatchingSubPlans: * Returns indexes of matching subplans after evaluating the expressions * that are safe to evaluate at a given point. This function is first - * called during ExecInitPartitionPruning() to find the initially - * matching subplans based on performing the initial pruning steps and - * then must be called again each time the value of a Param listed in + * called during ExecDoInitialPruning() to find the initially matching + * subplans based on performing the initial pruning steps and then must be + * called again each time the value of a Param listed in * PartitionPruneState's 'execparamids' changes. *------------------------------------------------------------------------- */ /* - * ExecInitPartitionPruning - * Initialize data structure needed for run-time partition pruning and - * do initial pruning if needed + * ExecDoInitialPruning + * Perform runtime "initial" pruning, if necessary, to determine the set + * of child subnodes that need to be initialized during ExecInitNode() for + * plan nodes that support partition pruning. + * + * This function iterates over each PartitionPruneInfo entry in + * estate->es_part_prune_infos. For each entry, it creates a PartitionPruneState + * and adds it to es_part_prune_states, where ExecInitPartitionExecPruning() can + * access it for use during "exec" pruning. + * + * If initial pruning steps exist for a PartitionPruneInfo entry, this function + * executes those pruning steps and stores the result as a bitmapset of valid + * child subplans, identifying which subplans should be initialized for + * execution. The results are saved in estate->es_part_prune_results. + * + * If no initial pruning is performed for a given PartitionPruneInfo, a NULL + * entry is still added to es_part_prune_results to maintain alignment with + * es_part_prune_infos. This ensures that ExecInitPartitionExecPruning() can + * use the same index to retrieve the pruning results. + */ +void +ExecDoInitialPruning(EState *estate) +{ + ListCell *lc; + + foreach(lc, estate->es_part_prune_infos) + { + PartitionPruneInfo *pruneinfo = lfirst_node(PartitionPruneInfo, lc); + PartitionPruneState *prunestate; + Bitmapset *validsubplans = NULL; + + /* Create and save the PartitionPruneState. */ + prunestate = CreatePartitionPruneState(estate, pruneinfo); + estate->es_part_prune_states = lappend(estate->es_part_prune_states, + prunestate); + + /* + * Perform initial pruning steps, if any, and save the result + * bitmapset or NULL as described in the header comment. + */ + if (prunestate->do_initial_prune) + validsubplans = ExecFindMatchingSubPlans(prunestate, true); + estate->es_part_prune_results = lappend(estate->es_part_prune_results, + validsubplans); + } +} + +/* + * ExecInitPartitionExecPruning + * Initialize the data structures needed for runtime "exec" partition + * pruning and return the result of initial pruning, if available. * * 'relids' identifies the relation to which both the parent plan and the * PartitionPruneInfo given by 'part_prune_index' belong. * - * On return, *initially_valid_subplans is assigned the set of indexes of - * child subplans that must be initialized along with the parent plan node. - * Initial pruning is performed here if needed and in that case only the - * surviving subplans' indexes are added. + * The PartitionPruneState would have been created by ExecDoInitialPruning() + * and stored as the part_prune_index'th element of EState.es_part_prune_states. * - * If subplans are indeed pruned, subplan_map arrays contained in the returned - * PartitionPruneState are re-sequenced to not count those, though only if the - * maps will be needed for subsequent execution pruning passes. + * On return, *initially_valid_subplans is assigned the set of indexes of child + * subplans that must be initialized. Initial pruning would have been performed + * by ExecDoInitialPruning() if necessary, and the bitmapset of surviving + * subplans' indexes would have been stored as the part_prune_index'th element + * of EState.es_part_prune_results. + * + * If subplans were pruned during initial pruning, the subplan_map arrays in + * the returned PartitionPruneState are re-sequenced to exclude those subplans, + * but only if the maps will be needed for subsequent execution pruning passes. */ PartitionPruneState * -ExecInitPartitionPruning(PlanState *planstate, - int n_total_subplans, - int part_prune_index, - Bitmapset *relids, - Bitmapset **initially_valid_subplans) +ExecInitPartitionExecPruning(PlanState *planstate, + int n_total_subplans, + int part_prune_index, + Bitmapset *relids, + Bitmapset **initially_valid_subplans) { PartitionPruneState *prunestate; EState *estate = planstate->state; @@ -1818,11 +1875,12 @@ ExecInitPartitionPruning(PlanState *planstate, bmsToString(pruneinfo->relids), part_prune_index, bmsToString(relids)); - /* We may need an expression context to evaluate partition exprs */ - ExecAssignExprContext(estate, planstate); - - /* Create the working data structure for pruning */ - prunestate = CreatePartitionPruneState(estate, pruneinfo); + /* + * ExecDoInitialPruning() must have initialized the PartitionPruneState to + * perform the initial pruning. + */ + prunestate = list_nth(estate->es_part_prune_states, part_prune_index); + Assert(prunestate != NULL); /* * Store PlanState for using it to initialize exec pruning contexts later @@ -1831,11 +1889,11 @@ ExecInitPartitionPruning(PlanState *planstate, if (prunestate->do_exec_prune) prunestate->parent_plan = planstate; - /* - * Perform an initial partition prune pass, if required. - */ + /* Use the result of initial pruning done by ExecDoInitialPruning(). */ if (prunestate->do_initial_prune) - *initially_valid_subplans = ExecFindMatchingSubPlans(prunestate, true); + *initially_valid_subplans = list_nth_node(Bitmapset, + estate->es_part_prune_results, + part_prune_index); else { /* No pruning, so we'll need to initialize all subplans */ @@ -1846,8 +1904,8 @@ ExecInitPartitionPruning(PlanState *planstate, /* * Re-sequence subplan indexes contained in prunestate to account for any - * that were removed above due to initial pruning. No need to do this if - * no steps were removed. + * that were removed due to initial pruning. No need to do this if no + * partitions were removed. */ if (bms_num_members(*initially_valid_subplans) < n_total_subplans) { @@ -1868,6 +1926,8 @@ ExecInitPartitionPruning(PlanState *planstate, /* * CreatePartitionPruneState * Build the data structure required for calling ExecFindMatchingSubPlans + * Details stored include how to map the partition index returned by the + * partition pruning code into subplan indexes. * * 'pruneinfo' is a PartitionPruneInfo as generated by * make_partition_pruneinfo. Here we build a PartitionPruneState containing a diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index de7ebab5c2..b77ff84840 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -143,11 +143,11 @@ ExecInitAppend(Append *node, EState *estate, int eflags) * subplans to initialize (validsubplans) by taking into account the * result of performing initial pruning if any. */ - prunestate = ExecInitPartitionPruning(&appendstate->ps, - list_length(node->appendplans), - node->part_prune_index, - node->apprelids, - &validsubplans); + prunestate = ExecInitPartitionExecPruning(&appendstate->ps, + list_length(node->appendplans), + node->part_prune_index, + node->apprelids, + &validsubplans); appendstate->as_prune_state = prunestate; nplans = bms_num_members(validsubplans); diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c index 3ed91808dd..e2032afcb7 100644 --- a/src/backend/executor/nodeMergeAppend.c +++ b/src/backend/executor/nodeMergeAppend.c @@ -91,11 +91,11 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags) * subplans to initialize (validsubplans) by taking into account the * result of performing initial pruning if any. */ - prunestate = ExecInitPartitionPruning(&mergestate->ps, - list_length(node->mergeplans), - node->part_prune_index, - node->apprelids, - &validsubplans); + prunestate = ExecInitPartitionExecPruning(&mergestate->ps, + list_length(node->mergeplans), + node->part_prune_index, + node->apprelids, + &validsubplans); mergestate->ms_prune_state = prunestate; nplans = bms_num_members(validsubplans); diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h index 7e470c82f6..0b34784922 100644 --- a/src/include/executor/execPartition.h +++ b/src/include/executor/execPartition.h @@ -133,11 +133,12 @@ typedef struct PartitionPruneState PartitionPruningData *partprunedata[FLEXIBLE_ARRAY_MEMBER]; } PartitionPruneState; -extern PartitionPruneState *ExecInitPartitionPruning(PlanState *planstate, - int n_total_subplans, - int part_prune_index, - Bitmapset *relids, - Bitmapset **initially_valid_subplans); +void ExecDoInitialPruning(EState *estate); +extern PartitionPruneState *ExecInitPartitionExecPruning(PlanState *planstate, + int n_total_subplans, + int part_prune_index, + Bitmapset *relids, + Bitmapset **initially_valid_subplans); extern Bitmapset *ExecFindMatchingSubPlans(PartitionPruneState *prunestate, bool initial_prune); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b1471e68fe..f93061c7bf 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -640,6 +640,8 @@ typedef struct EState List *es_rteperminfos; /* List of RTEPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ List *es_part_prune_infos; /* List of PartitionPruneInfo */ + List *es_part_prune_states; /* List of PartitionPruneState */ + List *es_part_prune_results; /* List of Bitmapset */ const char *es_sourceText; /* Source text from QueryDesc */ JunkFilter *es_junkFilter; /* top-level junk filter, if any */ -- 2.43.0