1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-05 23:56:58 +03:00

Track unpruned relids to avoid processing pruned relations

This commit introduces changes to track unpruned relations explicitly,
making it possible for top-level plan nodes, such as ModifyTable and
LockRows, to avoid processing partitions pruned during initial
pruning.  Scan-level nodes, such as Append and MergeAppend, already
avoid the unnecessary processing by accessing partition pruning
results directly via part_prune_index. In contrast, top-level nodes
cannot access pruning results directly and need to determine which
partitions remain unpruned.

To address this, this commit introduces a new bitmapset field,
es_unpruned_relids, which the executor uses to track the set of
unpruned relations.  This field is referenced during plan
initialization to skip initializing certain nodes for pruned
partitions. It is initialized with PlannedStmt.unprunableRelids,
a new field that the planner populates with RT indexes of relations
that cannot be pruned during runtime pruning. These include relations
not subject to partition pruning and those required for execution
regardless of pruning.

PlannedStmt.unprunableRelids is computed during set_plan_refs() by
removing the RT indexes of runtime-prunable relations, identified
from PartitionPruneInfos, from the full set of relation RT indexes.
ExecDoInitialPruning() then updates es_unpruned_relids by adding
partitions that survive initial pruning.

To support this, PartitionedRelPruneInfo and PartitionedRelPruningData
now include a leafpart_rti_map[] array that maps partition indexes to
their corresponding RT indexes. The former is used in set_plan_refs()
when constructing unprunableRelids, while the latter is used in
ExecDoInitialPruning() to convert partition indexes returned by
get_matching_partitions() into RT indexes, which are then added to
es_unpruned_relids.

These changes make it possible for ModifyTable and LockRows nodes to
process only relations that remain unpruned after initial pruning.
ExecInitModifyTable() trims lists, such as resultRelations,
withCheckOptionLists, returningLists, and updateColnosLists, to
consider only unpruned partitions. It also creates ResultRelInfo
structs only for these partitions. Similarly, child RowMarks for
pruned relations are skipped.

By avoiding unnecessary initialization of structures for pruned
partitions, these changes improve the performance of updates and
deletes on partitioned tables during initial runtime pruning.

Due to ExecInitModifyTable() changes as described above, EXPLAIN on a
plan for UPDATE and DELETE that uses runtime initial pruning no longer
lists partitions pruned during initial pruning.

Reviewed-by: Robert Haas <robertmhaas@gmail.com> (earlier versions)
Reviewed-by: Tomas Vondra <tomas@vondra.me>
Discussion: https://postgr.es/m/CA+HiwqFGkMSge6TgC9KQzde0ohpAycLQuV7ooitEEpbKB0O_mg@mail.gmail.com
This commit is contained in:
Amit Langote 2025-02-07 17:15:09 +09:00
parent 926c7fce03
commit cbc127917e
21 changed files with 340 additions and 51 deletions

View File

@ -774,7 +774,8 @@ CopyFrom(CopyFromState cstate)
* index-entry-making machinery. (There used to be a huge amount of code
* here that basically duplicated execUtils.c ...)
*/
ExecInitRangeTable(estate, cstate->range_table, cstate->rteperminfos);
ExecInitRangeTable(estate, cstate->range_table, cstate->rteperminfos,
bms_make_singleton(1));
resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo);
ExecInitResultRelation(estate, resultRelInfo, 1);

View File

