Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a6897ef

Browse files
committed
Fix overeager pushdown of HAVING clauses when grouping sets are used.
In 61444bf we started to allow HAVING clauses to be fully pushed down into WHERE, even when grouping sets are in use. That turns out not to work correctly, because grouping sets can "produce" NULLs, meaning that filtering in WHERE and HAVING can have different results, even when no aggregates or volatile functions are involved. Instead only allow pushdown of empty grouping sets. It'd be nice to do better, but the exact mechanics of deciding which cases are safe are still being debated. It's important to give correct results till we find a good solution, and such a solution might not be appropriate for backpatching anyway. Bug: #13863 Reported-By: 'wrb' Diagnosed-By: Dean Rasheed Author: Andrew Gierth Reviewed-By: Dean Rasheed and Andres Freund Discussion: 20160113183558.12989.56904@wrigleys.postgresql.org Backpatch: 9.5, where grouping sets were introduced
1 parent c477e84 commit a6897ef

File tree

3 files changed

+81
-8
lines changed

3 files changed

+81
-8
lines changed

src/backend/optimizer/plan/planner.c

+15-8
Original file line numberDiff line numberDiff line change
@@ -653,13 +653,19 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
653653
* In some cases we may want to transfer a HAVING clause into WHERE. We
654654
* cannot do so if the HAVING clause contains aggregates (obviously) or
655655
* volatile functions (since a HAVING clause is supposed to be executed
656-
* only once per group). Also, it may be that the clause is so expensive
657-
* to execute that we're better off doing it only once per group, despite
658-
* the loss of selectivity. This is hard to estimate short of doing the
659-
* entire planning process twice, so we use a heuristic: clauses
660-
* containing subplans are left in HAVING. Otherwise, we move or copy the
661-
* HAVING clause into WHERE, in hopes of eliminating tuples before
662-
* aggregation instead of after.
656+
* only once per group). We also can't do this if there are any nonempty
657+
* grouping sets; moving such a clause into WHERE would potentially change
658+
* the results, if any referenced column isn't present in all the grouping
659+
* sets. (If there are only empty grouping sets, then the HAVING clause
660+
* must be degenerate as discussed below.)
661+
*
662+
* Also, it may be that the clause is so expensive to execute that we're
663+
* better off doing it only once per group, despite the loss of
664+
* selectivity. This is hard to estimate short of doing the entire
665+
* planning process twice, so we use a heuristic: clauses containing
666+
* subplans are left in HAVING. Otherwise, we move or copy the HAVING
667+
* clause into WHERE, in hopes of eliminating tuples before aggregation
668+
* instead of after.
663669
*
664670
* If the query has explicit grouping then we can simply move such a
665671
* clause into WHERE; any group that fails the clause will not be in the
@@ -679,7 +685,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
679685
{
680686
Node *havingclause = (Node *) lfirst(l);
681687

682-
if (contain_agg_clause(havingclause) ||
688+
if ((parse->groupClause && parse->groupingSets) ||
689+
contain_agg_clause(havingclause) ||
683690
contain_volatile_functions(havingclause) ||
684691
contain_subplans(havingclause))
685692
{

src/test/regress/expected/groupingsets.out

+54
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,60 @@ having exists (select 1 from onek b where sum(distinct a.four) = b.four);
607607
9 | 3
608608
(25 rows)
609609

610+
-- Tests around pushdown of HAVING clauses, partially testing against previous bugs
611+
select a,count(*) from gstest2 group by rollup(a) order by a;
612+
a | count
613+
---+-------
614+
1 | 8
615+
2 | 1
616+
| 9
617+
(3 rows)
618+
619+
select a,count(*) from gstest2 group by rollup(a) having a is distinct from 1 order by a;
620+
a | count
621+
---+-------
622+
2 | 1
623+
| 9
624+
(2 rows)
625+
626+
explain (costs off)
627+
select a,count(*) from gstest2 group by rollup(a) having a is distinct from 1 order by a;
628+
QUERY PLAN
629+
----------------------------------
630+
GroupAggregate
631+
Group Key: a
632+
Group Key: ()
633+
Filter: (a IS DISTINCT FROM 1)
634+
-> Sort
635+
Sort Key: a
636+
-> Seq Scan on gstest2
637+
(7 rows)
638+
639+
select v.c, (select count(*) from gstest2 group by () having v.c)
640+
from (values (false),(true)) v(c) order by v.c;
641+
c | count
642+
---+-------
643+
f |
644+
t | 9
645+
(2 rows)
646+
647+
explain (costs off)
648+
select v.c, (select count(*) from gstest2 group by () having v.c)
649+
from (values (false),(true)) v(c) order by v.c;
650+
QUERY PLAN
651+
-----------------------------------------------------------
652+
Sort
653+
Sort Key: "*VALUES*".column1
654+
-> Values Scan on "*VALUES*"
655+
SubPlan 1
656+
-> Aggregate
657+
Group Key: ()
658+
Filter: "*VALUES*".column1
659+
-> Result
660+
One-Time Filter: "*VALUES*".column1
661+
-> Seq Scan on gstest2
662+
(10 rows)
663+
610664
-- HAVING with GROUPING queries
611665
select ten, grouping(ten) from onek
612666
group by grouping sets(ten) having grouping(ten) >= 0

src/test/regress/sql/groupingsets.sql

+12
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,18 @@ select ten, sum(distinct four) from onek a
183183
group by grouping sets((ten,four),(ten))
184184
having exists (select 1 from onek b where sum(distinct a.four) = b.four);
185185

186+
-- Tests around pushdown of HAVING clauses, partially testing against previous bugs
187+
select a,count(*) from gstest2 group by rollup(a) order by a;
188+
select a,count(*) from gstest2 group by rollup(a) having a is distinct from 1 order by a;
189+
explain (costs off)
190+
select a,count(*) from gstest2 group by rollup(a) having a is distinct from 1 order by a;
191+
192+
select v.c, (select count(*) from gstest2 group by () having v.c)
193+
from (values (false),(true)) v(c) order by v.c;
194+
explain (costs off)
195+
select v.c, (select count(*) from gstest2 group by () having v.c)
196+
from (values (false),(true)) v(c) order by v.c;
197+
186198
-- HAVING with GROUPING queries
187199
select ten, grouping(ten) from onek
188200
group by grouping sets(ten) having grouping(ten) >= 0

0 commit comments

Comments
 (0)