diff options
21 files changed, 340 insertions, 51 deletions
diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index b70f4691b72..8875d79d59a 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -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); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 604cb0625b8..74ef35cd250 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -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) */ @@ -2934,6 +2940,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 * ExecInitSubPlan expects to be able to find these entries. Some of the diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index 9c313d81315..134ff62f5cb 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -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; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 57245349cec..b6e89d0620d 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -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 { /* diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 00564985668..c9c756f8568 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -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; @@ -783,6 +784,15 @@ ExecInitRangeTable(EState *estate, List *rangeTable, List *permInfos) 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 * and stored here as needed. diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 2397e5e17b0..15c4227cc62 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -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); diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c index 4e4e3db0b38..a8afbf93b48 100644 --- a/src/backend/executor/nodeLockRows.c +++ b/src/backend/executor/nodeLockRows.c @@ -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 */ diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c index b2dc6626c99..405e8f94285 100644 --- a/src/backend/executor/nodeMergeAppend.c +++ b/src/backend/executor/nodeMergeAppend.c @@ -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, diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index bc82e035ba2..349ed2d6d2c 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -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; @@ -4464,6 +4468,45 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) 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 */ mtstate = makeNode(ModifyTableState); @@ -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 */ diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index ffd7517ea97..7b1a8a0a9f1 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -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; diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 0868249be94..999a5a8ab5a 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -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]); + } + } } } diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 4693eef0c58..ff926732f36 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -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); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 6966037d2ef..f09ab41c605 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -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); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 0227fcbca3d..2f89996a757 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -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); diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h index 855fed4fea5..626613012f9 100644 --- a/src/include/executor/execPartition.h +++ b/src/include/executor/execPartition.h @@ -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 */ diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 45b80e6b98e..30e2a82346f 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -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); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index aca15f771a2..a2cba97e3d5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -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; /* ---------------- diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 52d44f43021..00c700cc3e7 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -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; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 06d9559ebb9..2a2cf816cb6 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -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)); diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index f0707e7f7ea..e667503c961 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -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; diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index ea9a4fe4a23..730545e86a7 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -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; |