use std::sync::Arc;
use arrow::array::Array;
use arrow::array::*;
use arrow::compute::eq_dyn;
use arrow::compute::kernels::boolean::nullif;
use arrow::datatypes::DataType;
use datafusion_common::{DataFusionError, Result};
use datafusion_expr::ColumnarValue;
use super::binary::array_eq_scalar;
macro_rules! compute_bool_array_op {
($LEFT:expr, $RIGHT:expr, $OP:ident, $DT:ident) => {{
let ll = $LEFT
.as_any()
.downcast_ref::<$DT>()
.expect("compute_op failed to downcast array");
let rr = $RIGHT
.as_any()
.downcast_ref::<BooleanArray>()
.expect("compute_op failed to downcast array");
Ok(Arc::new($OP(&ll, &rr)?) as ArrayRef)
}};
}
macro_rules! primitive_bool_array_op {
($LEFT:expr, $RIGHT:expr, $OP:ident) => {{
match $LEFT.data_type() {
DataType::Int8 => compute_bool_array_op!($LEFT, $RIGHT, $OP, Int8Array),
DataType::Int16 => compute_bool_array_op!($LEFT, $RIGHT, $OP, Int16Array),
DataType::Int32 => compute_bool_array_op!($LEFT, $RIGHT, $OP, Int32Array),
DataType::Int64 => compute_bool_array_op!($LEFT, $RIGHT, $OP, Int64Array),
DataType::UInt8 => compute_bool_array_op!($LEFT, $RIGHT, $OP, UInt8Array),
DataType::UInt16 => compute_bool_array_op!($LEFT, $RIGHT, $OP, UInt16Array),
DataType::UInt32 => compute_bool_array_op!($LEFT, $RIGHT, $OP, UInt32Array),
DataType::UInt64 => compute_bool_array_op!($LEFT, $RIGHT, $OP, UInt64Array),
DataType::Float32 => compute_bool_array_op!($LEFT, $RIGHT, $OP, Float32Array),
DataType::Float64 => compute_bool_array_op!($LEFT, $RIGHT, $OP, Float64Array),
other => Err(DataFusionError::Internal(format!(
"Unsupported data type {:?} for NULLIF/primitive/boolean operator",
other
))),
}
}};
}
pub fn nullif_func(args: &[ColumnarValue]) -> Result<ColumnarValue> {
if args.len() != 2 {
return Err(DataFusionError::Internal(format!(
"{:?} args were supplied but NULLIF takes exactly two args",
args.len(),
)));
}
let (lhs, rhs) = (&args[0], &args[1]);
match (lhs, rhs) {
(ColumnarValue::Array(lhs), ColumnarValue::Scalar(rhs)) => {
let cond_array = array_eq_scalar(lhs, rhs)?;
let array = primitive_bool_array_op!(lhs, *cond_array, nullif)?;
Ok(ColumnarValue::Array(array))
}
(ColumnarValue::Array(lhs), ColumnarValue::Array(rhs)) => {
let cond_array = eq_dyn(lhs, rhs)?;
let array = primitive_bool_array_op!(lhs, cond_array, nullif)?;
Ok(ColumnarValue::Array(array))
}
_ => Err(DataFusionError::NotImplemented(
"nullif does not support a literal as first argument".to_string(),
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use datafusion_common::{Result, ScalarValue};
#[test]
fn nullif_int32() -> Result<()> {
let a = Int32Array::from(vec![
Some(1),
Some(2),
None,
None,
Some(3),
None,
None,
Some(4),
Some(5),
]);
let a = ColumnarValue::Array(Arc::new(a));
let lit_array = ColumnarValue::Scalar(ScalarValue::Int32(Some(2i32)));
let result = nullif_func(&[a, lit_array])?;
let result = result.into_array(0);
let expected = Arc::new(Int32Array::from(vec![
Some(1),
None,
None,
None,
Some(3),
None,
None,
Some(4),
Some(5),
])) as ArrayRef;
assert_eq!(expected.as_ref(), result.as_ref());
Ok(())
}
#[test]
fn nullif_int32_nonulls() -> Result<()> {
let a = Int32Array::from(vec![1, 3, 10, 7, 8, 1, 2, 4, 5]);
let a = ColumnarValue::Array(Arc::new(a));
let lit_array = ColumnarValue::Scalar(ScalarValue::Int32(Some(1i32)));
let result = nullif_func(&[a, lit_array])?;
let result = result.into_array(0);
let expected = Arc::new(Int32Array::from(vec![
None,
Some(3),
Some(10),
Some(7),
Some(8),
None,
Some(2),
Some(4),
Some(5),
])) as ArrayRef;
assert_eq!(expected.as_ref(), result.as_ref());
Ok(())
}
}