Skip to content

Commit 940d4eb

Browse files
liukun4515alamb
andauthored
suppport bitwise and as an example (#1653)
* suppport bitwise and as an example * Use $OP in macro rather than `&` * fix: change signature to &dyn Array * fmt Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
1 parent cfb655d commit 940d4eb

File tree

4 files changed

+158
-0
lines changed

4 files changed

+158
-0
lines changed

datafusion/src/logical_plan/operators.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ pub enum Operator {
6464
RegexNotMatch,
6565
/// Case insensitive regex not match
6666
RegexNotIMatch,
67+
/// Bitwise and, like `&`
68+
BitwiseAnd,
6769
}
6870

6971
impl fmt::Display for Operator {
@@ -90,6 +92,7 @@ impl fmt::Display for Operator {
9092
Operator::RegexNotIMatch => "!~*",
9193
Operator::IsDistinctFrom => "IS DISTINCT FROM",
9294
Operator::IsNotDistinctFrom => "IS NOT DISTINCT FROM",
95+
Operator::BitwiseAnd => "&",
9396
};
9497
write!(f, "{}", display)
9598
}

datafusion/src/physical_plan/coercion_rule/binary_rule.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub(crate) fn coerce_types(
3131
) -> Result<DataType> {
3232
// This result MUST be compatible with `binary_coerce`
3333
let result = match op {
34+
Operator::BitwiseAnd => bitwise_coercion(lhs_type, rhs_type),
3435
Operator::And | Operator::Or => match (lhs_type, rhs_type) {
3536
// logical binary boolean operators can only be evaluated in bools
3637
(DataType::Boolean, DataType::Boolean) => Some(DataType::Boolean),
@@ -72,6 +73,25 @@ pub(crate) fn coerce_types(
7273
}
7374
}
7475

76+
fn bitwise_coercion(left_type: &DataType, right_type: &DataType) -> Option<DataType> {
77+
use arrow::datatypes::DataType::*;
78+
79+
if !is_numeric(left_type) || !is_numeric(right_type) {
80+
return None;
81+
}
82+
if left_type == right_type && !is_dictionary(left_type) {
83+
return Some(left_type.clone());
84+
}
85+
// TODO support other data type
86+
match (left_type, right_type) {
87+
(Int64, _) | (_, Int64) => Some(Int64),
88+
(Int32, _) | (_, Int32) => Some(Int32),
89+
(Int16, _) | (_, Int16) => Some(Int16),
90+
(Int8, _) | (_, Int8) => Some(Int8),
91+
_ => None,
92+
}
93+
}
94+
7595
fn comparison_eq_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
7696
// can't compare dictionaries directly due to
7797
// https://github.com/apache/arrow-rs/issues/1201

datafusion/src/physical_plan/expressions/binary.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,103 @@ fn modulus_decimal(left: &DecimalArray, right: &DecimalArray) -> Result<DecimalA
348348
Ok(decimal_builder.finish())
349349
}
350350

351+
/// The binary_bitwise_array_op macro only evaluates for integer types
352+
/// like int64, int32.
353+
/// It is used to do bitwise operation.
354+
macro_rules! binary_bitwise_array_op {
355+
($LEFT:expr, $RIGHT:expr, $OP:tt, $ARRAY_TYPE:ident, $TYPE:ty) => {{
356+
let len = $LEFT.len();
357+
let left = $LEFT.as_any().downcast_ref::<$ARRAY_TYPE>().unwrap();
358+
let right = $RIGHT.as_any().downcast_ref::<$ARRAY_TYPE>().unwrap();
359+
let result = (0..len)
360+
.into_iter()
361+
.map(|i| {
362+
if left.is_null(i) || right.is_null(i) {
363+
None
364+
} else {
365+
Some(left.value(i) $OP right.value(i))
366+
}
367+
})
368+
.collect::<$ARRAY_TYPE>();
369+
Ok(Arc::new(result))
370+
}};
371+
}
372+
373+
/// The binary_bitwise_array_op macro only evaluates for integer types
374+
/// like int64, int32.
375+
/// It is used to do bitwise operation on an array with a scalar.
376+
macro_rules! binary_bitwise_array_scalar {
377+
($LEFT:expr, $RIGHT:expr, $OP:tt, $ARRAY_TYPE:ident, $TYPE:ty) => {{
378+
let len = $LEFT.len();
379+
let array = $LEFT.as_any().downcast_ref::<$ARRAY_TYPE>().unwrap();
380+
let scalar = $RIGHT;
381+
if scalar.is_null() {
382+
Ok(new_null_array(array.data_type(), len))
383+
} else {
384+
let right: $TYPE = scalar.try_into().unwrap();
385+
let result = (0..len)
386+
.into_iter()
387+
.map(|i| {
388+
if array.is_null(i) {
389+
None
390+
} else {
391+
Some(array.value(i) $OP right)
392+
}
393+
})
394+
.collect::<$ARRAY_TYPE>();
395+
Ok(Arc::new(result) as ArrayRef)
396+
}
397+
}};
398+
}
399+
400+
fn bitwise_and(left: ArrayRef, right: ArrayRef) -> Result<ArrayRef> {
401+
match &left.data_type() {
402+
DataType::Int8 => {
403+
binary_bitwise_array_op!(left, right, &, Int8Array, i8)
404+
}
405+
DataType::Int16 => {
406+
binary_bitwise_array_op!(left, right, &, Int16Array, i16)
407+
}
408+
DataType::Int32 => {
409+
binary_bitwise_array_op!(left, right, &, Int32Array, i32)
410+
}
411+
DataType::Int64 => {
412+
binary_bitwise_array_op!(left, right, &, Int64Array, i64)
413+
}
414+
other => Err(DataFusionError::Internal(format!(
415+
"Data type {:?} not supported for binary operation '{}' on dyn arrays",
416+
other,
417+
Operator::BitwiseAnd
418+
))),
419+
}
420+
}
421+
422+
fn bitwise_and_scalar(
423+
array: &dyn Array,
424+
scalar: ScalarValue,
425+
) -> Option<Result<ArrayRef>> {
426+
let result = match array.data_type() {
427+
DataType::Int8 => {
428+
binary_bitwise_array_scalar!(array, scalar, &, Int8Array, i8)
429+
}
430+
DataType::Int16 => {
431+
binary_bitwise_array_scalar!(array, scalar, &, Int16Array, i16)
432+
}
433+
DataType::Int32 => {
434+
binary_bitwise_array_scalar!(array, scalar, &, Int32Array, i32)
435+
}
436+
DataType::Int64 => {
437+
binary_bitwise_array_scalar!(array, scalar, &, Int64Array, i64)
438+
}
439+
other => Err(DataFusionError::Internal(format!(
440+
"Data type {:?} not supported for binary operation '{}' on dyn arrays",
441+
other,
442+
Operator::BitwiseAnd
443+
))),
444+
};
445+
Some(result)
446+
}
447+
351448
/// Binary expression
352449
#[derive(Debug)]
353450
pub struct BinaryExpr {
@@ -880,6 +977,8 @@ pub fn binary_operator_data_type(
880977
| Operator::RegexNotIMatch
881978
| Operator::IsDistinctFrom
882979
| Operator::IsNotDistinctFrom => Ok(DataType::Boolean),
980+
// bitwise operations return the common coerced type
981+
Operator::BitwiseAnd => Ok(result_type),
883982
// math operations return the same value as the common coerced type
884983
Operator::Plus
885984
| Operator::Minus
@@ -1055,6 +1154,7 @@ impl BinaryExpr {
10551154
true,
10561155
true
10571156
),
1157+
Operator::BitwiseAnd => bitwise_and_scalar(array, scalar.clone()),
10581158
// if scalar operation is not supported - fallback to array implementation
10591159
_ => None,
10601160
};
@@ -1143,6 +1243,7 @@ impl BinaryExpr {
11431243
Operator::RegexNotIMatch => {
11441244
binary_string_array_flag_op!(left, right, regexp_is_match, true, true)
11451245
}
1246+
Operator::BitwiseAnd => bitwise_and(left, right),
11461247
}
11471248
}
11481249
}
@@ -1580,6 +1681,18 @@ mod tests {
15801681
DataType::Boolean,
15811682
vec![false, false, false, false, true]
15821683
);
1684+
test_coercion!(
1685+
Int16Array,
1686+
DataType::Int16,
1687+
vec![1i16, 2i16, 3i16],
1688+
Int64Array,
1689+
DataType::Int64,
1690+
vec![10i64, 4i64, 5i64],
1691+
Operator::BitwiseAnd,
1692+
Int64Array,
1693+
DataType::Int64,
1694+
vec![0i64, 0i64, 1i64]
1695+
);
15831696
Ok(())
15841697
}
15851698

