From e01b23b84e4346c1941e2aad78e3d80a5498038d Mon Sep 17 00:00:00 2001 From: Richard Guo Date: Thu, 9 Jul 2026 09:44:56 +0900 Subject: [PATCH] Add an enable_groupagg GUC parameter We've long had enable_hashagg to discourage hashed aggregation, but there was no equivalent for grouping that works by reading presorted input. That's handy when investigating planner cost misestimates, and as an escape hatch when a sorted grouping plan is chosen over a much cheaper hashed one. enable_groupagg (on by default) covers the GroupAggregate and Group nodes, the sort-based Unique step used for DISTINCT and semijoin unique-ification, and the sorted mode of SetOp. It isn't a hard switch; it only bumps disabled_nodes, so plans with no other choice are still produced. Several regression and module tests had been setting enable_sort off, and one enable_indexscan off, only to force a hashed plan. Use enable_groupagg there instead, where that was the real intent. In union.sql this also lets us test the hashed UNION path, which we had no way to reach before. Author: Tatsuro Yamada Co-authored-by: Richard Guo Reviewed-by: Ashutosh Bapat Reviewed-by: David Rowley Discussion: https://postgr.es/m/CAOKkKFvYHSEsFazkrf9bRH14p-H27XMaqbZfRYjS6EHBruvZMQ@mail.gmail.com --- contrib/hstore/expected/hstore.out | 4 +- contrib/hstore/sql/hstore.sql | 4 +- contrib/ltree/expected/ltree.out | 4 +- contrib/ltree/sql/ltree.sql | 4 +- doc/src/sgml/config.sgml | 14 +++++++ src/backend/optimizer/path/costsize.c | 33 ++++++++++++++--- src/backend/optimizer/util/pathnode.c | 20 ++++++++++ src/backend/utils/misc/guc_parameters.dat | 7 ++++ src/backend/utils/misc/postgresql.conf.sample | 1 + src/include/optimizer/cost.h | 1 + .../injection_points/expected/hashagg.out | 2 +- .../modules/injection_points/sql/hashagg.sql | 2 +- src/test/regress/expected/aggregates.out | 10 ++--- src/test/regress/expected/groupingsets.out | 8 ++-- src/test/regress/expected/jsonb.out | 6 +-- src/test/regress/expected/multirangetypes.out | 4 +- src/test/regress/expected/rangetypes.out | 4 +- src/test/regress/expected/select_distinct.out | 4 +- src/test/regress/expected/subselect.out | 2 +- src/test/regress/expected/sysviews.out | 3 +- src/test/regress/expected/union.out | 37 ++++++++++++------- src/test/regress/sql/aggregates.sql | 10 ++--- src/test/regress/sql/groupingsets.sql | 8 ++-- src/test/regress/sql/jsonb.sql | 6 +-- src/test/regress/sql/multirangetypes.sql | 4 +- src/test/regress/sql/rangetypes.sql | 4 +- src/test/regress/sql/select_distinct.sql | 4 +- src/test/regress/sql/subselect.sql | 2 +- src/test/regress/sql/union.sql | 16 ++++---- 29 files changed, 153 insertions(+), 75 deletions(-) diff --git a/contrib/hstore/expected/hstore.out b/contrib/hstore/expected/hstore.out index acea8806ba4..9713372b7a4 100644 --- a/contrib/hstore/expected/hstore.out +++ b/contrib/hstore/expected/hstore.out @@ -1509,7 +1509,7 @@ select count(*) from (select h from (select * from testhstore union all select * (1 row) set enable_hashagg = true; -set enable_sort = false; +set enable_groupagg = false; select count(*) from (select h from (select * from testhstore union all select * from testhstore) hs group by h) hs2; count ------- @@ -1522,7 +1522,7 @@ select distinct * from (values (hstore '' || ''),('')) v(h); (1 row) -set enable_sort = true; +set enable_groupagg = true; -- btree drop index hidx; create index hidx on testhstore using btree (h); diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql index 8ae95e8a510..ac929e07509 100644 --- a/contrib/hstore/sql/hstore.sql +++ b/contrib/hstore/sql/hstore.sql @@ -345,10 +345,10 @@ select count(distinct h) from testhstore; set enable_hashagg = false; select count(*) from (select h from (select * from testhstore union all select * from testhstore) hs group by h) hs2; set enable_hashagg = true; -set enable_sort = false; +set enable_groupagg = false; select count(*) from (select h from (select * from testhstore union all select * from testhstore) hs group by h) hs2; select distinct * from (values (hstore '' || ''),('')) v(h); -set enable_sort = true; +set enable_groupagg = true; -- btree drop index hidx; diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out index f1d0eb37b81..0ab623cc204 100644 --- a/contrib/ltree/expected/ltree.out +++ b/contrib/ltree/expected/ltree.out @@ -7891,7 +7891,7 @@ reset enable_seqscan; reset enable_bitmapscan; -- test hash aggregate set enable_hashagg=on; -set enable_sort=off; +set enable_groupagg=off; EXPLAIN (COSTS OFF) SELECT count(*) FROM ( SELECT t FROM (SELECT * FROM ltreetest UNION ALL SELECT * FROM ltreetest) t1 GROUP BY t @@ -7915,7 +7915,7 @@ SELECT t FROM (SELECT * FROM ltreetest UNION ALL SELECT * FROM ltreetest) t1 GRO (1 row) reset enable_hashagg; -reset enable_sort; +reset enable_groupagg; drop index tstidx; -- test gist index create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=0)); diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql index 833091dc6bb..5c324160afd 100644 --- a/contrib/ltree/sql/ltree.sql +++ b/contrib/ltree/sql/ltree.sql @@ -372,7 +372,7 @@ reset enable_bitmapscan; -- test hash aggregate set enable_hashagg=on; -set enable_sort=off; +set enable_groupagg=off; EXPLAIN (COSTS OFF) SELECT count(*) FROM ( @@ -384,7 +384,7 @@ SELECT t FROM (SELECT * FROM ltreetest UNION ALL SELECT * FROM ltreetest) t1 GRO ) t2; reset enable_hashagg; -reset enable_sort; +reset enable_groupagg; drop index tstidx; diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 9172a4c5c95..c67130c620e 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -5863,6 +5863,20 @@ ANY num_sync ( + enable_groupagg (boolean) + + enable_groupagg configuration parameter + + + + + Enables or disables the query planner's use of sort-based grouping + and aggregation plan types. The default is on. + + + + enable_hashagg (boolean) diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 1c575e56ff6..ac523ecf9a8 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -151,6 +151,7 @@ bool enable_tidscan = true; bool enable_sort = true; bool enable_incremental_sort = true; bool enable_hashagg = true; +bool enable_groupagg = true; bool enable_nestloop = true; bool enable_material = true; bool enable_memoize = true; @@ -2837,14 +2838,14 @@ cost_agg(Path *path, PlannerInfo *root, /* we aren't grouping */ total_cost = startup_cost + cpu_tuple_cost; output_tuples = 1; + + /* AGG_PLAIN neither hashes nor sorts, so neither switch disables it */ } else if (aggstrategy == AGG_SORTED || aggstrategy == AGG_MIXED) { /* Here we are able to deliver output on-the-fly */ startup_cost = input_startup_cost; total_cost = input_total_cost; - if (aggstrategy == AGG_MIXED && !enable_hashagg) - ++disabled_nodes; /* calcs phrased this way to match HASHED case, see note above */ total_cost += aggcosts->transCost.startup; total_cost += aggcosts->transCost.per_tuple * input_tuples; @@ -2853,13 +2854,31 @@ cost_agg(Path *path, PlannerInfo *root, total_cost += aggcosts->finalCost.per_tuple * numGroups; total_cost += cpu_tuple_cost * numGroups; output_tuples = numGroups; + + /* + * AGG_MIXED hashes at least one grouping set, so it is disabled when + * enable_hashagg is off. Any sorted grouping it also performs is + * costed separately, since create_groupingsets_path() calls + * cost_agg() once per rollup and the non-hashed rollups come through + * as AGG_SORTED. + * + * AGG_SORTED is disabled when enable_groupagg is off, but only when + * there are grouping columns. The empty grouping set arrives with + * numGroupCols == 0 and is computed like AGG_PLAIN, with no hashing + * or sorting, so it isn't disabled. + */ + if (aggstrategy == AGG_MIXED) + { + if (!enable_hashagg) + ++disabled_nodes; + } + else if (numGroupCols > 0 && !enable_groupagg) /* AGG_SORTED */ + ++disabled_nodes; } else { /* must be AGG_HASHED */ startup_cost = input_total_cost; - if (!enable_hashagg) - ++disabled_nodes; startup_cost += aggcosts->transCost.startup; startup_cost += aggcosts->transCost.per_tuple * input_tuples; /* cost of computing hash value */ @@ -2871,6 +2890,10 @@ cost_agg(Path *path, PlannerInfo *root, /* cost of retrieving from hash table */ total_cost += cpu_tuple_cost * numGroups; output_tuples = numGroups; + + /* AGG_HASHED is disabled when enable_hashagg is off */ + if (!enable_hashagg) + ++disabled_nodes; } /* @@ -3340,7 +3363,7 @@ cost_group(Path *path, PlannerInfo *root, } path->rows = output_tuples; - path->disabled_nodes = input_disabled_nodes; + path->disabled_nodes = input_disabled_nodes + (enable_groupagg ? 0 : 1); path->startup_cost = startup_cost; path->total_cost = total_cost; } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 73518c8f870..3875e3dd571 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -3036,6 +3036,16 @@ create_unique_path(PlannerInfo *root, cpu_operator_cost * subpath->rows * numCols; pathnode->path.rows = numGroups; + /* + * Mark the path as disabled if enable_groupagg is off. While this isn't + * a grouping Agg node, it is the sort-based way of removing duplicates + * and so is the natural counterpart to the AGG_HASHED path that + * enable_hashagg controls; it seems close enough to justify letting that + * switch control it. + */ + if (!enable_groupagg) + pathnode->path.disabled_nodes++; + return pathnode; } @@ -3522,6 +3532,16 @@ create_setop_path(PlannerInfo *root, * qual-checking or projection. */ pathnode->path.total_cost += cpu_operator_cost * outputRows; + + /* + * Mark the path as disabled if enable_groupagg is off. While this + * isn't a grouping Agg node, it is the sort-based implementation and + * so is the natural counterpart to the SETOP_HASHED path that + * enable_hashagg controls; it seems close enough to justify letting + * that switch control it. + */ + if (!enable_groupagg) + pathnode->path.disabled_nodes++; } else { diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat index c9118e71988..dd799a5e70f 100644 --- a/src/backend/utils/misc/guc_parameters.dat +++ b/src/backend/utils/misc/guc_parameters.dat @@ -905,6 +905,13 @@ boot_val => 'true', }, +{ name => 'enable_groupagg', type => 'bool', context => 'PGC_USERSET', group => 'QUERY_TUNING_METHOD', + short_desc => 'Enables the planner\'s use of sort-based grouping and aggregation plans.', + flags => 'GUC_EXPLAIN', + variable => 'enable_groupagg', + boot_val => 'true', +}, + { name => 'enable_hashagg', type => 'bool', context => 'PGC_USERSET', group => 'QUERY_TUNING_METHOD', short_desc => 'Enables the planner\'s use of hashed aggregation plans.', flags => 'GUC_EXPLAIN', diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index d7942f50a70..47e1221f3c3 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -426,6 +426,7 @@ #enable_async_append = on #enable_bitmapscan = on #enable_gathermerge = on +#enable_groupagg = on #enable_hashagg = on #enable_hashjoin = on #enable_incremental_sort = on diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index f2fd5d31507..bda3f1690c0 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -57,6 +57,7 @@ extern PGDLLIMPORT bool enable_tidscan; extern PGDLLIMPORT bool enable_sort; extern PGDLLIMPORT bool enable_incremental_sort; extern PGDLLIMPORT bool enable_hashagg; +extern PGDLLIMPORT bool enable_groupagg; extern PGDLLIMPORT bool enable_nestloop; extern PGDLLIMPORT bool enable_material; extern PGDLLIMPORT bool enable_memoize; diff --git a/src/test/modules/injection_points/expected/hashagg.out b/src/test/modules/injection_points/expected/hashagg.out index cc4247af97d..2f75e9be8b7 100644 --- a/src/test/modules/injection_points/expected/hashagg.out +++ b/src/test/modules/injection_points/expected/hashagg.out @@ -36,7 +36,7 @@ CREATE TABLE hashagg_ij(x INTEGER); INSERT INTO hashagg_ij SELECT g FROM generate_series(1,5100) g; SET max_parallel_workers=0; SET max_parallel_workers_per_gather=0; -SET enable_sort=FALSE; +SET enable_groupagg=FALSE; SET work_mem='4MB'; SELECT COUNT(*) FROM (SELECT DISTINCT x FROM hashagg_ij) s; NOTICE: notice triggered for injection point hash-aggregate-spill-1000 diff --git a/src/test/modules/injection_points/sql/hashagg.sql b/src/test/modules/injection_points/sql/hashagg.sql index 51d814623fc..5d580f8e425 100644 --- a/src/test/modules/injection_points/sql/hashagg.sql +++ b/src/test/modules/injection_points/sql/hashagg.sql @@ -17,7 +17,7 @@ INSERT INTO hashagg_ij SELECT g FROM generate_series(1,5100) g; SET max_parallel_workers=0; SET max_parallel_workers_per_gather=0; -SET enable_sort=FALSE; +SET enable_groupagg=FALSE; SET work_mem='4MB'; SELECT COUNT(*) FROM (SELECT DISTINCT x FROM hashagg_ij) s; diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 728a3ecd03f..f21f4e225da 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1459,7 +1459,7 @@ drop cascades to table minmaxtest2 drop cascades to table minmaxtest3 -- DISTINCT can also trigger wrong answers with hash aggregation (bug #18465) begin; -set local enable_sort = off; +set local enable_groupagg = off; explain (costs off) select f1, (select distinct min(t1.f1) from int4_tbl t1 where t1.f1 = t0.f1) from int4_tbl t0; @@ -3866,7 +3866,7 @@ select v||'a', case when v||'a' = 'aa' then 1 else 0 end, count(*) -- -- Hash Aggregation Spill tests -- -set enable_sort=false; +set enable_groupagg=false; set work_mem='64kB'; select unique1, count(*), sum(twothousand) from tenk1 group by unique1 @@ -3925,7 +3925,7 @@ order by sum(twothousand); (48 rows) set work_mem to default; -set enable_sort to default; +set enable_groupagg to default; -- -- Compare results between plans using sorting and plans using hash -- aggregation. Force spilling in both cases by setting work_mem low. @@ -3974,7 +3974,7 @@ select (g/2)::numeric as c1, array_agg(g::numeric) as c2, count(*) as c3 from agg_data_2k group by g/2; -- Produce results with hash aggregation set enable_hashagg = true; -set enable_sort = false; +set enable_groupagg = false; set jit_above_cost = 0; explain (costs off) select g%10000 as c1, sum(g::numeric) as c2, count(*) as c3 @@ -4006,7 +4006,7 @@ select (g/2)::numeric as c1, sum(7::int4) as c2, count(*) as c3 create table agg_hash_4 as select (g/2)::numeric as c1, array_agg(g::numeric) as c2, count(*) as c3 from agg_data_2k group by g/2; -set enable_sort = true; +set enable_groupagg = true; set work_mem to default; -- Compare group aggregation results to hash aggregation results (select * from agg_hash_1 except select * from agg_group_1) diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out index b08083ec54c..c3f00771f6e 100644 --- a/src/test/regress/expected/groupingsets.out +++ b/src/test/regress/expected/groupingsets.out @@ -2010,7 +2010,7 @@ alter table bug_16784 set (autovacuum_enabled = 'false'); update pg_class set reltuples = 10 where relname='bug_16784'; insert into bug_16784 select g/10, g from generate_series(1,40) g; set work_mem='64kB'; -set enable_sort = false; +set enable_groupagg = false; select * from (values (1),(2)) v(a), lateral (select a, i, j, count(*) from @@ -2205,7 +2205,7 @@ alter table gs_data_1 set (autovacuum_enabled = 'false'); update pg_class set reltuples = 10 where relname='gs_data_1'; set work_mem='64kB'; -- Produce results with sorting. -set enable_sort = true; +set enable_groupagg = true; set enable_hashagg = false; set jit_above_cost = 0; explain (costs off) @@ -2234,7 +2234,7 @@ select g100, g10, sum(g::numeric), count(*), max(g::text) from gs_data_1 group by cube (g1000, g100,g10); -- Produce results with hash aggregation. set enable_hashagg = true; -set enable_sort = false; +set enable_groupagg = false; explain (costs off) select g100, g10, sum(g::numeric), count(*), max(g::text) from gs_data_1 group by cube (g1000, g100,g10); @@ -2255,7 +2255,7 @@ from gs_data_1 group by cube (g1000, g100,g10); create table gs_hash_1 as select g100, g10, sum(g::numeric), count(*), max(g::text) from gs_data_1 group by cube (g1000, g100,g10); -set enable_sort = true; +set enable_groupagg = true; set work_mem to default; set hash_mem_multiplier to default; -- Compare results diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out index 4e2467852db..b1d2ce5f03a 100644 --- a/src/test/regress/expected/jsonb.out +++ b/src/test/regress/expected/jsonb.out @@ -3473,7 +3473,7 @@ SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * (1 row) SET enable_hashagg = on; -SET enable_sort = off; +SET enable_groupagg = off; SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2; count ------- @@ -3486,9 +3486,9 @@ SELECT distinct * FROM (values (jsonb '{}' || ''::text),('{}')) v(j); {} (1 row) -SET enable_sort = on; +SET enable_groupagg = on; RESET enable_hashagg; -RESET enable_sort; +RESET enable_groupagg; DROP INDEX jidx; DROP INDEX jidx_array; -- btree diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out index 6006aede31d..d47ce4b6d6a 100644 --- a/src/test/regress/expected/multirangetypes.out +++ b/src/test/regress/expected/multirangetypes.out @@ -3442,14 +3442,14 @@ NOTICE: drop cascades to type two_ints_range -- -- Check behavior when subtype lacks a hash function -- -set enable_sort = off; -- try to make it pick a hash setop implementation +set enable_groupagg = off; -- try to make it pick a hash setop implementation select '{(01,10)}'::varbitmultirange except select '{(10,11)}'::varbitmultirange; varbitmultirange ------------------ {(01,10)} (1 row) -reset enable_sort; +reset enable_groupagg; -- -- OUT/INOUT/TABLE functions -- diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out index 98ba738fb1d..083e948bc41 100644 --- a/src/test/regress/expected/rangetypes.out +++ b/src/test/regress/expected/rangetypes.out @@ -1819,14 +1819,14 @@ NOTICE: drop cascades to type two_ints_range -- Check behavior when subtype lacks a hash function -- create type varbitrange as range (subtype = varbit); -set enable_sort = off; -- try to make it pick a hash setop implementation +set enable_groupagg = off; -- try to make it pick a hash setop implementation select '(01,10)'::varbitrange except select '(10,11)'::varbitrange; varbitrange ------------- (01,10) (1 row) -reset enable_sort; +reset enable_groupagg; -- -- OUT/INOUT/TABLE functions -- diff --git a/src/test/regress/expected/select_distinct.out b/src/test/regress/expected/select_distinct.out index 379ba0bc9fa..741f7238742 100644 --- a/src/test/regress/expected/select_distinct.out +++ b/src/test/regress/expected/select_distinct.out @@ -187,7 +187,7 @@ SELECT DISTINCT hundred, two FROM tenk1; RESET enable_seqscan; SET enable_hashagg=TRUE; -- Produce results with hash aggregation. -SET enable_sort=FALSE; +SET enable_groupagg=FALSE; SET jit_above_cost=0; EXPLAIN (costs off) SELECT DISTINCT g%1000 FROM generate_series(0,9999) g; @@ -203,7 +203,7 @@ SELECT DISTINCT g%1000 FROM generate_series(0,9999) g; SET jit_above_cost TO DEFAULT; CREATE TABLE distinct_hash_2 AS SELECT DISTINCT (g%1000)::text FROM generate_series(0,9999) g; -SET enable_sort=TRUE; +SET enable_groupagg=TRUE; SET work_mem TO DEFAULT; -- Compare results (SELECT * FROM distinct_hash_1 EXCEPT SELECT * FROM distinct_group_1) diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out index 20140f171af..e7ff7191082 100644 --- a/src/test/regress/expected/subselect.out +++ b/src/test/regress/expected/subselect.out @@ -1454,7 +1454,7 @@ where o.ten = 0; -- Test rescan of a hashed SetOp node -- begin; -set local enable_sort = off; +set local enable_groupagg = off; explain (costs off) select count(*) from onek o cross join lateral ( diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out index 132b56a5864..1e327c2afa4 100644 --- a/src/test/regress/expected/sysviews.out +++ b/src/test/regress/expected/sysviews.out @@ -161,6 +161,7 @@ select name, setting from pg_settings where name like 'enable%'; enable_eager_aggregate | on enable_gathermerge | on enable_group_by_reordering | on + enable_groupagg | on enable_hashagg | on enable_hashjoin | on enable_incremental_sort | on @@ -180,7 +181,7 @@ select name, setting from pg_settings where name like 'enable%'; enable_seqscan | on enable_sort | on enable_tidscan | on -(25 rows) +(26 rows) -- There are always wait event descriptions for various types. InjectionPoint -- may be present or absent, depending on history since last postmaster start. diff --git a/src/test/regress/expected/union.out b/src/test/regress/expected/union.out index ca9089e9a7d..84abcd6b14f 100644 --- a/src/test/regress/expected/union.out +++ b/src/test/regress/expected/union.out @@ -386,14 +386,14 @@ select count(*) from (1 row) -- this query will prefer a sorted setop unless we force it. -set enable_indexscan to off; +set enable_groupagg to off; explain (costs off) select unique1 from tenk1 except select unique2 from tenk1 where unique2 != 10; - QUERY PLAN ---------------------------------- + QUERY PLAN +------------------------------------------------------------ HashSetOp Except - -> Seq Scan on tenk1 - -> Seq Scan on tenk1 tenk1_1 + -> Index Only Scan using tenk1_unique1 on tenk1 + -> Index Only Scan using tenk1_unique2 on tenk1 tenk1_1 Filter: (unique2 <> 10) (4 rows) @@ -403,7 +403,7 @@ select unique1 from tenk1 except select unique2 from tenk1 where unique2 != 10; 10 (1 row) -reset enable_indexscan; +reset enable_groupagg; -- the hashed implementation is sensitive to child plans' tuple slot types explain (costs off) select * from int8_tbl intersect select q2, q1 from int8_tbl order by 1, 2; @@ -981,19 +981,30 @@ select except select; -- check hashed implementation set enable_hashagg = true; -set enable_sort = false; --- We've no way to check hashed UNION as the empty pathkeys in the Append are --- fine to make use of Unique, which is cheaper than HashAggregate and we've --- no means to disable Unique. +set enable_groupagg = false; +explain (costs off) +select from generate_series(1,5) union select from generate_series(1,3); + QUERY PLAN +---------------------------------------------------------------- + HashAggregate + -> Append + -> Function Scan on generate_series + -> Function Scan on generate_series generate_series_1 +(4 rows) + explain (costs off) select from generate_series(1,5) intersect select from generate_series(1,3); QUERY PLAN ---------------------------------------------------------- - SetOp Intersect + HashSetOp Intersect -> Function Scan on generate_series -> Function Scan on generate_series generate_series_1 (3 rows) +select from generate_series(1,5) union select from generate_series(1,3); +-- +(1 row) + select from generate_series(1,5) union all select from generate_series(1,3); -- (8 rows) @@ -1016,7 +1027,7 @@ select from generate_series(1,5) except all select from generate_series(1,3); -- check sorted implementation set enable_hashagg = false; -set enable_sort = true; +set enable_groupagg = true; explain (costs off) select from generate_series(1,5) union select from generate_series(1,3); QUERY PLAN @@ -1075,7 +1086,7 @@ select from cte union select from cte; (1 row) reset enable_hashagg; -reset enable_sort; +reset enable_groupagg; -- -- Check handling of a case with unknown constants. We don't guarantee -- an undecorated constant will work in all cases, but historically this diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql index 342605d5497..5cb9a9dc1be 100644 --- a/src/test/regress/sql/aggregates.sql +++ b/src/test/regress/sql/aggregates.sql @@ -493,7 +493,7 @@ drop table minmaxtest cascade; -- DISTINCT can also trigger wrong answers with hash aggregation (bug #18465) begin; -set local enable_sort = off; +set local enable_groupagg = off; explain (costs off) select f1, (select distinct min(t1.f1) from int4_tbl t1 where t1.f1 = t0.f1) from int4_tbl t0; @@ -1702,7 +1702,7 @@ select v||'a', case when v||'a' = 'aa' then 1 else 0 end, count(*) -- Hash Aggregation Spill tests -- -set enable_sort=false; +set enable_groupagg=false; set work_mem='64kB'; select unique1, count(*), sum(twothousand) from tenk1 @@ -1711,7 +1711,7 @@ having sum(fivethous) > 4975 order by sum(twothousand); set work_mem to default; -set enable_sort to default; +set enable_groupagg to default; -- -- Compare results between plans using sorting and plans using hash @@ -1766,7 +1766,7 @@ select (g/2)::numeric as c1, array_agg(g::numeric) as c2, count(*) as c3 -- Produce results with hash aggregation set enable_hashagg = true; -set enable_sort = false; +set enable_groupagg = false; set jit_above_cost = 0; @@ -1799,7 +1799,7 @@ create table agg_hash_4 as select (g/2)::numeric as c1, array_agg(g::numeric) as c2, count(*) as c3 from agg_data_2k group by g/2; -set enable_sort = true; +set enable_groupagg = true; set work_mem to default; -- Compare group aggregation results to hash aggregation results diff --git a/src/test/regress/sql/groupingsets.sql b/src/test/regress/sql/groupingsets.sql index a594449b697..c5d4ed2eb57 100644 --- a/src/test/regress/sql/groupingsets.sql +++ b/src/test/regress/sql/groupingsets.sql @@ -583,7 +583,7 @@ update pg_class set reltuples = 10 where relname='bug_16784'; insert into bug_16784 select g/10, g from generate_series(1,40) g; set work_mem='64kB'; -set enable_sort = false; +set enable_groupagg = false; select * from (values (1),(2)) v(a), @@ -609,7 +609,7 @@ set work_mem='64kB'; -- Produce results with sorting. -set enable_sort = true; +set enable_groupagg = true; set enable_hashagg = false; set jit_above_cost = 0; @@ -624,7 +624,7 @@ from gs_data_1 group by cube (g1000, g100,g10); -- Produce results with hash aggregation. set enable_hashagg = true; -set enable_sort = false; +set enable_groupagg = false; explain (costs off) select g100, g10, sum(g::numeric), count(*), max(g::text) @@ -634,7 +634,7 @@ create table gs_hash_1 as select g100, g10, sum(g::numeric), count(*), max(g::text) from gs_data_1 group by cube (g1000, g100,g10); -set enable_sort = true; +set enable_groupagg = true; set work_mem to default; set hash_mem_multiplier to default; diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql index d28ed1c1e85..d4e8d7d8c9e 100644 --- a/src/test/regress/sql/jsonb.sql +++ b/src/test/regress/sql/jsonb.sql @@ -927,13 +927,13 @@ SELECT count(distinct j) FROM testjsonb; SET enable_hashagg = off; SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2; SET enable_hashagg = on; -SET enable_sort = off; +SET enable_groupagg = off; SELECT count(*) FROM (SELECT j FROM (SELECT * FROM testjsonb UNION ALL SELECT * FROM testjsonb) js GROUP BY j) js2; SELECT distinct * FROM (values (jsonb '{}' || ''::text),('{}')) v(j); -SET enable_sort = on; +SET enable_groupagg = on; RESET enable_hashagg; -RESET enable_sort; +RESET enable_groupagg; DROP INDEX jidx; DROP INDEX jidx_array; diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql index ddff722b28c..cf0fff6fddd 100644 --- a/src/test/regress/sql/multirangetypes.sql +++ b/src/test/regress/sql/multirangetypes.sql @@ -862,11 +862,11 @@ drop type two_ints cascade; -- Check behavior when subtype lacks a hash function -- -set enable_sort = off; -- try to make it pick a hash setop implementation +set enable_groupagg = off; -- try to make it pick a hash setop implementation select '{(01,10)}'::varbitmultirange except select '{(10,11)}'::varbitmultirange; -reset enable_sort; +reset enable_groupagg; -- -- OUT/INOUT/TABLE functions diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql index 5c4b0337b7a..dbfe0d049da 100644 --- a/src/test/regress/sql/rangetypes.sql +++ b/src/test/regress/sql/rangetypes.sql @@ -587,11 +587,11 @@ drop type two_ints cascade; create type varbitrange as range (subtype = varbit); -set enable_sort = off; -- try to make it pick a hash setop implementation +set enable_groupagg = off; -- try to make it pick a hash setop implementation select '(01,10)'::varbitrange except select '(10,11)'::varbitrange; -reset enable_sort; +reset enable_groupagg; -- -- OUT/INOUT/TABLE functions diff --git a/src/test/regress/sql/select_distinct.sql b/src/test/regress/sql/select_distinct.sql index 50ac7dde396..2ed1616b098 100644 --- a/src/test/regress/sql/select_distinct.sql +++ b/src/test/regress/sql/select_distinct.sql @@ -81,7 +81,7 @@ SET enable_hashagg=TRUE; -- Produce results with hash aggregation. -SET enable_sort=FALSE; +SET enable_groupagg=FALSE; SET jit_above_cost=0; @@ -96,7 +96,7 @@ SET jit_above_cost TO DEFAULT; CREATE TABLE distinct_hash_2 AS SELECT DISTINCT (g%1000)::text FROM generate_series(0,9999) g; -SET enable_sort=TRUE; +SET enable_groupagg=TRUE; SET work_mem TO DEFAULT; diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql index 3defbc29177..76bce5cdba5 100644 --- a/src/test/regress/sql/subselect.sql +++ b/src/test/regress/sql/subselect.sql @@ -730,7 +730,7 @@ where o.ten = 0; -- Test rescan of a hashed SetOp node -- begin; -set local enable_sort = off; +set local enable_groupagg = off; explain (costs off) select count(*) from diff --git a/src/test/regress/sql/union.sql b/src/test/regress/sql/union.sql index 780b704b53b..c8de276c2b5 100644 --- a/src/test/regress/sql/union.sql +++ b/src/test/regress/sql/union.sql @@ -135,13 +135,13 @@ select count(*) from ( select unique1 from tenk1 intersect select fivethous from tenk1 ) ss; -- this query will prefer a sorted setop unless we force it. -set enable_indexscan to off; +set enable_groupagg to off; explain (costs off) select unique1 from tenk1 except select unique2 from tenk1 where unique2 != 10; select unique1 from tenk1 except select unique2 from tenk1 where unique2 != 10; -reset enable_indexscan; +reset enable_groupagg; -- the hashed implementation is sensitive to child plans' tuple slot types explain (costs off) @@ -321,14 +321,14 @@ select except select; -- check hashed implementation set enable_hashagg = true; -set enable_sort = false; +set enable_groupagg = false; --- We've no way to check hashed UNION as the empty pathkeys in the Append are --- fine to make use of Unique, which is cheaper than HashAggregate and we've --- no means to disable Unique. +explain (costs off) +select from generate_series(1,5) union select from generate_series(1,3); explain (costs off) select from generate_series(1,5) intersect select from generate_series(1,3); +select from generate_series(1,5) union select from generate_series(1,3); select from generate_series(1,5) union all select from generate_series(1,3); select from generate_series(1,5) intersect select from generate_series(1,3); select from generate_series(1,5) intersect all select from generate_series(1,3); @@ -337,7 +337,7 @@ select from generate_series(1,5) except all select from generate_series(1,3); -- check sorted implementation set enable_hashagg = false; -set enable_sort = true; +set enable_groupagg = true; explain (costs off) select from generate_series(1,5) union select from generate_series(1,3); @@ -363,7 +363,7 @@ with cte as not materialized (select s from generate_series(1,5) s) select from cte union select from cte; reset enable_hashagg; -reset enable_sort; +reset enable_groupagg; -- -- Check handling of a case with unknown constants. We don't guarantee