1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

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
Discussion: http://postgr.es/m/CA+TgmoYYHC4i-Gpzc9-JYHqih6pOK+6LTYFiNR-PioXG1nKkOQ@mail.gmail.com
This commit is contained in:
Robert Haas
2026-10-08 12:27:44 -04:00
parent 0f7dfd7936
commit 8606fb3f44
3 changed files with 82 additions and 2 deletions

View File

@@ -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)

View File

@@ -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);
}
/*

View File

@@ -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;