mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
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
126 lines
4.9 KiB
PL/PgSQL
126 lines
4.9 KiB
PL/PgSQL
LOAD 'pg_plan_advice';
|
|
SET max_parallel_workers_per_gather = 0;
|
|
SET enable_partitionwise_join = true;
|
|
|
|
CREATE TABLE pt1 (id integer primary key, dim1 text, val1 int)
|
|
PARTITION BY RANGE (id);
|
|
CREATE TABLE pt1a PARTITION OF pt1 FOR VALUES FROM (1) to (1001)
|
|
WITH (autovacuum_enabled = false);
|
|
CREATE TABLE pt1b PARTITION OF pt1 FOR VALUES FROM (1001) to (2001)
|
|
WITH (autovacuum_enabled = false);
|
|
CREATE TABLE pt1c PARTITION OF pt1 FOR VALUES FROM (2001) to (3001)
|
|
WITH (autovacuum_enabled = false);
|
|
INSERT INTO pt1 (id, dim1, val1)
|
|
SELECT g, 'some filler text ' || g, (g % 3) + 1
|
|
FROM generate_series(1,3000) g;
|
|
VACUUM ANALYZE pt1;
|
|
|
|
CREATE TABLE pt2 (id integer primary key, dim2 text, val2 int)
|
|
PARTITION BY RANGE (id);
|
|
CREATE TABLE pt2a PARTITION OF pt2 FOR VALUES FROM (1) to (1001)
|
|
WITH (autovacuum_enabled = false);
|
|
CREATE TABLE pt2b PARTITION OF pt2 FOR VALUES FROM (1001) to (2001)
|
|
WITH (autovacuum_enabled = false);
|
|
CREATE TABLE pt2c PARTITION OF pt2 FOR VALUES FROM (2001) to (3001)
|
|
WITH (autovacuum_enabled = false);
|
|
INSERT INTO pt2 (id, dim2, val2)
|
|
SELECT g, 'some other text ' || g, (g % 5) + 1
|
|
FROM generate_series(1,3000,2) g;
|
|
VACUUM ANALYZE pt2;
|
|
|
|
CREATE TABLE pt3 (id integer primary key, dim3 text, val3 int)
|
|
PARTITION BY RANGE (id);
|
|
CREATE TABLE pt3a PARTITION OF pt3 FOR VALUES FROM (1) to (1001)
|
|
WITH (autovacuum_enabled = false);
|
|
CREATE TABLE pt3b PARTITION OF pt3 FOR VALUES FROM (1001) to (2001)
|
|
WITH (autovacuum_enabled = false);
|
|
CREATE TABLE pt3c PARTITION OF pt3 FOR VALUES FROM (2001) to (3001)
|
|
WITH (autovacuum_enabled = false);
|
|
INSERT INTO pt3 (id, dim3, val3)
|
|
SELECT g, 'a third random text ' || g, (g % 7) + 1
|
|
FROM generate_series(1,3000,3) g;
|
|
VACUUM ANALYZE pt3;
|
|
|
|
CREATE TABLE ptmismatch (id integer primary key, dimm text, valm int)
|
|
PARTITION BY RANGE (id);
|
|
CREATE TABLE ptmismatcha PARTITION OF ptmismatch
|
|
FOR VALUES FROM (1) to (1501)
|
|
WITH (autovacuum_enabled = false);
|
|
CREATE TABLE ptmismatchb PARTITION OF ptmismatch
|
|
FOR VALUES FROM (1501) to (3001)
|
|
WITH (autovacuum_enabled = false);
|
|
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;
|
|
|
|
-- Suppress partitionwise join, or do it just partially.
|
|
BEGIN;
|
|
SET LOCAL pg_plan_advice.advice = 'PARTITIONWISE(pt1 pt2 pt3)';
|
|
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;
|
|
SET LOCAL pg_plan_advice.advice = 'PARTITIONWISE((pt1 pt2) pt3)';
|
|
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;
|
|
|
|
-- Test conflicting advice.
|
|
BEGIN;
|
|
SET LOCAL pg_plan_advice.advice = 'PARTITIONWISE((pt1 pt2) (pt1 pt3))';
|
|
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;
|
|
|
|
-- Test use of join order for the partitionwise join case.
|
|
BEGIN;
|
|
SET LOCAL pg_plan_advice.advice = 'PARTITIONWISE((pt1 pt2)) JOIN_ORDER({pt1 pt2} pt3)';
|
|
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;
|
|
|
|
-- Can't force a partitionwise join with a mismatched table.
|
|
BEGIN;
|
|
SET LOCAL pg_plan_advice.advice = 'PARTITIONWISE((pt1 ptmismatch))';
|
|
EXPLAIN (PLAN_ADVICE, COSTS OFF)
|
|
SELECT * FROM pt1, ptmismatch WHERE pt1.id = ptmismatch.id;
|
|
COMMIT;
|
|
|
|
-- Force join order for a particular branch of the partitionwise join.
|
|
BEGIN;
|
|
SET LOCAL pg_plan_advice.advice = 'JOIN_ORDER(pt3/public.pt3a pt2/public.pt2a pt1/public.pt1a)';
|
|
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;
|