From 6ba5abb5c4dc5d01a27d2414e715dd689af6db5e Mon Sep 17 00:00:00 2001 From: "dgrowley@gmail.com" Date: Fri, 6 Apr 2018 00:37:13 +1200 Subject: [PATCH v20 5/5] Improve planning speed of partitioned table UPDATE/DELETEs By making a call to grouping_planner for the complete parse of the query we can make use of the faster partition pruning code used there. This will identify all partitions which could be pruned as IS_DUMMY_RELs, of which we can skip performing each individual grouping_planner call inside inheritance_planner. This can improve planner performance significantly when there are many partitions. There may be a slight slowdown when no partitions could be pruned or when there are very few (1 or 2) partitions. However it seems better to optimize the case when partitions are pruned, rather than the case where they're not, as those queries are less likely to be fast to execute. The case for partitioned tables with just 1 or 2 leaf partitions does not seem worth worrying about too much. The measured regression on 1 partition was just 10% of overall planning time. This commit also implements run-time partition pruning for UPDATE/DELETE. --- src/backend/commands/explain.c | 4 +- src/backend/executor/execMerge.c | 6 +- src/backend/executor/execPartition.c | 22 ++--- src/backend/executor/nodeModifyTable.c | 163 ++++++++++++++++++++++++-------- src/backend/nodes/copyfuncs.c | 1 + src/backend/nodes/outfuncs.c | 1 + src/backend/nodes/readfuncs.c | 1 + src/backend/optimizer/plan/createplan.c | 32 ++++++- src/backend/optimizer/plan/planner.c | 59 ++++++++++++ src/include/nodes/execnodes.h | 11 ++- src/include/nodes/plannodes.h | 2 + 11 files changed, 244 insertions(+), 58 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 549622da93..12c933056d 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -3028,14 +3028,14 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors, /* Should we explicitly label target relations? */ labeltargets = (mtstate->mt_nplans > 1 || (mtstate->mt_nplans == 1 && - mtstate->resultRelInfo->ri_RangeTableIndex != node->nominalRelation)); + mtstate->resultRelInfos[0]->ri_RangeTableIndex != node->nominalRelation)); if (labeltargets) ExplainOpenGroup("Target Tables", "Target Tables", false, es); for (j = 0; j < mtstate->mt_nplans; j++) { - ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j; + ResultRelInfo *resultRelInfo = mtstate->resultRelInfos[j]; FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine; if (labeltargets) diff --git a/src/backend/executor/execMerge.c b/src/backend/executor/execMerge.c index d39ddd3034..7ff1bfc96b 100644 --- a/src/backend/executor/execMerge.c +++ b/src/backend/executor/execMerge.c @@ -203,8 +203,8 @@ ExecMergeMatched(ModifyTableState *mtstate, EState *estate, if (resultRelInfo == NULL) { resultRelInfo = ExecInitPartitionInfo(mtstate, - mtstate->resultRelInfo, - proute, estate, leaf_part_index); + mtstate->resultRelInfos[0], + proute, estate, leaf_part_index); Assert(resultRelInfo != NULL); } } @@ -500,7 +500,7 @@ ExecMergeNotMatched(ModifyTableState *mtstate, EState *estate, * the currently active result relation, which corresponds to the root * of the partition tree. */ - resultRelInfo = mtstate->resultRelInfo; + resultRelInfo = mtstate->resultRelInfos[0]; /* * For INSERT actions, root relation's merge action is OK since the diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 800b6acbd4..ea8f21de2f 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -103,7 +103,7 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel) if (is_update) { - update_rri = mtstate->resultRelInfo; + update_rri = mtstate->resultRelInfos[0]; num_update_rri = list_length(node->plans); proute->subplan_partition_offsets = palloc(num_update_rri * sizeof(int)); @@ -423,8 +423,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, List *wcoList; List *wcoExprs = NIL; ListCell *ll; - int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; - Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc; + int firstVarno = mtstate->resultRelInfos[0]->ri_RangeTableIndex; + Relation firstResultRel = mtstate->resultRelInfos[0]->ri_RelationDesc; /* * In the case of INSERT on a partitioned table, there is only one @@ -479,8 +479,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, TupleTableSlot *slot; ExprContext *econtext; List *returningList; - int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; - Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc; + int firstVarno = mtstate->resultRelInfos[0]->ri_RangeTableIndex; + Relation firstResultRel = mtstate->resultRelInfos[0]->ri_RelationDesc; /* See the comment above for WCO lists. */ Assert((node->operation == CMD_INSERT && @@ -535,8 +535,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, if (node && node->onConflictAction != ONCONFLICT_NONE) { TupleConversionMap *map = proute->parent_child_tupconv_maps[partidx]; - int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; - Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc; + int firstVarno = mtstate->resultRelInfos[0]->ri_RangeTableIndex; + Relation firstResultRel = mtstate->resultRelInfos[0]->ri_RelationDesc; TupleDesc partrelDesc = RelationGetDescr(partrel); ExprContext *econtext = mtstate->ps.ps_ExprContext; ListCell *lc; @@ -676,8 +676,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, { TupleDesc partrelDesc = RelationGetDescr(partrel); TupleConversionMap *map = proute->parent_child_tupconv_maps[partidx]; - int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; - Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc; + int firstVarno = mtstate->resultRelInfos[0]->ri_RangeTableIndex; + Relation firstResultRel = mtstate->resultRelInfos[0]->ri_RelationDesc; /* * If the root parent and partition have the same tuple @@ -1612,7 +1612,7 @@ ExecFindInitialMatchingSubPlans(PartitionPruning *partprune, int nsubplans) * Recursive worker function for ExecFindInitialMatchingSubPlans. */ static void -find_subplans_for_extparams_recurse(PartitionRelPruning * partrelprune, +find_subplans_for_extparams_recurse(PartitionRelPruning *partrelprune, Bitmapset **validsubplans) { PartitionPruneContext *context = &partrelprune->context; @@ -1701,7 +1701,7 @@ ExecFindMatchingSubPlans(PartitionPruning *partprune) * Recursive worker function for ExecFindMatchingSubPlans. */ static void -find_subplans_for_allparams_recurse(PartitionRelPruning * partrelprune, +find_subplans_for_allparams_recurse(PartitionRelPruning *partrelprune, Bitmapset **validsubplans) { PartitionPruneContext *context = &partrelprune->context; diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 0ebf37bd24..1513ffcf90 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1214,12 +1214,12 @@ lreplace:; map_index = resultRelInfo->ri_PartitionLeafIndex; Assert(mtstate->rootResultRelInfo == NULL); tupconv_map = TupConvMapForLeaf(proute, - mtstate->resultRelInfo, - map_index); + mtstate->resultRelInfos[0], + map_index); } else { - map_index = resultRelInfo - mtstate->resultRelInfo; + map_index = mtstate->mt_whichplan; Assert(map_index >= 0 && map_index < mtstate->mt_nplans); tupconv_map = tupconv_map_for_subplan(mtstate, map_index); } @@ -1632,12 +1632,12 @@ static void fireBSTriggers(ModifyTableState *node) { ModifyTable *plan = (ModifyTable *) node->ps.plan; - ResultRelInfo *resultRelInfo = node->resultRelInfo; + ResultRelInfo *resultRelInfo = node->resultRelInfos[0]; /* * If the node modifies a partitioned table, we must fire its triggers. - * Note that in that case, node->resultRelInfo points to the first leaf - * partition, not the root table. + * Note that in that case, node->resultRelInfos[0] points to the first + * leaf partition, not the root table. */ if (node->rootResultRelInfo != NULL) resultRelInfo = node->rootResultRelInfo; @@ -1683,13 +1683,14 @@ static ResultRelInfo * getTargetResultRelInfo(ModifyTableState *node) { /* - * Note that if the node modifies a partitioned table, node->resultRelInfo - * points to the first leaf partition, not the root table. + * Note that if the node modifies a partitioned table, + * node->resultRelInfos[0] points to the first leaf partition, not the + * root table. */ if (node->rootResultRelInfo != NULL) return node->rootResultRelInfo; else - return node->resultRelInfo; + return node->resultRelInfos[0]; } /* @@ -1910,7 +1911,7 @@ static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate) { ResultRelInfo *targetRelInfo = getTargetResultRelInfo(mtstate); - ResultRelInfo *resultRelInfos = mtstate->resultRelInfo; + ResultRelInfo **resultRelInfos = mtstate->resultRelInfos; TupleDesc outdesc; int numResultRelInfos = mtstate->mt_nplans; int i; @@ -1941,7 +1942,7 @@ ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate) for (i = 0; i < numResultRelInfos; ++i) { mtstate->mt_per_subplan_tupconv_maps[i] = - convert_tuples_by_name(RelationGetDescr(resultRelInfos[i].ri_RelationDesc), + convert_tuples_by_name(RelationGetDescr(resultRelInfos[i]->ri_RelationDesc), outdesc, gettext_noop("could not convert row type")); } @@ -2080,7 +2081,7 @@ ExecModifyTable(PlanState *pstate) } /* Preload local variables */ - resultRelInfo = node->resultRelInfo + node->mt_whichplan; + resultRelInfo = node->resultRelInfos[node->mt_whichplan]; subplanstate = node->mt_plans[node->mt_whichplan]; junkfilter = resultRelInfo->ri_junkFilter; @@ -2118,7 +2119,7 @@ ExecModifyTable(PlanState *pstate) if (node->mt_whichplan < node->mt_nplans) { - resultRelInfo++; + resultRelInfo = node->resultRelInfos[node->mt_whichplan]; subplanstate = node->mt_plans[node->mt_whichplan]; junkfilter = resultRelInfo->ri_junkFilter; estate->es_result_relation_info = resultRelInfo; @@ -2309,9 +2310,11 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) ResultRelInfo *resultRelInfo; Plan *subplan; ListCell *l; - int i; + int i, + j; Relation rel; bool update_tuple_routing_needed = node->partColsUpdated; + Bitmapset *validsubplans; /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); @@ -2328,8 +2331,70 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) mtstate->canSetTag = node->canSetTag; mtstate->mt_done = false; + /* If run-time partition pruning is enabled, then setup that up now */ + if (node->part_prune_infos != NIL) + { + PartitionPruning *partprune; + + ExecAssignExprContext(estate, &mtstate->ps); + + partprune = ExecSetupPartitionPruning(&mtstate->ps, + node->part_prune_infos); + + /* + * When there are external params matching the partition key we may be + * able to prune away ModifyTable plans. + */ + if (!bms_is_empty(partprune->extparams)) + { + /* Determine which subplans match the external params */ + validsubplans = ExecFindInitialMatchingSubPlans(partprune, + list_length(node->plans)); + + /* + * If no plans match the given parameters then we must handle this + * case in a special way. The problem here is that code in + * explain.c requires a ModifyTable to have at least one plan in + * order for it to properly determine the Vars in that plan's + * targetlist. We sidestep this issue by just initializing the + * first subplan, but we set the mt_done flag so that we never + * actually bother scanning it. + */ + if (bms_is_empty(validsubplans)) + { + mtstate->mt_done = true; + + /* Mark the first as valid so that it's initialized below */ + validsubplans = bms_make_singleton(0); + } + + nplans = bms_num_members(validsubplans); + } + else + { + /* We'll need to initialize all subplans */ + nplans = list_length(node->plans); + validsubplans = bms_add_range(NULL, 0, nplans - 1); + } + + mtstate->partition_pruning = partprune; + } + else + { + nplans = list_length(node->plans); + + /* + * When run-time partition pruning is not enabled we can just mark all + * plans as valid, they must also all be initialized. + */ + validsubplans = bms_add_range(NULL, 0, nplans - 1); + mtstate->partition_pruning = NULL; + } + + mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans); - mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex; + mtstate->resultRelInfos = (ResultRelInfo **) + palloc(sizeof(ResultRelInfo *) * nplans); /* If modifying a partitioned table, initialize the root table info */ if (node->rootResultRelIndex >= 0) @@ -2353,8 +2418,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) */ saved_resultRelInfo = estate->es_result_relation_info; - resultRelInfo = mtstate->resultRelInfo; - /* * mergeTargetRelation must be set if we're running MERGE and mustn't be * set if we're not. @@ -2362,13 +2425,20 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) Assert(operation != CMD_MERGE || node->mergeTargetRelation > 0); Assert(operation == CMD_MERGE || node->mergeTargetRelation == 0); - resultRelInfo->ri_mergeTargetRTI = node->mergeTargetRelation; - - i = 0; + j = i = 0; foreach(l, node->plans) { + if (!bms_is_member(i, validsubplans)) + { + i++; + continue; + } + subplan = (Plan *) lfirst(l); + resultRelInfo = estate->es_result_relations + node->resultRelIndex + i; + mtstate->resultRelInfos[j] = resultRelInfo; + /* Initialize the usesFdwDirectModify flag */ resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i, node->fdwDirectModifyPlans); @@ -2405,7 +2475,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) /* Now init the plan for this result rel */ estate->es_result_relation_info = resultRelInfo; - mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags); + mtstate->mt_plans[j] = ExecInitNode(subplan, estate, eflags); /* Also let FDWs init themselves for foreign-table result rels */ if (!resultRelInfo->ri_usesFdwDirectModify && @@ -2421,10 +2491,12 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) eflags); } - resultRelInfo++; i++; + j++; } + mtstate->resultRelInfos[0]->ri_mergeTargetRTI = node->mergeTargetRelation; + estate->es_result_relation_info = saved_resultRelInfo; /* Get the target relation */ @@ -2477,26 +2549,34 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) /* * Initialize any WITH CHECK OPTION constraints if needed. */ - resultRelInfo = mtstate->resultRelInfo; - i = 0; + j = i = 0; foreach(l, node->withCheckOptionLists) { - List *wcoList = (List *) lfirst(l); + List *wcoList; List *wcoExprs = NIL; ListCell *ll; + if (!bms_is_member(i, validsubplans)) + { + i++; + continue; + } + + wcoList = (List *) lfirst(l); + foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); ExprState *wcoExpr = ExecInitQual((List *) wco->qual, - mtstate->mt_plans[i]); + mtstate->mt_plans[j]); wcoExprs = lappend(wcoExprs, wcoExpr); } - + resultRelInfo = mtstate->resultRelInfos[j]; resultRelInfo->ri_WithCheckOptions = wcoList; resultRelInfo->ri_WithCheckOptionExprs = wcoExprs; - resultRelInfo++; + + j++; i++; } @@ -2526,15 +2606,25 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) /* * Build a projection for each result rel. */ - resultRelInfo = mtstate->resultRelInfo; + j = i = 0; foreach(l, node->returningLists) { - List *rlist = (List *) lfirst(l); + List *rlist; + + if (!bms_is_member(i, validsubplans)) + { + i++; + continue; + } + + rlist = (List *) lfirst(l); + resultRelInfo = mtstate->resultRelInfos[j]; resultRelInfo->ri_projectReturning = ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, resultRelInfo->ri_RelationDesc->rd_att); - resultRelInfo++; + j++; + i++; } } else @@ -2550,7 +2640,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) } /* Set the list of arbiter indexes if needed for ON CONFLICT */ - resultRelInfo = mtstate->resultRelInfo; + resultRelInfo = mtstate->resultRelInfos[0]; if (node->onConflictAction != ONCONFLICT_NONE) resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes; @@ -2654,7 +2744,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) } } - resultRelInfo = mtstate->resultRelInfo; + resultRelInfo = mtstate->resultRelInfos[0]; if (mtstate->operation == CMD_MERGE) ExecInitMerge(mtstate, estate, resultRelInfo); @@ -2709,11 +2799,11 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) if (junk_filter_needed) { - resultRelInfo = mtstate->resultRelInfo; for (i = 0; i < nplans; i++) { JunkFilter *j; + resultRelInfo = mtstate->resultRelInfos[i]; subplan = mtstate->mt_plans[i]->plan; if (operation == CMD_INSERT || operation == CMD_UPDATE) @@ -2766,13 +2856,12 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) } resultRelInfo->ri_junkFilter = j; - resultRelInfo++; } } else { if (operation == CMD_INSERT) - ExecCheckPlanOutput(mtstate->resultRelInfo->ri_RelationDesc, + ExecCheckPlanOutput(mtstate->resultRelInfos[0]->ri_RelationDesc, subplan->targetlist); } } @@ -2819,7 +2908,7 @@ ExecEndModifyTable(ModifyTableState *node) */ for (i = 0; i < node->mt_nplans; i++) { - ResultRelInfo *resultRelInfo = node->resultRelInfo + i; + ResultRelInfo *resultRelInfo = node->resultRelInfos[i]; if (!resultRelInfo->ri_usesFdwDirectModify && resultRelInfo->ri_FdwRoutine != NULL && diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 739a023965..87339d5e79 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -225,6 +225,7 @@ _copyModifyTable(const ModifyTable *from) COPY_NODE_FIELD(exclRelTlist); COPY_NODE_FIELD(mergeSourceTargetList); COPY_NODE_FIELD(mergeActionList); + COPY_NODE_FIELD(part_prune_infos); return newnode; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index e31b6a9c33..b748a1d204 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -393,6 +393,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node) WRITE_NODE_FIELD(exclRelTlist); WRITE_NODE_FIELD(mergeSourceTargetList); WRITE_NODE_FIELD(mergeActionList); + WRITE_NODE_FIELD(part_prune_infos); } static void diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 5bf3d28c51..85d7f38d72 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1640,6 +1640,7 @@ _readModifyTable(void) READ_NODE_FIELD(exclRelTlist); READ_NODE_FIELD(mergeSourceTargetList); READ_NODE_FIELD(mergeActionList); + READ_NODE_FIELD(part_prune_infos); READ_DONE(); } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 4e54fe6d25..cd75c59496 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -295,7 +295,8 @@ static ModifyTable *make_modifytable(PlannerInfo *root, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeSourceTargetList, - List *mergeActionList, int epqParam); + List *mergeActionList, int epqParam, + List *partpruneinfos); static GatherMerge *create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path); @@ -2484,6 +2485,7 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) List *subplans = NIL; ListCell *subpaths, *subroots; + List *partpruneinfos = NIL; /* Build the plan for each input path */ forboth(subpaths, best_path->subpaths, @@ -2512,6 +2514,27 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) subplans = lappend(subplans, subplan); } + if (best_path->partitioned_rels != NIL) + { + int partrelid = linitial_int(best_path->partitioned_rels); + RelOptInfo *rel = root->simple_rel_array[partrelid]; + List *prunequal = NIL; + + prunequal = extract_actual_clauses(rel->baserestrictinfo, false); + + /* + * If any quals exist, then these may be useful to allow us to perform + * further partition pruning during execution. We'll generate a + * PartitionPruneInfo for each partitioned rel to store these quals + * and allow translation of partition indexes into subpath indexes. + */ + if (prunequal != NIL) + partpruneinfos = make_partition_pruneinfo(root, + best_path->partitioned_rels, + best_path->resultRelations, + best_path->subpaths, prunequal); + } + plan = make_modifytable(root, best_path->operation, best_path->canSetTag, @@ -2527,7 +2550,8 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) best_path->onconflict, best_path->mergeSourceTargetList, best_path->mergeActionList, - best_path->epqParam); + best_path->epqParam, + partpruneinfos); copy_generic_path_info(&plan->plan, &best_path->path); @@ -6600,7 +6624,8 @@ make_modifytable(PlannerInfo *root, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, List *mergeSourceTargetList, - List *mergeActionList, int epqParam) + List *mergeActionList, int epqParam, + List *partpruneinfos) { ModifyTable *node = makeNode(ModifyTable); List *fdw_private_list; @@ -6662,6 +6687,7 @@ make_modifytable(PlannerInfo *root, node->mergeSourceTargetList = mergeSourceTargetList; node->mergeActionList = mergeActionList; node->epqParam = epqParam; + node->part_prune_infos = partpruneinfos; /* * For each result relation that is a foreign table, allow the FDW to diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 421dc79cc4..5cfc665347 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1197,6 +1197,7 @@ inheritance_planner(PlannerInfo *root) Query *parent_parse; Bitmapset *parent_relids = bms_make_singleton(top_parentRTindex); PlannerInfo **parent_roots = NULL; + PlannerInfo *partition_root = NULL; Assert(parse->commandType != CMD_INSERT); @@ -1274,6 +1275,32 @@ inheritance_planner(PlannerInfo *root) * the ModifyTable node, if one is needed at all. */ partitioned_relids = bms_make_singleton(top_parentRTindex); + + /* + * For partitioned tables, since we're able to determine the minimum + * set of partitions required much more easily than what we can do + * with an inheritance hierarchy, we invoke the grouping_planner on + * the entire given query in order to determine the minimum set of + * partitions which will be required below. This may mean that we + * invoke the grouping planner far fewer times, as otherwise we'd have + * to invoke it once for each partition. + */ + + /* + * Since the planner tends to scribble on the parse, we must make a + * copy of it. We also must make copies of the PlannerInfo and + * PlannerGlobal since these will also be modified from the call to + * grouping_planner. + */ + partition_root = makeNode(PlannerInfo); + partition_root->glob = makeNode(PlannerGlobal); + + memcpy(partition_root, root, sizeof(PlannerInfo)); + memcpy(partition_root->glob, root->glob, sizeof(PlannerGlobal)); + + partition_root->parse = copyObject(partition_root->parse); + + grouping_planner(partition_root, true, 0.0 /* retrieve all tuples */ ); } /* @@ -1304,6 +1331,21 @@ inheritance_planner(PlannerInfo *root) if (!bms_is_member(appinfo->parent_relid, parent_relids)) continue; + /* + * If the target rel is a partitioned table then skip any child + * partitions which were found to be dummies by the grouping_planner + * call performed above. + */ + if (partition_root) + { + RelOptInfo *rel; + + rel = find_base_rel(partition_root, appinfo->child_relid); + + if (IS_DUMMY_REL(rel)) + continue; + } + /* * expand_inherited_rtentry() always processes a parent before any of * that parent's children, so the parent_root for this relation should @@ -1629,6 +1671,23 @@ inheritance_planner(PlannerInfo *root) Assert(list_length(partitioned_rels) >= 1); } + /* + * The individual grouping_planner calls per partition above performed no + * planning on the actual partitioned tables, however, in order to allow + * partition pruning at run-time we must know the baserestrictinfo of each + * partition. We simply replace the RelOptInfos from the initial full + * plan which was generated and replace the non-complete RelOptInfos which + * are stored in root. + */ + if (partition_root) + { + int i; + + i = -1; + while ((i = bms_next_member(partitioned_relids, i)) >= 0) + root->simple_rel_array[i] = partition_root->simple_rel_array[i]; + } + /* Create Path representing a ModifyTable to do the UPDATE/DELETE work */ add_path(final_rel, (Path *) create_modifytable_path(root, final_rel, diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 1986abaa9c..526bc2019f 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1027,6 +1027,8 @@ typedef struct EPQState } EPQState; +struct PartitionPruning; + /* ---------------- * ResultState information * ---------------- @@ -1083,7 +1085,7 @@ typedef struct ModifyTableState PlanState **mt_plans; /* subplans (one per target rel) */ int mt_nplans; /* number of plans in the array */ int mt_whichplan; /* which one is being executed (0..n-1) */ - ResultRelInfo *resultRelInfo; /* per-subplan target relations */ + ResultRelInfo **resultRelInfos; /* per-subplan target relations */ ResultRelInfo *rootResultRelInfo; /* root target relation (partitioned * table root) */ List **mt_arowmarks; /* per-subplan ExecAuxRowMark lists */ @@ -1109,6 +1111,12 @@ typedef struct ModifyTableState /* Flags showing which subcommands are present INS/UPD/DEL/DO NOTHING */ int mt_merge_subcommands; + + /* + * Details required to allow partitions to be eliminated from the scan, or + * NULL if not possible. + */ + struct PartitionPruning *partition_pruning; } ModifyTableState; /* ---------------- @@ -1130,7 +1138,6 @@ struct AppendState; typedef struct AppendState AppendState; struct ParallelAppendState; typedef struct ParallelAppendState ParallelAppendState; -struct PartitionPruning; struct AppendState { diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index a7dbd31466..c1b6c21f70 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -242,6 +242,8 @@ typedef struct ModifyTable List *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */ List *mergeSourceTargetList; List *mergeActionList; /* actions for MERGE */ + List *part_prune_infos; /* Mapping details for run-time subplan + * pruning, one per partitioned_rels */ } ModifyTable; /* ---------------- -- 2.16.2.windows.1