blob: 5a16398ec49a44b65b5851cb51fa8ea3c386323b [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use crate::arithmetic_overflow_error;
use arrow::array::*;
use arrow::datatypes::*;
use arrow::error::ArrowError;
use datafusion::common::{exec_err, DataFusionError, Result, ScalarValue};
use datafusion::logical_expr::ColumnarValue;
use std::sync::Arc;
macro_rules! legacy_compute_op {
($ARRAY:expr, $FUNC:ident, $TYPE:ident, $RESULT:ident) => {{
let n = $ARRAY.as_any().downcast_ref::<$TYPE>();
match n {
Some(array) => {
let res: $RESULT = arrow::compute::kernels::arity::unary(array, |x| x.$FUNC());
Ok(res)
}
_ => Err(DataFusionError::Internal(format!(
"Invalid data type for abs"
))),
}
}};
}
macro_rules! ansi_compute_op {
($ARRAY:expr, $FUNC:ident, $TYPE:ident, $RESULT:ident, $NATIVE:ident, $FROM_TYPE:expr) => {{
let n = $ARRAY.as_any().downcast_ref::<$TYPE>();
match n {
Some(array) => {
match arrow::compute::kernels::arity::try_unary(array, |x| {
if x == $NATIVE::MIN {
Err(ArrowError::ArithmeticOverflow($FROM_TYPE.to_string()))
} else {
Ok(x.$FUNC())
}
}) {
Ok(res) => Ok(ColumnarValue::Array(Arc::<PrimitiveArray<$RESULT>>::new(
res,
))),
Err(_) => Err(arithmetic_overflow_error($FROM_TYPE).into()),
}
}
_ => Err(DataFusionError::Internal("Invalid data type".to_string())),
}
}};
}
/// This function mimics SparkSQL's [Abs]: https://github.com/apache/spark/blob/v4.0.1/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/arithmetic.scala#L148
/// Spark's [ANSI-compliant]: https://spark.apache.org/docs/latest/sql-ref-ansi-compliance.html#arithmetic-operations dialect mode throws org.apache.spark.SparkArithmeticException
/// when abs causes overflow.
pub fn abs(args: &[ColumnarValue]) -> Result<ColumnarValue, DataFusionError> {
if args.is_empty() || args.len() > 2 {
return exec_err!("abs takes 1 or 2 arguments, but got: {}", args.len());
}
let fail_on_error = if args.len() == 2 {
match &args[1] {
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error))) => *fail_on_error,
_ => {
return exec_err!(
"The second argument must be boolean scalar, but got: {:?}",
args[1]
);
}
}
} else {
false
};
match &args[0] {
ColumnarValue::Array(array) => match array.data_type() {
DataType::Null
| DataType::UInt8
| DataType::UInt16
| DataType::UInt32
| DataType::UInt64 => Ok(args[0].clone()),
DataType::Int8 => {
if !fail_on_error {
let result = legacy_compute_op!(array, wrapping_abs, Int8Array, Int8Array);
Ok(ColumnarValue::Array(Arc::new(result?)))
} else {
ansi_compute_op!(array, abs, Int8Array, Int8Type, i8, "Int8")
}
}
DataType::Int16 => {
if !fail_on_error {
let result = legacy_compute_op!(array, wrapping_abs, Int16Array, Int16Array);
Ok(ColumnarValue::Array(Arc::new(result?)))
} else {
ansi_compute_op!(array, abs, Int16Array, Int16Type, i16, "Int16")
}
}
DataType::Int32 => {
if !fail_on_error {
let result = legacy_compute_op!(array, wrapping_abs, Int32Array, Int32Array);
Ok(ColumnarValue::Array(Arc::new(result?)))
} else {
ansi_compute_op!(array, abs, Int32Array, Int32Type, i32, "Int32")
}
}
DataType::Int64 => {
if !fail_on_error {
let result = legacy_compute_op!(array, wrapping_abs, Int64Array, Int64Array);
Ok(ColumnarValue::Array(Arc::new(result?)))
} else {
ansi_compute_op!(array, abs, Int64Array, Int64Type, i64, "Int64")
}
}
DataType::Float32 => {
let result = legacy_compute_op!(array, abs, Float32Array, Float32Array);
Ok(ColumnarValue::Array(Arc::new(result?)))
}
DataType::Float64 => {
let result = legacy_compute_op!(array, abs, Float64Array, Float64Array);
Ok(ColumnarValue::Array(Arc::new(result?)))
}
DataType::Decimal128(precision, scale) => {
if !fail_on_error {
let result =
legacy_compute_op!(array, wrapping_abs, Decimal128Array, Decimal128Array)?;
let result = result.with_data_type(DataType::Decimal128(*precision, *scale));
Ok(ColumnarValue::Array(Arc::new(result)))
} else {
// Need to pass precision and scale from input, so not using ansi_compute_op
let input = array.as_any().downcast_ref::<Decimal128Array>();
match input {
Some(i) => {
match arrow::compute::kernels::arity::try_unary(i, |x| {
if x == i128::MIN {
Err(ArrowError::ArithmeticOverflow("Decimal128".to_string()))
} else {
Ok(x.abs())
}
}) {
Ok(res) => Ok(ColumnarValue::Array(Arc::<
PrimitiveArray<Decimal128Type>,
>::new(
res.with_data_type(DataType::Decimal128(*precision, *scale)),
))),
Err(_) => Err(arithmetic_overflow_error("Decimal128").into()),
}
}
_ => Err(DataFusionError::Internal("Invalid data type".to_string())),
}
}
}
DataType::Decimal256(precision, scale) => {
if !fail_on_error {
let result =
legacy_compute_op!(array, wrapping_abs, Decimal256Array, Decimal256Array)?;
let result = result.with_data_type(DataType::Decimal256(*precision, *scale));
Ok(ColumnarValue::Array(Arc::new(result)))
} else {
// Need to pass precision and scale from input, so not using ansi_compute_op
let input = array.as_any().downcast_ref::<Decimal256Array>();
match input {
Some(i) => {
match arrow::compute::kernels::arity::try_unary(i, |x| {
if x == i256::MIN {
Err(ArrowError::ArithmeticOverflow("Decimal256".to_string()))
} else {
Ok(x.wrapping_abs()) // i256 doesn't define abs() method
}
}) {
Ok(res) => Ok(ColumnarValue::Array(Arc::<
PrimitiveArray<Decimal256Type>,
>::new(
res.with_data_type(DataType::Decimal256(*precision, *scale)),
))),
Err(_) => Err(arithmetic_overflow_error("Decimal256").into()),
}
}
_ => Err(DataFusionError::Internal("Invalid data type".to_string())),
}
}
}
dt => exec_err!("Not supported datatype for ABS: {dt}"),
},
ColumnarValue::Scalar(sv) => match sv {
ScalarValue::Null
| ScalarValue::UInt8(_)
| ScalarValue::UInt16(_)
| ScalarValue::UInt32(_)
| ScalarValue::UInt64(_) => Ok(args[0].clone()),
ScalarValue::Int8(a) => match a {
None => Ok(args[0].clone()),
Some(v) => match v.checked_abs() {
Some(abs_val) => Ok(ColumnarValue::Scalar(ScalarValue::Int8(Some(abs_val)))),
None => {
if !fail_on_error {
// return the original value
Ok(ColumnarValue::Scalar(ScalarValue::Int8(Some(*v))))
} else {
Err(arithmetic_overflow_error("Int8").into())
}
}
},
},
ScalarValue::Int16(a) => match a {
None => Ok(args[0].clone()),
Some(v) => match v.checked_abs() {
Some(abs_val) => Ok(ColumnarValue::Scalar(ScalarValue::Int16(Some(abs_val)))),
None => {
if !fail_on_error {
// return the original value
Ok(ColumnarValue::Scalar(ScalarValue::Int16(Some(*v))))
} else {
Err(arithmetic_overflow_error("Int16").into())
}
}
},
},
ScalarValue::Int32(a) => match a {
None => Ok(args[0].clone()),
Some(v) => match v.checked_abs() {
Some(abs_val) => Ok(ColumnarValue::Scalar(ScalarValue::Int32(Some(abs_val)))),
None => {
if !fail_on_error {
// return the original value
Ok(ColumnarValue::Scalar(ScalarValue::Int32(Some(*v))))
} else {
Err(arithmetic_overflow_error("Int32").into())
}
}
},
},
ScalarValue::Int64(a) => match a {
None => Ok(args[0].clone()),
Some(v) => match v.checked_abs() {
Some(abs_val) => Ok(ColumnarValue::Scalar(ScalarValue::Int64(Some(abs_val)))),
None => {
if !fail_on_error {
// return the original value
Ok(ColumnarValue::Scalar(ScalarValue::Int64(Some(*v))))
} else {
Err(arithmetic_overflow_error("Int64").into())
}
}
},
},
ScalarValue::Float32(a) => Ok(ColumnarValue::Scalar(ScalarValue::Float32(
a.map(|x| x.abs()),
))),
ScalarValue::Float64(a) => Ok(ColumnarValue::Scalar(ScalarValue::Float64(
a.map(|x| x.abs()),
))),
ScalarValue::Decimal128(a, precision, scale) => match a {
None => Ok(args[0].clone()),
Some(v) => match v.checked_abs() {
Some(abs_val) => Ok(ColumnarValue::Scalar(ScalarValue::Decimal128(
Some(abs_val),
*precision,
*scale,
))),
None => {
if !fail_on_error {
// return the original value
Ok(ColumnarValue::Scalar(ScalarValue::Decimal128(
Some(*v),
*precision,
*scale,
)))
} else {
Err(arithmetic_overflow_error("Decimal128").into())
}
}
},
},
ScalarValue::Decimal256(a, precision, scale) => match a {
None => Ok(args[0].clone()),
Some(v) => match v.checked_abs() {
Some(abs_val) => Ok(ColumnarValue::Scalar(ScalarValue::Decimal256(
Some(abs_val),
*precision,
*scale,
))),
None => {
if !fail_on_error {
// return the original value
Ok(ColumnarValue::Scalar(ScalarValue::Decimal256(
Some(*v),
*precision,
*scale,
)))
} else {
Err(arithmetic_overflow_error("Decimal256").into())
}
}
},
},
dt => exec_err!("Not supported datatype for ABS: {dt}"),
},
}
}
#[cfg(test)]
mod tests {
use super::*;
use datafusion::common::cast::{
as_decimal128_array, as_decimal256_array, as_float32_array, as_float64_array,
as_int16_array, as_int32_array, as_int64_array, as_int8_array, as_uint64_array,
};
fn with_fail_on_error<F: Fn(bool) -> Result<()>>(test_fn: F) {
for fail_on_error in [true, false] {
test_fn(fail_on_error).expect("test should pass on error successfully");
}
}
// Unsigned types, return as is
#[test]
fn test_abs_u8_scalar() {
with_fail_on_error(|fail_on_error| {
let args = ColumnarValue::Scalar(ScalarValue::UInt8(Some(u8::MAX)));
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Scalar(ScalarValue::UInt8(Some(result)))) => {
assert_eq!(result, u8::MAX);
Ok(())
}
Err(e) => {
unreachable!("Didn't expect error, but got: {e:?}")
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_i8_scalar() {
with_fail_on_error(|fail_on_error| {
let args = ColumnarValue::Scalar(ScalarValue::Int8(Some(i8::MIN)));
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Scalar(ScalarValue::Int8(Some(result)))) => {
assert_eq!(result, i8::MIN);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
unreachable!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_i16_scalar() {
with_fail_on_error(|fail_on_error| {
let args = ColumnarValue::Scalar(ScalarValue::Int16(Some(i16::MIN)));
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Scalar(ScalarValue::Int16(Some(result)))) => {
assert_eq!(result, i16::MIN);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
unreachable!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_i32_scalar() {
with_fail_on_error(|fail_on_error| {
let args = ColumnarValue::Scalar(ScalarValue::Int32(Some(i32::MIN)));
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Scalar(ScalarValue::Int32(Some(result)))) => {
assert_eq!(result, i32::MIN);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_i64_scalar() {
with_fail_on_error(|fail_on_error| {
let args = ColumnarValue::Scalar(ScalarValue::Int64(Some(i64::MIN)));
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Scalar(ScalarValue::Int64(Some(result)))) => {
assert_eq!(result, i64::MIN);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_decimal128_scalar() {
with_fail_on_error(|fail_on_error| {
let args = ColumnarValue::Scalar(ScalarValue::Decimal128(Some(i128::MIN), 18, 10));
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Scalar(ScalarValue::Decimal128(
Some(result),
precision,
scale,
))) => {
assert_eq!(result, i128::MIN);
assert_eq!(precision, 18);
assert_eq!(scale, 10);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_decimal256_scalar() {
with_fail_on_error(|fail_on_error| {
let args = ColumnarValue::Scalar(ScalarValue::Decimal256(Some(i256::MIN), 10, 2));
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Scalar(ScalarValue::Decimal256(
Some(result),
precision,
scale,
))) => {
assert_eq!(result, i256::MIN);
assert_eq!(precision, 10);
assert_eq!(scale, 2);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_i8_array() {
with_fail_on_error(|fail_on_error| {
let input = Int8Array::from(vec![Some(-1), Some(i8::MIN), Some(i8::MAX), None]);
let args = ColumnarValue::Array(Arc::new(input));
let expected = Int8Array::from(vec![Some(1), Some(i8::MIN), Some(i8::MAX), None]);
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Array(result)) => {
let actual = as_int8_array(&result)?;
assert_eq!(actual, &expected);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_i16_array() {
with_fail_on_error(|fail_on_error| {
let input = Int16Array::from(vec![Some(-1), Some(i16::MIN), Some(i16::MAX), None]);
let args = ColumnarValue::Array(Arc::new(input));
let expected = Int16Array::from(vec![Some(1), Some(i16::MIN), Some(i16::MAX), None]);
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Array(result)) => {
let actual = as_int16_array(&result)?;
assert_eq!(actual, &expected);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_i32_array() {
with_fail_on_error(|fail_on_error| {
let input = Int32Array::from(vec![Some(-1), Some(i32::MIN), Some(i32::MAX), None]);
let args = ColumnarValue::Array(Arc::new(input));
let expected = Int32Array::from(vec![Some(1), Some(i32::MIN), Some(i32::MAX), None]);
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Array(result)) => {
let actual = as_int32_array(&result)?;
assert_eq!(actual, &expected);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_i64_array() {
with_fail_on_error(|fail_on_error| {
let input = Int64Array::from(vec![Some(-1), Some(i64::MIN), Some(i64::MAX), None]);
let args = ColumnarValue::Array(Arc::new(input));
let expected = Int64Array::from(vec![Some(1), Some(i64::MIN), Some(i64::MAX), None]);
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Array(result)) => {
let actual = as_int64_array(&result)?;
assert_eq!(actual, &expected);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_f32_array() {
with_fail_on_error(|fail_on_error| {
let input = Float32Array::from(vec![
Some(-1f32),
Some(f32::MIN),
Some(f32::MAX),
None,
Some(f32::NAN),
Some(f32::NEG_INFINITY),
Some(f32::INFINITY),
Some(-0.0),
Some(0.0),
]);
let args = ColumnarValue::Array(Arc::new(input));
let expected = Float32Array::from(vec![
Some(1f32),
Some(f32::MAX),
Some(f32::MAX),
None,
Some(f32::NAN),
Some(f32::INFINITY),
Some(f32::INFINITY),
Some(0.0),
Some(0.0),
]);
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Array(result)) => {
let actual = as_float32_array(&result)?;
assert_eq!(actual, &expected);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_f64_array() {
with_fail_on_error(|fail_on_error| {
let input = Float64Array::from(vec![Some(-1f64), Some(f64::MIN), Some(f64::MAX), None]);
let args = ColumnarValue::Array(Arc::new(input));
let expected =
Float64Array::from(vec![Some(1f64), Some(f64::MAX), Some(f64::MAX), None]);
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Array(result)) => {
let actual = as_float64_array(&result)?;
assert_eq!(actual, &expected);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_decimal128_array() {
with_fail_on_error(|fail_on_error| {
let input = Decimal128Array::from(vec![Some(i128::MIN), None])
.with_precision_and_scale(38, 37)?;
let args = ColumnarValue::Array(Arc::new(input));
let expected = Decimal128Array::from(vec![Some(i128::MIN), None])
.with_precision_and_scale(38, 37)?;
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Array(result)) => {
let actual = as_decimal128_array(&result)?;
assert_eq!(actual, &expected);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_decimal256_array() {
with_fail_on_error(|fail_on_error| {
let input = Decimal256Array::from(vec![Some(i256::MIN), None])
.with_precision_and_scale(5, 2)?;
let args = ColumnarValue::Array(Arc::new(input));
let expected = Decimal256Array::from(vec![Some(i256::MIN), None])
.with_precision_and_scale(5, 2)?;
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Array(result)) => {
let actual = as_decimal256_array(&result)?;
assert_eq!(actual, &expected);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_u64_array() {
with_fail_on_error(|fail_on_error| {
let input = UInt64Array::from(vec![Some(u64::MIN), Some(u64::MAX), None]);
let args = ColumnarValue::Array(Arc::new(input));
let expected = UInt64Array::from(vec![Some(u64::MIN), Some(u64::MAX), None]);
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
match abs(&[args, fail_on_error_arg]) {
Ok(ColumnarValue::Array(result)) => {
let actual = as_uint64_array(&result)?;
assert_eq!(actual, &expected);
Ok(())
}
Err(e) => {
if fail_on_error {
assert!(
e.to_string().contains("ARITHMETIC_OVERFLOW"),
"Error message did not match. Actual message: {e}"
);
Ok(())
} else {
panic!("Didn't expect error, but got: {e:?}")
}
}
_ => unreachable!(),
}
});
}
#[test]
fn test_abs_null_scalars() {
// Test that NULL scalars return NULL (no panic) for all signed types
with_fail_on_error(|fail_on_error| {
let fail_on_error_arg =
ColumnarValue::Scalar(ScalarValue::Boolean(Some(fail_on_error)));
// Test Int8
let args = ColumnarValue::Scalar(ScalarValue::Int8(None));
match abs(&[args.clone(), fail_on_error_arg.clone()]) {
Ok(ColumnarValue::Scalar(ScalarValue::Int8(None))) => {}
_ => panic!("Expected NULL Int8, got different result"),
}
// Test Int16
let args = ColumnarValue::Scalar(ScalarValue::Int16(None));
match abs(&[args.clone(), fail_on_error_arg.clone()]) {
Ok(ColumnarValue::Scalar(ScalarValue::Int16(None))) => {}
_ => panic!("Expected NULL Int16, got different result"),
}
// Test Int32
let args = ColumnarValue::Scalar(ScalarValue::Int32(None));
match abs(&[args.clone(), fail_on_error_arg.clone()]) {
Ok(ColumnarValue::Scalar(ScalarValue::Int32(None))) => {}
_ => panic!("Expected NULL Int32, got different result"),
}
// Test Int64
let args = ColumnarValue::Scalar(ScalarValue::Int64(None));
match abs(&[args.clone(), fail_on_error_arg.clone()]) {
Ok(ColumnarValue::Scalar(ScalarValue::Int64(None))) => {}
_ => panic!("Expected NULL Int64, got different result"),
}
// Test Decimal128
let args = ColumnarValue::Scalar(ScalarValue::Decimal128(None, 10, 2));
match abs(&[args.clone(), fail_on_error_arg.clone()]) {
Ok(ColumnarValue::Scalar(ScalarValue::Decimal128(None, 10, 2))) => {}
_ => panic!("Expected NULL Decimal128, got different result"),
}
// Test Decimal256
let args = ColumnarValue::Scalar(ScalarValue::Decimal256(None, 10, 2));
match abs(&[args.clone(), fail_on_error_arg.clone()]) {
Ok(ColumnarValue::Scalar(ScalarValue::Decimal256(None, 10, 2))) => {}
_ => panic!("Expected NULL Decimal256, got different result"),
}
// Test Float32
let args = ColumnarValue::Scalar(ScalarValue::Float32(None));
match abs(&[args.clone(), fail_on_error_arg.clone()]) {
Ok(ColumnarValue::Scalar(ScalarValue::Float32(None))) => {}
_ => panic!("Expected NULL Float32, got different result"),
}
// Test Float64
let args = ColumnarValue::Scalar(ScalarValue::Float64(None));
match abs(&[args.clone(), fail_on_error_arg.clone()]) {
Ok(ColumnarValue::Scalar(ScalarValue::Float64(None))) => {}
_ => panic!("Expected NULL Float64, got different result"),
}
Ok(())
});
}
}