@ -851,7 +851,8 @@ InitPlan(QueryDesc *queryDesc, int eflags)
/*
* initialize the node's execution state
*/
ExecInitRangeTable(estate, rangeTable, plannedstmt->permInfos);
ExecInitRangeTable(estate, rangeTable, plannedstmt->permInfos,
bms_copy(plannedstmt->unprunableRelids));
estate->es_plannedstmt = plannedstmt;
estate->es_part_prune_infos = plannedstmt->partPruneInfos;
@ -881,8 +882,13 @@ InitPlan(QueryDesc *queryDesc, int eflags)
Relation relation;
ExecRowMark *erm;
/* ignore "parent" rowmarks; they are irrelevant at runtime */
if (rc->isParent)
/*
* Ignore "parent" rowmarks, because they are irrelevant at
* runtime. Also ignore the rowmarks belonging to child tables
* that have been pruned in ExecDoInitialPruning().
*/
if (rc->isParent ||
!bms_is_member(rc->rti, estate->es_unpruned_relids))
continue;
/* get relation's OID (will produce InvalidOid if subquery) */
@ -2933,6 +2939,13 @@ EvalPlanQualStart(EPQState *epqstate, Plan *planTree)
}
}
/*
* Copy es_unpruned_relids so that pruned relations are ignored by
* ExecInitLockRows() and ExecInitModifyTable() when initializing the plan
* trees below.
*/
rcestate->es_unpruned_relids = parentestate->es_unpruned_relids;
/*
* Initialize private state information for each SubPlan. We must do this
* before running ExecInitNode on the main query tree, since

View File

@ -183,6 +183,7 @@ ExecSerializePlan(Plan *plan, EState *estate)
pstmt->planTree = plan;
pstmt->partPruneInfos = estate->es_part_prune_infos;
pstmt->rtable = estate->es_range_table;
pstmt->unprunableRelids = estate->es_unpruned_relids;
pstmt->permInfos = estate->es_rteperminfos;
pstmt->resultRelations = NIL;
pstmt->appendRelations = NIL;

View File

@ -182,7 +182,8 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
static List *adjust_partition_colnos(List *colnos, ResultRelInfo *leaf_part_rri);
static List *adjust_partition_colnos_using_map(List *colnos, AttrMap *attrMap);
static PartitionPruneState *CreatePartitionPruneState(EState *estate,
PartitionPruneInfo *pruneinfo);
PartitionPruneInfo *pruneinfo,
Bitmapset **all_leafpart_rtis);
static void InitPartitionPruneContext(PartitionPruneContext *context,
List *pruning_steps,
PartitionDesc partdesc,
@ -196,7 +197,8 @@ static void InitExecPartitionPruneContexts(PartitionPruneState *prunstate,
static void find_matching_subplans_recurse(PartitionPruningData *prunedata,
PartitionedRelPruningData *pprune,
bool initial_prune,
Bitmapset **validsubplans);
Bitmapset **validsubplans,
Bitmapset **validsubplan_rtis);
/*
@ -1820,9 +1822,12 @@ ExecDoInitialPruning(EState *estate)
PartitionPruneInfo *pruneinfo = lfirst_node(PartitionPruneInfo, lc);
PartitionPruneState *prunestate;
Bitmapset *validsubplans = NULL;
Bitmapset *all_leafpart_rtis = NULL;
Bitmapset *validsubplan_rtis = NULL;
/* Create and save the PartitionPruneState. */
prunestate = CreatePartitionPruneState(estate, pruneinfo);
prunestate = CreatePartitionPruneState(estate, pruneinfo,
&all_leafpart_rtis);
estate->es_part_prune_states = lappend(estate->es_part_prune_states,
prunestate);
@ -1831,7 +1836,13 @@ ExecDoInitialPruning(EState *estate)
* bitmapset or NULL as described in the header comment.
*/
if (prunestate->do_initial_prune)
validsubplans = ExecFindMatchingSubPlans(prunestate, true);
validsubplans = ExecFindMatchingSubPlans(prunestate, true,
&validsubplan_rtis);
else
validsubplan_rtis = all_leafpart_rtis;
estate->es_unpruned_relids = bms_add_members(estate->es_unpruned_relids,
validsubplan_rtis);
estate->es_part_prune_results = lappend(estate->es_part_prune_results,
validsubplans);
}
@ -1944,9 +1955,16 @@ ExecInitPartitionExecPruning(PlanState *planstate,
* initialized here. Those required for exec pruning are initialized later in
* ExecInitPartitionExecPruning(), as they depend on the availability of the
* parent plan node's PlanState.
*
* If initial pruning steps are to be skipped (e.g., during EXPLAIN
* (GENERIC_PLAN)), *all_leafpart_rtis will be populated with the RT indexes of
* all leaf partitions whose scanning subnode is included in the parent plan
* node's list of child plans. The caller must add these RT indexes to
* estate->es_unpruned_relids.
*/
static PartitionPruneState *
CreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo)
CreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo,
Bitmapset **all_leafpart_rtis)
{
PartitionPruneState *prunestate;
int n_part_hierarchies;
@ -2039,8 +2057,8 @@ CreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo)
* The set of partitions that exist now might not be the same that
* existed when the plan was made. The normal case is that it is;
* optimize for that case with a quick comparison, and just copy
* the subplan_map and make subpart_map point to the one in
* PruneInfo.
* the subplan_map and make subpart_map, leafpart_rti_map point to
* the ones in PruneInfo.
*
* For the case where they aren't identical, we could have more
* partitions on either side; or even exactly the same number of
@ -2059,6 +2077,7 @@ CreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo)
sizeof(int) * partdesc->nparts) == 0)
{
pprune->subpart_map = pinfo->subpart_map;
pprune->leafpart_rti_map = pinfo->leafpart_rti_map;
memcpy(pprune->subplan_map, pinfo->subplan_map,
sizeof(int) * pinfo->nparts);
}
@ -2079,6 +2098,7 @@ CreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo)
* mismatches.
*/
pprune->subpart_map = palloc(sizeof(int) * partdesc->nparts);
pprune->leafpart_rti_map = palloc(sizeof(int) * partdesc->nparts);
for (pp_idx = 0; pp_idx < partdesc->nparts; pp_idx++)
{
@ -2096,6 +2116,8 @@ CreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo)
pinfo->subplan_map[pd_idx];
pprune->subpart_map[pp_idx] =
pinfo->subpart_map[pd_idx];
pprune->leafpart_rti_map[pp_idx] =
pinfo->leafpart_rti_map[pd_idx];
pd_idx++;
continue;
}
@ -2133,6 +2155,7 @@ CreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo)
pprune->subpart_map[pp_idx] = -1;
pprune->subplan_map[pp_idx] = -1;
pprune->leafpart_rti_map[pp_idx] = 0;
}
}
@ -2174,6 +2197,25 @@ CreatePartitionPruneState(EState *estate, PartitionPruneInfo *pruneinfo)
prunestate->execparamids = bms_add_members(prunestate->execparamids,
pinfo->execparamids);
/*
* Return all leaf partition indexes if we're skipping pruning in
* the EXPLAIN (GENERIC_PLAN) case.
*/
if (pinfo->initial_pruning_steps && !prunestate->do_initial_prune)
{
int part_index = -1;
while ((part_index = bms_next_member(pprune->present_parts,
part_index)) >= 0)
{
Index rtindex = pprune->leafpart_rti_map[part_index];
if (rtindex)
*all_leafpart_rtis = bms_add_member(*all_leafpart_rtis,
rtindex);
}
}
j++;
}
i++;
@ -2439,10 +2481,15 @@ InitExecPartitionPruneContexts(PartitionPruneState *prunestate,
* Pass initial_prune if PARAM_EXEC Params cannot yet be evaluated. This
* differentiates the initial executor-time pruning step from later
* runtime pruning.
*
* The caller must pass a non-NULL validsubplan_rtis during initial pruning
* to collect the RT indexes of leaf partitions whose subnodes will be
* executed. These RT indexes are later added to EState.es_unpruned_relids.
*/
Bitmapset *
ExecFindMatchingSubPlans(PartitionPruneState *prunestate,
bool initial_prune)
bool initial_prune,
Bitmapset **validsubplan_rtis)
{
Bitmapset *result = NULL;
MemoryContext oldcontext;
@ -2454,6 +2501,7 @@ ExecFindMatchingSubPlans(PartitionPruneState *prunestate,
* evaluated *and* there are steps in which to do so.
*/
Assert(initial_prune || prunestate->do_exec_prune);
Assert(validsubplan_rtis != NULL || !initial_prune);
/*
* Switch to a temp context to avoid leaking memory in the executor's
@ -2477,7 +2525,7 @@ ExecFindMatchingSubPlans(PartitionPruneState *prunestate,
*/
pprune = &prunedata->partrelprunedata[0];
find_matching_subplans_recurse(prunedata, pprune, initial_prune,
&result);
&result, validsubplan_rtis);
/*
* Expression eval may have used space in ExprContext too. Avoid
@ -2495,6 +2543,8 @@ ExecFindMatchingSubPlans(PartitionPruneState *prunestate,
/* Copy result out of the temp context before we reset it */
result = bms_copy(result);
if (validsubplan_rtis)
*validsubplan_rtis = bms_copy(*validsubplan_rtis);
MemoryContextReset(prunestate->prune_context);
@ -2505,13 +2555,16 @@ ExecFindMatchingSubPlans(PartitionPruneState *prunestate,
* find_matching_subplans_recurse
* Recursive worker function for ExecFindMatchingSubPlans
*
* Adds valid (non-prunable) subplan IDs to *validsubplans
* Adds valid (non-prunable) subplan IDs to *validsubplans and the RT indexes
* of their corresponding leaf partitions to *validsubplan_rtis if
* it's non-NULL.
*/
static void
find_matching_subplans_recurse(PartitionPruningData *prunedata,
PartitionedRelPruningData *pprune,
bool initial_prune,
Bitmapset **validsubplans)
Bitmapset **validsubplans,
Bitmapset **validsubplan_rtis)
{
Bitmapset *partset;
int i;
@ -2538,8 +2591,13 @@ find_matching_subplans_recurse(PartitionPruningData *prunedata,
while ((i = bms_next_member(partset, i)) >= 0)
{
if (pprune->subplan_map[i] >= 0)
{
*validsubplans = bms_add_member(*validsubplans,
pprune->subplan_map[i]);
if (validsubplan_rtis)
*validsubplan_rtis = bms_add_member(*validsubplan_rtis,
pprune->leafpart_rti_map[i]);
}
else
{
int partidx = pprune->subpart_map[i];
@ -2547,7 +2605,8 @@ find_matching_subplans_recurse(PartitionPruningData *prunedata,
if (partidx >= 0)
find_matching_subplans_recurse(prunedata,
&prunedata->partrelprunedata[partidx],
initial_prune, validsubplans);
initial_prune, validsubplans,
validsubplan_rtis);
else
{
/*

View File

@ -771,7 +771,8 @@ ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
* indexed by rangetable index.
*/
void
ExecInitRangeTable(EState *estate, List *rangeTable, List *permInfos)
ExecInitRangeTable(EState *estate, List *rangeTable, List *permInfos,
Bitmapset *unpruned_relids)
{
/* Remember the range table List as-is */
estate->es_range_table = rangeTable;
@ -782,6 +783,15 @@ ExecInitRangeTable(EState *estate, List *rangeTable, List *permInfos)
/* Set size of associated arrays */
estate->es_range_table_size = list_length(rangeTable);
/*
* Initialize the bitmapset of RT indexes (es_unpruned_relids)
* representing relations that will be scanned during execution. This set
* is initially populated by the caller and may be extended later by
* ExecDoInitialPruning() to include RT indexes of unpruned leaf
* partitions.
*/
estate->es_unpruned_relids = unpruned_relids;
/*
* Allocate an array to store an open Relation corresponding to each
* rangetable entry, and initialize entries to NULL. Relations are opened

View File

@ -595,7 +595,7 @@ choose_next_subplan_locally(AppendState *node)
else if (!node->as_valid_subplans_identified)
{
node->as_valid_subplans =
ExecFindMatchingSubPlans(node->as_prune_state, false);
ExecFindMatchingSubPlans(node->as_prune_state, false, NULL);
node->as_valid_subplans_identified = true;
}
@ -662,7 +662,7 @@ choose_next_subplan_for_leader(AppendState *node)
if (!node->as_valid_subplans_identified)
{
node->as_valid_subplans =
ExecFindMatchingSubPlans(node->as_prune_state, false);
ExecFindMatchingSubPlans(node->as_prune_state, false, NULL);
node->as_valid_subplans_identified = true;
/*
@ -738,7 +738,7 @@ choose_next_subplan_for_worker(AppendState *node)
else if (!node->as_valid_subplans_identified)
{
node->as_valid_subplans =
ExecFindMatchingSubPlans(node->as_prune_state, false);
ExecFindMatchingSubPlans(node->as_prune_state, false, NULL);
node->as_valid_subplans_identified = true;
mark_invalid_subplans_as_finished(node);
@ -891,7 +891,7 @@ ExecAppendAsyncBegin(AppendState *node)
if (!node->as_valid_subplans_identified)
{
node->as_valid_subplans =
ExecFindMatchingSubPlans(node->as_prune_state, false);
ExecFindMatchingSubPlans(node->as_prune_state, false, NULL);
node->as_valid_subplans_identified = true;
classify_matching_subplans(node);

View File

@ -347,8 +347,13 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
ExecRowMark *erm;
ExecAuxRowMark *aerm;
/* ignore "parent" rowmarks; they are irrelevant at runtime */
if (rc->isParent)
/*
* Ignore "parent" rowmarks, because they are irrelevant at runtime.
* Also ignore the rowmarks belonging to child tables that have been
* pruned in ExecDoInitialPruning().
*/
if (rc->isParent ||
!bms_is_member(rc->rti, estate->es_unpruned_relids))
continue;
/* find ExecRowMark and build ExecAuxRowMark */

View File

@ -233,7 +233,7 @@ ExecMergeAppend(PlanState *pstate)
*/
if (node->ms_valid_subplans == NULL)
node->ms_valid_subplans =
ExecFindMatchingSubPlans(node->ms_prune_state, false);
ExecFindMatchingSubPlans(node->ms_prune_state, false, NULL);
/*
* First time through: pull the first tuple from each valid subplan,

View File

@ -690,7 +690,7 @@ ExecInitUpdateProjection(ModifyTableState *mtstate,
Assert(whichrel >= 0 && whichrel < mtstate->mt_nrels);
}
updateColnos = (List *) list_nth(node->updateColnosLists, whichrel);
updateColnos = (List *) list_nth(mtstate->mt_updateColnosLists, whichrel);
/*
* For UPDATE, we use the old tuple to fill up missing values in the tuple
@ -4453,7 +4453,11 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
ModifyTableState *mtstate;
Plan *subplan = outerPlan(node);
CmdType operation = node->operation;
int nrels = list_length(node->resultRelations);
int nrels;
List *resultRelations = NIL;
List *withCheckOptionLists = NIL;
List *returningLists = NIL;
List *updateColnosLists = NIL;
ResultRelInfo *resultRelInfo;
List *arowmarks;
ListCell *l;
@ -4463,6 +4467,45 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
/*
* Only consider unpruned relations for initializing their ResultRelInfo
* struct and other fields such as withCheckOptions, etc.
*/
i = 0;
foreach(l, node->resultRelations)
{
Index rti = lfirst_int(l);
if (bms_is_member(rti, estate->es_unpruned_relids))
{
resultRelations = lappend_int(resultRelations, rti);
if (node->withCheckOptionLists)
{
List *withCheckOptions = list_nth_node(List,
node->withCheckOptionLists,
i);
withCheckOptionLists = lappend(withCheckOptionLists, withCheckOptions);
}
if (node->returningLists)
{
List *returningList = list_nth_node(List,
node->returningLists,
i);
returningLists = lappend(returningLists, returningList);
}
if (node->updateColnosLists)
{
List *updateColnosList = list_nth(node->updateColnosLists, i);
updateColnosLists = lappend(updateColnosLists, updateColnosList);
}
}
i++;
}
nrels = list_length(resultRelations);
/*
* create state structure
*/
@ -4483,6 +4526,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->mt_merge_inserted = 0;
mtstate->mt_merge_updated = 0;
mtstate->mt_merge_deleted = 0;
mtstate->mt_updateColnosLists = updateColnosLists;
/*----------
* Resolve the target relation. This is the same as:
@ -4500,6 +4544,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
*/
if (node->rootRelation > 0)
{
Assert(bms_is_member(node->rootRelation, estate->es_unpruned_relids));
mtstate->rootResultRelInfo = makeNode(ResultRelInfo);
ExecInitResultRelation(estate, mtstate->rootResultRelInfo,
node->rootRelation);
@ -4514,7 +4559,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
/* set up epqstate with dummy subplan data for the moment */
EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL,
node->epqParam, node->resultRelations);
node->epqParam, resultRelations);
mtstate->fireBSTriggers = true;
/*
@ -4532,7 +4577,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
*/
resultRelInfo = mtstate->resultRelInfo;
i = 0;
foreach(l, node->resultRelations)
foreach(l, resultRelations)
{
Index resultRelation = lfirst_int(l);
List *mergeActions = NIL;
@ -4676,7 +4721,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* Initialize any WITH CHECK OPTION constraints if needed.
*/
resultRelInfo = mtstate->resultRelInfo;
foreach(l, node->withCheckOptionLists)
foreach(l, withCheckOptionLists)
{
List *wcoList = (List *) lfirst(l);
List *wcoExprs = NIL;
@ -4699,7 +4744,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
/*
* Initialize RETURNING projections if needed.
*/
if (node->returningLists)
if (returningLists)
{
TupleTableSlot *slot;
ExprContext *econtext;
@ -4708,7 +4753,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* Initialize result tuple slot and assign its rowtype using the first
* RETURNING list. We assume the rest will look the same.
*/
mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
mtstate->ps.plan->targetlist = (List *) linitial(returningLists);
/* Set up a slot for the output of the RETURNING projection(s) */
ExecInitResultTupleSlotTL(&mtstate->ps, &TTSOpsVirtual);
@ -4723,7 +4768,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* Build a projection for each result rel.
*/
resultRelInfo = mtstate->resultRelInfo;
foreach(l, node->returningLists)
foreach(l, returningLists)
{
List *rlist = (List *) lfirst(l);
@ -4824,8 +4869,13 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
ExecRowMark *erm;
ExecAuxRowMark *aerm;
/* ignore "parent" rowmarks; they are irrelevant at runtime */
if (rc->isParent)
/*
* Ignore "parent" rowmarks, because they are irrelevant at runtime.
* Also ignore the rowmarks belonging to child tables that have been
* pruned in ExecDoInitialPruning().
*/
if (rc->isParent ||
!bms_is_member(rc->rti, estate->es_unpruned_relids))
continue;
/* Find ExecRowMark and build ExecAuxRowMark */

View File

@ -557,6 +557,8 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
result->planTree = top_plan;
result->partPruneInfos = glob->partPruneInfos;
result->rtable = glob->finalrtable;
result->unprunableRelids = bms_difference(glob->allRelids,
glob->prunableRelids);
result->permInfos = glob->finalrteperminfos;
result->resultRelations = glob->resultRelations;
result->appendRelations = glob->appendRelations;

View File

@ -564,7 +564,9 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, List *rteperminfos,
/*
* If it's a plain relation RTE (or a subquery that was once a view
* reference), add the relation OID to relationOids.
* reference), add the relation OID to relationOids. Also add its new RT
* index to the set of relations to be potentially accessed during
* execution.
*
* We do this even though the RTE might be unreferenced in the plan tree;
* this would correspond to cases such as views that were expanded, child
@ -576,7 +578,11 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, List *rteperminfos,
*/
if (newrte->rtekind == RTE_RELATION ||
(newrte->rtekind == RTE_SUBQUERY && OidIsValid(newrte->relid)))
{
glob->relationOids = lappend_oid(glob->relationOids, newrte->relid);
glob->allRelids = bms_add_member(glob->allRelids,
list_length(glob->finalrtable));
}
/*
* Add a copy of the RTEPermissionInfo, if any, corresponding to this RTE
@ -1740,6 +1746,10 @@ set_customscan_references(PlannerInfo *root,
*
* Also update the RT indexes present in PartitionedRelPruneInfos to add the
* offset.
*
* Finally, if there are initial pruning steps, add the RT indexes of the
* leaf partitions to the set of relations that are prunable at execution
* startup time.
*/
static int
register_partpruneinfo(PlannerInfo *root, int part_prune_index, int rtoffset)
@ -1762,6 +1772,7 @@ register_partpruneinfo(PlannerInfo *root, int part_prune_index, int rtoffset)
foreach(l2, prune_infos)
{
PartitionedRelPruneInfo *prelinfo = lfirst(l2);
int i;
prelinfo->rtindex += rtoffset;
prelinfo->initial_pruning_steps =
@ -1770,6 +1781,22 @@ register_partpruneinfo(PlannerInfo *root, int part_prune_index, int rtoffset)
prelinfo->exec_pruning_steps =
fix_scan_list(root, prelinfo->exec_pruning_steps,
rtoffset, 1);
for (i = 0; i < prelinfo->nparts; i++)
{
/*
* Non-leaf partitions and partitions that do not have a
* subplan are not included in this map as mentioned in
* make_partitionedrel_pruneinfo().
*/
if (prelinfo->leafpart_rti_map[i])
{
prelinfo->leafpart_rti_map[i] += rtoffset;
if (prelinfo->initial_pruning_steps)
glob->prunableRelids = bms_add_member(glob->prunableRelids,
prelinfo->leafpart_rti_map[i]);
}
}
}
}

View File

@ -645,6 +645,7 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
int *subplan_map;
int *subpart_map;
Oid *relid_map;
int *leafpart_rti_map;
/*
* Construct the subplan and subpart maps for this partitioning level.
@ -657,6 +658,7 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
subpart_map = (int *) palloc(nparts * sizeof(int));
memset(subpart_map, -1, nparts * sizeof(int));
relid_map = (Oid *) palloc0(nparts * sizeof(Oid));
leafpart_rti_map = (int *) palloc0(nparts * sizeof(int));
present_parts = NULL;
i = -1;
@ -671,9 +673,21 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;
subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
relid_map[i] = planner_rt_fetch(partrel->relid, root)->relid;
/*
* Track the RT indexes of "leaf" partitions so they can be
* included in the PlannerGlobal.prunableRelids set, indicating
* relations that may be pruned during executor startup.
*
* Only leaf partitions with a valid subplan that are prunable
* using initial pruning are added to prunableRelids. So
* partitions without a subplan due to constraint exclusion will
* remain in PlannedStmt.unprunableRelids.
*/
if (subplanidx >= 0)
{
present_parts = bms_add_member(present_parts, i);
leafpart_rti_map[i] = (int) partrel->relid;
/* Record finding this subplan */
subplansfound = bms_add_member(subplansfound, subplanidx);
@ -695,6 +709,7 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
pinfo->subplan_map = subplan_map;
pinfo->subpart_map = subpart_map;
pinfo->relid_map = relid_map;
pinfo->leafpart_rti_map = leafpart_rti_map;
}
pfree(relid_subpart_map);

View File

@ -668,7 +668,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel)
addRTEPermissionInfo(&perminfos, rte);
ExecInitRangeTable(estate, list_make1(rte), perminfos);
ExecInitRangeTable(estate, list_make1(rte), perminfos,
bms_make_singleton(1));
edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo);

View File

@ -820,7 +820,8 @@ create_estate_for_relation(Relation rel)
addRTEPermissionInfo(&perminfos, rte);
ExecInitRangeTable(estate, list_make1(rte), perminfos);
ExecInitRangeTable(estate, list_make1(rte), perminfos,
bms_make_singleton(1));
estate->es_output_cid = GetCurrentCommandId(false);

View File

@ -48,6 +48,7 @@ extern void ExecCleanupTupleRouting(ModifyTableState *mtstate,
* nparts Length of subplan_map[] and subpart_map[].
* subplan_map Subplan index by partition index, or -1.
* subpart_map Subpart index by partition index, or -1.
* leafpart_rti_map RT index by partition index, or 0.
* present_parts A Bitmapset of the partition indexes that we
* have subplans or subparts for.
* initial_pruning_steps List of PartitionPruneSteps used to
@ -65,6 +66,7 @@ typedef struct PartitionedRelPruningData
int nparts;
int *subplan_map;
int *subpart_map;
int *leafpart_rti_map;
Bitmapset *present_parts;
List *initial_pruning_steps;
List *exec_pruning_steps;
@ -135,6 +137,7 @@ extern PartitionPruneState *ExecInitPartitionExecPruning(PlanState *planstate,
Bitmapset *relids,
Bitmapset **initially_valid_subplans);
extern Bitmapset *ExecFindMatchingSubPlans(PartitionPruneState *prunestate,
bool initial_prune);
bool initial_prune,
Bitmapset **validsubplan_rtis);
#endif /* EXECPARTITION_H */

View File

@ -595,7 +595,8 @@ extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid);
extern Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags);
extern void ExecInitRangeTable(EState *estate, List *rangeTable, List *permInfos);
extern void ExecInitRangeTable(EState *estate, List *rangeTable, List *permInfos,
Bitmapset *unpruned_relids);
extern void ExecCloseRangeTableRelations(EState *estate);
extern void ExecCloseResultRelations(EState *estate);

View File

@ -658,6 +658,10 @@ typedef struct EState
List *es_part_prune_infos; /* List of PartitionPruneInfo */
List *es_part_prune_states; /* List of PartitionPruneState */
List *es_part_prune_results; /* List of Bitmapset */
Bitmapset *es_unpruned_relids; /* PlannedStmt.unprunableRelids + RT
* indexes of leaf partitions that survive
* initial pruning; see
* ExecDoInitialPruning() */
const char *es_sourceText; /* Source text from QueryDesc */
JunkFilter *es_junkFilter; /* top-level junk filter, if any */
@ -1440,6 +1444,12 @@ typedef struct ModifyTableState
double mt_merge_inserted;
double mt_merge_updated;
double mt_merge_deleted;
/*
* List of valid updateColnosLists. Contains only those belonging to
* unpruned relations from ModifyTable.updateColnosLists.
*/
List *mt_updateColnosLists;
} ModifyTableState;
/* ----------------

View File

@ -116,6 +116,19 @@ typedef struct PlannerGlobal
/* "flat" rangetable for executor */
List *finalrtable;
/*
* RT indexes of all relation RTEs in finalrtable (RTE_RELATION and
* RTE_SUBQUERY RTEs of views)
*/
Bitmapset *allRelids;
/*
* RT indexes of all leaf partitions in nodes that support pruning and are
* subject to runtime pruning at plan initialization time ("initial"
* pruning).
*/
Bitmapset *prunableRelids;
/* "flat" list of RTEPermissionInfos */
List *finalrteperminfos;

View File

@ -74,6 +74,10 @@ typedef struct PlannedStmt
List *rtable; /* list of RangeTblEntry nodes */
Bitmapset *unprunableRelids; /* RT indexes of relations that are not
* subject to runtime pruning or are
* needed to perform runtime pruning */
List *permInfos; /* list of RTEPermissionInfo nodes for rtable
* entries needing one */
@ -1449,18 +1453,22 @@ typedef struct PartitionPruneInfo
* PartitionedRelPruneInfo - Details required to allow the executor to prune
* partitions for a single partitioned table.
*
* subplan_map[] and subpart_map[] are indexed by partition index of the
* partitioned table referenced by 'rtindex', the partition index being the
* order that the partitions are defined in the table's PartitionDesc. For a
* leaf partition p, subplan_map[p] contains the zero-based index of the
* partition's subplan in the parent plan's subplan list; it is -1 if the
* partition is non-leaf or has been pruned. For a non-leaf partition p,
* subpart_map[p] contains the zero-based index of that sub-partition's
* PartitionedRelPruneInfo in the hierarchy's PartitionedRelPruneInfo list;
* it is -1 if the partition is a leaf or has been pruned. Note that subplan
* indexes, as stored in 'subplan_map', are global across the parent plan
* node, but partition indexes are valid only within a particular hierarchy.
* relid_map[p] contains the partition's OID, or 0 if the partition was pruned.
* subplan_map[], subpart_map[], and leafpart_rti_map[] are indexed by partition
* index of the partitioned table referenced by 'rtindex', the partition index
* being the order that the partitions are defined in the table's
* PartitionDesc. For a leaf partition p, subplan_map[p] contains the
* zero-based index of the partition's subplan in the parent plan's subplan
* list; it is -1 if the partition is non-leaf or has been pruned. For a
* non-leaf partition p, subpart_map[p] contains the zero-based index of that
* sub-partition's PartitionedRelPruneInfo in the hierarchy's
* PartitionedRelPruneInfo list; it is -1 if the partition is a leaf or has
* been pruned. leafpart_rti_map[p] contains the RT index of a leaf partition
* if its subplan is in the parent plan' subplan list; it is 0 either if the
* partition is non-leaf or it is leaf but has been pruned during planning.
* Note that subplan indexes, as stored in 'subplan_map', are global across the
* parent plan node, but partition indexes are valid only within a particular
* hierarchy. relid_map[p] contains the partition's OID, or 0 if the partition
* was pruned.
*/
typedef struct PartitionedRelPruneInfo
{
@ -1483,6 +1491,9 @@ typedef struct PartitionedRelPruneInfo
/* subpart index by partition index, or -1 */
int *subpart_map pg_node_attr(array_size(nparts));
/* RT index by partition index, or 0 */
int *leafpart_rti_map pg_node_attr(array_size(nparts));
/* relation OID by partition index, or 0 */
Oid *relid_map pg_node_attr(array_size(nparts));

View File

@ -4469,3 +4469,49 @@ drop table hp_contradict_test;
drop operator class part_test_int4_ops2 using hash;
drop operator ===(int4, int4);
drop function explain_analyze(text);
-- Runtime pruning on UPDATE using WITH CHECK OPTIONS and RETURNING
create table part_abc (a int, b text, c bool) partition by list (a);
create table part_abc_1 (b text, a int, c bool);
create table part_abc_2 (a int, c bool, b text);
alter table part_abc attach partition part_abc_1 for values in (1);
alter table part_abc attach partition part_abc_2 for values in (2);
insert into part_abc values (1, 'b', true);
insert into part_abc values (2, 'c', true);
create view part_abc_view as select * from part_abc where b <> 'a' with check option;
prepare update_part_abc_view as update part_abc_view set b = $2 where a = $1 returning *;
-- Only the unpruned partition should be shown in the list of relations to be
-- updated
explain (costs off) execute update_part_abc_view (1, 'd');
QUERY PLAN
-------------------------------------------------------
Update on part_abc
Update on part_abc_1
-> Append
Subplans Removed: 1
-> Seq Scan on part_abc_1
Filter: ((b <> 'a'::text) AND (a = $1))
(6 rows)
execute update_part_abc_view (1, 'd');
a | b | c
---+---+---
1 | d | t
(1 row)
explain (costs off) execute update_part_abc_view (2, 'a');
QUERY PLAN
-------------------------------------------------------
Update on part_abc
Update on part_abc_2 part_abc_1
-> Append
Subplans Removed: 1
-> Seq Scan on part_abc_2 part_abc_1
Filter: ((b <> 'a'::text) AND (a = $1))
(6 rows)
execute update_part_abc_view (2, 'a');
ERROR: new row violates check option for view "part_abc_view"
DETAIL: Failing row contains (2, a, t).
deallocate update_part_abc_view;
drop view part_abc_view;
drop table part_abc;

View File

@ -1354,3 +1354,23 @@ drop operator class part_test_int4_ops2 using hash;
drop operator ===(int4, int4);
drop function explain_analyze(text);
-- Runtime pruning on UPDATE using WITH CHECK OPTIONS and RETURNING
create table part_abc (a int, b text, c bool) partition by list (a);
create table part_abc_1 (b text, a int, c bool);
create table part_abc_2 (a int, c bool, b text);
alter table part_abc attach partition part_abc_1 for values in (1);
alter table part_abc attach partition part_abc_2 for values in (2);
insert into part_abc values (1, 'b', true);
insert into part_abc values (2, 'c', true);
create view part_abc_view as select * from part_abc where b <> 'a' with check option;
prepare update_part_abc_view as update part_abc_view set b = $2 where a = $1 returning *;
-- Only the unpruned partition should be shown in the list of relations to be
-- updated
explain (costs off) execute update_part_abc_view (1, 'd');
execute update_part_abc_view (1, 'd');
explain (costs off) execute update_part_abc_view (2, 'a');
execute update_part_abc_view (2, 'a');
deallocate update_part_abc_view;
drop view part_abc_view;
drop table part_abc;