You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
DataFusion's column-wise aggregation path has type-specialized GroupColumn implementations for many native scalar group-key types. Dictionary and run-end encoded (REE) scalar group keys do not currently have equivalent specialized builders, so they are routed through row-format storage and then cast/re-encoded when emitted.
Today the effective fallback path for these encoded types is:
arrow-array -> row format -> arrow-array -> cast/re-encode -> arrow-array
This path is correct, and must remain available for generic row-backed fallback cases. However, for top-level encoded scalar group keys it adds conversion work that native scalar group keys avoid.
Dictionary and REE encodings are commonly used to reduce memory and CPU for repeated values. Routing top-level encoded scalar group keys through row encoding and then casting/re-encoding preserves SQL semantics, but can reduce the benefit of those encodings in aggregation hot paths.
A specialized implementation could:
Avoid row-format round trips for supported encoded scalar group keys.
Avoid emit-time cast/re-encode work for common dictionary / REE cases.
Keep GroupValuesColumn semantics clearer: supported encoded scalar keys have explicit builders; row-backed fallback remains for generic nested cases.
Provide a focused place to optimize encoded-key equality, append, take_n, memory accounting, and null handling.
Invariant / desired behavior
For supported dictionary and run-end encoded group-key columns:
Equal logical values must receive the same group id.
Null values must compare equal to null values and unequal to non-null values.
Emitted group-key arrays must preserve the declared schema data type, including dictionary / REE wrappers, value type, nullability, and field metadata where applicable.
Hashing and equality must agree for every value accepted by the specialized builder.
GroupValuesColumn::supported_schema and make_group_column must stay in lockstep: every accepted type must have a valid GroupColumn implementation.
Existing GroupValuesRows and row-backed nested fallback semantics must not change.
Proposed direction
Add focused GroupColumn implementations for encoded scalar types. This is a behavior-preserving specialization / performance improvement, not a broad rewrite of the row fallback.
Suggested approach:
Start with top-level DictionaryArray<K> group keys.
Store group values in a representation that preserves logical dictionary semantics without row conversion.
Make equality by logical value, not by dictionary key or dictionary identity.
Preserve null semantics and output data type on build / take_n.
Add RunArray<R> / REE support separately unless the dictionary implementation naturally exposes a small shared abstraction.
Treat runs as an encoding of logical values, not as group identity.
Ensure slicing and run boundaries do not affect grouping correctness.
Wire each implementation through group_column_supported_type and make_group_column.
Keep the existing biconditional tests updated.
Do not route unrelated unsupported scalar types through GroupValuesColumn as part of this work.
Keep encode_array_if_necessary for row-format fallback paths.
Do not remove schema re-encoding from GroupValuesRows / RowsGroupColumn; nested and generic row paths still need it.
Scope
In
Add dictionary GroupColumn support for supported top-level scalar dictionary values.
Add REE GroupColumn support only if it stays small and clearly shares the same design; otherwise split it into a follow-up.
Preserve schema and logical equality semantics on append_val, vectorized_append, equal_to, vectorized_equal_to, build, and take_n.
Update group_column_supported_type / make_group_column invariants and tests.
Add focused unit tests and benchmark coverage for encoded group keys.
Out
Rewriting the generic row-backed nested fallback.
Removing encode_array_if_necessary from row-format emit paths.
Supporting unrelated unsupported scalar types such as Float16 or Decimal256.
Changing public SQL semantics for grouping dictionary or REE values.
Large Arrow row-format changes; upstream Arrow improvements can be referenced but should not block the DataFusion-local specialization unless required.
Acceptance criteria
GroupValuesColumn can use a specialized GroupColumn for accepted top-level dictionary group-key columns.
If REE is included, GroupValuesColumn can use a specialized GroupColumn for accepted top-level REE group-key columns.
Emitted group-key arrays from EmitTo::All and EmitTo::First(n) have the declared dictionary / REE data type.
Equal logical values with different dictionary keys or different dictionary arrays group together.
Nulls, slices, repeated values, and multiple intern batches behave identically to GroupValuesRows.
group_column_supported_type_matches_make_group_column or equivalent tests cover the newly supported encoded types.
Benchmarks include at least one encoded-key aggregation scenario and show no broad regression.
Tests / verification
Unit tests for dictionary keys:
No nulls / with nulls.
Repeated values.
Sliced arrays.
Multiple dictionaries representing the same logical values.
EmitTo::First(n) followed by EmitTo::All.
Mixed schema: primitive column plus dictionary column.
Unit tests for REE keys if implemented:
Runs crossing slice boundaries.
Null run values.
Duplicate logical values with different run layouts.
take_n preserving expected output type.
Cross-check grouping against GroupValuesRows using canonicalized group ids where ordering is not part of the contract.
Run targeted tests, for example:
cargo test -p datafusion-physical-plan --lib aggregates::group_values::multi_group_by
Run relevant benchmarks:
Existing dictionary benchmarks.
At least one mixed primitive + dictionary group-by benchmark.
Related PR
#23523
Problem
DataFusion's column-wise aggregation path has type-specialized
GroupColumnimplementations for many native scalar group-key types. Dictionary and run-end encoded (REE) scalar group keys do not currently have equivalent specialized builders, so they are routed through row-format storage and then cast/re-encoded when emitted.Today the effective fallback path for these encoded types is:
This path is correct, and must remain available for generic row-backed fallback cases. However, for top-level encoded scalar group keys it adds conversion work that native scalar group keys avoid.
Affected areas:
datafusion/physical-plan/src/aggregates/group_values/mod.rsdatafusion/physical-plan/src/aggregates/group_values/multi_group_by/mod.rsdatafusion/physical-plan/src/aggregates/group_values/multi_group_by/row_backed.rsdatafusion/physical-plan/src/aggregates/group_values/row.rsdictionary_group_valuesWhy it matters
Dictionary and REE encodings are commonly used to reduce memory and CPU for repeated values. Routing top-level encoded scalar group keys through row encoding and then casting/re-encoding preserves SQL semantics, but can reduce the benefit of those encodings in aggregation hot paths.
A specialized implementation could:
GroupValuesColumnsemantics clearer: supported encoded scalar keys have explicit builders; row-backed fallback remains for generic nested cases.take_n, memory accounting, and null handling.Invariant / desired behavior
For supported dictionary and run-end encoded group-key columns:
GroupValuesColumn::supported_schemaandmake_group_columnmust stay in lockstep: every accepted type must have a validGroupColumnimplementation.GroupValuesRowsand row-backed nested fallback semantics must not change.Proposed direction
Add focused
GroupColumnimplementations for encoded scalar types. This is a behavior-preserving specialization / performance improvement, not a broad rewrite of the row fallback.Suggested approach:
Start with top-level
DictionaryArray<K>group keys.build/take_n.Add
RunArray<R>/ REE support separately unless the dictionary implementation naturally exposes a small shared abstraction.Wire each implementation through
group_column_supported_typeandmake_group_column.GroupValuesColumnas part of this work.Keep
encode_array_if_necessaryfor row-format fallback paths.GroupValuesRows/RowsGroupColumn; nested and generic row paths still need it.Scope
In
GroupColumnsupport for supported top-level scalar dictionary values.GroupColumnsupport only if it stays small and clearly shares the same design; otherwise split it into a follow-up.append_val,vectorized_append,equal_to,vectorized_equal_to,build, andtake_n.group_column_supported_type/make_group_columninvariants and tests.Out
encode_array_if_necessaryfrom row-format emit paths.Float16orDecimal256.Acceptance criteria
GroupValuesColumncan use a specializedGroupColumnfor accepted top-level dictionary group-key columns.GroupValuesColumncan use a specializedGroupColumnfor accepted top-level REE group-key columns.EmitTo::AllandEmitTo::First(n)have the declared dictionary / REE data type.internbatches behave identically toGroupValuesRows.group_column_supported_type_matches_make_group_columnor equivalent tests cover the newly supported encoded types.Tests / verification
EmitTo::First(n)followed byEmitTo::All.take_npreserving expected output type.GroupValuesRowsusing canonicalized group ids where ordering is not part of the contract.cargo test -p datafusion-physical-plan --lib aggregates::group_values::multi_group_byNotes / open questions