From 8e37fb762b9df0bcd3262b6f8d3e798cb067ee5b Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Mon, 20 Oct 2025 14:23:07 -0400 Subject: [PATCH] Store information about Append node consolidation in the final plan. An extension (or core code) might want to reconstruct the planner's decisions about whether and where to perform partitionwise joins from the final plan. To do so, it must be possible to find all of the RTIs of partitioned tables appearing in the plan. But when an AppendPath or MergeAppendPath pulls up child paths from a subordinate AppendPath or MergeAppendPath, the RTIs of the subordinate path do not appear in the final plan, making this kind of reconstruction impossible. To avoid this, propagate the RTI sets that would have been present in the 'apprelids' field of the subordinate Append or MergeAppend nodes that would have been created into the surviving Append or MergeAppend node, using a new 'child_append_relid_sets' field for that purpose. The value of this field is a list of Bitmapsets, because each relation whose append-list was pulled up had its own set of RTIs: just one, if it was a partitionwise scan, or more than one, if it was a partitionwise join. Since our goal is to see where partitionwise joins were done, it is essential to avoid losing the information about how the RTIs were grouped in the pulled-up relations. This commit also updates pg_overexplain so that EXPLAIN (RANGE_TABLE) will display the saved RTI sets. --- .../expected/pg_overexplain.out | 4 +- contrib/pg_overexplain/pg_overexplain.c | 56 +++++++++++ src/backend/optimizer/path/allpaths.c | 98 +++++++++++++++---- src/backend/optimizer/path/joinrels.c | 2 +- src/backend/optimizer/plan/createplan.c | 2 + src/backend/optimizer/plan/planner.c | 1 + src/backend/optimizer/prep/prepunion.c | 11 ++- src/backend/optimizer/util/pathnode.c | 5 + src/include/nodes/pathnodes.h | 10 ++ src/include/nodes/plannodes.h | 11 +++ src/include/optimizer/pathnode.h | 2 + 11 files changed, 175 insertions(+), 27 deletions(-) diff --git a/contrib/pg_overexplain/expected/pg_overexplain.out b/contrib/pg_overexplain/expected/pg_overexplain.out index ca9a23ea61..a377fb2571 100644 --- a/contrib/pg_overexplain/expected/pg_overexplain.out +++ b/contrib/pg_overexplain/expected/pg_overexplain.out @@ -104,6 +104,7 @@ $$); Parallel Safe: true Plan Node ID: 2 Append RTIs: 1 + Child Append RTIs: none -> Seq Scan on brassica vegetables_1 Disabled Nodes: 0 Parallel Safe: true @@ -142,7 +143,7 @@ $$); Relation Kind: relation Relation Lock Mode: AccessShareLock Unprunable RTIs: 1 3 4 -(53 rows) +(54 rows) -- Test a different output format. SELECT explain_filter($$ @@ -197,6 +198,7 @@ $$); none + none + 1 + + none + 0 + + + diff --git a/contrib/pg_overexplain/pg_overexplain.c b/contrib/pg_overexplain/pg_overexplain.c index fa907fa472..6538ffcafb 100644 --- a/contrib/pg_overexplain/pg_overexplain.c +++ b/contrib/pg_overexplain/pg_overexplain.c @@ -54,6 +54,8 @@ static void overexplain_alias(const char *qlabel, Alias *alias, ExplainState *es); static void overexplain_bitmapset(const char *qlabel, Bitmapset *bms, ExplainState *es); +static void overexplain_bitmapset_list(const char *qlabel, List *bms_list, + ExplainState *es); static void overexplain_intlist(const char *qlabel, List *list, ExplainState *es); @@ -232,11 +234,17 @@ overexplain_per_node_hook(PlanState *planstate, List *ancestors, overexplain_bitmapset("Append RTIs", ((Append *) plan)->apprelids, es); + overexplain_bitmapset_list("Child Append RTIs", + ((Append *) plan)->child_append_relid_sets, + es); break; case T_MergeAppend: overexplain_bitmapset("Append RTIs", ((MergeAppend *) plan)->apprelids, es); + overexplain_bitmapset_list("Child Append RTIs", + ((MergeAppend *) plan)->child_append_relid_sets, + es); break; case T_Result: @@ -815,6 +823,54 @@ overexplain_bitmapset(const char *qlabel, Bitmapset *bms, ExplainState *es) pfree(buf.data); } +/* + * Emit a text property describing the contents of a list of bitmapsets. + * If a bitmapset contains exactly 1 member, we just print an integer; + * otherwise, we surround the list of members by parentheses. + * + * If there are no bitmapsets in the list, we print the word "none". + */ +static void +overexplain_bitmapset_list(const char *qlabel, List *bms_list, + ExplainState *es) +{ + StringInfoData buf; + + initStringInfo(&buf); + + foreach_node(Bitmapset, bms, bms_list) + { + if (bms_membership(bms) == BMS_SINGLETON) + appendStringInfo(&buf, " %d", bms_singleton_member(bms)); + else + { + int x = -1; + bool first = true; + + appendStringInfoString(&buf, " ("); + while ((x = bms_next_member(bms, x)) >= 0) + { + if (first) + first = false; + else + appendStringInfoChar(&buf, ' '); + appendStringInfo(&buf, "%d", x); + } + appendStringInfoChar(&buf, ')'); + } + } + + if (buf.len == 0) + { + ExplainPropertyText(qlabel, "none", es); + return; + } + + Assert(buf.data[0] == ' '); + ExplainPropertyText(qlabel, buf.data + 1, es); + pfree(buf.data); +} + /* * Emit a text property describing the contents of a list of integers, OIDs, * or XIDs -- either a space-separated list of integer members, or the word diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 9c6436eb72..349863fb19 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -128,8 +128,10 @@ static Path *get_cheapest_parameterized_child_path(PlannerInfo *root, Relids required_outer); static void accumulate_append_subpath(Path *path, List **subpaths, - List **special_subpaths); -static Path *get_singleton_append_subpath(Path *path); + List **special_subpaths, + List **child_append_relid_sets); +static Path *get_singleton_append_subpath(Path *path, + List **child_append_relid_sets); static void set_dummy_rel_pathlist(RelOptInfo *rel); static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); @@ -1406,11 +1408,15 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, { List *subpaths = NIL; bool subpaths_valid = true; + List *subpath_cars = NIL; List *startup_subpaths = NIL; bool startup_subpaths_valid = true; + List *startup_subpath_cars = NIL; List *partial_subpaths = NIL; + List *partial_subpath_cars = NIL; List *pa_partial_subpaths = NIL; List *pa_nonpartial_subpaths = NIL; + List *pa_subpath_cars = NIL; bool partial_subpaths_valid = true; bool pa_subpaths_valid; List *all_child_pathkeys = NIL; @@ -1443,7 +1449,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, - &subpaths, NULL); + &subpaths, NULL, &subpath_cars); else subpaths_valid = false; @@ -1472,7 +1478,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, Assert(cheapest_path->param_info == NULL); accumulate_append_subpath(cheapest_path, &startup_subpaths, - NULL); + NULL, + &startup_subpath_cars); } else startup_subpaths_valid = false; @@ -1483,7 +1490,8 @@ 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_subpath_cars); } else partial_subpaths_valid = false; @@ -1512,7 +1520,8 @@ 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_subpath_cars); } else { @@ -1531,7 +1540,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, */ accumulate_append_subpath(nppath, &pa_nonpartial_subpaths, - NULL); + NULL, + &pa_subpath_cars); } } @@ -1606,14 +1616,16 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, * if we have zero or one live subpath due to constraint exclusion.) */ if (subpaths_valid) - add_path(rel, (Path *) create_append_path(root, rel, subpaths, NIL, + add_path(rel, (Path *) create_append_path(root, rel, subpaths, + NIL, subpath_cars, NIL, NULL, 0, false, -1)); /* build an AppendPath for the cheap startup paths, if valid */ if (startup_subpaths_valid) add_path(rel, (Path *) create_append_path(root, rel, startup_subpaths, - NIL, NIL, NULL, 0, false, -1)); + NIL, startup_subpath_cars, + NIL, NULL, 0, false, -1)); /* * Consider an append of unordered, unparameterized partial paths. Make @@ -1654,6 +1666,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, /* Generate a partial append path. */ appendpath = create_append_path(root, rel, NIL, partial_subpaths, + partial_subpath_cars, NIL, NULL, parallel_workers, enable_parallel_append, -1); @@ -1704,6 +1717,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, appendpath = create_append_path(root, rel, pa_nonpartial_subpaths, pa_partial_subpaths, + pa_subpath_cars, NIL, NULL, parallel_workers, true, partial_rows); add_partial_path(rel, (Path *) appendpath); @@ -1737,6 +1751,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, /* Select the child paths for an Append with this parameterization */ subpaths = NIL; + subpath_cars = NIL; subpaths_valid = true; foreach(lcr, live_childrels) { @@ -1759,12 +1774,13 @@ 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, + &subpath_cars); } if (subpaths_valid) add_path(rel, (Path *) - create_append_path(root, rel, subpaths, NIL, + create_append_path(root, rel, subpaths, NIL, subpath_cars, NIL, required_outer, 0, false, -1)); } @@ -1791,6 +1807,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, continue; appendpath = create_append_path(root, rel, NIL, list_make1(path), + list_make1(rel->relids), NIL, NULL, path->parallel_workers, true, partial_rows); @@ -1872,8 +1889,11 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, { List *pathkeys = (List *) lfirst(lcp); List *startup_subpaths = NIL; + List *startup_subpath_cars = NIL; List *total_subpaths = NIL; + List *total_subpath_cars = NIL; List *fractional_subpaths = NIL; + List *fractional_subpath_cars = NIL; bool startup_neq_total = false; bool match_partition_order; bool match_partition_order_desc; @@ -2025,16 +2045,23 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, * just a single subpath (and hence aren't doing anything * useful). */ - cheapest_startup = get_singleton_append_subpath(cheapest_startup); - cheapest_total = get_singleton_append_subpath(cheapest_total); + cheapest_startup = + get_singleton_append_subpath(cheapest_startup, + &startup_subpath_cars); + cheapest_total = + get_singleton_append_subpath(cheapest_total, + &total_subpath_cars); startup_subpaths = lappend(startup_subpaths, cheapest_startup); total_subpaths = lappend(total_subpaths, cheapest_total); if (cheapest_fractional) { - cheapest_fractional = get_singleton_append_subpath(cheapest_fractional); - fractional_subpaths = lappend(fractional_subpaths, cheapest_fractional); + cheapest_fractional = + get_singleton_append_subpath(cheapest_fractional, + &fractional_subpath_cars); + fractional_subpaths = + lappend(fractional_subpaths, cheapest_fractional); } } else @@ -2044,13 +2071,16 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, * child paths for the MergeAppend. */ accumulate_append_subpath(cheapest_startup, - &startup_subpaths, NULL); + &startup_subpaths, NULL, + &startup_subpath_cars); accumulate_append_subpath(cheapest_total, - &total_subpaths, NULL); + &total_subpaths, NULL, + &total_subpath_cars); if (cheapest_fractional) accumulate_append_subpath(cheapest_fractional, - &fractional_subpaths, NULL); + &fractional_subpaths, NULL, + &fractional_subpath_cars); } } @@ -2062,6 +2092,7 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, rel, startup_subpaths, NIL, + startup_subpath_cars, pathkeys, NULL, 0, @@ -2072,6 +2103,7 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, rel, total_subpaths, NIL, + total_subpath_cars, pathkeys, NULL, 0, @@ -2083,6 +2115,7 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, rel, fractional_subpaths, NIL, + fractional_subpath_cars, pathkeys, NULL, 0, @@ -2095,12 +2128,14 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, add_path(rel, (Path *) create_merge_append_path(root, rel, startup_subpaths, + startup_subpath_cars, pathkeys, NULL)); if (startup_neq_total) add_path(rel, (Path *) create_merge_append_path(root, rel, total_subpaths, + total_subpath_cars, pathkeys, NULL)); @@ -2108,6 +2143,7 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, add_path(rel, (Path *) create_merge_append_path(root, rel, fractional_subpaths, + fractional_subpath_cars, pathkeys, NULL)); } @@ -2210,7 +2246,8 @@ 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(Path *path, List **subpaths, List **special_subpaths, + List **child_append_relid_sets) { if (IsA(path, AppendPath)) { @@ -2219,6 +2256,8 @@ 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); + *child_append_relid_sets = + lappend(*child_append_relid_sets, path->parent->relids); return; } else if (special_subpaths != NULL) @@ -2233,6 +2272,8 @@ accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths) apath->first_partial_path); *special_subpaths = list_concat(*special_subpaths, new_special_subpaths); + *child_append_relid_sets = + lappend(*child_append_relid_sets, path->parent->relids); return; } } @@ -2241,6 +2282,8 @@ accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths) MergeAppendPath *mpath = (MergeAppendPath *) path; *subpaths = list_concat(*subpaths, mpath->subpaths); + *child_append_relid_sets = + lappend(*child_append_relid_sets, path->parent->relids); return; } @@ -2252,10 +2295,15 @@ accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths) * Returns the single subpath of an Append/MergeAppend, or just * return 'path' if it's not a single sub-path Append/MergeAppend. * + * As a side effect, whenever we return a single subpath rather than the + * original path, add the relid set for the original path to + * child_append_relid_sets, so that those relids don't entirely disappear + * from the final plan. + * * Note: 'path' must not be a parallel-aware path. */ static Path * -get_singleton_append_subpath(Path *path) +get_singleton_append_subpath(Path *path, List **child_append_relid_sets) { Assert(!path->parallel_aware); @@ -2264,14 +2312,22 @@ get_singleton_append_subpath(Path *path) AppendPath *apath = (AppendPath *) path; if (list_length(apath->subpaths) == 1) + { + *child_append_relid_sets = + lappend(*child_append_relid_sets, path->parent->relids); return (Path *) linitial(apath->subpaths); + } } else if (IsA(path, MergeAppendPath)) { MergeAppendPath *mpath = (MergeAppendPath *) path; if (list_length(mpath->subpaths) == 1) + { + *child_append_relid_sets = + lappend(*child_append_relid_sets, path->parent->relids); return (Path *) linitial(mpath->subpaths); + } } return path; @@ -2300,7 +2356,7 @@ set_dummy_rel_pathlist(RelOptInfo *rel) rel->partial_pathlist = NIL; /* Set up the dummy path */ - add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NIL, + add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NIL, NIL, NIL, rel->lateral_relids, 0, false, -1)); diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 5d1fc3899d..c1ed0d3870 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -1530,7 +1530,7 @@ mark_dummy_rel(RelOptInfo *rel) /* Set up the dummy path */ add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NIL, - NIL, rel->lateral_relids, + NIL, NIL, rel->lateral_relids, 0, false, -1)); /* Set or update cheapest_total_path and related fields */ diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 63fe663715..655c109781 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -1265,6 +1265,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags) plan->plan.lefttree = NULL; plan->plan.righttree = NULL; plan->apprelids = rel->relids; + plan->child_append_relid_sets = best_path->child_append_relid_sets; if (pathkeys != NIL) { @@ -1477,6 +1478,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path, plan->lefttree = NULL; plan->righttree = NULL; node->apprelids = rel->relids; + node->child_append_relid_sets = best_path->child_append_relid_sets; /* * Compute sort column info, and adjust MergeAppend's tlist as needed. diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 9d5262651e..eb62794aec 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -4027,6 +4027,7 @@ create_degenerate_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, paths, NIL, NIL, + NIL, NULL, 0, false, diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 5566582417..b7c4c0686d 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -839,7 +839,7 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root, * union child. */ apath = (Path *) create_append_path(root, result_rel, cheapest_pathlist, - NIL, NIL, NULL, 0, false, -1); + NIL, NIL, NIL, NULL, 0, false, -1); /* * Estimate number of groups. For now we just assume the output is unique @@ -885,7 +885,7 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root, papath = (Path *) create_append_path(root, result_rel, NIL, partial_pathlist, - NIL, NULL, parallel_workers, + NIL, NIL, NULL, parallel_workers, enable_parallel_append, -1); gpath = (Path *) create_gather_path(root, result_rel, papath, @@ -1011,6 +1011,7 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root, path = (Path *) create_merge_append_path(root, result_rel, ordered_pathlist, + NIL, union_pathkeys, NULL); @@ -1217,8 +1218,10 @@ generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root, * between the set op targetlist and the targetlist of the * left input. The Append will be removed in setrefs.c. */ - apath = (Path *) create_append_path(root, result_rel, list_make1(lpath), - NIL, NIL, NULL, 0, false, -1); + apath = (Path *) create_append_path(root, result_rel, + list_make1(lpath), + NIL, NIL, NIL, NULL, 0, + false, -1); add_path(result_rel, apath); diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 44ac5312ed..0c0098691a 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -1299,6 +1299,7 @@ AppendPath * create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, List *partial_subpaths, + List *child_append_relid_sets, List *pathkeys, Relids required_outer, int parallel_workers, bool parallel_aware, double rows) @@ -1308,6 +1309,7 @@ create_append_path(PlannerInfo *root, Assert(!parallel_aware || parallel_workers > 0); + pathnode->child_append_relid_sets = child_append_relid_sets; pathnode->path.pathtype = T_Append; pathnode->path.parent = rel; pathnode->path.pathtarget = rel->reltarget; @@ -1470,6 +1472,7 @@ MergeAppendPath * create_merge_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, + List *child_append_relid_sets, List *pathkeys, Relids required_outer) { @@ -1485,6 +1488,7 @@ create_merge_append_path(PlannerInfo *root, */ Assert(bms_is_empty(rel->lateral_relids) && bms_is_empty(required_outer)); + pathnode->child_append_relid_sets = child_append_relid_sets; pathnode->path.pathtype = T_MergeAppend; pathnode->path.parent = rel; pathnode->path.pathtarget = rel->reltarget; @@ -3948,6 +3952,7 @@ reparameterize_path(PlannerInfo *root, Path *path, } return (Path *) create_append_path(root, rel, childpaths, partialpaths, + apath->child_append_relid_sets, apath->path.pathkeys, required_outer, apath->path.parallel_workers, apath->path.parallel_aware, diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index cf3a16b8b0..75a70489e5 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -2171,6 +2171,12 @@ typedef struct CustomPath * For partial Append, 'subpaths' contains non-partial subpaths followed by * partial subpaths. * + * Whenever accumulate_append_subpath() allows us to consolidate multiple + * levels of Append paths are consolidated down to one, we store the RTI + * sets for the omitted paths in child_append_relid_sets. This is not necessary + * for planning or execution; we do it for the benefit of code that wants + * to inspect the final plan and understand how it came to be. + * * Note: it is possible for "subpaths" to contain only one, or even no, * elements. These cases are optimized during create_append_plan. * In particular, an AppendPath with no subpaths is a "dummy" path that @@ -2186,6 +2192,7 @@ typedef struct AppendPath /* Index of first partial path in subpaths; list_length(subpaths) if none */ int first_partial_path; Cardinality limit_tuples; /* hard limit on output tuples, or -1 */ + List *child_append_relid_sets; } AppendPath; #define IS_DUMMY_APPEND(p) \ @@ -2202,12 +2209,15 @@ extern bool is_dummy_rel(RelOptInfo *rel); /* * MergeAppendPath represents a MergeAppend plan, ie, the merging of sorted * results from several member plans to produce similarly-sorted output. + * + * child_append_relid_sets has the same meaning here as for AppendPath. */ typedef struct MergeAppendPath { Path path; List *subpaths; /* list of component Paths */ Cardinality limit_tuples; /* hard limit on output tuples, or -1 */ + List *child_append_relid_sets; } MergeAppendPath; /* diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index c8d9797f73..7d0846ea78 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -394,9 +394,16 @@ struct PartitionPruneInfo; /* forward reference to struct below */ typedef struct Append { Plan plan; + /* RTIs of appendrel(s) formed by this node */ Bitmapset *apprelids; + + /* sets of RTIs of appendrels consolidated into this node */ + List *child_append_relid_sets; + + /* plans to run */ List *appendplans; + /* # of asynchronous plans */ int nasyncplans; @@ -426,6 +433,10 @@ typedef struct MergeAppend /* RTIs of appendrel(s) formed by this node */ Bitmapset *apprelids; + /* sets of RTIs of appendrels consolidated into this node */ + List *child_append_relid_sets; + + /* plans to run */ List *mergeplans; /* these fields are just like the sort-key info in struct Sort: */ diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 955e905685..4437248cb6 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -70,12 +70,14 @@ extern TidRangePath *create_tidrangescan_path(PlannerInfo *root, Relids required_outer); extern AppendPath *create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, List *partial_subpaths, + List *child_append_relid_sets, List *pathkeys, Relids required_outer, int parallel_workers, bool parallel_aware, double rows); extern MergeAppendPath *create_merge_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, + List *child_append_relid_sets, List *pathkeys, Relids required_outer); extern GroupResultPath *create_group_result_path(PlannerInfo *root, -- 2.39.5