]> granicus.if.org Git - postgresql/commitdiff
Repair logic for reordering grouping sets optimization.
authorAndrew Gierth <rhodiumtoad@postgresql.org>
Sun, 30 Jun 2019 22:49:25 +0000 (23:49 +0100)
committerAndrew Gierth <rhodiumtoad@postgresql.org>
Sun, 30 Jun 2019 22:49:25 +0000 (23:49 +0100)
The logic in reorder_grouping_sets to order grouping set elements to
match a pre-specified sort ordering was defective, resulting in
unnecessary sort nodes (though the query output would still be
correct). Repair, simplifying the code a little, and add a test.

Per report from Richard Guo, though I didn't use their patch. Original
bug seems to have been my fault.

Backpatch back to 9.5 where grouping sets were introduced.

Discussion: https://postgr.es/m/CAN_9JTzyjGcUjiBHxLsgqfk7PkdLGXiM=pwM+=ph2LsWw0WO1A@mail.gmail.com

src/backend/optimizer/plan/planner.c
src/test/regress/expected/groupingsets.out
src/test/regress/sql/groupingsets.sql

index 2eeaf8539df5b2b43e64c65e0bd80efde58a90fd..f604780490442d41903e3bd08c269d9618ab0293 100644 (file)
@@ -3255,7 +3255,6 @@ static List *
 reorder_grouping_sets(List *groupingsets, List *sortclause)
 {
        ListCell   *lc;
-       ListCell   *lc2;
        List       *previous = NIL;
        List       *result = NIL;
 
@@ -3265,35 +3264,33 @@ reorder_grouping_sets(List *groupingsets, List *sortclause)
                List       *new_elems = list_difference_int(candidate, previous);
                GroupingSetData *gs = makeNode(GroupingSetData);
 
-               if (list_length(new_elems) > 0)
+               while (list_length(sortclause) > list_length(previous) &&
+                          list_length(new_elems) > 0)
                {
-                       while (list_length(sortclause) > list_length(previous))
-                       {
-                               SortGroupClause *sc = list_nth(sortclause, list_length(previous));
-                               int                     ref = sc->tleSortGroupRef;
+                       SortGroupClause *sc = list_nth(sortclause, list_length(previous));
+                       int                     ref = sc->tleSortGroupRef;
 
-                               if (list_member_int(new_elems, ref))
-                               {
-                                       previous = lappend_int(previous, ref);
-                                       new_elems = list_delete_int(new_elems, ref);
-                               }
-                               else
-                               {
-                                       /* diverged from the sortclause; give up on it */
-                                       sortclause = NIL;
-                                       break;
-                               }
+                       if (list_member_int(new_elems, ref))
+                       {
+                               previous = lappend_int(previous, ref);
+                               new_elems = list_delete_int(new_elems, ref);
                        }
-
-                       foreach(lc2, new_elems)
+                       else
                        {
-                               previous = lappend_int(previous, lfirst_int(lc2));
+                               /* diverged from the sortclause; give up on it */
+                               sortclause = NIL;
+                               break;
                        }
                }
 
+               /*
+                * Safe to use list_concat (which shares cells of the second arg)
+                * because we know that new_elems does not share cells with anything.
+                */
+               previous = list_concat(previous, new_elems);
+
                gs->set = list_copy(previous);
                result = lcons(gs, result);
-               list_free(new_elems);
        }
 
        list_free(previous);
index 381ebce8a1e876b1d3fdf5babb95e7f473afa808..5d92b08d20ad4785a303accde3d5a585e109c0e3 100644 (file)
@@ -637,6 +637,19 @@ select a, b, sum(v.x)
    |   |   9
 (12 rows)
 
+-- Test reordering of grouping sets
+explain (costs off)
+select * from gstest1 group by grouping sets((a,b,v),(v)) order by v,b,a;
+                                  QUERY PLAN                                  
+------------------------------------------------------------------------------
+ GroupAggregate
+   Group Key: "*VALUES*".column3, "*VALUES*".column2, "*VALUES*".column1
+   Group Key: "*VALUES*".column3
+   ->  Sort
+         Sort Key: "*VALUES*".column3, "*VALUES*".column2, "*VALUES*".column1
+         ->  Values Scan on "*VALUES*"
+(6 rows)
+
 -- Agg level check. This query should error out.
 select (select grouping(a,b) from gstest2) from gstest2 group by a,b;
 ERROR:  arguments to GROUPING must be grouping expressions of the associated query level
index 5d6485913b3220954f0846ab43f4059d021842fe..d8f78fcc0006f05af84399693269e68498c5dedc 100644 (file)
@@ -213,6 +213,9 @@ select a, b, sum(v.x)
   from (values (1),(2)) v(x), gstest_data(v.x)
  group by cube (a,b) order by a,b;
 
+-- Test reordering of grouping sets
+explain (costs off)
+select * from gstest1 group by grouping sets((a,b,v),(v)) order by v,b,a;
 
 -- Agg level check. This query should error out.
 select (select grouping(a,b) from gstest2) from gstest2 group by a,b;