diff --git a/datafusion/expr-common/src/type_coercion/binary.rs b/datafusion/expr-common/src/type_coercion/binary.rs index 6d66b8b4df44..3fe62cc3c0b5 100644 --- a/datafusion/expr-common/src/type_coercion/binary.rs +++ b/datafusion/expr-common/src/type_coercion/binary.rs @@ -370,6 +370,8 @@ impl From<&DataType> for TypeCategory { /// align with the behavior of Postgres. Therefore, we've made slight adjustments to the rules /// to better match the behavior of both Postgres and DuckDB. For example, we expect adjusted /// decimal precision and scale when coercing decimal types. +/// +/// This function doesn't preserve correct field name and nullability for the nested data type, we only care about data type. pub fn type_union_resolution(data_types: &[DataType]) -> Option { if data_types.is_empty() { return None; @@ -476,6 +478,26 @@ fn type_union_resolution_coercion( type_union_resolution_coercion(lhs.data_type(), rhs.data_type()); new_item_type.map(|t| DataType::List(Arc::new(Field::new("item", t, true)))) } + (DataType::Struct(lhs), DataType::Struct(rhs)) => { + if lhs.len() != rhs.len() { + return None; + } + + let types = std::iter::zip(lhs.iter(), rhs.iter()) + .map(|(lhs, rhs)| { + type_union_resolution_coercion(lhs.data_type(), rhs.data_type()) + }) + .collect::>>()?; + + let fields = types + .into_iter() + .enumerate() + .map(|(i, datatype)| { + Arc::new(Field::new(format!("c{i}"), datatype, true)) + }) + .collect::>(); + Some(DataType::Struct(fields.into())) + } _ => { // numeric coercion is the same as comparison coercion, both find the narrowest type // that can accommodate both types diff --git a/datafusion/sqllogictest/test_files/struct.slt b/datafusion/sqllogictest/test_files/struct.slt index d2e7160d0010..5d8c6bf8ca51 100644 --- a/datafusion/sqllogictest/test_files/struct.slt +++ b/datafusion/sqllogictest/test_files/struct.slt @@ -373,3 +373,19 @@ You reached the bottom! statement ok drop view complex_view; + +statement ok +create table t(a struct, b struct) as values (struct('red', 1), struct('blue', 2.3)); + +query T +select arrow_typeof([a, b]) from t; +---- +List(Field { name: "item", data_type: Struct([Field { name: "c0", data_type: Utf8, nullable: true, dict_id: 0, dict_is_ordered: false, metadata: {} }, Field { name: "c1", data_type: Float32, nullable: true, dict_id: 0, dict_is_ordered: false, metadata: {} }]), nullable: true, dict_id: 0, dict_is_ordered: false, metadata: {} }) + +query ? +select [a, b] from t; +---- +[{c0: red, c1: 1.0}, {c0: blue, c1: 2.3}] + +statement ok +drop table t;