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

alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new beb3d5a735 Improve split_part udf by using a GenericStringBuilder 
(#12093)
beb3d5a735 is described below

commit beb3d5a735e9fe3036e65bfea3424750498d27ea
Author: Lordworms <[email protected]>
AuthorDate: Thu Aug 22 03:15:14 2024 -0700

    Improve split_part udf by using a GenericStringBuilder (#12093)
    
    * optimize code
    
    * optimize code
---
 datafusion/functions/src/string/split_part.rs | 206 ++++++++++++++++----------
 1 file changed, 126 insertions(+), 80 deletions(-)

diff --git a/datafusion/functions/src/string/split_part.rs 
b/datafusion/functions/src/string/split_part.rs
index 19721f0fad..8d292315a3 100644
--- a/datafusion/functions/src/string/split_part.rs
+++ b/datafusion/functions/src/string/split_part.rs
@@ -15,21 +15,23 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use std::any::Any;
-use std::sync::Arc;
-
-use arrow::array::{ArrayRef, GenericStringArray, OffsetSizeTrait};
-use arrow::datatypes::DataType;
-
-use datafusion_common::cast::{
-    as_generic_string_array, as_int64_array, as_string_view_array,
+use arrow::array::{
+    ArrayRef, GenericStringArray, Int64Array, OffsetSizeTrait, StringViewArray,
 };
-use datafusion_common::{exec_err, Result};
+use arrow::array::{AsArray, GenericStringBuilder};
+use arrow::datatypes::DataType;
+use datafusion_common::cast::as_int64_array;
+use datafusion_common::ScalarValue;
+use datafusion_common::{exec_err, DataFusionError, Result};
 use datafusion_expr::TypeSignature::*;
 use datafusion_expr::{ColumnarValue, Volatility};
 use datafusion_expr::{ScalarUDFImpl, Signature};
+use std::any::Any;
+use std::sync::Arc;
+
+use crate::utils::utf8_to_str_type;
 
-use crate::utils::{make_scalar_function, utf8_to_str_type};
+use super::common::StringArrayType;
 
 #[derive(Debug)]
 pub struct SplitPartFunc {
@@ -82,36 +84,121 @@ impl ScalarUDFImpl for SplitPartFunc {
     }
 
     fn invoke(&self, args: &[ColumnarValue]) -> Result<ColumnarValue> {
-        match (args[0].data_type(), args[1].data_type()) {
-            (
-                DataType::Utf8 | DataType::Utf8View,
-                DataType::Utf8 | DataType::Utf8View,
-            ) => make_scalar_function(split_part::<i32, i32>, vec![])(args),
+        // First, determine if any of the arguments is an Array
+        let len = args.iter().find_map(|arg| match arg {
+            ColumnarValue::Array(a) => Some(a.len()),
+            _ => None,
+        });
+
+        let inferred_length = len.unwrap_or(1);
+        let is_scalar = len.is_none();
+
+        // Convert all ColumnarValues to ArrayRefs
+        let args = args
+            .iter()
+            .map(|arg| match arg {
+                ColumnarValue::Scalar(scalar) => 
scalar.to_array_of_size(inferred_length),
+                ColumnarValue::Array(array) => Ok(Arc::clone(array)),
+            })
+            .collect::<Result<Vec<_>>>()?;
+
+        // Unpack the ArrayRefs from the arguments
+        let n_array = as_int64_array(&args[2])?;
+        let result = match (args[0].data_type(), args[1].data_type()) {
+            (DataType::Utf8View, DataType::Utf8View) => {
+                split_part_impl::<&StringViewArray, &StringViewArray, i32>(
+                    args[0].as_string_view(),
+                    args[1].as_string_view(),
+                    n_array,
+                )
+            }
+            (DataType::Utf8View, DataType::Utf8) => {
+                split_part_impl::<&StringViewArray, &GenericStringArray<i32>, 
i32>(
+                    args[0].as_string_view(),
+                    args[1].as_string::<i32>(),
+                    n_array,
+                )
+            }
+            (DataType::Utf8View, DataType::LargeUtf8) => {
+                split_part_impl::<&StringViewArray, &GenericStringArray<i64>, 
i32>(
+                    args[0].as_string_view(),
+                    args[1].as_string::<i64>(),
+                    n_array,
+                )
+            }
+            (DataType::Utf8, DataType::Utf8View) => {
+                split_part_impl::<&GenericStringArray<i32>, &StringViewArray, 
i32>(
+                    args[0].as_string::<i32>(),
+                    args[1].as_string_view(),
+                    n_array,
+                )
+            }
+            (DataType::LargeUtf8, DataType::Utf8View) => {
+                split_part_impl::<&GenericStringArray<i64>, &StringViewArray, 
i64>(
+                    args[0].as_string::<i64>(),
+                    args[1].as_string_view(),
+                    n_array,
+                )
+            }
+            (DataType::Utf8, DataType::Utf8) => {
+                split_part_impl::<&GenericStringArray<i32>, 
&GenericStringArray<i32>, i32>(
+                    args[0].as_string::<i32>(),
+                    args[1].as_string::<i32>(),
+                    n_array,
+                )
+            }
             (DataType::LargeUtf8, DataType::LargeUtf8) => {
-                make_scalar_function(split_part::<i64, i64>, vec![])(args)
+                split_part_impl::<&GenericStringArray<i64>, 
&GenericStringArray<i64>, i64>(
+                    args[0].as_string::<i64>(),
+                    args[1].as_string::<i64>(),
+                    n_array,
+                )
             }
-            (_, DataType::LargeUtf8) => {
-                make_scalar_function(split_part::<i32, i64>, vec![])(args)
+            (DataType::Utf8, DataType::LargeUtf8) => {
+                split_part_impl::<&GenericStringArray<i32>, 
&GenericStringArray<i64>, i32>(
+                    args[0].as_string::<i32>(),
+                    args[1].as_string::<i64>(),
+                    n_array,
+                )
             }
-            (DataType::LargeUtf8, _) => {
-                make_scalar_function(split_part::<i64, i32>, vec![])(args)
+            (DataType::LargeUtf8, DataType::Utf8) => {
+                split_part_impl::<&GenericStringArray<i64>, 
&GenericStringArray<i32>, i64>(
+                    args[0].as_string::<i64>(),
+                    args[1].as_string::<i32>(),
+                    n_array,
+                )
             }
-            (first_type, second_type) => exec_err!(
-                "unsupported first type {} and second type {} for split_part 
function",
-                first_type,
-                second_type
-            ),
+            _ => exec_err!("Unsupported combination of argument types for 
split_part"),
+        };
+        if is_scalar {
+            // If all inputs are scalar, keep the output as scalar
+            let result = result.and_then(|arr| 
ScalarValue::try_from_array(&arr, 0));
+            result.map(ColumnarValue::Scalar)
+        } else {
+            result.map(ColumnarValue::Array)
         }
     }
 }
 
-macro_rules! process_split_part {
-    ($string_array: expr, $delimiter_array: expr, $n_array: expr) => {{
-        let result = $string_array
-            .iter()
-            .zip($delimiter_array.iter())
-            .zip($n_array.iter())
-            .map(|((string, delimiter), n)| match (string, delimiter, n) {
+/// impl
+pub fn split_part_impl<'a, StringArrType, DelimiterArrType, StringArrayLen>(
+    string_array: StringArrType,
+    delimiter_array: DelimiterArrType,
+    n_array: &Int64Array,
+) -> Result<ArrayRef>
+where
+    StringArrType: StringArrayType<'a>,
+    DelimiterArrType: StringArrayType<'a>,
+    StringArrayLen: OffsetSizeTrait,
+{
+    let mut builder: GenericStringBuilder<StringArrayLen> = 
GenericStringBuilder::new();
+
+    string_array
+        .iter()
+        .zip(delimiter_array.iter())
+        .zip(n_array.iter())
+        .try_for_each(|((string, delimiter), n)| -> Result<(), 
DataFusionError> {
+            match (string, delimiter, n) {
                 (Some(string), Some(delimiter), Some(n)) => {
                     let split_string: Vec<&str> = 
string.split(delimiter).collect();
                     let len = split_string.len();
@@ -125,58 +212,17 @@ macro_rules! process_split_part {
                     } as usize;
 
                     if index < len {
-                        Ok(Some(split_string[index]))
+                        builder.append_value(split_string[index]);
                     } else {
-                        Ok(Some(""))
+                        builder.append_value("");
                     }
                 }
-                _ => Ok(None),
-            })
-            .collect::<Result<GenericStringArray<StringLen>>>()?;
-        Ok(Arc::new(result) as ArrayRef)
-    }};
-}
-
-/// Splits string at occurrences of delimiter and returns the n'th field 
(counting from one).
-/// split_part('abc~@~def~@~ghi', '~@~', 2) = 'def'
-fn split_part<StringLen: OffsetSizeTrait, DelimiterLen: OffsetSizeTrait>(
-    args: &[ArrayRef],
-) -> Result<ArrayRef> {
-    let n_array = as_int64_array(&args[2])?;
-    match (args[0].data_type(), args[1].data_type()) {
-        (DataType::Utf8View, _) => {
-            let string_array = as_string_view_array(&args[0])?;
-            match args[1].data_type() {
-                DataType::Utf8View => {
-                    let delimiter_array = as_string_view_array(&args[1])?;
-                    process_split_part!(string_array, delimiter_array, n_array)
-                }
-                _ => {
-                    let delimiter_array =
-                        as_generic_string_array::<DelimiterLen>(&args[1])?;
-                    process_split_part!(string_array, delimiter_array, n_array)
-                }
-            }
-        }
-        (_, DataType::Utf8View) => {
-            let delimiter_array = as_string_view_array(&args[1])?;
-            match args[0].data_type() {
-                DataType::Utf8View => {
-                    let string_array = as_string_view_array(&args[0])?;
-                    process_split_part!(string_array, delimiter_array, n_array)
-                }
-                _ => {
-                    let string_array = 
as_generic_string_array::<StringLen>(&args[0])?;
-                    process_split_part!(string_array, delimiter_array, n_array)
-                }
+                _ => builder.append_null(),
             }
-        }
-        (_, _) => {
-            let string_array = as_generic_string_array::<StringLen>(&args[0])?;
-            let delimiter_array = 
as_generic_string_array::<DelimiterLen>(&args[1])?;
-            process_split_part!(string_array, delimiter_array, n_array)
-        }
-    }
+            Ok(())
+        })?;
+
+    Ok(Arc::new(builder.finish()) as ArrayRef)
 }
 
 #[cfg(test)]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to