@@ -2954,4 +3067,25 @@ mod tests {
29543067

29553068
Ok(())
29563069
}
3070+
3071+
#[test]
3072+
fn bitwise_array_test() -> Result<()> {
3073+
let left = Arc::new(Int32Array::from(vec![Some(12), None, Some(11)])) as ArrayRef;
3074+
let right =
3075+
Arc::new(Int32Array::from(vec![Some(1), Some(3), Some(7)])) as ArrayRef;
3076+
let result = bitwise_and(left, right)?;
3077+
let expected = Int32Array::from(vec![Some(0), None, Some(3)]);
3078+
assert_eq!(result.as_ref(), &expected);
3079+
Ok(())
3080+
}
3081+
3082+
#[test]
3083+
fn bitwise_scalar_test() -> Result<()> {
3084+
let left = Arc::new(Int32Array::from(vec![Some(12), None, Some(11)])) as ArrayRef;
3085+
let right = ScalarValue::from(3i32);
3086+
let result = bitwise_and_scalar(&left, right).unwrap()?;
3087+
let expected = Int32Array::from(vec![Some(0), None, Some(3)]);
3088+
assert_eq!(result.as_ref(), &expected);
3089+
Ok(())
3090+
}
29573091
}

datafusion/src/sql/planner.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
12761276
BinaryOperator::PGRegexIMatch => Ok(Operator::RegexIMatch),
12771277
BinaryOperator::PGRegexNotMatch => Ok(Operator::RegexNotMatch),
12781278
BinaryOperator::PGRegexNotIMatch => Ok(Operator::RegexNotIMatch),
1279+
BinaryOperator::BitwiseAnd => Ok(Operator::BitwiseAnd),
12791280
_ => Err(DataFusionError::NotImplemented(format!(
12801281
"Unsupported SQL binary operator {:?}",
12811282
op

0 commit comments

Comments
 (0)