From 3545ba64cd49d7a5c64b88f408f9b11211ab199e Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Mon, 28 Sep 2026 17:17:24 -0400 Subject: [PATCH v7 1/5] Fix failure of setrefs.c to process child_append_relid_sets Commit 7358abcc6076f4b2530d10126ab379f8aea612a5 taught the planner to store information about Append node consolidation in the final plan, but it forgot that child_append_relid_sets needs to be adjusted at setrefs.c time just like every other place where we store RTIs in a plan tree. Fix that. Although this can cause incorrect plan advice to be emitted in cases involving multi-level partitioning, it seems more appropriate to put the test case for it in the pg_overexplain test case, so this commit does it that way. Backpatch-through: 19 --- .../expected/pg_overexplain.out | 75 +++++++++++++++++++ contrib/pg_overexplain/sql/pg_overexplain.sql | 23 ++++++ src/backend/optimizer/plan/setrefs.c | 23 ++++++ 3 files changed, 121 insertions(+) diff --git a/contrib/pg_overexplain/expected/pg_overexplain.out b/contrib/pg_overexplain/expected/pg_overexplain.out index 90f8622a490..d3ce932be4c 100644 --- a/contrib/pg_overexplain/expected/pg_overexplain.out +++ b/contrib/pg_overexplain/expected/pg_overexplain.out @@ -60,6 +60,24 @@ INSERT INTO vegetables (name, genus) ('cabbage', 'brassica'), ('kohlrabi', 'brassica'), ('rutabaga', 'brassica'), ('turnip', 'brassica'); VACUUM ANALYZE vegetables; +-- Create a multi-level partitioned table. +CREATE TABLE creatures (id serial, name text, class text, clade text) + PARTITION BY LIST (class); +CREATE TABLE mammalia PARTITION OF creatures FOR VALUES IN ('mammalia') + PARTITION BY LIST (clade); +CREATE TABLE carnivora PARTITION OF mammalia FOR VALUES IN ('carnivora'); +CREATE TABLE rodentia PARTITION OF mammalia FOR VALUES IN ('rodentia'); +CREATE TABLE aves PARTITION OF creatures FOR VALUES IN ('aves'); +INSERT INTO creatures (name, class, clade) + VALUES ('fossa', 'mammalia', 'carnivora'), + ('meerkat', 'mammalia', 'carnivora'), + ('binturong', 'mammalia', 'carnivora'), + ('capybara', 'mammalia', 'rodentia'), + ('chinchilla', 'mammalia', 'rodentia'), + ('agouti', 'mammalia', 'rodentia'), + ('kakapo', 'aves', NULL), + ('hoatzin', 'aves', NULL); +VACUUM ANALYZE creatures; -- We filter relation OIDs out of the test output in order to avoid -- test instability. This is currently only needed for EXPLAIN (DEBUG), not -- EXPLAIN (RANGE_TABLE). Also suppress actual row counts, which are not @@ -766,3 +784,60 @@ SELECT * FROM vegetables v, Unprunable RTIs: 1 3 4 5 6 (51 rows) +-- test display of child append RTIs +EXPLAIN (RANGE_TABLE, COSTS OFF) +SELECT * FROM (SELECT * FROM creatures OFFSET 0) ss; + QUERY PLAN +---------------------------------------------- + Append + Append RTIs: 2 + Child Append RTIs: 4 + Elided Node Type: SubqueryScan + Elided Node RTIs: 1 + -> Seq Scan on aves creatures_1 + Scan RTI: 3 + -> Seq Scan on carnivora creatures_2 + Scan RTI: 5 + -> Seq Scan on rodentia creatures_3 + Scan RTI: 6 + RTI 1 (subquery, in-from-clause): + Alias: ss () + Eref: ss (id, name, class, clade) + RTI 2 (relation, inherited, in-from-clause): + Subplan: ss + Eref: creatures (id, name, class, clade) + Relation: creatures + Relation Kind: partitioned_table + Relation Lock Mode: AccessShareLock + Permission Info Index: 1 + RTI 3 (relation, in-from-clause): + Subplan: ss + Alias: creatures (id, name, class, clade) + Eref: creatures (id, name, class, clade) + Relation: aves + Relation Kind: relation + Relation Lock Mode: AccessShareLock + RTI 4 (relation, inherited, in-from-clause): + Subplan: ss + Alias: creatures (id, name, class, clade) + Eref: creatures (id, name, class, clade) + Relation: mammalia + Relation Kind: partitioned_table + Relation Lock Mode: AccessShareLock + RTI 5 (relation, in-from-clause): + Subplan: ss + Alias: creatures (id, name, class, clade) + Eref: creatures (id, name, class, clade) + Relation: carnivora + Relation Kind: relation + Relation Lock Mode: AccessShareLock + RTI 6 (relation, in-from-clause): + Subplan: ss + Alias: creatures (id, name, class, clade) + Eref: creatures (id, name, class, clade) + Relation: rodentia + Relation Kind: relation + Relation Lock Mode: AccessShareLock + Unprunable RTIs: 2 3 4 5 6 +(50 rows) + diff --git a/contrib/pg_overexplain/sql/pg_overexplain.sql b/contrib/pg_overexplain/sql/pg_overexplain.sql index 631ec2a32fb..ac71da3012a 100644 --- a/contrib/pg_overexplain/sql/pg_overexplain.sql +++ b/contrib/pg_overexplain/sql/pg_overexplain.sql @@ -31,6 +31,25 @@ INSERT INTO vegetables (name, genus) ('rutabaga', 'brassica'), ('turnip', 'brassica'); VACUUM ANALYZE vegetables; +-- Create a multi-level partitioned table. +CREATE TABLE creatures (id serial, name text, class text, clade text) + PARTITION BY LIST (class); +CREATE TABLE mammalia PARTITION OF creatures FOR VALUES IN ('mammalia') + PARTITION BY LIST (clade); +CREATE TABLE carnivora PARTITION OF mammalia FOR VALUES IN ('carnivora'); +CREATE TABLE rodentia PARTITION OF mammalia FOR VALUES IN ('rodentia'); +CREATE TABLE aves PARTITION OF creatures FOR VALUES IN ('aves'); +INSERT INTO creatures (name, class, clade) + VALUES ('fossa', 'mammalia', 'carnivora'), + ('meerkat', 'mammalia', 'carnivora'), + ('binturong', 'mammalia', 'carnivora'), + ('capybara', 'mammalia', 'rodentia'), + ('chinchilla', 'mammalia', 'rodentia'), + ('agouti', 'mammalia', 'rodentia'), + ('kakapo', 'aves', NULL), + ('hoatzin', 'aves', NULL); +VACUUM ANALYZE creatures; + -- We filter relation OIDs out of the test output in order to avoid -- test instability. This is currently only needed for EXPLAIN (DEBUG), not -- EXPLAIN (RANGE_TABLE). Also suppress actual row counts, which are not @@ -136,3 +155,7 @@ SELECT * FROM vegetables v, EXPLAIN (RANGE_TABLE, COSTS OFF) SELECT * FROM vegetables v, (SELECT * FROM vegetables WHERE genus = 'daucus' OFFSET 0); + +-- test display of child append RTIs +EXPLAIN (RANGE_TABLE, COSTS OFF) +SELECT * FROM (SELECT * FROM creatures OFFSET 0) ss; diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 327c0febe89..8aba20af25d 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -155,6 +155,7 @@ static Plan *set_mergeappend_references(PlannerInfo *root, int rtoffset); static void set_hash_references(PlannerInfo *root, Plan *plan, int rtoffset); static Relids offset_relid_set(Relids relids, int rtoffset); +static List *offset_relid_set_list(List *relid_sets, int rtoffset); static Node *fix_dummy_setop_vars_mutator(Node *node, int *first_child_relid); static Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec); @@ -1913,6 +1914,8 @@ set_append_references(PlannerInfo *root, set_dummy_tlist_references((Plan *) aplan, rtoffset); aplan->apprelids = offset_relid_set(aplan->apprelids, rtoffset); + aplan->child_append_relid_sets = + offset_relid_set_list(aplan->child_append_relid_sets, rtoffset); /* * Add PartitionPruneInfo, if any, to PlannerGlobal and update the index. @@ -1991,6 +1994,8 @@ set_mergeappend_references(PlannerInfo *root, set_dummy_tlist_references((Plan *) mplan, rtoffset); mplan->apprelids = offset_relid_set(mplan->apprelids, rtoffset); + mplan->child_append_relid_sets = + offset_relid_set_list(mplan->child_append_relid_sets, rtoffset); /* * Add PartitionPruneInfo, if any, to PlannerGlobal and update the index. @@ -2052,6 +2057,24 @@ offset_relid_set(Relids relids, int rtoffset) return bms_offset_members(relids, rtoffset); } +/* + * offset_relid_set_list + * Apply rtoffset to the members of each Relid set in a List. + */ +static List * +offset_relid_set_list(List *relid_sets, int rtoffset) +{ + List *result = NIL; + + if (rtoffset == 0) + return relid_sets; + + foreach_ptr(Bitmapset, relids, relid_sets) + result = lappend(result, offset_relid_set(relids, rtoffset)); + + return result; +} + /* * copyVar * Copy a Var node. -- 2.53.0