Store information about Append node consolidation in the final plan.
authorRobert Haas <[email protected]>
Mon, 20 Oct 2025 18:23:07 +0000 (14:23 -0400)
committerRobert Haas <[email protected]>
Thu, 11 Dec 2025 14:34:06 +0000 (09:34 -0500)
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.

contrib/pg_overexplain/expected/pg_overexplain.out
contrib/pg_overexplain/pg_overexplain.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/joinrels.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/pathnode.c
src/include/nodes/pathnodes.h
src/include/nodes/plannodes.h
src/include/optimizer/pathnode.h

index ca9a23ea61f6b17d1b285f9e6e41b7b3079d7a21..a377fb2571d52dc38f406bcdd8579405a2c2ef38 100644 (file)
@@ -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 @@ $$);
                <extParam>none</extParam>                            +
                <allParam>none</allParam>                            +
                <Append-RTIs>1</Append-RTIs>                         +
+               <Child-Append-RTIs>none</Child-Append-RTIs>          +
                <Subplans-Removed>0</Subplans-Removed>               +
                <Plans>                                              +
                  <Plan>                                             +
index e54f8cfc332cbe1c0b8d4f76e7a4cf6238274148..c28348ea96696faedf5d230499eb151a66da9693 100644 (file)
@@ -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
index 4c43fd0b19b2343a40c264c381ac591704a3878b..928b8d84ad887cb6194f28abb9b87db3fd253ce1 100644 (file)
@@ -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);
@@ -1874,8 +1891,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            fraction_neq_total = false;
                bool            match_partition_order;
@@ -2038,16 +2058,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
@@ -2057,13 +2084,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);
                        }
                }
 
@@ -2075,6 +2105,7 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel,
                                                                                                          rel,
                                                                                                          startup_subpaths,
                                                                                                          NIL,
+                                                                                                         startup_subpath_cars,
                                                                                                          pathkeys,
                                                                                                          NULL,
                                                                                                          0,
@@ -2085,6 +2116,7 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel,
                                                                                                                  rel,
                                                                                                                  total_subpaths,
                                                                                                                  NIL,
+                                                                                                                 total_subpath_cars,
                                                                                                                  pathkeys,
                                                                                                                  NULL,
                                                                                                                  0,
@@ -2096,6 +2128,7 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel,
                                                                                                                  rel,
                                                                                                                  fractional_subpaths,
                                                                                                                  NIL,
+                                                                                                                 fractional_subpath_cars,
                                                                                                                  pathkeys,
                                                                                                                  NULL,
                                                                                                                  0,
@@ -2108,12 +2141,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));
 
@@ -2121,6 +2156,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));
                }
@@ -2223,7 +2259,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))
        {
@@ -2232,6 +2269,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)
@@ -2246,6 +2285,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;
                }
        }
@@ -2254,6 +2295,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;
        }
 
@@ -2265,10 +2308,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);
 
@@ -2277,14 +2325,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;
@@ -2313,7 +2369,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));
 
index 8827b9a5245a02d21528bdd1fc351f856a469797..76478389653b8ff2830611db65628c01e5f347de 100644 (file)
@@ -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 */
index bc417f9384018cb4a3c87fd121b1428995bdec22..e3f27a586ca72df92b60a32f303867f94a06b67b 100644 (file)
@@ -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.
index df9d03d549298aba946dae9a1daeb3a93efd5026..94e1ac96ed91fae805abf1dfc84ea8f3e8717469 100644 (file)
@@ -4027,6 +4027,7 @@ create_degenerate_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
                                                           paths,
                                                           NIL,
                                                           NIL,
+                                                          NIL,
                                                           NULL,
                                                           0,
                                                           false,
index a01b02f3a7b6ffcc6945106714086237db739aa4..12d0c821ed7f6d10b3db9633e497345e1eb3fb31 100644 (file)
@@ -843,7 +843,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
@@ -889,7 +889,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,
@@ -1018,6 +1018,7 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
                        path = (Path *) create_merge_append_path(root,
                                                                                                         result_rel,
                                                                                                         ordered_pathlist,
+                                                                                                        NIL,
                                                                                                         union_pathkeys,
                                                                                                         NULL);
 
@@ -1224,8 +1225,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);
 
index b6be4ddbd01b21cc8c8b094d3380416a42c0e540..33ce34f0088577af69fd2b8b5b4b5369d94532c3 100644 (file)
@@ -1301,6 +1301,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)
@@ -1310,6 +1311,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;
@@ -1472,6 +1474,7 @@ MergeAppendPath *
 create_merge_append_path(PlannerInfo *root,
                                                 RelOptInfo *rel,
                                                 List *subpaths,
+                                                List *child_append_relid_sets,
                                                 List *pathkeys,
                                                 Relids required_outer)
 {
@@ -1487,6 +1490,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;
@@ -3951,6 +3955,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,
index 42c146d802a9c964bcbfddea67b153e048082456..ce623da71cc6026d89d3b886957140a6fde19a3f 100644 (file)
@@ -2172,6 +2172,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 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
@@ -2187,6 +2193,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) \
@@ -2203,12 +2210,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;
 
 /*
index 5d0520d5e58f642d995504b29639f389c76e47f3..045b7ee84a797c0791078b9e63c278724e63b184 100644 (file)
@@ -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: */
index 6b010f0b1a5a5b2b9b9e66f6d7673ad05055e216..dbf4702acc9afda67bf185d53cc2db4df2163d27 100644 (file)
@@ -71,12 +71,14 @@ extern TidRangePath *create_tidrangescan_path(PlannerInfo *root,
                                                                                          int parallel_workers);
 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,