This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] 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 b79d3965b7 Use `concat_elements_dyn` from `arrow-rs` (#23211)
b79d3965b7 is described below
commit b79d3965b7a2c29853e83def04656408d9d7ee7f
Author: Pepijn Van Eeckhoudt <[email protected]>
AuthorDate: Fri Jul 10 17:21:24 2026 +0200
Use `concat_elements_dyn` from `arrow-rs` (#23211)
## Which issue does this PR close?
- Closes #23210
## Rationale for this change
https://github.com/apache/arrow-rs/pull/9876 added `ByteView` and
`FixedSizeBinary` support to `concat_elements_dyn` in `arrow-rs`. As a
consequence the extended implementation in DataFusion can now be
replaced by a call to the `arrow-rs` implementation.
## What changes are included in this PR?
- Remove the `kernels::concat_elements_utf8view` and
`kernels::concat_elements_binary_view_array`
- Replace implementation of `binary::concat_elements` with a call to
`arrow::compute::kernels::concat_elements::concat_elements_dyn`
## Are these changes tested?
- Usage is covered by existing tests
- The kernels themselves are tested in `arrow-rs`
## Are there any user-facing changes?
No, two `pub` functions have been removed from `kernel`, but `kernel`
itself is not `pub`.
---
datafusion/expr-common/src/type_coercion/binary.rs | 85 ++++++++++++---------
.../src/type_coercion/binary/tests/comparison.rs | 3 +-
.../src/type_coercion/binary/tests/mod.rs | 10 ++-
datafusion/physical-expr/src/expressions/binary.rs | 43 +----------
.../src/expressions/binary/kernels.rs | 89 ----------------------
datafusion/sqllogictest/test_files/binary.slt | 2 +-
6 files changed, 63 insertions(+), 169 deletions(-)
diff --git a/datafusion/expr-common/src/type_coercion/binary.rs
b/datafusion/expr-common/src/type_coercion/binary.rs
index 29bf1df9d3..c7a73a7c6c 100644
--- a/datafusion/expr-common/src/type_coercion/binary.rs
+++ b/datafusion/expr-common/src/type_coercion/binary.rs
@@ -237,7 +237,7 @@ impl<'a> BinaryTypeCoercer<'a> {
})
}
StringConcat => {
- string_concat_coercion(lhs,
rhs).map(Signature::uniform).ok_or_else(|| {
+ string_concat_coercion(lhs, rhs).ok_or_else(|| {
plan_datafusion_err!(
"Cannot infer common string type for string concat
operation {} {} {}", self.lhs, self.op, self.rhs
)
@@ -1629,42 +1629,55 @@ fn ree_coercion(
/// 1. At least one side of lhs and rhs should be string type (Utf8 /
LargeUtf8)
/// 2. Data type of the other side should be able to cast to string type
/// 3. Binary and string types cannot be mixed
-fn string_concat_coercion(lhs_type: &DataType, rhs_type: &DataType) ->
Option<DataType> {
+fn string_concat_coercion(lhs_type: &DataType, rhs_type: &DataType) ->
Option<Signature> {
use arrow::datatypes::DataType::*;
- string_coercion(lhs_type, rhs_type).or_else(|| match (lhs_type, rhs_type) {
- // Allow pure binary + binary
- (
- Binary | LargeBinary | BinaryView | FixedSizeBinary(_),
- Binary | LargeBinary | BinaryView | FixedSizeBinary(_),
- ) => {
- // Coerce fixed-sized binary to variable-sized `Binary` to make
uniform signature
- // with the `Binary` result
- let lhs_type = match lhs_type {
- FixedSizeBinary(_) => &Binary,
- val => val,
- };
- let rhs_type = match rhs_type {
- FixedSizeBinary(_) => &Binary,
- val => val,
- };
- binary_coercion(lhs_type, rhs_type)
- }
- // Predicate-based coercion rules are following,
- // including mixed binary + string combinations
- (Utf8View, from_type) | (from_type, Utf8View) => {
- string_concat_internal_coercion(from_type, &Utf8View)
- }
- (Utf8, from_type) | (from_type, Utf8) => {
- string_concat_internal_coercion(from_type, &Utf8)
- }
- (LargeUtf8, from_type) | (from_type, LargeUtf8) => {
- string_concat_internal_coercion(from_type, &LargeUtf8)
- }
- (Dictionary(_, lhs_value_type), Dictionary(_, rhs_value_type)) => {
- string_coercion(lhs_value_type, rhs_value_type).or(None)
- }
- _ => None,
- })
+
+ string_coercion(lhs_type, rhs_type)
+ .map(Signature::uniform)
+ .or_else(|| match (lhs_type, rhs_type) {
+ // Allow concatenation of mixed fixed size binary
+ (FixedSizeBinary(l), FixedSizeBinary(r)) => Some(Signature {
+ lhs: lhs_type.clone(),
+ rhs: rhs_type.clone(),
+ ret: FixedSizeBinary(l + r),
+ }),
+ // Allow pure binary + binary
+ (
+ Binary | LargeBinary | BinaryView | FixedSizeBinary(_),
+ Binary | LargeBinary | BinaryView | FixedSizeBinary(_),
+ ) => {
+ // Coerce fixed-sized binary to variable-sized `Binary` to
make uniform signature
+ // with the `Binary` result
+ let lhs_type = match lhs_type {
+ FixedSizeBinary(_) => &Binary,
+ val => val,
+ };
+ let rhs_type = match rhs_type {
+ FixedSizeBinary(_) => &Binary,
+ val => val,
+ };
+ binary_coercion(lhs_type, rhs_type).map(Signature::uniform)
+ }
+ // Predicate-based coercion rules are following,
+ // including mixed binary + string combinations
+ (Utf8View, from_type) | (from_type, Utf8View) => {
+ string_concat_internal_coercion(from_type, &Utf8View)
+ .map(Signature::uniform)
+ }
+ (Utf8, from_type) | (from_type, Utf8) => {
+ string_concat_internal_coercion(from_type,
&Utf8).map(Signature::uniform)
+ }
+ (LargeUtf8, from_type) | (from_type, LargeUtf8) => {
+ string_concat_internal_coercion(from_type, &LargeUtf8)
+ .map(Signature::uniform)
+ }
+ (Dictionary(_, lhs_value_type), Dictionary(_, rhs_value_type)) => {
+ string_coercion(lhs_value_type, rhs_value_type)
+ .or(None)
+ .map(Signature::uniform)
+ }
+ _ => None,
+ })
}
fn array_coercion(lhs_type: &DataType, rhs_type: &DataType) ->
Option<DataType> {
diff --git
a/datafusion/expr-common/src/type_coercion/binary/tests/comparison.rs
b/datafusion/expr-common/src/type_coercion/binary/tests/comparison.rs
index 2d7bf7cd12..5871f24e7f 100644
--- a/datafusion/expr-common/src/type_coercion/binary/tests/comparison.rs
+++ b/datafusion/expr-common/src/type_coercion/binary/tests/comparison.rs
@@ -971,7 +971,8 @@ fn test_string_concat_coercion() -> Result<()> {
DataType::FixedSizeBinary(4),
DataType::FixedSizeBinary(16),
Operator::StringConcat,
- DataType::Binary
+ DataType::FixedSizeBinary(4),
+ DataType::FixedSizeBinary(16)
);
test_coercion_binary_rule!(
DataType::FixedSizeBinary(4),
diff --git a/datafusion/expr-common/src/type_coercion/binary/tests/mod.rs
b/datafusion/expr-common/src/type_coercion/binary/tests/mod.rs
index e4653d4955..f771b10d93 100644
--- a/datafusion/expr-common/src/type_coercion/binary/tests/mod.rs
+++ b/datafusion/expr-common/src/type_coercion/binary/tests/mod.rs
@@ -27,12 +27,18 @@ use super::*;
/// - op: The binary operator (e.g., "+", "-", etc.)
/// - expected_type: The type both sides should be coerced to
macro_rules! test_coercion_binary_rule {
- ($LHS_TYPE:expr, $RHS_TYPE:expr, $OP:expr, $RESULT_TYPE:expr) => {{
+ ($LHS_TYPE:expr, $RHS_TYPE:expr, $OP:expr, $RESULT_TYPE:expr) => {
let (lhs, rhs) =
BinaryTypeCoercer::new(&$LHS_TYPE, &$OP,
&$RHS_TYPE).get_input_types()?;
assert_eq!(lhs, $RESULT_TYPE);
assert_eq!(rhs, $RESULT_TYPE);
- }};
+ };
+ ($LHS_TYPE:expr, $RHS_TYPE:expr, $OP:expr, $L_RESULT_TYPE:expr,
$R_RESULT_TYPE:expr) => {
+ let (lhs, rhs) =
+ BinaryTypeCoercer::new(&$LHS_TYPE, &$OP,
&$RHS_TYPE).get_input_types()?;
+ assert_eq!(lhs, $L_RESULT_TYPE);
+ assert_eq!(rhs, $R_RESULT_TYPE);
+ };
}
/// Tests that coercion for a binary operator between one type and multiple
right-hand side types
diff --git a/datafusion/physical-expr/src/expressions/binary.rs
b/datafusion/physical-expr/src/expressions/binary.rs
index 6f0b60556a..7945cbbe00 100644
--- a/datafusion/physical-expr/src/expressions/binary.rs
+++ b/datafusion/physical-expr/src/expressions/binary.rs
@@ -24,9 +24,7 @@ use std::sync::Arc;
use arrow::array::*;
use arrow::compute::kernels::boolean::{and_kleene, or_kleene};
-use arrow::compute::kernels::concat_elements::{
- concat_element_binary, concat_elements_utf8,
-};
+use arrow::compute::kernels::concat_elements::concat_elements_dyn;
use arrow::compute::{SlicesIterator, cast, filter_record_batch};
use arrow::datatypes::*;
use arrow::error::ArrowError;
@@ -50,8 +48,7 @@ use kernels::{
bitwise_and_dyn, bitwise_and_dyn_scalar, bitwise_or_dyn,
bitwise_or_dyn_scalar,
bitwise_shift_left_dyn, bitwise_shift_left_dyn_scalar,
bitwise_shift_right_dyn,
bitwise_shift_right_dyn_scalar, bitwise_xor_dyn, bitwise_xor_dyn_scalar,
- concat_elements_binary_view_array, concat_elements_utf8view,
regex_match_dyn,
- regex_match_dyn_scalar,
+ regex_match_dyn, regex_match_dyn_scalar,
};
/// Binary expression
@@ -833,7 +830,7 @@ impl BinaryExpr {
BitwiseXor => bitwise_xor_dyn(left, right),
BitwiseShiftRight => bitwise_shift_right_dyn(left, right),
BitwiseShiftLeft => bitwise_shift_left_dyn(left, right),
- StringConcat => concat_elements(&left, &right),
+ StringConcat => concat_elements_dyn(&left, &right).map_err(|e|
e.into()),
AtArrow | ArrowAt | Arrow | LongArrow | HashArrow | HashLongArrow
| AtAt
| HashMinus | AtQuestion | Question | QuestionAnd | QuestionPipe
| IntegerDivide | Colon => {
@@ -1053,40 +1050,6 @@ fn pre_selection_scatter(
Ok(ColumnarValue::Array(Arc::new(boolean_result)))
}
-fn concat_elements(left: &ArrayRef, right: &ArrayRef) -> Result<ArrayRef> {
- Ok(match left.data_type() {
- DataType::Utf8 => Arc::new(concat_elements_utf8(
- left.as_string::<i32>(),
- right.as_string::<i32>(),
- )?),
- DataType::LargeUtf8 => Arc::new(concat_elements_utf8(
- left.as_string::<i64>(),
- right.as_string::<i64>(),
- )?),
- DataType::Utf8View => Arc::new(concat_elements_utf8view(
- left.as_string_view(),
- right.as_string_view(),
- )?),
- DataType::Binary => Arc::new(concat_element_binary::<i32>(
- left.as_binary(),
- right.as_binary(),
- )?),
- DataType::LargeBinary => Arc::new(concat_element_binary::<i64>(
- left.as_binary(),
- right.as_binary(),
- )?),
- DataType::BinaryView => Arc::new(concat_elements_binary_view_array(
- left.as_binary_view(),
- right.as_binary_view(),
- )?),
- other => {
- return internal_err!(
- "Data type {other:?} not supported for binary operation
'concat_elements' on string arrays"
- );
- }
- })
-}
-
/// Create a binary expression whose arguments are correctly coerced.
/// This function errors if it is not possible to coerce the arguments
/// to computational types supported by the operator.
diff --git a/datafusion/physical-expr/src/expressions/binary/kernels.rs
b/datafusion/physical-expr/src/expressions/binary/kernels.rs
index e573d7ece2..39e9c40dbd 100644
--- a/datafusion/physical-expr/src/expressions/binary/kernels.rs
+++ b/datafusion/physical-expr/src/expressions/binary/kernels.rs
@@ -18,7 +18,6 @@
//! This module contains computation kernels that are specific to
//! datafusion and not (yet) targeted to port upstream to arrow
use arrow::array::*;
-use arrow::buffer::{MutableBuffer, NullBuffer};
use arrow::compute::kernels::bitwise::{
bitwise_and, bitwise_and_scalar, bitwise_or, bitwise_or_scalar,
bitwise_shift_left,
bitwise_shift_left_scalar, bitwise_shift_right, bitwise_shift_right_scalar,
@@ -27,7 +26,6 @@ use arrow::compute::kernels::bitwise::{
use arrow::compute::kernels::boolean::not;
use arrow::compute::kernels::comparison::{regexp_is_match,
regexp_is_match_scalar};
use arrow::datatypes::DataType;
-use arrow::error::ArrowError;
use datafusion_common::{Result, ScalarValue};
use datafusion_common::{internal_err, plan_err};
@@ -161,93 +159,6 @@ create_left_integral_dyn_scalar_kernel!(
bitwise_shift_left_scalar
);
-/// Concatenates two `StringViewArray`s element-wise.
-/// If either element is `Null`, the result element is also `Null`.
-///
-/// # Errors
-/// - Returns an error if the input arrays have different lengths.
-/// - Returns an error if any concatenated string exceeds `u32::MAX` (≈4 GB)
in length.
-pub fn concat_elements_utf8view(
- left: &StringViewArray,
- right: &StringViewArray,
-) -> std::result::Result<StringViewArray, ArrowError> {
- if left.len() != right.len() {
- return Err(ArrowError::ComputeError(format!(
- "Arrays must have the same length: {} != {}",
- left.len(),
- right.len()
- )));
- }
- let mut result = StringViewBuilder::with_capacity(left.len());
-
- // Avoid reallocations by writing to a reused buffer (note we could be even
- // more efficient by creating the view directly here and avoid the buffer
- // but that would be more complex)
- let mut buffer = String::new();
-
- // Pre-compute combined null bitmap, so the per-row NULL check is more
- // efficient
- let nulls = NullBuffer::union(left.nulls(), right.nulls());
-
- for i in 0..left.len() {
- if nulls.as_ref().is_some_and(|n| n.is_null(i)) {
- result.append_null();
- } else {
- let l = left.value(i);
- let r = right.value(i);
- buffer.clear();
- buffer.push_str(l);
- buffer.push_str(r);
- result.try_append_value(&buffer)?;
- }
- }
- Ok(result.finish())
-}
-
-/// Concatenates two `BinaryViewArray`s element-wise.
-/// If either element is `Null`, the result element is also `Null`.
-///
-/// # Errors
-/// - Returns an error if the input arrays have different lengths.
-/// - Returns an error if any concatenated string exceeds `u32::MAX` in length.
-pub fn concat_elements_binary_view_array(
- left: &BinaryViewArray,
- right: &BinaryViewArray,
-) -> std::result::Result<BinaryViewArray, ArrowError> {
- if left.len() != right.len() {
- return Err(ArrowError::ComputeError(format!(
- "Arrays must have the same length: {} != {}",
- left.len(),
- right.len()
- )));
- }
- let mut result = BinaryViewBuilder::with_capacity(left.len());
-
- // Avoid reallocations by writing to a reused buffer (note we could be even
- // more efficient by creating the view directly here and avoid the buffer
- // but that would be more complex)
- let mut buffer = MutableBuffer::new(0);
-
- // Pre-compute combined null bitmap, so the per-row NULL check is more
- // efficient
- let nulls = NullBuffer::union(left.nulls(), right.nulls());
-
- for i in 0..left.len() {
- if nulls.as_ref().is_some_and(|n| n.is_null(i)) {
- result.append_null();
- } else {
- let l = left.value(i);
- let r = right.value(i);
- buffer.clear();
- buffer.extend_from_slice(l);
- buffer.extend_from_slice(r);
- // No try-version of append_value
- result.try_append_value(&buffer)?;
- }
- }
- Ok(result.finish())
-}
-
/// Invoke a compute kernel on a pair of binary data arrays with flags
macro_rules! regexp_is_match_flag {
($LEFT:expr, $RIGHT:expr, $ARRAYTYPE:ident, $NOT:expr, $FLAG:expr) => {{
diff --git a/datafusion/sqllogictest/test_files/binary.slt
b/datafusion/sqllogictest/test_files/binary.slt
index 64672ec90c..94c1365cb9 100644
--- a/datafusion/sqllogictest/test_files/binary.slt
+++ b/datafusion/sqllogictest/test_files/binary.slt
@@ -347,7 +347,7 @@ SELECT x'636166c3a9' || arrow_cast(x'68656c6c6f',
'FixedSizeBinary(5)'), arrow_t
query ?T
SELECT arrow_cast(x'6361', 'FixedSizeBinary(2)') || arrow_cast(x'68656c6c6f',
'FixedSizeBinary(5)'), arrow_typeof(arrow_cast(x'6361', 'FixedSizeBinary(2)')
|| arrow_cast(x'68656c6c6f', 'FixedSizeBinary(5)'));
----
-636168656c6c6f Binary
+636168656c6c6f FixedSizeBinary(7)
# Byte pipe operator is allowed for mixed binary and text
query T
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]