From f2dd8060b19344e37fde97d7b7322251b36ad7ed Mon Sep 17 00:00:00 2001 From: Fredrik Fornwall Date: Mon, 14 Sep 2026 01:39:40 +0200 Subject: [PATCH] fix: support zero-field struct group keys in aggregate emit `GROUP BY` / `SELECT DISTINCT` over a key containing a zero-field struct (`Struct()`) panicked when the aggregate emitted its groups. The row-format decode path rebuilt each struct column with `StructArray::try_new`, which arrow rejects for zero fields because it cannot infer the length from a child column. Use `StructArray::try_new_with_length`, passing the source array's length, so zero-field structs are rebuilt like any other struct. Signed-off-by: Fredrik Fornwall --- .../src/aggregates/group_values/row.rs | 4 +++- datafusion/sqllogictest/test_files/struct.slt | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/datafusion/physical-plan/src/aggregates/group_values/row.rs b/datafusion/physical-plan/src/aggregates/group_values/row.rs index 01e9f3eaa71ee..3953d4aa8b8b8 100644 --- a/datafusion/physical-plan/src/aggregates/group_values/row.rs +++ b/datafusion/physical-plan/src/aggregates/group_values/row.rs @@ -320,10 +320,12 @@ pub(crate) fn encode_array_if_necessary( }) .collect::>>()?; - Ok(Arc::new(StructArray::try_new( + // A zero-field struct has no child to infer the length from. + Ok(Arc::new(StructArray::try_new_with_length( expected_fields.clone(), arrays, struct_array.nulls().cloned(), + struct_array.len(), )?)) } (DataType::List(expected_field), &DataType::List(_)) => { diff --git a/datafusion/sqllogictest/test_files/struct.slt b/datafusion/sqllogictest/test_files/struct.slt index 183bfbd04e506..54aefca577590 100644 --- a/datafusion/sqllogictest/test_files/struct.slt +++ b/datafusion/sqllogictest/test_files/struct.slt @@ -1752,3 +1752,21 @@ limit 2; statement ok drop table list_cast_limit; + +# GROUP BY / DISTINCT over a zero-field struct key must not panic on emit +query ?I +select arrow_cast(null, 'Struct()') as s, count(*) from (values (1),(2),(3)) group by 1; +---- +NULL 3 + +query ? +select distinct arrow_cast(null, 'Struct()') as s from (values (1),(2)); +---- +NULL + +query ?I rowsort +select named_struct('a', column1, 'z', arrow_cast(null, 'Struct()')) as s, count(*) +from (values (1),(2),(1)) group by 1; +---- +{a: 1, z: NULL} 2 +{a: 2, z: NULL} 1