From a2533556a20e5c890005b801001b08b27aa2cbb8 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Mon, 28 Sep 2026 17:38:13 -0400 Subject: [PATCH v6 2/4] pg_plan_advice: Avoid miscategorizing partitionwise scans as ordinary. Given an Append or MergeAppend node, pgpa_build_scan needs to call pgpa_make_scan for the node's own apprelids, and also for each element of child_append_relid_sets. It did that, but it overlooked the fact that the scan type applicable to the child scans might differ from the scan type applicable to the parent scan. The problem can only occur in one direction: an RTE_SUBQUERY (Merge)Append can appear on top of a RTE_RELATION (Merge)Append, but not the reverse, so the only failure mode here is to classify a partitionwise scan of a child appendrel as PGPA_SCAN_ORDINARY rather than PGPA_SCAN_PARTITIONWISE, leading to failure to include PARTITIONWISE(something) in the generated advice string. Backpatch-through: 19 --- .../pg_plan_advice/expected/partitionwise.out | 52 +++++++++++++++++++ contrib/pg_plan_advice/pgpa_scan.c | 9 +++- contrib/pg_plan_advice/sql/partitionwise.sql | 23 ++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/contrib/pg_plan_advice/expected/partitionwise.out b/contrib/pg_plan_advice/expected/partitionwise.out index 0ae22600a4f..e87d35d04b7 100644 --- a/contrib/pg_plan_advice/expected/partitionwise.out +++ b/contrib/pg_plan_advice/expected/partitionwise.out @@ -49,6 +49,24 @@ INSERT INTO ptmismatch (id, dimm, valm) SELECT g, 'yet another text ' || g, (g % 2) + 1 FROM generate_series(1,3000) g; VACUUM ANALYZE ptmismatch; +CREATE TABLE mllpt (a int not null, b int not null) + PARTITION BY LIST (a); +CREATE TABLE mllpt_a1 PARTITION OF mllpt FOR VALUES IN (1) + PARTITION BY LIST (b); +CREATE TABLE mllpt_a1_b1 PARTITION OF mllpt_a1 FOR VALUES IN (1) + WITH (autovacuum_enabled = false); +CREATE TABLE mllpt_a1_b2 PARTITION OF mllpt_a1 FOR VALUES IN (2) + WITH (autovacuum_enabled = false); +CREATE TABLE mllpt_a1_b3 PARTITION OF mllpt_a1 FOR VALUES IN (3) + WITH (autovacuum_enabled = false); +CREATE TABLE mllpt_a2 PARTITION OF mllpt FOR VALUES IN (2) + PARTITION BY LIST (b); +CREATE TABLE mllpt_a2_b1 PARTITION OF mllpt_a2 FOR VALUES IN (1) + WITH (autovacuum_enabled = false); +CREATE TABLE mllpt_a2_b2 PARTITION OF mllpt_a2 FOR VALUES IN (2) + WITH (autovacuum_enabled = false); +CREATE TABLE mllpt_a2_b3 PARTITION OF mllpt_a2 FOR VALUES IN (3) + WITH (autovacuum_enabled = false); EXPLAIN (PLAN_ADVICE, COSTS OFF) SELECT * FROM pt1, pt2, pt3 WHERE pt1.id = pt2.id AND pt2.id = pt3.id AND val1 = 1 AND val2 = 1 AND val3 = 1; @@ -424,3 +442,37 @@ SELECT * FROM pt1, pt2, pt3 WHERE pt1.id = pt2.id AND pt2.id = pt3.id (49 rows) COMMIT; +-- We should get PARTITIONWISE advice for all unpruned partition tables. +EXPLAIN (PLAN_ADVICE, COSTS OFF) +SELECT * FROM mllpt WHERE a = 1 UNION ALL SELECT * FROM mllpt; + QUERY PLAN +----------------------------------------------------------------------------------------- + Append + -> Append + -> Seq Scan on mllpt_a1_b1 mllpt_7 + Filter: (a = 1) + -> Seq Scan on mllpt_a1_b2 mllpt_8 + Filter: (a = 1) + -> Seq Scan on mllpt_a1_b3 mllpt_9 + Filter: (a = 1) + -> Seq Scan on mllpt_a1_b1 mllpt + -> Seq Scan on mllpt_a1_b2 mllpt_1 + -> Seq Scan on mllpt_a1_b3 mllpt_2 + -> Seq Scan on mllpt_a2_b1 mllpt_3 + -> Seq Scan on mllpt_a2_b2 mllpt_4 + -> Seq Scan on mllpt_a2_b3 mllpt_5 + Generated Plan Advice: + SEQ_SCAN(mllpt/public.mllpt_a1_b1@unnamed_subquery + mllpt/public.mllpt_a1_b2@unnamed_subquery + mllpt/public.mllpt_a1_b3@unnamed_subquery mllpt/public.mllpt_a1_b1 + mllpt/public.mllpt_a1_b2 mllpt/public.mllpt_a1_b3 mllpt/public.mllpt_a2_b1 + mllpt/public.mllpt_a2_b2 mllpt/public.mllpt_a2_b3) + PARTITIONWISE(mllpt mllpt/public.mllpt_a1 mllpt/public.mllpt_a2 + mllpt/public.mllpt_a1@unnamed_subquery mllpt@unnamed_subquery) + NO_GATHER(unnamed_subquery#3 mllpt/public.mllpt_a1_b1 + mllpt/public.mllpt_a1_b2 mllpt/public.mllpt_a1_b3 mllpt/public.mllpt_a2_b1 + mllpt/public.mllpt_a2_b2 mllpt/public.mllpt_a2_b3 + mllpt/public.mllpt_a1_b1@unnamed_subquery + mllpt/public.mllpt_a1_b2@unnamed_subquery mllpt/public.mllpt_a1_b3@unnamed_subquery) +(27 rows) + diff --git a/contrib/pg_plan_advice/pgpa_scan.c b/contrib/pg_plan_advice/pgpa_scan.c index 1d8894e5440..61b8f5eb9b7 100644 --- a/contrib/pg_plan_advice/pgpa_scan.c +++ b/contrib/pg_plan_advice/pgpa_scan.c @@ -200,8 +200,13 @@ pgpa_build_scan(pgpa_plan_walker_context *walker, Plan *plan, child_nonjoin_relids = pgpa_filter_out_join_relids(child_relids, walker->pstmt->rtable); - (void) pgpa_make_scan(walker, plan, strategy, - child_nonjoin_relids); + if (unique_nonjoin_rtekind(child_nonjoin_relids, walker->pstmt->rtable) + == RTE_RELATION) + (void) pgpa_make_scan(walker, plan, PGPA_SCAN_PARTITIONWISE, + child_nonjoin_relids); + else + (void) pgpa_make_scan(walker, plan, PGPA_SCAN_ORDINARY, + child_nonjoin_relids); } /* diff --git a/contrib/pg_plan_advice/sql/partitionwise.sql b/contrib/pg_plan_advice/sql/partitionwise.sql index ce10d2abd7a..940ea49e64d 100644 --- a/contrib/pg_plan_advice/sql/partitionwise.sql +++ b/contrib/pg_plan_advice/sql/partitionwise.sql @@ -54,6 +54,25 @@ INSERT INTO ptmismatch (id, dimm, valm) FROM generate_series(1,3000) g; VACUUM ANALYZE ptmismatch; +CREATE TABLE mllpt (a int not null, b int not null) + PARTITION BY LIST (a); +CREATE TABLE mllpt_a1 PARTITION OF mllpt FOR VALUES IN (1) + PARTITION BY LIST (b); +CREATE TABLE mllpt_a1_b1 PARTITION OF mllpt_a1 FOR VALUES IN (1) + WITH (autovacuum_enabled = false); +CREATE TABLE mllpt_a1_b2 PARTITION OF mllpt_a1 FOR VALUES IN (2) + WITH (autovacuum_enabled = false); +CREATE TABLE mllpt_a1_b3 PARTITION OF mllpt_a1 FOR VALUES IN (3) + WITH (autovacuum_enabled = false); +CREATE TABLE mllpt_a2 PARTITION OF mllpt FOR VALUES IN (2) + PARTITION BY LIST (b); +CREATE TABLE mllpt_a2_b1 PARTITION OF mllpt_a2 FOR VALUES IN (1) + WITH (autovacuum_enabled = false); +CREATE TABLE mllpt_a2_b2 PARTITION OF mllpt_a2 FOR VALUES IN (2) + WITH (autovacuum_enabled = false); +CREATE TABLE mllpt_a2_b3 PARTITION OF mllpt_a2 FOR VALUES IN (3) + WITH (autovacuum_enabled = false); + EXPLAIN (PLAN_ADVICE, COSTS OFF) SELECT * FROM pt1, pt2, pt3 WHERE pt1.id = pt2.id AND pt2.id = pt3.id AND val1 = 1 AND val2 = 1 AND val3 = 1; @@ -100,3 +119,7 @@ EXPLAIN (PLAN_ADVICE, COSTS OFF) SELECT * FROM pt1, pt2, pt3 WHERE pt1.id = pt2.id AND pt2.id = pt3.id AND val1 = 1 AND val2 = 1 AND val3 = 1; COMMIT; + +-- We should get PARTITIONWISE advice for all unpruned partition tables. +EXPLAIN (PLAN_ADVICE, COSTS OFF) +SELECT * FROM mllpt WHERE a = 1 UNION ALL SELECT * FROM mllpt; -- 2.53.0