v55-0003-Initialize-PartitionPruneContext-for-exec-prunin.patch
application/octet-stream
Filename: v55-0003-Initialize-PartitionPruneContext-for-exec-prunin.patch
Type: application/octet-stream
Part: 1
Patch
Format: format-patch
Series: patch v55-0003
Subject: Initialize PartitionPruneContext for exec pruning lazily
| File | + | − |
|---|---|---|
| src/backend/executor/execPartition.c | 62 | 104 |
| src/include/executor/execPartition.h | 1 | 0 |
| src/include/partitioning/partprune.h | 2 | 0 |
From 92d87cdbb3ad675ac6ffa2767f1d7d5876bd5369 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Wed, 18 Sep 2024 11:16:48 +0900
Subject: [PATCH v55 3/5] Initialize PartitionPruneContext for exec pruning
lazily
Currently, ExecInitPartitionPruning() iterates over PartitionPruningDatas
and nested PartitionedRelPruningDatas in a PartitionPruneState solely
to initialize the exec_context of the PartitionedRelPruningData.
This commit moves the initialization to find_matching_subplans_recurse(),
where the exec_context is actually needed, eliminating the need for
the above iteration. To track whether the context has been initialized
and is ready for use, a boolean field is_valid is added to
PartitionPruneContext.
---
src/backend/executor/execPartition.c | 166 ++++++++++-----------------
src/include/executor/execPartition.h | 1 +
src/include/partitioning/partprune.h | 2 +
3 files changed, 65 insertions(+), 104 deletions(-)
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 3c7c631867..d9fa593785 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -190,10 +190,8 @@ static void InitPartitionPruneContext(PartitionPruneContext *context,
static void PartitionPruneFixSubPlanMap(PartitionPruneState *prunestate,
Bitmapset *initially_valid_subplans,
int n_total_subplans);
-static void PartitionPruneInitExecPruning(PartitionPruneInfo *pruneinfo,
- PartitionPruneState *prunestate,
- PlanState *planstate);
-static void find_matching_subplans_recurse(PartitionPruningData *prunedata,
+static void find_matching_subplans_recurse(PlanState *parent_plan,
+ PartitionPruningData *prunedata,
PartitionedRelPruningData *pprune,
bool initial_prune,
Bitmapset **validsubplans);
@@ -1830,13 +1828,14 @@ ExecInitPartitionPruning(PlanState *planstate,
/*
* ExecDoInitialPruning() must have initialized the PartitionPruneState to
- * perform the initial pruning. Now we simply need to initialize the
- * context information for exec pruning.
+ * perform the initial pruning. Store PlanState so that the exec_context
+ * can be initialized using it later when find_matching_subplans_recurse()
+ * needs it.
*/
prunestate = list_nth(estate->es_part_prune_states, part_prune_index);
Assert(prunestate != NULL);
if (prunestate->do_exec_prune)
- PartitionPruneInitExecPruning(pruneinfo, prunestate, planstate);
+ prunestate->parent_plan = planstate;
/* Use the result of initial pruning done by ExecDoInitialPruning(). */
if (prunestate->do_initial_prune)
@@ -1893,8 +1892,7 @@ ExecInitPartitionPruning(PlanState *planstate,
* each PartitionedRelPruningData) for initial pruning here. Execution pruning
* requires access to the parent plan node's PlanState, which is not available
* when this function is called from ExecDoInitialPruning(), so it is
- * initialized later during ExecInitPartitionPruning() by calling
- * PartitionPruneInitExecPruning().
+ * initialized lazily during find_matching_subplans_recurse().
*/
PartitionPruneState *
ExecCreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo)
@@ -2099,25 +2097,30 @@ ExecCreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo)
}
/*
- * The exec pruning context will be initialized in
- * ExecInitPartitionPruning() when called during the initialization
- * of the parent plan node.
+ * The exec pruning context will be initialized lazily when it
+ * will be used for the first time in
+ * find_matching_subplans_recurse().
*
- * pprune->exec_pruning_steps is set to NIL to prevent
- * ExecFindMatchingSubPlans() from accessing an uninitialized
- * pprune->exec_context during the initial pruning by
- * ExecDoInitialPruning().
- *
- * prunestate->do_exec_prune is set to indicate whether
- * PartitionPruneInitExecPruning() needs to be called by
- * ExecInitPartitionPruning(). This optimization avoids
- * unnecessary cycles when only initial pruning is required.
+ * prunestate->do_exec_prune is set to indicate whether we're
+ * actually going to perform exec pruning to inform
+ * ExecInitPartitionPruning() whether it should fix the
+ * subplan_map array based on the result of initial pruning
+ * and also the parent node's code to allow it set up its
+ * data structure accordingly.
*/
- pprune->exec_pruning_steps = NIL;
+ pprune->exec_pruning_steps = pinfo->exec_pruning_steps;
+ pprune->exec_context.is_valid = false;
if (pinfo->exec_pruning_steps &&
!(econtext->ecxt_estate->es_top_eflags & EXEC_FLAG_EXPLAIN_GENERIC))
prunestate->do_exec_prune = true;
+ /*
+ * Accumulate the IDs of all PARAM_EXEC Params affecting the
+ * partitioning decisions at this plan node.
+ */
+ prunestate->execparamids = bms_add_members(prunestate->execparamids,
+ pinfo->execparamids);
+
j++;
}
i++;
@@ -2208,6 +2211,8 @@ InitPartitionPruneContext(PartitionPruneContext *context,
}
}
}
+
+ context->is_valid = true;
}
/*
@@ -2326,84 +2331,6 @@ PartitionPruneFixSubPlanMap(PartitionPruneState *prunestate,
pfree(new_subplan_indexes);
}
-/*
- * PartitionPruneInitExecPruning
- * Initialize PartitionPruneState for exec pruning.
- */
-static void
-PartitionPruneInitExecPruning(PartitionPruneInfo *pruneinfo,
- PartitionPruneState *prunestate,
- PlanState *planstate)
-{
- EState *estate = planstate->state;
- int i;
- ExprContext *econtext;
-
- /* CreatePartitionPruneState() must have initialized. */
- Assert(estate->es_partition_directory != NULL);
-
- /* CreatePartitionPruneState() must have set this. */
- Assert(prunestate->do_exec_prune);
-
- /*
- * Create ExprContext if not already done for the planstate. We may need
- * an expression context to evaluate partition exprs.
- */
- ExecAssignExprContext(estate, planstate);
- econtext = planstate->ps_ExprContext;
- for (i = 0; i < prunestate->num_partprunedata; i++)
- {
- List *partrel_pruneinfos =
- list_nth_node(List, pruneinfo->prune_infos, i);
- PartitionPruningData *prunedata = prunestate->partprunedata[i];
- int j;
-
- for (j = 0; j < prunedata->num_partrelprunedata; j++)
- {
- PartitionedRelPruneInfo *pinfo =
- list_nth_node(PartitionedRelPruneInfo, partrel_pruneinfos, j);
- PartitionedRelPruningData *pprune = &prunedata->partrelprunedata[j];
- Relation partrel = pprune->partrel;
- PartitionDesc partdesc;
- PartitionKey partkey;
-
- /*
- * Nothing to do if there are no exec pruning steps, but do set
- * pprune->exec_pruning_steps, becasue
- * find_matching_subplans_recurse() looks at it.
- *
- * Also skip if doing EXPLAIN (GENERIC_PLAN), since parameter
- * values may be missing.
- */
- pprune->exec_pruning_steps = pinfo->exec_pruning_steps;
- if (pprune->exec_pruning_steps == NIL ||
- (econtext->ecxt_estate->es_top_eflags & EXEC_FLAG_EXPLAIN_GENERIC))
- continue;
-
- /*
- * We can rely on the copies of the partitioned table's partition
- * key and partition descriptor appearing in its relcache entry,
- * because that entry will be held open and locked for the
- * duration of this executor run.
- */
- partkey = RelationGetPartitionKey(partrel);
- partdesc = PartitionDirectoryLookup(estate->es_partition_directory,
- partrel);
- InitPartitionPruneContext(&pprune->exec_context,
- pprune->exec_pruning_steps,
- partdesc, partkey, planstate,
- econtext);
-
- /*
- * Accumulate the IDs of all PARAM_EXEC Params affecting the
- * partitioning decisions at this plan node.
- */
- prunestate->execparamids = bms_add_members(prunestate->execparamids,
- pinfo->execparamids);
- }
- }
-}
-
/*
* ExecFindMatchingSubPlans
* Determine which subplans match the pruning steps detailed in
@@ -2449,12 +2376,16 @@ ExecFindMatchingSubPlans(PartitionPruneState *prunestate,
* recursing to other (lower-level) parents as needed.
*/
pprune = &prunedata->partrelprunedata[0];
- find_matching_subplans_recurse(prunedata, pprune, initial_prune,
+ find_matching_subplans_recurse(prunestate->parent_plan,
+ prunedata, pprune, initial_prune,
&result);
/* Expression eval may have used space in ExprContext too */
- if (pprune->exec_pruning_steps)
+ if (pprune->exec_context.is_valid)
+ {
+ Assert(pprune->exec_pruning_steps != NIL);
ResetExprContext(pprune->exec_context.exprcontext);
+ }
}
/* Add in any subplans that partition pruning didn't account for */
@@ -2477,7 +2408,8 @@ ExecFindMatchingSubPlans(PartitionPruneState *prunestate,
* Adds valid (non-prunable) subplan IDs to *validsubplans
*/
static void
-find_matching_subplans_recurse(PartitionPruningData *prunedata,
+find_matching_subplans_recurse(PlanState *parent_plan,
+ PartitionPruningData *prunedata,
PartitionedRelPruningData *pprune,
bool initial_prune,
Bitmapset **validsubplans)
@@ -2497,8 +2429,33 @@ find_matching_subplans_recurse(PartitionPruningData *prunedata,
partset = get_matching_partitions(&pprune->initial_context,
pprune->initial_pruning_steps);
else if (!initial_prune && pprune->exec_pruning_steps)
+ {
+ /* Initialize exec_context if not already done. */
+ if (unlikely(!pprune->exec_context.is_valid))
+ {
+ ExprContext *econtext;
+ EState *estate = parent_plan->state;
+ /* Must allocate the needed stuff in the query lifetime context. */
+ MemoryContext oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
+ Relation partrel = pprune->partrel;
+ PartitionKey partkey = RelationGetPartitionKey(partrel);
+ PartitionDesc partdesc = PartitionDirectoryLookup(estate->es_partition_directory,
+ partrel);
+
+ if (parent_plan->ps_ExprContext == NULL)
+ ExecAssignExprContext(estate, parent_plan);
+ econtext = parent_plan->ps_ExprContext;
+
+ InitPartitionPruneContext(&pprune->exec_context,
+ pprune->exec_pruning_steps,
+ partdesc, partkey, parent_plan,
+ econtext);
+
+ MemoryContextSwitchTo(oldcxt);
+ }
partset = get_matching_partitions(&pprune->exec_context,
pprune->exec_pruning_steps);
+ }
else
partset = pprune->present_parts;
@@ -2514,7 +2471,8 @@ find_matching_subplans_recurse(PartitionPruningData *prunedata,
int partidx = pprune->subpart_map[i];
if (partidx >= 0)
- find_matching_subplans_recurse(prunedata,
+ find_matching_subplans_recurse(parent_plan,
+ prunedata,
&prunedata->partrelprunedata[partidx],
initial_prune, validsubplans);
else
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index 2f45ac1cc8..ef6d8b2d48 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -122,6 +122,7 @@ typedef struct PartitionPruneState
bool do_initial_prune;
bool do_exec_prune;
int num_partprunedata;
+ PlanState *parent_plan;
PartitionPruningData *partprunedata[FLEXIBLE_ARRAY_MEMBER];
} PartitionPruneState;
diff --git a/src/include/partitioning/partprune.h b/src/include/partitioning/partprune.h
index c536a1fe19..b7f48eefcc 100644
--- a/src/include/partitioning/partprune.h
+++ b/src/include/partitioning/partprune.h
@@ -26,6 +26,7 @@ struct RelOptInfo;
* Stores information needed at runtime for pruning computations
* related to a single partitioned table.
*
+ * is_valid Has the information in this struct been initialized?
* strategy Partition strategy, e.g. LIST, RANGE, HASH.
* partnatts Number of columns in the partition key.
* nparts Number of partitions in this partitioned table.
@@ -48,6 +49,7 @@ struct RelOptInfo;
*/
typedef struct PartitionPruneContext
{
+ bool is_valid;
char strategy;
int partnatts;
int nparts;
--
2.43.0