Skip to content

[SPARK-58927][SQL] Support hash-based aggregation for collated groupi… - #58196

Open
Shinoaki0145 wants to merge 3 commits into
apache:masterfrom
Shinoaki0145:SPARK-58927-Support-hash-based-aggregation
Open

[SPARK-58927][SQL] Support hash-based aggregation for collated groupi…#58196
Shinoaki0145 wants to merge 3 commits into
apache:masterfrom
Shinoaki0145:SPARK-58927-Support-hash-based-aggregation

Conversation

@Shinoaki0145

@Shinoaki0145 Shinoaki0145 commented Aug 21, 2026

Copy link
Copy Markdown

What changes were proposed in this pull request?

This PR mirrors the approach introduced in RewriteCollationJoin (SPARK-48000) for hash joins and introduces hash-based aggregation support for non-binary collated grouping keys:

  1. RewriteCollationAggregate Optimizer Rule:

    • Added a new Catalyst optimizer rule RewriteCollationAggregate registered under the Finish Analysis batch.
    • For Aggregate operators with non-binary-stable grouping keys (e.g. UTF8_LCASE, structs/arrays containing non-binary collated strings), it rewrites the grouping expressions using CollationKey.injectCollationKey(...) so grouping is performed on binary-stable collation keys.
    • Preserves original grouping key expressions referenced in the output/aggregate expressions by wrapping them in First(origExpr, ignoreNulls = false).toAggregateExpression(), properly aliased with the original exprId and qualifier.
  2. ObjectHashAggregate Planner Update:

    • Updated Aggregate.supportsObjectHashAggregate to support aggregations with non-mutable buffer schemas (such as those containing First(string) from collation rewrites) when grouping expressions are binary-stable, allowing these queries to plan as ObjectHashAggregateExec instead of falling back to SortAggregateExec.
  3. Feature Flag:

    • Added a new configuration spark.sql.collation.hashAggregation.enabled (default: true) to allow reverting to the previous sort-based behavior if necessary.

Why are the changes needed?

Currently, a GROUP BY (or any aggregation) on non-binary collated keys (e.g., UTF8_LCASE) is planned as SortAggregateExec because UnsafeRowUtils.isBinaryStable returns false for non-binary collations, preventing hash aggregation.

This forces a full-input sort and can cause heavy disk spilling on large datasets. While hash join already solves this by injecting CollationKey in RewriteCollationJoin, aggregations were not given equivalent optimization. Rewriting grouping keys to collation keys and routing to ObjectHashAggregateExec avoids the mandatory sort and disk spill, significantly improving query performance on collated data.

Does this PR introduce any user-facing change?

Yes:

  • Aggregations on non-binary collated columns will now be executed using ObjectHashAggregateExec instead of SortAggregateExec by default, dramatically improving execution speed without requiring manual casts to binary.
  • Aggregations with non-mutable buffer schemas on binary-stable grouping keys can now be planned as ObjectHashAggregateExec when spark.sql.execution.useObjectHashAggregateExec is enabled.

Users can disable the collation aggregation rewrite optimization and restore the legacy sort-based execution by setting:

SET spark.sql.collation.hashAggregation.enabled = false;

How was this patch tested?

  • Added/updated unit tests in org.apache.spark.sql.collation.CollationAggregationSuite:
    • Verified ObjectHashAggregateExec is selected and output results are correct for GROUP BY on UTF8_LCASE.
    • Verified spark.sql.collation.hashAggregation.enabled = false falls back to SortAggregateExec.
    • Verified compatibility with imperative aggregate functions (e.g. collect_list).

Was this patch authored or co-authored using generative AI tooling?

No.

Copilot AI lite review requested due to automatic review settings August 21, 2026 05:07
@Shinoaki0145
Shinoaki0145 force-pushed the SPARK-58927-Support-hash-based-aggregation branch from 9766096 to 0a82a42 Compare August 21, 2026 05:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Catalyst/planner support to enable hash-based aggregation for non-binary-stable collated grouping keys by rewriting grouping expressions to collation keys, with a SQLConf flag and accompanying test coverage.

Changes:

  • Introduces RewriteCollationAggregate (FinishAnalysis) to rewrite non-binary-stable grouping keys using CollationKey(...) and preserve original grouping outputs via First(...).
  • Updates Aggregate.supportsObjectHashAggregate to allow ObjectHashAggregateExec when grouping keys are binary-stable and the aggregate buffer schema is non-mutable.
  • Adds spark.sql.collation.hashAggregation.enabled (default true) and expands CollationAggregationSuite to validate the new planning behavior and the disable switch.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
sql/core/src/test/scala/org/apache/spark/sql/collation/CollationAggregationSuite.scala Updates tests to assert ObjectHashAggregateExec is chosen for collated grouping keys and that the feature flag falls back to sort aggregation.
sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala Adds a new SQLConf flag to enable/disable collation hash aggregation rewrites.
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/logical/basicLogicalOperators.scala Broadens supportsObjectHashAggregate to permit object-hash aggregation with non-mutable aggregate buffers when keys are binary-stable.
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/RewriteCollationAggregate.scala New optimizer rule implementing the collation-key rewrite for aggregates and preserving original grouping outputs.
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/Optimizer.scala Registers RewriteCollationAggregate in the FinishAnalysis rule set.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +75 to +85
val newAggregateExpressions = aggregateExpressions.map {
case a @ Alias(child, name) =>
val newChild = replaceGroupingKeyReferences(child)
if (!newChild.fastEquals(child)) {
Alias(newChild, name)(exprId = a.exprId, explicitMetadata = a.explicitMetadata)
} else {
a
}
case other =>
replaceGroupingKeyReferences(other).asInstanceOf[NamedExpression]
}
Comment on lines +97 to +111
/**
* Recursively process the expression in order to replace non-binary collated strings with their
* associated collation keys. This is necessary to ensure grouping is evaluated correctly for all
* types containing non-binary collated strings, including structs and arrays.
*/
private def processExpression(expr: Expression, dt: DataType): Expression = {
dt match {
// For binary stable expressions, no special handling is needed.
case _ if UnsafeRowUtils.isBinaryStable(dt) =>
expr

// Inject CollationKey for non-binary collated strings.
case _: StringType =>
CollationKey(expr)

Comment on lines +1340 to 1345
val schema = DataTypeUtils.fromAttributes(
aggregateExpressions.flatMap(_.aggregateFunction.aggBufferAttributes))
!isAggregateBufferMutable(schema) || aggregateExpressions.map(_.aggregateFunction).exists {
case _: TypedImperativeAggregate[_] => true
case _ => false
}
@Shinoaki0145 Shinoaki0145 changed the title [SPARK-57743][SQL] Support hash-based aggregation for collated groupi… [SPARK-58927][SQL] Support hash-based aggregation for collated groupi… Aug 21, 2026
@Shinoaki0145 Shinoaki0145 reopened this Aug 21, 2026

@uros-b uros-b left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might be duplicating #58083, @Shinoaki0145 PTAL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants