Commit aa8798c
committed
[SQL] Rewrite UNION ALL of aggregates as ROLLUP
Add an optimizer rule, RewriteUnionAggregateAsRollup, that detects a UNION ALL
of N aggregates whose GROUP BY clauses form a complete prefix-shrinking
hierarchy ([c1..cN], [c1..cN-1], ..., [c1], []) and rewrites it into a single
Aggregate with ROLLUP. Machine-generated SQL (BI tools, hand-translated
queries) commonly emits this hand-expanded shape; collapsing it removes the
redundant repeated scans and aggregations.
Two patterns are recognized:
- Inner-aggregate fold (CTE pattern): the branches fold a shared inner
aggregate -- sum-of-sum, max-of-max, min-of-min, or sum-of-count. The rewrite
rolls up OVER the inner aggregate, so the inner level's decimal width and
overflow behavior are reproduced exactly.
- Source-aggregate (direct pattern): each branch independently aggregates the
same source, so any deterministic aggregate is allowed (SUM, MAX, MIN, COUNT,
AVG, COUNT(DISTINCT), COLLECT_SET, FILTERed), provided every branch's
per-position measure signature is identical.
Correctness is preserved by reconstructing every rollup level from a single
reference branch and binding to the Union output by position, with conservative
guards that REJECT (never mis-apply) any branch the rewrite cannot reproduce
exactly: per-position measure identity (matched by inner-output name, stable
across CTE-copy ExprIds and coercion casts), grouping-key passthrough names,
per-position role layout, eval-mode/timezone/FILTER/DISTINCT signatures, and
value-changing projections above a branch aggregate. The grand-total
(GROUP BY ()) level is unioned back verbatim so empty-input semantics (a global
aggregate emits one row, a grouped ROLLUP zero) are preserved.
Gated by spark.sql.optimizer.unionAggregateToRollup.enabled (internal, default
true).
Adds catalyst unit tests and end-to-end equivalence tests (each firing query is
checked rule-on == rule-off) and regenerates the affected TPCDS v2_7 golden
files (q5a, q14a, q18a, q67a, q77a, q80a).1 parent cf2a6f5 commit aa8798c
31 files changed
Lines changed: 9356 additions & 7342 deletions
File tree
- sql
- catalyst/src
- main/scala/org/apache/spark/sql
- catalyst
- optimizer
- rules
- internal
- test/scala/org/apache/spark/sql/catalyst/optimizer
- core/src/test
- resources
- tpcds-plan-stability/approved-plans-v2_7
- q14a.sf100
- q14a
- q18a.sf100
- q18a
- q5a.sf100
- q5a
- q67a.sf100
- q67a
- q77a.sf100
- q77a
- q80a.sf100
- q80a
- tpcds-query-results/v2_7
- scala/org/apache/spark/sql
Lines changed: 2 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
224 | 224 | | |
225 | 225 | | |
226 | 226 | | |
227 | | - | |
| 227 | + | |
| 228 | + | |
228 | 229 | | |
229 | 230 | | |
230 | 231 | | |
| |||
Lines changed: 1599 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
183 | 183 | | |
184 | 184 | | |
185 | 185 | | |
| 186 | + | |
186 | 187 | | |
187 | 188 | | |
188 | 189 | | |
| |||
Lines changed: 12 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
595 | 595 | | |
596 | 596 | | |
597 | 597 | | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
598 | 610 | | |
599 | 611 | | |
600 | 612 | | |
| |||
Lines changed: 1715 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 362 additions & 627 deletions
Large diffs are not rendered by default.
Lines changed: 236 additions & 314 deletions
Large diffs are not rendered by default.
Lines changed: 356 additions & 621 deletions
Large diffs are not rendered by default.
Lines changed: 216 additions & 294 deletions
Large diffs are not rendered by default.
Lines changed: 241 additions & 624 deletions
Large diffs are not rendered by default.
0 commit comments