aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/executor/nodeAgg.c34
-rw-r--r--src/test/regress/expected/groupingsets.out55
-rw-r--r--src/test/regress/sql/groupingsets.sql12
3 files changed, 99 insertions, 2 deletions
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 2342ca30185..a9a1fd022a5 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -2231,13 +2231,43 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
/*
* initialize source tuple type.
*/
+ aggstate->ss.ps.outerops =
+ ExecGetResultSlotOps(outerPlanState(&aggstate->ss),
+ &aggstate->ss.ps.outeropsfixed);
+ aggstate->ss.ps.outeropsset = true;
+
ExecCreateScanSlotFromOuterPlan(estate, &aggstate->ss,
- ExecGetResultSlotOps(outerPlanState(&aggstate->ss), NULL));
+ aggstate->ss.ps.outerops);
scanDesc = aggstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
- if (node->chain)
+
+ /*
+ * If there are more than two phases (including a potential dummy phase
+ * 0), input will be resorted using tuplesort. Need a slot for that.
+ */
+ if (numPhases > 2)
+ {
aggstate->sort_slot = ExecInitExtraTupleSlot(estate, scanDesc,
&TTSOpsMinimalTuple);
+ /*
+ * The output of the tuplesort, and the output from the outer child
+ * might not use the same type of slot. In most cases the child will
+ * be a Sort, and thus return a TTSOpsMinimalTuple type slot - but the
+ * input can also be be presorted due an index, in which case it could
+ * be a different type of slot.
+ *
+ * XXX: For efficiency it would be good to instead/additionally
+ * generate expressions with corresponding settings of outerops* for
+ * the individual phases - deforming is often a bottleneck for
+ * aggregations with lots of rows per group. If there's multiple
+ * sorts, we know that all but the first use TTSOpsMinimalTuple (via
+ * the nodeAgg.c internal tuplesort).
+ */
+ if (aggstate->ss.ps.outeropsfixed &&
+ aggstate->ss.ps.outerops != &TTSOpsMinimalTuple)
+ aggstate->ss.ps.outeropsfixed = false;
+ }
+
/*
* Initialize result type, slot and projection.
*/
diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out
index 5d92b08d20a..146c54f5bf1 100644
--- a/src/test/regress/expected/groupingsets.out
+++ b/src/test/regress/expected/groupingsets.out
@@ -1360,6 +1360,61 @@ explain (costs off)
-> Function Scan on gstest_data
(10 rows)
+-- Verify that we correctly handle the child node returning a
+-- non-minimal slot, which happens if the input is pre-sorted,
+-- e.g. due to an index scan.
+BEGIN;
+SET LOCAL enable_hashagg = false;
+EXPLAIN SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b;
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Sort (cost=1.20..1.21 rows=5 width=24)
+ Sort Key: a, b
+ -> GroupAggregate (cost=1.03..1.14 rows=5 width=24)
+ Group Key: a
+ Group Key: ()
+ Sort Key: b
+ Group Key: b
+ -> Sort (cost=1.03..1.03 rows=2 width=8)
+ Sort Key: a
+ -> Seq Scan on gstest3 (cost=0.00..1.02 rows=2 width=8)
+(10 rows)
+
+SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b;
+ a | b | count | max | max
+---+---+-------+-----+-----
+ 1 | | 1 | 1 | 1
+ 2 | | 1 | 2 | 2
+ | 1 | 1 | 1 | 1
+ | 2 | 1 | 2 | 2
+ | | 2 | 2 | 2
+(5 rows)
+
+SET LOCAL enable_seqscan = false;
+EXPLAIN SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b;
+ QUERY PLAN
+-----------------------------------------------------------------------------------------
+ Sort (cost=12.32..12.33 rows=5 width=24)
+ Sort Key: a, b
+ -> GroupAggregate (cost=0.13..12.26 rows=5 width=24)
+ Group Key: a
+ Group Key: ()
+ Sort Key: b
+ Group Key: b
+ -> Index Scan using gstest3_pkey on gstest3 (cost=0.13..12.16 rows=2 width=8)
+(8 rows)
+
+SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b;
+ a | b | count | max | max
+---+---+-------+-----+-----
+ 1 | | 1 | 1 | 1
+ 2 | | 1 | 2 | 2
+ | 1 | 1 | 1 | 1
+ | 2 | 1 | 2 | 2
+ | | 2 | 2 | 2
+(5 rows)
+
+COMMIT;
-- More rescan tests
select * from (values (1),(2)) v(a) left join lateral (select v.a, four, ten, count(*) from onek group by cube(four,ten)) s on true order by v.a,four,ten;
a | a | four | ten | count
diff --git a/src/test/regress/sql/groupingsets.sql b/src/test/regress/sql/groupingsets.sql
index d8f78fcc000..2633fbf4284 100644
--- a/src/test/regress/sql/groupingsets.sql
+++ b/src/test/regress/sql/groupingsets.sql
@@ -384,6 +384,18 @@ explain (costs off)
from (values (1),(2)) v(x), gstest_data(v.x)
group by cube (a,b) order by a,b;
+-- Verify that we correctly handle the child node returning a
+-- non-minimal slot, which happens if the input is pre-sorted,
+-- e.g. due to an index scan.
+BEGIN;
+SET LOCAL enable_hashagg = false;
+EXPLAIN SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b;
+SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b;
+SET LOCAL enable_seqscan = false;
+EXPLAIN SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b;
+SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b;
+COMMIT;
+
-- More rescan tests
select * from (values (1),(2)) v(a) left join lateral (select v.a, four, ten, count(*) from onek group by cube(four,ten)) s on true order by v.a,four,ten;
select array(select row(v.a,s1.*) from (select two,four, count(*) from onek group by cube(two,four) order by two,four) s1) from (values (1),(2)) v(a);