1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00
Files
postgres/contrib/pg_plan_advice/sql/gather.sql
Robert Haas f71a9969d5 pg_plan_advice: Don't suppress NO_GATHER advice for parent rels.
When a table is partitioned, the previous code did not emit NO_GATHER
advice for the parent table, on the theory that doing this for the
child tables was good enough. However, it isn't. I believe what
happened here is that, during development, I was planning to enforce
NO_GATHER by clearing the consider_parallel flag for the children,
which would have been good enough to ensure that parallelism could
not be used at any higher level of the plan tree. Eventually, I
switched course, but failed to realize that this code needed to be
updated as a result. Do that, and add a regression test.

Backpatch-through: 19
Discussion: http://postgr.es/m/CA+TgmoYYHC4i-Gpzc9-JYHqih6pOK+6LTYFiNR-PioXG1nKkOQ@mail.gmail.com
2026-10-09 13:00:10 -04:00

113 lines
4.4 KiB
PL/PgSQL

LOAD 'pg_plan_advice';
SET max_parallel_workers_per_gather = 1;
SET parallel_setup_cost = 0;
SET parallel_tuple_cost = 0;
SET min_parallel_table_scan_size = 0;
SET debug_parallel_query = off;
CREATE TABLE gt_dim (id serial primary key, dim text)
WITH (autovacuum_enabled = false);
INSERT INTO gt_dim (dim) SELECT random()::text FROM generate_series(1,100) g;
VACUUM ANALYZE gt_dim;
CREATE TABLE gt_fact (
id int not null,
dim_id integer not null references gt_dim (id)
) WITH (autovacuum_enabled = false);
INSERT INTO gt_fact
SELECT g, (g%3)+1 FROM generate_series(1,100000) g;
VACUUM ANALYZE gt_fact;
CREATE TABLE gt_part (id int not null, val int not null)
PARTITION BY RANGE (id);
CREATE TABLE gt_part1 PARTITION OF gt_part FOR VALUES FROM (0) TO (1000)
WITH (autovacuum_enabled = false);
CREATE TABLE gt_part2 PARTITION OF gt_part FOR VALUES FROM (1000) TO (2000)
WITH (autovacuum_enabled = false);
INSERT INTO gt_part SELECT g, g FROM generate_series(0, 1999) g;
VACUUM ANALYZE gt_part;
-- By default, we expect Gather Merge with a parallel hash join.
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id ORDER BY d.id;
-- Force Gather or Gather Merge of both relations together.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'gather_merge((f d))';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id ORDER BY d.id;
SET LOCAL pg_plan_advice.advice = 'gather((f d))';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id ORDER BY d.id;
COMMIT;
-- Force a separate Gather or Gather Merge operation for each relation.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'gather_merge(f d)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id ORDER BY d.id;
SET LOCAL pg_plan_advice.advice = 'gather(f d)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id ORDER BY d.id;
SET LOCAL pg_plan_advice.advice = 'gather((d d/d.d))';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id ORDER BY d.id;
COMMIT;
-- Force a Gather or Gather Merge on one relation but no parallelism on other.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'gather_merge(f) no_gather(d)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id ORDER BY d.id;
SET LOCAL pg_plan_advice.advice = 'gather_merge(d) no_gather(f)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id ORDER BY d.id;
SET LOCAL pg_plan_advice.advice = 'gather(f) no_gather(d)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id ORDER BY d.id;
SET LOCAL pg_plan_advice.advice = 'gather(d) no_gather(f)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id ORDER BY d.id;
COMMIT;
-- Force no Gather or Gather Merge use at all.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'no_gather(f d)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id ORDER BY d.id;
COMMIT;
-- Can't force Gather Merge without the ORDER BY clause, but just Gather is OK.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'gather_merge((f d))';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id;
SET LOCAL pg_plan_advice.advice = 'gather((f d))';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id;
COMMIT;
-- Test conflicting advice.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'gather((f d)) no_gather(f)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_fact f JOIN gt_dim d ON f.dim_id = d.id ORDER BY d.id;
COMMIT;
-- Test interaction of NO_GATHER with partitioned tables. By default, we
-- expect Gather over a Parallel Append, but no_gather(gt_part) should
-- suppress it, even if we prune down to a single partition such that the
-- Append itself doesn't appear in the plan. Previously, we had a bug where
-- NO_GATHER for the parent was omitted from the generated output even when
-- NO_GATHER was supplied, so this also serves to verify that this is no
-- longer happening.
BEGIN;
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_part WHERE val = 42;
SET LOCAL pg_plan_advice.advice = 'no_gather(gt_part)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_part WHERE val = 42;
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM gt_part WHERE id < 500 AND val = 42;
COMMIT;