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
feat(physical-plan): generic Rows-backed GroupColumn keeps mixed schemas on the column-wise path (#23523)
## Which issue does this PR close?
- Part of #22715 (nested type coverage in `GroupValuesColumn` EPIC)
- Alternative to #23128 (per-type approach) — implements the direction
@alamb proposed in
<#23128 (comment)>
- Step toward the terminal goal of retiring `GroupValuesRows` entirely
(#23404)
## Rationale for this change
Today `GroupValuesColumn` is **all-or-nothing**: a single nested column
in the GROUP BY key (\`Struct\`, \`List\`, \`FixedSizeList\`, …) makes
\`supported_schema\` return \`false\` and drops the *entire* aggregation
onto the row-wise \`GroupValuesRows\` fallback — even when every other
column would have qualified for the column-wise fast path. For a \`GROUP
BY int_col, struct_col\` shape, the \`int_col\` pays the row-encoded
storage cost for no reason.
## What changes are included in this PR?
Add \`RowsGroupColumn\`: a generic \`GroupColumn\` backed by a
single-field \`RowConverter\`, wired in as the nested-type dispatch arm
of \`group_column_supported_type\` / \`make_group_column\`. Native
columns keep their type-specialized builders; the nested column pays
row-encoding only for its one column.
Gated to \`data_type.is_nested()\` so intentionally excluded scalar
types (Float16, Decimal256) stay on \`GroupValuesRows\` and the
\`group_column_supported_type\` ⇔ \`make_group_column\` invariant holds.
## Impact
Memory, measured with 4000 groups of \`8 × Int64 + 1 ×
FixedSizeList<Int64, 4>\` in
\`mixed_schema_column_path_uses_less_memory_than_rows_fallback\`:
| | Bytes | vs baseline |
|-------------------------------------------------------|----------|-------------|
| \`GroupValuesRows\` (today's fallback) | 1096 KB | 100% |
| \`GroupValuesColumn\` + \`RowsGroupColumn\` fallback | 594 KB |
**54.2%** |
Speed: not benchmarked as a headline result — the wins come from native
columns keeping their type-specialized \`equal_to\`/\`append_val\` fast
paths instead of falling back to byte-encoded row comparisons.
## Are these changes tested?
Yes:
- Unit tests inside \`row_backed\`: FSL / Struct roundtrip, \`take_n\`,
\`supports_type\` matches \`RowConverter::supports_fields\`.
- \`mixed_schema_column_path_uses_less_memory_than_rows_fallback\`
(mod.rs): the 54.2% memory claim + identical group assignment vs
\`GroupValuesRows\`.
- \`nested_float_edge_cases_match_rows_fallback\`: nested \`-0.0\` /
\`NaN\` produce the same groupings as \`GroupValuesRows\` (the
correctness invariant to watch, since hashing runs on the raw column and
equality runs on the row bytes).
- \`multi_batch_and_emit_first_matches_rows_fallback\`: multi-batch
streaming intern + \`EmitTo::First\` + \`take_n\`.
All 39 tests in \`aggregates::group_values\` pass.
## Are there any user-facing changes?
No — internal aggregation representation only. Same query results, lower
memory footprint on mixed-schema GROUP BY keys.
## Follow-ups (out of scope)
- Add coverage for any type \`RowConverter\` cannot encode (currently
arrow-rs 59.x handles Map fine; \`supports_type\` delegates to
\`RowConverter::supports_fields\` so it auto-tracks upstream).
- Retire \`GroupValuesRows\` entirely once coverage is complete
(#23404).
0 commit comments