This is an automated email from the ASF dual-hosted git repository.
tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
The following commit(s) were added to refs/heads/master by this push:
new 1fa7afdb84 Use `try_new` when casting between structs to propagate
error (#5226)
1fa7afdb84 is described below
commit 1fa7afdb84857e10b8749be01b8fb3fae09baf93
Author: Liang-Chi Hsieh <[email protected]>
AuthorDate: Wed Dec 20 01:16:36 2023 -0800
Use `try_new` when casting between structs to propagate error (#5226)
* Consider nullability when casting between structs
* Fix
* Remove
---
arrow-cast/src/cast.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 81 insertions(+), 6 deletions(-)
diff --git a/arrow-cast/src/cast.rs b/arrow-cast/src/cast.rs
index 0775392b7d..92b9071a67 100644
--- a/arrow-cast/src/cast.rs
+++ b/arrow-cast/src/cast.rs
@@ -160,11 +160,13 @@ pub fn can_cast_types(from_type: &DataType, to_type:
&DataType) -> bool {
(Decimal128(_, _) | Decimal256(_, _), Utf8 | LargeUtf8) => true,
// Utf8 to decimal
(Utf8 | LargeUtf8, Decimal128(_, _) | Decimal256(_, _)) => true,
- (Struct(from_fields), Struct(to_fields)) => {
- from_fields.len() == to_fields.len() &&
-
from_fields.iter().zip(to_fields.iter()).all(|(f1, f2)| {
- can_cast_types(f1.data_type(),
f2.data_type())
- })
+ (Struct(from_fields), Struct(to_fields)) => {
+ from_fields.len() == to_fields.len() &&
+ from_fields.iter().zip(to_fields.iter()).all(|(f1, f2)| {
+ // Assume that nullability between two structs are
compatible, if not,
+ // cast kernel will return error.
+ can_cast_types(f1.data_type(), f2.data_type())
+ })
}
(Struct(_), _) => false,
(_, Struct(_)) => false,
@@ -1152,7 +1154,7 @@ pub fn cast_with_options(
.zip(to_fields.iter())
.map(|(l, field)| cast_with_options(l, field.data_type(),
cast_options))
.collect::<Result<Vec<ArrayRef>, ArrowError>>()?;
- let array = StructArray::new(to_fields.clone(), fields,
array.nulls().cloned());
+ let array = StructArray::try_new(to_fields.clone(), fields,
array.nulls().cloned())?;
Ok(Arc::new(array) as ArrayRef)
}
(Struct(_), _) => Err(ArrowError::CastError(
@@ -9525,4 +9527,77 @@ mod tests {
result.unwrap_err().to_string()
);
}
+
+ #[test]
+ fn test_cast_struct_to_struct_nullability() {
+ let boolean = Arc::new(BooleanArray::from(vec![false, false, true,
true]));
+ let int = Arc::new(Int32Array::from(vec![Some(42), None, Some(19),
None]));
+ let struct_array = StructArray::from(vec![
+ (
+ Arc::new(Field::new("b", DataType::Boolean, false)),
+ boolean.clone() as ArrayRef,
+ ),
+ (
+ Arc::new(Field::new("c", DataType::Int32, true)),
+ int.clone() as ArrayRef,
+ ),
+ ]);
+
+ // okay: nullable to nullable
+ let to_type = DataType::Struct(
+ vec![
+ Field::new("a", DataType::Utf8, false),
+ Field::new("b", DataType::Utf8, true),
+ ]
+ .into(),
+ );
+ cast(&struct_array, &to_type).expect("Cast nullable to nullable struct
field should work");
+
+ // error: nullable to non-nullable
+ let to_type = DataType::Struct(
+ vec![
+ Field::new("a", DataType::Utf8, false),
+ Field::new("b", DataType::Utf8, false),
+ ]
+ .into(),
+ );
+ cast(&struct_array, &to_type)
+ .expect_err("Cast nullable to non-nullable struct field should
fail");
+
+ let boolean = Arc::new(BooleanArray::from(vec![false, false, true,
true]));
+ let int = Arc::new(Int32Array::from(vec![i32::MAX, 25, 1, 100]));
+ let struct_array = StructArray::from(vec![
+ (
+ Arc::new(Field::new("b", DataType::Boolean, false)),
+ boolean.clone() as ArrayRef,
+ ),
+ (
+ Arc::new(Field::new("c", DataType::Int32, false)),
+ int.clone() as ArrayRef,
+ ),
+ ]);
+
+ // okay: non-nullable to non-nullable
+ let to_type = DataType::Struct(
+ vec![
+ Field::new("a", DataType::Utf8, false),
+ Field::new("b", DataType::Utf8, false),
+ ]
+ .into(),
+ );
+ cast(&struct_array, &to_type)
+ .expect("Cast non-nullable to non-nullable struct field should
work");
+
+ // err: non-nullable to non-nullable but overflowing return null
during casting
+ let to_type = DataType::Struct(
+ vec![
+ Field::new("a", DataType::Utf8, false),
+ Field::new("b", DataType::Int8, false),
+ ]
+ .into(),
+ );
+ cast(&struct_array, &to_type).expect_err(
+ "Cast non-nullable to non-nullable struct field returning null
should fail",
+ );
+ }
}