diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 530328af43..7e324c12e2 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2310,7 +2310,6 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node) WRITE_BITMAPSET_FIELD(top_parent_relids); WRITE_BOOL_FIELD(partbounds_merged); WRITE_BITMAPSET_FIELD(all_partrels); - WRITE_NODE_FIELD(partitioned_child_rels); } static void diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index b399592ff8..803d9bae7f 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -104,8 +104,13 @@ static void generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, static Path *get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer); +static List *accumulate_partitioned_rels(List *partitioned_rels, + List *sub_partitioned_rels, + bool flatten_partitioned_rels); static void accumulate_append_subpath(Path *path, - List **subpaths, List **special_subpaths); + List **subpaths, List **special_subpaths, + List **partitioned_rels, + bool flatten_partitioned_rels); static Path *get_singleton_append_subpath(Path *path); static void set_dummy_rel_pathlist(RelOptInfo *rel); static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, @@ -959,17 +964,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, Assert(IS_SIMPLE_REL(rel)); - /* - * Initialize partitioned_child_rels to contain this RT index. - * - * Note that during the set_append_rel_pathlist() phase, we will bubble up - * the indexes of partitioned relations that appear down in the tree, so - * that when we've created Paths for all the children, the root - * partitioned table's list will contain all such indexes. - */ - if (rte->relkind == RELKIND_PARTITIONED_TABLE) - rel->partitioned_child_rels = list_make1_int(rti); - /* * If this is a partitioned baserel, set the consider_partitionwise_join * flag; currently, we only consider partitionwise joins with the baserel @@ -1269,12 +1263,6 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, if (IS_DUMMY_REL(childrel)) continue; - /* Bubble up childrel's partitioned children. */ - if (rel->part_scheme) - rel->partitioned_child_rels = - list_concat(rel->partitioned_child_rels, - childrel->partitioned_child_rels); - /* * Child is live, so add it to the live_childrels list for use below. */ @@ -1312,56 +1300,35 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, List *all_child_outers = NIL; ListCell *l; List *partitioned_rels = NIL; + List *partial_partitioned_rels = NIL; + List *pa_partitioned_rels = NIL; double partial_rows = -1; + bool flatten_partitioned_rels; /* If appropriate, consider parallel append */ pa_subpaths_valid = enable_parallel_append && rel->consider_parallel; + /* What we do with the partitioned_rels list is different for UNION ALL */ + flatten_partitioned_rels = (rel->rtekind != RTE_SUBQUERY); + /* - * AppendPath generated for partitioned tables must record the RT indexes - * of partitioned tables that are direct or indirect children of this - * Append rel. - * - * AppendPath may be for a sub-query RTE (UNION ALL), in which case, 'rel' - * itself does not represent a partitioned relation, but the child sub- - * queries may contain references to partitioned relations. The loop - * below will look for such children and collect them in a list to be - * passed to the path creation function. (This assumes that we don't need - * to look through multiple levels of subquery RTEs; if we ever do, we - * could consider stuffing the list we generate here into sub-query RTE's - * RelOptInfo, just like we do for partitioned rels, which would be used - * when populating our parent rel with paths. For the present, that - * appears to be unnecessary.) + * For partitioned tables, we accumulate a list of Relids of each + * partitioned table which has at least one of its subpartitions directly + * present as a subpath in this Append. This is used later for run-time + * partition pruning. We must maintain separate lists for each Append + * Path that we create as some paths that we create here can't flatten + * sub-Appends and sub-MergeAppends into the top-level Append. We needn't + * bother doing this for join rels as no run-time pruning is done on + * those. */ - if (rel->part_scheme != NULL) + if (rel->reloptkind != RELOPT_JOINREL && rel->part_scheme != NULL) { - if (IS_SIMPLE_REL(rel)) - partitioned_rels = list_make1(rel->partitioned_child_rels); - else if (IS_JOIN_REL(rel)) - { - int relid = -1; - List *partrels = NIL; + partitioned_rels = list_make1(bms_make_singleton(rel->relid)); + partial_partitioned_rels = list_make1(bms_make_singleton(rel->relid)); - /* - * For a partitioned joinrel, concatenate the component rels' - * partitioned_child_rels lists. - */ - while ((relid = bms_next_member(rel->relids, relid)) >= 0) - { - RelOptInfo *component; - - Assert(relid >= 1 && relid < root->simple_rel_array_size); - component = root->simple_rel_array[relid]; - Assert(component->part_scheme != NULL); - Assert(list_length(component->partitioned_child_rels) >= 1); - partrels = list_concat(partrels, - component->partitioned_child_rels); - } - - partitioned_rels = list_make1(partrels); - } - - Assert(list_length(partitioned_rels) >= 1); + /* skip this one if we're not going to make a Parallel Append path */ + if (pa_subpaths_valid) + pa_partitioned_rels = list_make1(bms_make_singleton(rel->relid)); } /* @@ -1375,14 +1342,6 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, ListCell *lcp; Path *cheapest_partial_path = NULL; - /* - * For UNION ALLs with non-empty partitioned_child_rels, accumulate - * the Lists of child relations. - */ - if (rel->rtekind == RTE_SUBQUERY && childrel->partitioned_child_rels != NIL) - partitioned_rels = lappend(partitioned_rels, - childrel->partitioned_child_rels); - /* * If child has an unparameterized cheapest-total path, add that to * the unparameterized Append path we are constructing for the parent. @@ -1394,7 +1353,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, if (childrel->pathlist != NIL && childrel->cheapest_total_path->param_info == NULL) accumulate_append_subpath(childrel->cheapest_total_path, - &subpaths, NULL); + &subpaths, NULL, &partitioned_rels, + flatten_partitioned_rels); else subpaths_valid = false; @@ -1403,7 +1363,9 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, { cheapest_partial_path = linitial(childrel->partial_pathlist); accumulate_append_subpath(cheapest_partial_path, - &partial_subpaths, NULL); + &partial_subpaths, NULL, + &partial_partitioned_rels, + flatten_partitioned_rels); } else partial_subpaths_valid = false; @@ -1432,7 +1394,9 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, Assert(cheapest_partial_path != NULL); accumulate_append_subpath(cheapest_partial_path, &pa_partial_subpaths, - &pa_nonpartial_subpaths); + &pa_nonpartial_subpaths, + &pa_partitioned_rels, + flatten_partitioned_rels); } else @@ -1452,7 +1416,9 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, */ accumulate_append_subpath(nppath, &pa_nonpartial_subpaths, - NULL); + NULL, + &pa_partitioned_rels, + flatten_partitioned_rels); } } @@ -1572,7 +1538,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, appendpath = create_append_path(root, rel, NIL, partial_subpaths, NIL, NULL, parallel_workers, enable_parallel_append, - partitioned_rels, -1); + partial_partitioned_rels, -1); /* * Make sure any subsequent partial paths use the same row count @@ -1621,7 +1587,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, appendpath = create_append_path(root, rel, pa_nonpartial_subpaths, pa_partial_subpaths, NIL, NULL, parallel_workers, true, - partitioned_rels, partial_rows); + pa_partitioned_rels, partial_rows); add_partial_path(rel, (Path *) appendpath); } @@ -1651,6 +1617,10 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, { Relids required_outer = (Relids) lfirst(l); ListCell *lcr; + List *part_rels = NIL; + + if (rel->reloptkind != RELOPT_JOINREL && rel->part_scheme != NULL) + part_rels = list_make1(bms_make_singleton(rel->relid)); /* Select the child paths for an Append with this parameterization */ subpaths = NIL; @@ -1676,14 +1646,15 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, subpaths_valid = false; break; } - accumulate_append_subpath(subpath, &subpaths, NULL); + accumulate_append_subpath(subpath, &subpaths, NULL, &part_rels, + flatten_partitioned_rels); } if (subpaths_valid) add_path(rel, (Path *) create_append_path(root, rel, subpaths, NIL, NIL, required_outer, 0, false, - partitioned_rels, -1)); + part_rels, -1)); } /* @@ -1697,17 +1668,14 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, { RelOptInfo *childrel = (RelOptInfo *) linitial(live_childrels); - foreach(l, childrel->partial_pathlist) + /* skip the cheapest partial path, since we already used that above */ + for_each_from(l, childrel->partial_pathlist, 1) { Path *path = (Path *) lfirst(l); AppendPath *appendpath; - /* - * Skip paths with no pathkeys. Also skip the cheapest partial - * path, since we already used that above. - */ - if (path->pathkeys == NIL || - path == linitial(childrel->partial_pathlist)) + /* skip paths with no pathkeys. */ + if (path->pathkeys == NIL) continue; appendpath = create_append_path(root, rel, NIL, list_make1(path), @@ -1757,6 +1725,18 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, List *partition_pathkeys_desc = NIL; bool partition_pathkeys_partial = true; bool partition_pathkeys_desc_partial = true; + List *startup_partitioned_rels = NIL; + List *total_partitioned_rels = NIL; + bool flatten_partitioned_rels; + + /* Set up the method for building the partitioned rels lists */ + flatten_partitioned_rels = (rel->rtekind != RTE_SUBQUERY); + + if (rel->reloptkind != RELOPT_JOINREL && rel->part_scheme != NULL) + { + startup_partitioned_rels = list_make1(bms_make_singleton(rel->relid)); + total_partitioned_rels = list_make1(bms_make_singleton(rel->relid)); + } /* * Some partitioned table setups may allow us to use an Append node @@ -1898,9 +1878,13 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, * child paths for the MergeAppend. */ accumulate_append_subpath(cheapest_startup, - &startup_subpaths, NULL); + &startup_subpaths, NULL, + &startup_partitioned_rels, + flatten_partitioned_rels); accumulate_append_subpath(cheapest_total, - &total_subpaths, NULL); + &total_subpaths, NULL, + &total_partitioned_rels, + flatten_partitioned_rels); } } @@ -1916,7 +1900,7 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, NULL, 0, false, - partitioned_rels, + startup_partitioned_rels, -1)); if (startup_neq_total) add_path(rel, (Path *) create_append_path(root, @@ -1927,7 +1911,7 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, NULL, 0, false, - partitioned_rels, + total_partitioned_rels, -1)); } else @@ -1938,14 +1922,14 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, startup_subpaths, pathkeys, NULL, - partitioned_rels)); + startup_partitioned_rels)); if (startup_neq_total) add_path(rel, (Path *) create_merge_append_path(root, rel, total_subpaths, pathkeys, NULL, - partitioned_rels)); + total_partitioned_rels)); } } } @@ -2024,6 +2008,54 @@ get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel, return cheapest; } +/* + * accumulate_partitioned_rels + * Record 'sub_partitioned_rels' in the 'partitioned_rels' list, + * flattening as appropriate. + */ +static List * +accumulate_partitioned_rels(List *partitioned_rels, + List *sub_partitioned_rels, + bool flatten) +{ + if (flatten) + { + /* + * We're only called with flatten == true when the partitioned_rels + * list has at most 1 element. So we can just add the members from + * sub list's first element onto the first element of + * partitioned_rels. Only later in planning when doing UNION ALL + * Append processing will we see flatten == false. partitioned_rels + * may end up with more than 1 element then, but we never expect to be + * called with flatten == true again after that, so we needn't bother + * doing anything here for anything but the initial element. + */ + if (partitioned_rels != NIL && sub_partitioned_rels != NIL) + { + Relids partrels = (Relids) linitial(partitioned_rels); + Relids subpartrels = (Relids) linitial(sub_partitioned_rels); + + /* Ensure the above comment holds true */ + Assert(list_length(partitioned_rels) == 1); + Assert(list_length(sub_partitioned_rels) == 1); + + linitial(partitioned_rels) = bms_add_members(partrels, subpartrels); + } + } + else + { + /* + * Handle UNION ALL to partitioned tables. This always occurs after + * we've done the accumulation for sub-partitioned tables, so there's + * no need to consider how adding multiple elements to the top level + * list affects the flatten == true case above. + */ + partitioned_rels = list_concat(partitioned_rels, sub_partitioned_rels); + } + + return partitioned_rels; +} + /* * accumulate_append_subpath * Add a subpath to the list being built for an Append or MergeAppend. @@ -2044,9 +2076,24 @@ get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel, * children to subpaths and the rest to special_subpaths. If the latter is * NULL, we don't flatten the path at all (unless it contains only partial * paths). + * + * When pulling up sub-Appends and sub-Merge Appends, we also gather the + * path's list of partitioned tables and store in 'partitioned_rels'. The + * exact behavior here depends on the value of 'flatten_partitioned_rels'. + * + * When 'flatten_partitioned_rels' is true, 'partitioned_rels' will contain at + * most one element which is a Relids of the partitioned relations which there + * are subpaths for. In this case, we just add the RT indexes for the + * partitioned tables for the subpath we're pulling up to the single entry in + * 'partitioned_rels'. When 'flatten_partitioned_rels' is false we + * concatenate the path's partitioned rel list onto the top-level list. This + * done for UNION ALLs which could have a partitioned table in each union + * branch. */ static void -accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths) +accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths, + List **partitioned_rels, + bool flatten_partitioned_rels) { if (IsA(path, AppendPath)) { @@ -2055,6 +2102,9 @@ accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths) if (!apath->path.parallel_aware || apath->first_partial_path == 0) { *subpaths = list_concat(*subpaths, apath->subpaths); + *partitioned_rels = accumulate_partitioned_rels(*partitioned_rels, + apath->partitioned_rels, + flatten_partitioned_rels); return; } else if (special_subpaths != NULL) @@ -2070,6 +2120,9 @@ accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths) apath->first_partial_path); *special_subpaths = list_concat(*special_subpaths, new_special_subpaths); + *partitioned_rels = accumulate_partitioned_rels(*partitioned_rels, + apath->partitioned_rels, + flatten_partitioned_rels); return; } } @@ -2078,6 +2131,9 @@ accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths) MergeAppendPath *mpath = (MergeAppendPath *) path; *subpaths = list_concat(*subpaths, mpath->subpaths); + *partitioned_rels = accumulate_partitioned_rels(*partitioned_rels, + mpath->partitioned_rels, + flatten_partitioned_rels); return; } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 94280a730c..40abe6f9f6 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -1228,7 +1228,6 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags) * do partition pruning. */ if (enable_partition_pruning && - rel->reloptkind == RELOPT_BASEREL && best_path->partitioned_rels != NIL) { List *prunequal; @@ -1395,7 +1394,6 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path, * do partition pruning. */ if (enable_partition_pruning && - rel->reloptkind == RELOPT_BASEREL && best_path->partitioned_rels != NIL) { List *prunequal; diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index a203e6f1ff..76245c1ff3 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -257,7 +257,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->all_partrels = NULL; rel->partexprs = NULL; rel->nullable_partexprs = NULL; - rel->partitioned_child_rels = NIL; /* * Pass assorted information down the inheritance hierarchy. @@ -672,7 +671,6 @@ build_join_rel(PlannerInfo *root, joinrel->all_partrels = NULL; joinrel->partexprs = NULL; joinrel->nullable_partexprs = NULL; - joinrel->partitioned_child_rels = NIL; /* Compute information relevant to the foreign relations. */ set_foreign_rel_properties(joinrel, outer_rel, inner_rel); @@ -850,7 +848,6 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel, joinrel->all_partrels = NULL; joinrel->partexprs = NULL; joinrel->nullable_partexprs = NULL; - joinrel->partitioned_child_rels = NIL; joinrel->top_parent_relids = bms_union(outer_rel->top_parent_relids, inner_rel->top_parent_relids); diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 6268623d56..8e1187e31f 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -141,7 +141,7 @@ typedef struct PruneStepResult static List *make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, int *relid_subplan_map, - List *partitioned_rels, List *prunequal, + Relids partrelids, List *prunequal, Bitmapset **matchedsubplans); static void gen_partprune_steps(RelOptInfo *rel, List *clauses, PartClauseTarget target, @@ -267,13 +267,13 @@ make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, prunerelinfos = NIL; foreach(lc, partitioned_rels) { - List *rels = (List *) lfirst(lc); + Relids partrelids = (Relids) lfirst(lc); List *pinfolist; Bitmapset *matchedsubplans = NULL; pinfolist = make_partitionedrel_pruneinfo(root, parentrel, relid_subplan_map, - rels, prunequal, + partrelids, prunequal, &matchedsubplans); /* When pruning is possible, record the matched subplans */ @@ -342,7 +342,7 @@ make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, static List * make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, int *relid_subplan_map, - List *partitioned_rels, List *prunequal, + Relids partrelids, List *prunequal, Bitmapset **matchedsubplans) { RelOptInfo *targetpart = NULL; @@ -351,6 +351,7 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, int *relid_subpart_map; Bitmapset *subplansfound = NULL; ListCell *lc; + int rti; int i; /* @@ -364,9 +365,9 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, relid_subpart_map = palloc0(sizeof(int) * root->simple_rel_array_size); i = 1; - foreach(lc, partitioned_rels) + rti = -1; + while ((rti = bms_next_member(partrelids, rti)) > 0) { - Index rti = lfirst_int(lc); RelOptInfo *subpart = find_base_rel(root, rti); PartitionedRelPruneInfo *pinfo; List *partprunequal; @@ -379,14 +380,11 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, * Fill the mapping array. * * relid_subpart_map maps relid of a non-leaf partition to the index - * in 'partitioned_rels' of that rel (which will also be the index in - * the returned PartitionedRelPruneInfo list of the info for that - * partition). We use 1-based indexes here, so that zero can - * represent an un-filled array entry. + * in the returned PartitionedRelPruneInfo list of the info for that + * partition. We use 1-based indexes here, so that zero can represent + * an un-filled array entry. */ Assert(rti < root->simple_rel_array_size); - /* No duplicates please */ - Assert(relid_subpart_map[rti] == 0); relid_subpart_map[rti] = i++; /* @@ -582,6 +580,13 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, present_parts = bms_add_member(present_parts, i); } + /* + * Ensure there were no stray PartitionedRelPruneInfo generated for + * partitioned tables that we have no sub-paths or + * sub-PartitionedRelPruneInfo for. + */ + Assert(!bms_is_empty(present_parts)); + /* Record the maps and other information. */ pinfo->present_parts = present_parts; pinfo->nparts = nparts; diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 45cbf6045a..8f62d61702 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -601,9 +601,6 @@ typedef struct PartitionSchemeData *PartitionScheme; * part_rels - RelOptInfos for each partition * all_partrels - Relids set of all partition relids * partexprs, nullable_partexprs - Partition key expressions - * partitioned_child_rels - RT indexes of unpruned partitions of - * this relation that are partitioned tables - * themselves, in hierarchical order * * The partexprs and nullable_partexprs arrays each contain * part_scheme->partnatts elements. Each of the elements is a list of @@ -751,7 +748,6 @@ typedef struct RelOptInfo Relids all_partrels; /* Relids set of all partition relids */ List **partexprs; /* Non-nullable partition key expressions */ List **nullable_partexprs; /* Nullable partition key expressions */ - List *partitioned_child_rels; /* List of RT indexes */ } RelOptInfo; /* @@ -1401,8 +1397,9 @@ typedef struct CustomPath typedef struct AppendPath { Path path; - /* RT indexes of non-leaf tables in a partition tree */ - List *partitioned_rels; + List *partitioned_rels; /* List of Relids containing RT indexes of + * non-leaf tables for each partition + * hierarchy whose paths are in 'subpaths' */ List *subpaths; /* list of component Paths */ /* Index of first partial path in subpaths; list_length(subpaths) if none */ int first_partial_path; @@ -1427,8 +1424,9 @@ extern bool is_dummy_rel(RelOptInfo *rel); typedef struct MergeAppendPath { Path path; - /* RT indexes of non-leaf tables in a partition tree */ - List *partitioned_rels; + List *partitioned_rels; /* List of Relids containing RT indexes of + * non-leaf tables for each partition + * hierarchy whose paths are in 'subpaths' */ List *subpaths; /* list of component Paths */ double limit_tuples; /* hard limit on output tuples, or -1 */ } MergeAppendPath; diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 50d2a7e4b9..80e71b8e2b 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -3671,6 +3671,108 @@ explain (costs off) update listp1 set a = 1 where a = 2; reset constraint_exclusion; reset enable_partition_pruning; drop table listp; +-- Ensure run-time pruning works correctly for nested Append nodes +set parallel_setup_cost to 0; +set parallel_tuple_cost to 0; +create table listp (a int) partition by list(a); +create table listp_12 partition of listp for values in(1,2) partition by list(a); +create table listp_12_1 partition of listp_12 for values in(1); +create table listp_12_2 partition of listp_12 for values in(2); +-- Force the 2nd subnode of the Append to be non-parallel. This results in +-- a nested Append node because the mixed parallel / non-parallel paths cannot +-- be pulled into the top-level Append. +alter table listp_12_1 set (parallel_workers = 0); +-- Ensure that listp_12_2 is not scanned. (The nested Append is not seen in +-- the plan as it's pulled in setref.c due to having just a single subnode). +explain (analyze on, costs off, timing off, summary off) +select * from listp where a = (select 1); + QUERY PLAN +---------------------------------------------------------------------- + Gather (actual rows=0 loops=1) + Workers Planned: 2 + Params Evaluated: $0 + Workers Launched: 2 + InitPlan 1 (returns $0) + -> Result (actual rows=1 loops=1) + -> Parallel Append (actual rows=0 loops=3) + -> Seq Scan on listp_12_1 listp_1 (actual rows=0 loops=1) + Filter: (a = $0) + -> Parallel Seq Scan on listp_12_2 listp_2 (never executed) + Filter: (a = $0) +(11 rows) + +-- Like the above but throw some more complexity at the planner by adding +-- a UNION ALL. We expect both sides of the union not to scan the +-- non-required partitions. +explain (analyze on, costs off, timing off, summary off) +select * from listp where a = (select 1) + union all +select * from listp where a = (select 2); + QUERY PLAN +----------------------------------------------------------------------------------- + Append (actual rows=0 loops=1) + -> Gather (actual rows=0 loops=1) + Workers Planned: 2 + Params Evaluated: $0 + Workers Launched: 2 + InitPlan 1 (returns $0) + -> Result (actual rows=1 loops=1) + -> Parallel Append (actual rows=0 loops=3) + -> Seq Scan on listp_12_1 listp_1 (actual rows=0 loops=1) + Filter: (a = $0) + -> Parallel Seq Scan on listp_12_2 listp_2 (never executed) + Filter: (a = $0) + -> Gather (actual rows=0 loops=1) + Workers Planned: 2 + Params Evaluated: $1 + Workers Launched: 2 + InitPlan 2 (returns $1) + -> Result (actual rows=1 loops=1) + -> Parallel Append (actual rows=0 loops=3) + -> Seq Scan on listp_12_1 listp_4 (never executed) + Filter: (a = $1) + -> Parallel Seq Scan on listp_12_2 listp_5 (actual rows=0 loops=1) + Filter: (a = $1) +(23 rows) + +drop table listp; +reset parallel_tuple_cost; +reset parallel_setup_cost; +-- Test case for run-time pruning with a nested Merge Append +set enable_sort to 0; +create table rangep (a int, b int) partition by range (a); +create table rangep_0_to_100 partition of rangep for values from (0) to (100) partition by list (b); +-- We need 3 sub-partitions. 1 to validate pruning worked and another two +-- because a single remaining partition would be pulled up to the main Append. +create table rangep_0_to_100_1 partition of rangep_0_to_100 for values in(1); +create table rangep_0_to_100_2 partition of rangep_0_to_100 for values in(2); +create table rangep_0_to_100_3 partition of rangep_0_to_100 for values in(3); +create table rangep_100_to_200 partition of rangep for values from (100) to (200); +create index on rangep (a); +-- Ensure run-time pruning works on the nested Merge Append +explain (analyze on, costs off, timing off, summary off) +select * from rangep where b IN((select 1),(select 2)) order by a; + QUERY PLAN +------------------------------------------------------------------------------------------------------------ + Append (actual rows=0 loops=1) + InitPlan 1 (returns $0) + -> Result (actual rows=1 loops=1) + InitPlan 2 (returns $1) + -> Result (actual rows=1 loops=1) + -> Merge Append (actual rows=0 loops=1) + Sort Key: rangep_2.a + -> Index Scan using rangep_0_to_100_1_a_idx on rangep_0_to_100_1 rangep_2 (actual rows=0 loops=1) + Filter: (b = ANY (ARRAY[$0, $1])) + -> Index Scan using rangep_0_to_100_2_a_idx on rangep_0_to_100_2 rangep_3 (actual rows=0 loops=1) + Filter: (b = ANY (ARRAY[$0, $1])) + -> Index Scan using rangep_0_to_100_3_a_idx on rangep_0_to_100_3 rangep_4 (never executed) + Filter: (b = ANY (ARRAY[$0, $1])) + -> Index Scan using rangep_100_to_200_a_idx on rangep_100_to_200 rangep_5 (actual rows=0 loops=1) + Filter: (b = ANY (ARRAY[$0, $1])) +(15 rows) + +reset enable_sort; +drop table rangep; -- -- Check that gen_prune_steps_from_opexps() works well for various cases of -- clauses for different partition keys diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index 1e904a8c5b..939a9b1193 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -1051,6 +1051,55 @@ reset enable_partition_pruning; drop table listp; +-- Ensure run-time pruning works correctly for nested Append nodes +set parallel_setup_cost to 0; +set parallel_tuple_cost to 0; + +create table listp (a int) partition by list(a); +create table listp_12 partition of listp for values in(1,2) partition by list(a); +create table listp_12_1 partition of listp_12 for values in(1); +create table listp_12_2 partition of listp_12 for values in(2); + +-- Force the 2nd subnode of the Append to be non-parallel. This results in +-- a nested Append node because the mixed parallel / non-parallel paths cannot +-- be pulled into the top-level Append. +alter table listp_12_1 set (parallel_workers = 0); + +-- Ensure that listp_12_2 is not scanned. (The nested Append is not seen in +-- the plan as it's pulled in setref.c due to having just a single subnode). +explain (analyze on, costs off, timing off, summary off) +select * from listp where a = (select 1); + +-- Like the above but throw some more complexity at the planner by adding +-- a UNION ALL. We expect both sides of the union not to scan the +-- non-required partitions. +explain (analyze on, costs off, timing off, summary off) +select * from listp where a = (select 1) + union all +select * from listp where a = (select 2); + +drop table listp; +reset parallel_tuple_cost; +reset parallel_setup_cost; + +-- Test case for run-time pruning with a nested Merge Append +set enable_sort to 0; +create table rangep (a int, b int) partition by range (a); +create table rangep_0_to_100 partition of rangep for values from (0) to (100) partition by list (b); +-- We need 3 sub-partitions. 1 to validate pruning worked and another two +-- because a single remaining partition would be pulled up to the main Append. +create table rangep_0_to_100_1 partition of rangep_0_to_100 for values in(1); +create table rangep_0_to_100_2 partition of rangep_0_to_100 for values in(2); +create table rangep_0_to_100_3 partition of rangep_0_to_100 for values in(3); +create table rangep_100_to_200 partition of rangep for values from (100) to (200); +create index on rangep (a); + +-- Ensure run-time pruning works on the nested Merge Append +explain (analyze on, costs off, timing off, summary off) +select * from rangep where b IN((select 1),(select 2)) order by a; +reset enable_sort; +drop table rangep; + -- -- Check that gen_prune_steps_from_opexps() works well for various cases of -- clauses for different partition keys