This is an automated email from the ASF dual-hosted git repository.

Jefffrey pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new c1f99af1dd Support concatenation of mixed FixedSizeBinary via 
`concat_elements_dyn` (#10222)
c1f99af1dd is described below

commit c1f99af1dd9e8b79546440d3f69ada217e0f70b4
Author: Pepijn Van Eeckhoudt <[email protected]>
AuthorDate: Sat Jun 27 23:49:02 2026 +0200

    Support concatenation of mixed FixedSizeBinary via `concat_elements_dyn` 
(#10222)
    
    # Which issue does this PR close?
    
    None; relates to https://github.com/apache/datafusion/pull/23211
    
    # Rationale for this change
    
    `concat_elements_fixed_size_binary` supports concatenation of
    `FixedSizeBinary(n)` and `FixedSizeBinary(m)`, but the guard clause in
    `concat_elements_dyn` prevents this from actually being possible with
    `dyn Array`.
    
    # What changes are included in this PR?
    
    Adjust the guard clause in `concat_elements_dyn` to allow concatenation
    of mixed `FixedSizeBinary` types.
    
    # Are these changes tested?
    
    - Added an extra test case for mixed `FixedSizeBinary` specifically
    - Adjusted the existing unit tests to use `concat_elements_dyn`. This
    maintains coverage of the functions that were being called (since
    they're still called indirectly) while increasing the coverage
    `concat_elements_dyn`
    
    # Are there any user-facing changes?
    
    Yes, the pre conditions of the function are relaxed. This should not be
    a breaking change.
---
 arrow-string/src/concat_elements.rs | 166 ++++++++++++++++++------------------
 1 file changed, 84 insertions(+), 82 deletions(-)

diff --git a/arrow-string/src/concat_elements.rs 
b/arrow-string/src/concat_elements.rs
index 8493dfafe0..8def0d5abb 100644
--- a/arrow-string/src/concat_elements.rs
+++ b/arrow-string/src/concat_elements.rs
@@ -402,23 +402,26 @@ pub fn concat_elements_string_view_array(
 
 /// Returns the elementwise concatenation of [`Array`]s.
 ///
+/// The output array will have the same type as the input arrays (which must 
have the same type).
+///
+/// Concatenation of `FixedSizeBinaryArray` instances with different sizes is 
supported. The output
+/// type is `FixedSizeBinaryArray` with the sum of the sizes of the two input 
arrays as size.
+///
 /// # Errors
 ///
 /// This function errors if the arrays are of different types.
 pub fn concat_elements_dyn(left: &dyn Array, right: &dyn Array) -> 
Result<ArrayRef, ArrowError> {
-    if left.data_type() != right.data_type() {
-        return Err(ArrowError::ComputeError(format!(
-            "Cannot concat arrays of different types: {} != {}",
-            left.data_type(),
-            right.data_type()
-        )));
-    }
     match (left.data_type(), right.data_type()) {
         (DataType::Utf8, DataType::Utf8) => {
             let left = left.as_any().downcast_ref::<StringArray>().unwrap();
             let right = right.as_any().downcast_ref::<StringArray>().unwrap();
             Ok(Arc::new(concat_elements_utf8(left, right)?))
         }
+        (DataType::Utf8View, DataType::Utf8View) => {
+            let left = 
left.as_any().downcast_ref::<StringViewArray>().unwrap();
+            let right = 
right.as_any().downcast_ref::<StringViewArray>().unwrap();
+            Ok(Arc::new(concat_elements_string_view_array(left, right)?))
+        }
         (DataType::LargeUtf8, DataType::LargeUtf8) => {
             let left = 
left.as_any().downcast_ref::<LargeStringArray>().unwrap();
             let right = 
right.as_any().downcast_ref::<LargeStringArray>().unwrap();
@@ -429,20 +432,15 @@ pub fn concat_elements_dyn(left: &dyn Array, right: &dyn 
Array) -> Result<ArrayR
             let right = right.as_any().downcast_ref::<BinaryArray>().unwrap();
             Ok(Arc::new(concat_element_binary(left, right)?))
         }
-        (DataType::LargeBinary, DataType::LargeBinary) => {
-            let left = 
left.as_any().downcast_ref::<LargeBinaryArray>().unwrap();
-            let right = 
right.as_any().downcast_ref::<LargeBinaryArray>().unwrap();
-            Ok(Arc::new(concat_element_binary(left, right)?))
-        }
         (DataType::BinaryView, DataType::BinaryView) => {
             let left = 
left.as_any().downcast_ref::<BinaryViewArray>().unwrap();
             let right = 
right.as_any().downcast_ref::<BinaryViewArray>().unwrap();
             Ok(Arc::new(concat_elements_binary_view_array(left, right)?))
         }
-        (DataType::Utf8View, DataType::Utf8View) => {
-            let left = 
left.as_any().downcast_ref::<StringViewArray>().unwrap();
-            let right = 
right.as_any().downcast_ref::<StringViewArray>().unwrap();
-            Ok(Arc::new(concat_elements_string_view_array(left, right)?))
+        (DataType::LargeBinary, DataType::LargeBinary) => {
+            let left = 
left.as_any().downcast_ref::<LargeBinaryArray>().unwrap();
+            let right = 
right.as_any().downcast_ref::<LargeBinaryArray>().unwrap();
+            Ok(Arc::new(concat_element_binary(left, right)?))
         }
         (DataType::FixedSizeBinary(_), DataType::FixedSizeBinary(_)) => {
             let left = left
@@ -456,16 +454,26 @@ pub fn concat_elements_dyn(left: &dyn Array, right: &dyn 
Array) -> Result<ArrayR
             Ok(Arc::new(concat_elements_fixed_size_binary(left, right)?))
         }
         // unimplemented
-        _ => Err(ArrowError::NotYetImplemented(format!(
-            "concat not supported for {}",
-            left.data_type()
-        ))),
+        (l, r) => {
+            if l != r {
+                Err(ArrowError::ComputeError(format!(
+                    "Cannot concat arrays of different types: {} != {}",
+                    l, r
+                )))
+            } else {
+                Err(ArrowError::NotYetImplemented(format!(
+                    "concat not supported for {}",
+                    left.data_type()
+                )))
+            }
+        }
     }
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
+    use arrow_array::cast::AsArray;
     use arrow_buffer::Buffer;
 
     #[test]
@@ -477,13 +485,13 @@ mod tests {
             .into_iter()
             .collect::<StringArray>();
 
-        let output = concat_elements_utf8(&left, &right).unwrap();
+        let output = concat_elements_dyn(&left, &right).unwrap();
 
         let expected = [None, Some("baryyy"), None]
             .into_iter()
             .collect::<StringArray>();
 
-        assert_eq!(output, expected);
+        assert_eq!(output.as_string(), &expected);
     }
 
     #[test]
@@ -495,13 +503,13 @@ mod tests {
             .into_iter()
             .collect::<StringArray>();
 
-        let output = concat_elements_utf8(&left, &right).unwrap();
+        let output = concat_elements_dyn(&left, &right).unwrap();
 
         let expected = [Some("foobaz"), Some(""), Some("bar")]
             .into_iter()
             .collect::<StringArray>();
 
-        assert_eq!(output, expected);
+        assert_eq!(output.as_string(), &expected);
     }
 
     #[test]
@@ -509,11 +517,11 @@ mod tests {
         let left = StringArray::from(vec!["foo", "bar"]);
         let right = StringArray::from(vec!["bar", "baz"]);
 
-        let output = concat_elements_utf8(&left, &right).unwrap();
+        let output = concat_elements_dyn(&left, &right).unwrap();
 
         let expected = StringArray::from(vec!["foobar", "barbaz"]);
 
-        assert_eq!(output, expected);
+        assert_eq!(output.as_string(), &expected);
     }
 
     #[test]
@@ -521,7 +529,7 @@ mod tests {
         let left = StringArray::from(vec!["foo", "bar"]);
         let right = StringArray::from(vec!["baz"]);
 
-        let output = concat_elements_utf8(&left, &right);
+        let output = concat_elements_dyn(&left, &right);
 
         assert_eq!(
             output.unwrap_err().to_string(),
@@ -536,7 +544,7 @@ mod tests {
 
         let left_slice = left.slice(0, 3);
         let right_slice = right.slice(1, 3);
-        let output = concat_elements_utf8(
+        let output = concat_elements_dyn(
             left_slice
                 .as_any()
                 .downcast_ref::<GenericStringArray<i32>>()
@@ -552,12 +560,12 @@ mod tests {
             .into_iter()
             .collect::<StringArray>();
 
-        assert_eq!(output, expected);
+        assert_eq!(output.as_string(), &expected);
 
         let left_slice = left.slice(2, 2);
         let right_slice = right.slice(1, 2);
 
-        let output = concat_elements_utf8(
+        let output = concat_elements_dyn(
             left_slice
                 .as_any()
                 .downcast_ref::<GenericStringArray<i32>>()
@@ -571,7 +579,7 @@ mod tests {
 
         let expected = [None, 
Some("bazfar")].into_iter().collect::<StringArray>();
 
-        assert_eq!(output, expected);
+        assert_eq!(output.as_string(), &expected);
     }
 
     #[test]
@@ -617,11 +625,26 @@ mod tests {
         let right = FixedSizeBinaryArray::try_from(vec![None, Some(b"yyy" as 
&[u8]), Some(b"zzz")])
             .unwrap();
 
-        let output = concat_elements_fixed_size_binary(&left, &right).unwrap();
+        let output = concat_elements_dyn(&left, &right).unwrap();
 
         let expected =
             FixedSizeBinaryArray::try_from(vec![None, Some(b"baryyy" as 
&[u8]), None]).unwrap();
-        assert_eq!(output, expected);
+        assert_eq!(output.as_fixed_size_binary(), &expected);
+    }
+
+    #[test]
+    fn test_mixed_fixed_size_binary_concat() {
+        let left =
+            FixedSizeBinaryArray::try_from(vec![Some(b"foobar" as &[u8]), 
Some(b"barbaz"), None])
+                .unwrap();
+        let right = FixedSizeBinaryArray::try_from(vec![None, Some(b"yyy" as 
&[u8]), Some(b"zzz")])
+            .unwrap();
+
+        let output = concat_elements_dyn(&left, &right).unwrap();
+
+        let expected =
+            FixedSizeBinaryArray::try_from(vec![None, Some(b"barbazyyy" as 
&[u8]), None]).unwrap();
+        assert_eq!(output.as_fixed_size_binary(), &expected);
     }
 
     #[test]
@@ -629,10 +652,10 @@ mod tests {
         let left = FixedSizeBinaryArray::try_from(vec![b"ab" as &[u8], 
b"cd"]).unwrap();
         let right = FixedSizeBinaryArray::try_from(vec![b"12" as &[u8], 
b"34"]).unwrap();
 
-        let output = concat_elements_fixed_size_binary(&left, &right).unwrap();
+        let output = concat_elements_dyn(&left, &right).unwrap();
 
         let expected = FixedSizeBinaryArray::try_from(vec![b"ab12" as &[u8], 
b"cd34"]).unwrap();
-        assert_eq!(output, expected);
+        assert_eq!(output.as_fixed_size_binary(), &expected);
     }
 
     #[test]
@@ -640,7 +663,7 @@ mod tests {
         let left = FixedSizeBinaryArray::try_from(vec![b"ab" as &[u8], 
b"cd"]).unwrap();
         let right = FixedSizeBinaryArray::try_from(vec![b"12" as 
&[u8]]).unwrap();
 
-        let output = concat_elements_fixed_size_binary(&left, &right);
+        let output = concat_elements_dyn(&left, &right);
         assert_eq!(
             output.unwrap_err().to_string(),
             "Compute error: Arrays must have the same length: 2 != 
1".to_string()
@@ -652,10 +675,10 @@ mod tests {
         let left = FixedSizeBinaryArray::new(0, Buffer::from(&[]), None);
         let right = FixedSizeBinaryArray::new(0, Buffer::from(&[]), None);
 
-        let output = concat_elements_fixed_size_binary(&left, &right).unwrap();
+        let output = concat_elements_dyn(&left, &right).unwrap();
 
         let expected = FixedSizeBinaryArray::new(0, Buffer::from(&[]), None);
-        assert_eq!(output, expected);
+        assert_eq!(output.as_fixed_size_binary(), &expected);
     }
 
     #[test]
@@ -716,7 +739,7 @@ mod tests {
             Some(long),
         ]);
 
-        let output = concat_elements_string_view_array(&left, &right).unwrap();
+        let output = concat_elements_dyn(&left, &right).unwrap();
 
         let expected = StringViewArray::from_iter(vec![
             None,
@@ -727,7 +750,7 @@ mod tests {
             Some("ThisStringIsLongerThan12Bytesbar"),
             Some("ThisStringIsLongerThan12BytesThisStringIsLongerThan12Bytes"),
         ]);
-        assert_eq!(output, expected);
+        assert_eq!(output.as_string_view(), &expected);
 
         let left = StringViewArray::from_iter(vec![
             Some("a"),
@@ -746,7 +769,7 @@ mod tests {
             Some(long),
         ]);
 
-        let output = concat_elements_string_view_array(&left, &right).unwrap();
+        let output = concat_elements_dyn(&left, &right).unwrap();
 
         let expected = StringViewArray::from_iter(vec![
             Some("ac"),
@@ -756,7 +779,7 @@ mod tests {
             Some("ThisStringIsLongerThan12Bytesd"),
             Some("ThisStringIsLongerThan12BytesThisStringIsLongerThan12Bytes"),
         ]);
-        assert_eq!(output, expected);
+        assert_eq!(output.as_string_view(), &expected);
     }
 
     #[test]
@@ -774,7 +797,7 @@ mod tests {
             Some(b""),
         ]);
 
-        let output = concat_elements_binary_view_array(&left, &right).unwrap();
+        let output = concat_elements_dyn(&left, &right).unwrap();
 
         let expected = BinaryViewArray::from_iter(vec![
             Some(b"foobar" as &[u8]),
@@ -782,7 +805,7 @@ mod tests {
             Some(b""),
             Some(b"baz"),
         ]);
-        assert_eq!(output, expected);
+        assert_eq!(output.as_binary_view(), &expected);
     }
 
     #[test]
@@ -790,7 +813,7 @@ mod tests {
         let left = BinaryViewArray::from_iter(vec![Some(b"foo" as &[u8]), 
Some(b"bar")]);
         let right = BinaryViewArray::from_iter(vec![Some(b"baz" as &[u8])]);
 
-        let output = concat_elements_binary_view_array(&left, &right);
+        let output = concat_elements_dyn(&left, &right);
         assert_eq!(
             output.unwrap_err().to_string(),
             "Compute error: Arrays must have the same length: 2 != 
1".to_string()
@@ -802,9 +825,9 @@ mod tests {
         let left = BinaryViewArray::from_iter(vec![] as Vec<Option<&[u8]>>);
         let right = BinaryViewArray::from_iter(vec![] as Vec<Option<&[u8]>>);
 
-        let output = concat_elements_binary_view_array(&left, &right).unwrap();
+        let output = concat_elements_dyn(&left, &right).unwrap();
         let expected = BinaryViewArray::from_iter(vec![] as 
Vec<Option<&[u8]>>);
-        assert_eq!(output, expected);
+        assert_eq!(output.as_binary_view(), &expected);
     }
 
     #[test]
@@ -813,43 +836,31 @@ mod tests {
         let left = StringArray::from(vec![Some("foo"), Some("bar"), None]);
         let right = StringArray::from(vec![None, Some("yyy"), Some("zzz")]);
 
-        let output: StringArray = concat_elements_dyn(&left, &right)
-            .unwrap()
-            .into_data()
-            .into();
+        let output = concat_elements_dyn(&left, &right).unwrap();
         let expected = StringArray::from(vec![None, Some("baryyy"), None]);
-        assert_eq!(output, expected);
+        assert_eq!(output.as_string(), &expected);
 
         // test for LargeStringArray
         let left = LargeStringArray::from(vec![Some("foo"), Some("bar"), 
None]);
         let right = LargeStringArray::from(vec![None, Some("yyy"), 
Some("zzz")]);
 
-        let output: LargeStringArray = concat_elements_dyn(&left, &right)
-            .unwrap()
-            .into_data()
-            .into();
+        let output = concat_elements_dyn(&left, &right).unwrap();
         let expected = LargeStringArray::from(vec![None, Some("baryyy"), 
None]);
-        assert_eq!(output, expected);
+        assert_eq!(output.as_string(), &expected);
 
         // test for BinaryArray
         let left = BinaryArray::from_opt_vec(vec![Some(b"foo"), Some(b"bar"), 
None]);
         let right = BinaryArray::from_opt_vec(vec![None, Some(b"yyy"), 
Some(b"zzz")]);
-        let output: BinaryArray = concat_elements_dyn(&left, &right)
-            .unwrap()
-            .into_data()
-            .into();
+        let output = concat_elements_dyn(&left, &right).unwrap();
         let expected = BinaryArray::from_opt_vec(vec![None, Some(b"baryyy"), 
None]);
-        assert_eq!(output, expected);
+        assert_eq!(output.as_binary(), &expected);
 
         // test for LargeBinaryArray
         let left = LargeBinaryArray::from_opt_vec(vec![Some(b"foo"), 
Some(b"bar"), None]);
         let right = LargeBinaryArray::from_opt_vec(vec![None, Some(b"yyy"), 
Some(b"zzz")]);
-        let output: LargeBinaryArray = concat_elements_dyn(&left, &right)
-            .unwrap()
-            .into_data()
-            .into();
+        let output = concat_elements_dyn(&left, &right).unwrap();
         let expected = LargeBinaryArray::from_opt_vec(vec![None, 
Some(b"baryyy"), None]);
-        assert_eq!(output, expected);
+        assert_eq!(output.as_binary(), &expected);
 
         // test for BinaryViewArray
         let long = b"ThisStringIsLongerThan12Bytes" as &[u8];
@@ -871,10 +882,7 @@ mod tests {
             Some(b"bar"),
             Some(long),
         ]);
-        let output: BinaryViewArray = concat_elements_dyn(&left, &right)
-            .unwrap()
-            .into_data()
-            .into();
+        let output = concat_elements_dyn(&left, &right).unwrap();
         let expected = BinaryViewArray::from_iter(vec![
             None,
             Some(b"baryyy" as &[u8]),
@@ -884,7 +892,7 @@ mod tests {
             Some(b"ThisStringIsLongerThan12Bytesbar"),
             
Some(b"ThisStringIsLongerThan12BytesThisStringIsLongerThan12Bytes"),
         ]);
-        assert_eq!(output, expected);
+        assert_eq!(output.as_binary_view(), &expected);
 
         // test for StringViewArray
         let long = "ThisStringIsLongerThan12Bytes";
@@ -906,10 +914,7 @@ mod tests {
             Some("bar"),
             Some(long),
         ]);
-        let output: StringViewArray = concat_elements_dyn(&left, &right)
-            .unwrap()
-            .into_data()
-            .into();
+        let output = concat_elements_dyn(&left, &right).unwrap();
         let expected = StringViewArray::from_iter(vec![
             None,
             Some("baryyy"),
@@ -919,20 +924,17 @@ mod tests {
             Some("ThisStringIsLongerThan12Bytesbar"),
             Some("ThisStringIsLongerThan12BytesThisStringIsLongerThan12Bytes"),
         ]);
-        assert_eq!(output, expected);
+        assert_eq!(output.as_string_view(), &expected);
 
         // test for FixedSizeBinaryArray
         let left = FixedSizeBinaryArray::try_from(vec![Some(b"foo" as &[u8]), 
Some(b"bar"), None])
             .unwrap();
         let right = FixedSizeBinaryArray::try_from(vec![None, Some(b"yyy" as 
&[u8]), Some(b"zzz")])
             .unwrap();
-        let output: FixedSizeBinaryArray = concat_elements_dyn(&left, &right)
-            .unwrap()
-            .into_data()
-            .into();
+        let output = concat_elements_dyn(&left, &right).unwrap();
         let expected =
             FixedSizeBinaryArray::try_from(vec![None, Some(b"baryyy" as 
&[u8]), None]).unwrap();
-        assert_eq!(output, expected);
+        assert_eq!(output.as_fixed_size_binary(), &expected);
     }
 
     #[test]

Reply via email to