Skip to content

Commit c958a90

Browse files
committed
ORCA: keep HAVING above a scalar GbAgg in CNormalizer::FPushable
A scalar (plain) aggregate with no grouping columns always emits exactly one row regardless of input cardinality. Predicates above it (from a HAVING clause) filter that output row, so they cannot be moved onto the aggregate's input without changing semantics: SELECT count(*) FROM t HAVING false -- 0 rows SELECT count(*) FROM t WHERE false -- 1 row (count=0) CNormalizer::FPushable previously only blocked pushing volatile predicates below a GbAgg. Any other predicate -- including a constant false -- was considered pushable because its used-column set was trivially contained in the aggregate's output columns. The normalizer then routed the Select's predicate through the GbAgg and down into its logical child, dropping HAVING semantics for scalar aggregates.
1 parent bff7715 commit c958a90

4 files changed

Lines changed: 54 additions & 0 deletions

File tree

src/backend/gporca/libgpopt/src/operators/CNormalizer.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "gpopt/base/CUtils.h"
1919
#include "gpopt/operators/CLogical.h"
2020
#include "gpopt/operators/CLogicalConstTableGet.h"
21+
#include "gpopt/operators/CLogicalGbAgg.h"
2122
#include "gpopt/operators/CLogicalInnerJoin.h"
2223
#include "gpopt/operators/CLogicalLeftOuterCorrelatedApply.h"
2324
#include "gpopt/operators/CLogicalLeftOuterJoin.h"
@@ -126,6 +127,18 @@ CNormalizer::FPushable(CExpression *pexprLogical, CExpression *pexprPred)
126127
return false;
127128
}
128129

130+
// do not push predicates below a scalar (plain) aggregate, i.e. one with
131+
// no grouping columns. A scalar aggregate produces exactly one output row
132+
// regardless of input cardinality, so a predicate above it (HAVING clause)
133+
// must be evaluated against that output row, not the aggregate's input.
134+
// Pushing e.g. "HAVING false" below would leave the agg emitting one row
135+
// (e.g. count = 0) instead of zero rows.
136+
if (COperator::EopLogicalGbAgg == pexprLogical->Pop()->Eopid() &&
137+
0 == CLogicalGbAgg::PopConvert(pexprLogical->Pop())->Pdrgpcr()->Size())
138+
{
139+
return false;
140+
}
141+
129142

130143
CColRefSet *pcrsUsed = pexprPred->DeriveUsedColumns();
131144
CColRefSet *pcrsOutput = pexprLogical->DeriveOutputColumns();

src/test/regress/expected/groupingsets.out

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,25 @@ explain (costs off)
958958
Optimizer: Postgres query optimizer
959959
(13 rows)
960960

961+
-- HAVING with constant-false predicate on an empty grouping set must emit
962+
-- zero rows, not the default scalar-aggregate row.
963+
select count(*) from gstest2 group by grouping sets (()) having false;
964+
count
965+
-------
966+
(0 rows)
967+
968+
explain (costs off)
969+
select count(*) from gstest2 group by grouping sets (()) having false;
970+
QUERY PLAN
971+
-------------------------------------
972+
Aggregate
973+
Group Key: ()
974+
Filter: false
975+
-> Result
976+
One-Time Filter: false
977+
Optimizer: Postgres query optimizer
978+
(6 rows)
979+
961980
-- HAVING with GROUPING queries
962981
select ten, grouping(ten) from onek
963982
group by grouping sets(ten) having grouping(ten) >= 0

src/test/regress/expected/groupingsets_optimizer.out

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,22 @@ explain (costs off)
975975
Optimizer: GPORCA
976976
(13 rows)
977977

978+
-- HAVING with constant-false predicate on an empty grouping set must emit
979+
-- zero rows, not the default scalar-aggregate row.
980+
select count(*) from gstest2 group by grouping sets (()) having false;
981+
count
982+
-------
983+
(0 rows)
984+
985+
explain (costs off)
986+
select count(*) from gstest2 group by grouping sets (()) having false;
987+
QUERY PLAN
988+
--------------------------
989+
Result
990+
One-Time Filter: false
991+
Optimizer: GPORCA
992+
(3 rows)
993+
978994
-- HAVING with GROUPING queries
979995
select ten, grouping(ten) from onek
980996
group by grouping sets(ten) having grouping(ten) >= 0

src/test/regress/sql/groupingsets.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,12 @@ explain (costs off)
353353
select v.c, (select count(*) from gstest2 group by () having v.c)
354354
from (values (false),(true)) v(c) order by v.c;
355355

356+
-- HAVING with constant-false predicate on an empty grouping set must emit
357+
-- zero rows, not the default scalar-aggregate row.
358+
select count(*) from gstest2 group by grouping sets (()) having false;
359+
explain (costs off)
360+
select count(*) from gstest2 group by grouping sets (()) having false;
361+
356362
-- HAVING with GROUPING queries
357363
select ten, grouping(ten) from onek
358364
group by grouping sets(ten) having grouping(ten) >= 0

0 commit comments

Comments
 (0)