dont_flatten_append_paths_for_partitions.patch
application/octet-stream
Filename: dont_flatten_append_paths_for_partitions.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/optimizer/path/allpaths.c | 35 | 27 |
| src/backend/optimizer/plan/createplan.c | 7 | 2 |
| src/test/regress/expected/partition_prune.out | 21 | 0 |
| src/test/regress/sql/partition_prune.sql | 5 | 0 |
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 477b9f7fb8..6ac8d9767b 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -102,8 +102,9 @@ static void generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
static Path *get_cheapest_parameterized_child_path(PlannerInfo *root,
RelOptInfo *rel,
Relids required_outer);
-static void accumulate_append_subpath(Path *path,
- List **subpaths, List **special_subpaths);
+static void accumulate_append_subpath(RelOptInfo *parentrel,
+ RelOptInfo *childrel, Path *path, List **subpaths,
+ List **special_subpaths);
static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
@@ -1395,17 +1396,6 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
* 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.)
*/
if (rel->part_scheme != NULL)
{
@@ -1435,10 +1425,16 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
Assert(list_length(partitioned_rels) >= 1);
}
- else if (rel->rtekind == RTE_SUBQUERY)
- build_partitioned_rels = true;
/*
+ * For childrels that are themselves inheritance or UNION ALL parents,
+ * we recursively pullup their Append and MergeAppend subpaths into rel's
+ * path lists. This effectively flattens the hierarchy and stops nested
+ * Append/MergeAppend paths forming. This is not done for UNION ALL
+ * parents with partitioned tables in their subpaths. These are left
+ * unflattened as run-time partition pruning requires partitioned_rels to
+ * only contain partitions which belong to a single hierarchy.
+ *
* For every non-dummy child, remember the cheapest path. Also, identify
* all pathkeys (orderings) and parameterizations (required_outer sets)
* available for the non-dummy member relations.
@@ -1471,7 +1467,7 @@ 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,
+ accumulate_append_subpath(rel, childrel, childrel->cheapest_total_path,
&subpaths, NULL);
else
subpaths_valid = false;
@@ -1480,7 +1476,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
if (childrel->partial_pathlist != NIL)
{
cheapest_partial_path = linitial(childrel->partial_pathlist);
- accumulate_append_subpath(cheapest_partial_path,
+ accumulate_append_subpath(rel, childrel, cheapest_partial_path,
&partial_subpaths, NULL);
}
else
@@ -1508,7 +1504,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
{
/* Partial path is cheaper or the only option. */
Assert(cheapest_partial_path != NULL);
- accumulate_append_subpath(cheapest_partial_path,
+ accumulate_append_subpath(rel, childrel,
+ cheapest_partial_path,
&pa_partial_subpaths,
&pa_nonpartial_subpaths);
@@ -1528,7 +1525,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
* be given to different workers. For now, we don't try to
* figure that out.
*/
- accumulate_append_subpath(nppath,
+ accumulate_append_subpath(rel, childrel, nppath,
&pa_nonpartial_subpaths,
NULL);
}
@@ -1754,7 +1751,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
subpaths_valid = false;
break;
}
- accumulate_append_subpath(subpath, &subpaths, NULL);
+ accumulate_append_subpath(rel, childrel, subpath, &subpaths,
+ NULL);
}
if (subpaths_valid)
@@ -1845,9 +1843,9 @@ generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
if (cheapest_startup != cheapest_total)
startup_neq_total = true;
- accumulate_append_subpath(cheapest_startup,
+ accumulate_append_subpath(rel, childrel, cheapest_startup,
&startup_subpaths, NULL);
- accumulate_append_subpath(cheapest_total,
+ accumulate_append_subpath(rel, childrel, cheapest_total,
&total_subpaths, NULL);
}
@@ -1946,10 +1944,13 @@ get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel,
* accumulate_append_subpath
* Add a subpath to the list being built for an Append or MergeAppend.
*
- * It's possible that the child is itself an Append or MergeAppend path, in
- * which case we can "cut out the middleman" and just add its child paths to
- * our own list. (We don't try to do this earlier because we need to apply
- * both levels of transformation to the quals.)
+ * For UNION ALL or inheritance parent childrels, we pullup the childrels
+ * Append and MergeAppend subpaths into 'subpaths' effectively bypassing the
+ * childrel's Append and MergeAppend paths. We don't do this for partitioned
+ * childrels which are parented by UNION ALL parents as mixed partition
+ * hierarchies are not compatible with run-time partition pruning.
+ * (We perform the pullup operation here rather than earlier because we need
+ * to apply both levels of transformation to the quals.)
*
* Note that if we omit a child MergeAppend in this way, we are effectively
* omitting a sort step, which seems fine: if the parent is to be an Append,
@@ -1965,8 +1966,15 @@ get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel,
* paths).
*/
static void
-accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths)
+accumulate_append_subpath(RelOptInfo *parentrel, RelOptInfo *childrel,
+ Path *path, List **subpaths,
+ List **special_subpaths)
{
+ if (parentrel->rtekind == RTE_SUBQUERY && childrel->part_scheme != NULL)
+ {
+ *subpaths = lappend(*subpaths, path);
+ return;
+ }
if (IsA(path, AppendPath))
{
AppendPath *apath = (AppendPath *) path;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cf82b7052d..b848897693 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1065,6 +1065,12 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
return plan;
}
+ /*
+ * Ensure that partitioned_rels is set for partitioned tables, and never
+ * set otherwise.
+ */
+ Assert((best_path->partitioned_rels != NIL) == (rel->part_scheme != NULL));
+
/* Build the plan for each child */
foreach(subpaths, best_path->subpaths)
{
@@ -1077,8 +1083,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
subplans = lappend(subplans, subplan);
}
- if (enable_partition_pruning &&
- rel->reloptkind == RELOPT_BASEREL &&
+ if (enable_partition_pruning && IS_SIMPLE_REL(rel) &&
best_path->partitioned_rels != NIL)
{
List *prunequal;
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index ab32c7d67e..6ad59a1afe 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -2768,6 +2768,27 @@ select * from boolp where a = (select value from boolvalues where not value);
Filter: (a = $0)
(9 rows)
+-- Ensure runtime pruning works when partitioned tables are parented by
+-- UNION ALL parents
+explain (analyze, costs off, summary off, timing off)
+select * from (select * from boolp union all select * from boolp) b where a = (select true);
+ QUERY PLAN
+-------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+ InitPlan 1 (returns $0)
+ -> Result (actual rows=1 loops=1)
+ -> Append (actual rows=0 loops=1)
+ -> Seq Scan on boolp_f (never executed)
+ Filter: (a = $0)
+ -> Seq Scan on boolp_t (actual rows=0 loops=1)
+ Filter: (a = $0)
+ -> Append (actual rows=0 loops=1)
+ -> Seq Scan on boolp_f boolp_f_1 (never executed)
+ Filter: (a = $0)
+ -> Seq Scan on boolp_t boolp_t_1 (actual rows=0 loops=1)
+ Filter: (a = $0)
+(13 rows)
+
drop table boolp;
reset enable_indexonlyscan;
--
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index 609fe09aeb..a7610ef550 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -701,6 +701,11 @@ select * from boolp where a = (select value from boolvalues where value);
explain (analyze, costs off, summary off, timing off)
select * from boolp where a = (select value from boolvalues where not value);
+-- Ensure runtime pruning works when partitioned tables are parented by
+-- UNION ALL parents
+explain (analyze, costs off, summary off, timing off)
+select * from (select * from boolp union all select * from boolp) b where a = (select true);
+
drop table boolp;
reset enable_indexonlyscan;