You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by tu...@apache.org on 2023/07/10 16:54:39 UTC
[arrow-rs] branch master updated: Add Datum Arithmetic tests, Fix Interval Substraction (#4480) (#4493)
This is an automated email from the ASF dual-hosted git repository.
tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
The following commit(s) were added to refs/heads/master by this push:
new 6bbf2f0ce Add Datum Arithmetic tests, Fix Interval Substraction (#4480) (#4493)
6bbf2f0ce is described below
commit 6bbf2f0ce3e69de3ce1494917458cef56e9b6a8c
Author: Raphael Taylor-Davies <17...@users.noreply.github.com>
AuthorDate: Mon Jul 10 12:54:33 2023 -0400
Add Datum Arithmetic tests, Fix Interval Substraction (#4480) (#4493)
* Add arithmetic tests (#4480)
* Fix test
* Review feedback
---
arrow-arith/src/arithmetic.rs | 4 +-
arrow-arith/src/numeric.rs | 539 +++++++++++++++++++++++++++++++++++++++++-
arrow-array/src/arithmetic.rs | 5 +-
arrow-array/src/types.rs | 60 ++---
4 files changed, 564 insertions(+), 44 deletions(-)
diff --git a/arrow-arith/src/arithmetic.rs b/arrow-arith/src/arithmetic.rs
index 4566afc2e..f8c855af0 100644
--- a/arrow-arith/src/arithmetic.rs
+++ b/arrow-arith/src/arithmetic.rs
@@ -1317,7 +1317,7 @@ mod tests {
let c = c.as_any().downcast_ref::<Date32Array>().unwrap();
assert_eq!(
c.value(0),
- Date32Type::from_naive_date(NaiveDate::from_ymd_opt(2023, 3, 27).unwrap())
+ Date32Type::from_naive_date(NaiveDate::from_ymd_opt(2023, 3, 28).unwrap())
);
}
@@ -1364,7 +1364,7 @@ mod tests {
let c = c.as_any().downcast_ref::<Date64Array>().unwrap();
assert_eq!(
c.value(0),
- Date64Type::from_naive_date(NaiveDate::from_ymd_opt(2023, 3, 27).unwrap())
+ Date64Type::from_naive_date(NaiveDate::from_ymd_opt(2023, 3, 28).unwrap())
);
}
diff --git a/arrow-arith/src/numeric.rs b/arrow-arith/src/numeric.rs
index c2e867dc9..9816d3e3d 100644
--- a/arrow-arith/src/numeric.rs
+++ b/arrow-arith/src/numeric.rs
@@ -18,6 +18,7 @@
//! Defines numeric arithmetic kernels on [`PrimitiveArray`], such as [`add`]
use std::cmp::Ordering;
+use std::fmt::Formatter;
use std::sync::Arc;
use arrow_array::cast::AsArray;
@@ -180,6 +181,18 @@ enum Op {
Rem,
}
+impl std::fmt::Display for Op {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Op::AddWrapping | Op::Add => write!(f, "+"),
+ Op::SubWrapping | Op::Sub => write!(f, "-"),
+ Op::MulWrapping | Op::Mul => write!(f, "*"),
+ Op::Div => write!(f, "/"),
+ Op::Rem => write!(f, "%"),
+ }
+ }
+}
+
impl Op {
fn commutative(&self) -> bool {
matches!(self, Self::Add | Self::AddWrapping)
@@ -229,7 +242,7 @@ fn arithmetic_op(
arithmetic_op(op, rhs, lhs)
}
_ => Err(ArrowError::InvalidArgumentError(
- format!("Invalid arithmetic operation: {l_t} {op:?} {r_t}")
+ format!("Invalid arithmetic operation: {l_t} {op} {r_t}")
))
}
}
@@ -433,7 +446,7 @@ fn timestamp_op<T: TimestampOp>(
}
_ => {
return Err(ArrowError::InvalidArgumentError(format!(
- "Invalid timestamp arithmetic operation: {} {op:?} {}",
+ "Invalid timestamp arithmetic operation: {} {op} {}",
l.data_type(),
r.data_type()
)))
@@ -555,7 +568,7 @@ fn interval_op<T: IntervalOp>(
Op::Add | Op::AddWrapping => Ok(try_op_ref!(T, l, l_s, r, r_s, T::add(l, r))),
Op::Sub | Op::SubWrapping => Ok(try_op_ref!(T, l, l_s, r, r_s, T::sub(l, r))),
_ => Err(ArrowError::InvalidArgumentError(format!(
- "Invalid interval arithmetic operation: {} {op:?} {}",
+ "Invalid interval arithmetic operation: {} {op} {}",
l.data_type(),
r.data_type()
))),
@@ -575,7 +588,7 @@ fn duration_op<T: ArrowPrimitiveType>(
Op::Add | Op::AddWrapping => Ok(try_op_ref!(T, l, l_s, r, r_s, l.add_checked(r))),
Op::Sub | Op::SubWrapping => Ok(try_op_ref!(T, l, l_s, r, r_s, l.sub_checked(r))),
_ => Err(ArrowError::InvalidArgumentError(format!(
- "Invalid duration arithmetic operation: {} {op:?} {}",
+ "Invalid duration arithmetic operation: {} {op} {}",
l.data_type(),
r.data_type()
))),
@@ -593,7 +606,6 @@ fn date_op<T: DateOp>(
use DataType::*;
use IntervalUnit::*;
- // Note: interval arithmetic should account for timezones (#4457)
let l = l.as_primitive::<T>();
match (op, r.data_type()) {
(Op::Add | Op::AddWrapping, Interval(YearMonth)) => {
@@ -624,7 +636,7 @@ fn date_op<T: DateOp>(
}
_ => Err(ArrowError::InvalidArgumentError(format!(
- "Invalid date arithmetic operation: {} {op:?} {}",
+ "Invalid date arithmetic operation: {} {op} {}",
l.data_type(),
r.data_type()
))),
@@ -661,8 +673,8 @@ fn decimal_op<T: DecimalType>(
.saturating_add(1)
.min(T::MAX_PRECISION);
- let l_mul = T::Native::usize_as(10).pow_wrapping((result_scale - s1) as _);
- let r_mul = T::Native::usize_as(10).pow_wrapping((result_scale - s2) as _);
+ let l_mul = T::Native::usize_as(10).pow_checked((result_scale - s1) as _)?;
+ let r_mul = T::Native::usize_as(10).pow_checked((result_scale - s2) as _)?;
match op {
Op::Add | Op::AddWrapping => {
@@ -694,7 +706,7 @@ fn decimal_op<T: DecimalType>(
// SQL standard says that if the resulting scale of a multiply operation goes
// beyond the maximum, rounding is not acceptable and thus an error occurs
return Err(ArrowError::InvalidArgumentError(format!(
- "Output scale of {} {op:?} {} would exceed max scale of {}",
+ "Output scale of {} {op} {} would exceed max scale of {}",
l.data_type(),
r.data_type(),
T::MAX_SCALE
@@ -717,13 +729,13 @@ fn decimal_op<T: DecimalType>(
let (l_mul, r_mul) = match mul_pow.cmp(&0) {
Ordering::Greater => (
- T::Native::usize_as(10).pow_wrapping(mul_pow as _),
+ T::Native::usize_as(10).pow_checked(mul_pow as _)?,
T::Native::ONE,
),
Ordering::Equal => (T::Native::ONE, T::Native::ONE),
Ordering::Less => (
T::Native::ONE,
- T::Native::usize_as(10).pow_wrapping(mul_pow.neg_wrapping() as _),
+ T::Native::usize_as(10).pow_checked(mul_pow.neg_wrapping() as _)?,
),
};
@@ -765,7 +777,9 @@ fn decimal_op<T: DecimalType>(
#[cfg(test)]
mod tests {
use super::*;
+ use arrow_array::temporal_conversions::{as_date, as_datetime};
use arrow_buffer::{i256, ScalarBuffer};
+ use chrono::{DateTime, NaiveDate};
fn test_neg_primitive<T: ArrowPrimitiveType>(
input: &[T::Native],
@@ -905,4 +919,507 @@ mod tests {
]
);
}
+
+ #[test]
+ fn test_integer() {
+ let a = Int32Array::from(vec![4, 3, 5, -6, 100]);
+ let b = Int32Array::from(vec![6, 2, 5, -7, 3]);
+ let result = add(&a, &b).unwrap();
+ assert_eq!(
+ result.as_ref(),
+ &Int32Array::from(vec![10, 5, 10, -13, 103])
+ );
+ let result = sub(&a, &b).unwrap();
+ assert_eq!(result.as_ref(), &Int32Array::from(vec![-2, 1, 0, 1, 97]));
+ let result = div(&a, &b).unwrap();
+ assert_eq!(result.as_ref(), &Int32Array::from(vec![0, 1, 1, 0, 33]));
+ let result = mul(&a, &b).unwrap();
+ assert_eq!(result.as_ref(), &Int32Array::from(vec![24, 6, 25, 42, 300]));
+ let result = rem(&a, &b).unwrap();
+ assert_eq!(result.as_ref(), &Int32Array::from(vec![4, 1, 0, -6, 1]));
+
+ let a = Int8Array::from(vec![Some(2), None, Some(45)]);
+ let b = Int8Array::from(vec![Some(5), Some(3), None]);
+ let result = add(&a, &b).unwrap();
+ assert_eq!(result.as_ref(), &Int8Array::from(vec![Some(7), None, None]));
+
+ let a = UInt8Array::from(vec![56, 5, 3]);
+ let b = UInt8Array::from(vec![200, 2, 5]);
+ let err = add(&a, &b).unwrap_err().to_string();
+ assert_eq!(err, "Compute error: Overflow happened on: 56 + 200");
+ let result = add_wrapping(&a, &b).unwrap();
+ assert_eq!(result.as_ref(), &UInt8Array::from(vec![0, 7, 8]));
+
+ let a = UInt8Array::from(vec![34, 5, 3]);
+ let b = UInt8Array::from(vec![200, 2, 5]);
+ let err = sub(&a, &b).unwrap_err().to_string();
+ assert_eq!(err, "Compute error: Overflow happened on: 34 - 200");
+ let result = sub_wrapping(&a, &b).unwrap();
+ assert_eq!(result.as_ref(), &UInt8Array::from(vec![90, 3, 254]));
+
+ let a = UInt8Array::from(vec![34, 5, 3]);
+ let b = UInt8Array::from(vec![200, 2, 5]);
+ let err = mul(&a, &b).unwrap_err().to_string();
+ assert_eq!(err, "Compute error: Overflow happened on: 34 * 200");
+ let result = mul_wrapping(&a, &b).unwrap();
+ assert_eq!(result.as_ref(), &UInt8Array::from(vec![144, 10, 15]));
+
+ let a = Int16Array::from(vec![i16::MIN]);
+ let b = Int16Array::from(vec![-1]);
+ let err = div(&a, &b).unwrap_err().to_string();
+ assert_eq!(err, "Compute error: Overflow happened on: -32768 / -1");
+
+ let a = Int16Array::from(vec![21]);
+ let b = Int16Array::from(vec![0]);
+ let err = div(&a, &b).unwrap_err().to_string();
+ assert_eq!(err, "Divide by zero error");
+
+ let a = Int16Array::from(vec![21]);
+ let b = Int16Array::from(vec![0]);
+ let err = rem(&a, &b).unwrap_err().to_string();
+ assert_eq!(err, "Divide by zero error");
+ }
+
+ #[test]
+ fn test_float() {
+ let a = Float32Array::from(vec![1., f32::MAX, 6., -4., -1., 0.]);
+ let b = Float32Array::from(vec![1., f32::MAX, f32::MAX, -3., 45., 0.]);
+ let result = add(&a, &b).unwrap();
+ assert_eq!(
+ result.as_ref(),
+ &Float32Array::from(vec![2., f32::INFINITY, f32::MAX, -7., 44.0, 0.])
+ );
+
+ let result = sub(&a, &b).unwrap();
+ assert_eq!(
+ result.as_ref(),
+ &Float32Array::from(vec![0., 0., f32::MIN, -1., -46., 0.])
+ );
+
+ let result = mul(&a, &b).unwrap();
+ assert_eq!(
+ result.as_ref(),
+ &Float32Array::from(vec![1., f32::INFINITY, f32::INFINITY, 12., -45., 0.])
+ );
+
+ let result = div(&a, &b).unwrap();
+ let r = result.as_primitive::<Float32Type>();
+ assert_eq!(r.value(0), 1.);
+ assert_eq!(r.value(1), 1.);
+ assert!(r.value(2) < f32::EPSILON);
+ assert_eq!(r.value(3), -4. / -3.);
+ assert!(r.value(5).is_nan());
+
+ let result = rem(&a, &b).unwrap();
+ let r = result.as_primitive::<Float32Type>();
+ assert_eq!(&r.values()[..5], &[0., 0., 6., -1., -1.]);
+ assert!(r.value(5).is_nan());
+ }
+
+ #[test]
+ fn test_decimal() {
+ // 0.015 7.842 -0.577 0.334 -0.078 0.003
+ let a = Decimal128Array::from(vec![15, 0, -577, 334, -78, 3])
+ .with_precision_and_scale(12, 3)
+ .unwrap();
+
+ // 5.4 0 -35.6 0.3 0.6 7.45
+ let b = Decimal128Array::from(vec![54, 34, -356, 3, 6, 745])
+ .with_precision_and_scale(12, 1)
+ .unwrap();
+
+ let result = add(&a, &b).unwrap();
+ assert_eq!(result.data_type(), &DataType::Decimal128(15, 3));
+ assert_eq!(
+ result.as_primitive::<Decimal128Type>().values(),
+ &[5415, 3400, -36177, 634, 522, 74503]
+ );
+
+ let result = sub(&a, &b).unwrap();
+ assert_eq!(result.data_type(), &DataType::Decimal128(15, 3));
+ assert_eq!(
+ result.as_primitive::<Decimal128Type>().values(),
+ &[-5385, -3400, 35023, 34, -678, -74497]
+ );
+
+ let result = mul(&a, &b).unwrap();
+ assert_eq!(result.data_type(), &DataType::Decimal128(25, 4));
+ assert_eq!(
+ result.as_primitive::<Decimal128Type>().values(),
+ &[810, 0, 205412, 1002, -468, 2235]
+ );
+
+ let result = div(&a, &b).unwrap();
+ assert_eq!(result.data_type(), &DataType::Decimal128(17, 7));
+ assert_eq!(
+ result.as_primitive::<Decimal128Type>().values(),
+ &[27777, 0, 162078, 11133333, -1300000, 402]
+ );
+
+ let result = rem(&a, &b).unwrap();
+ assert_eq!(result.data_type(), &DataType::Decimal128(12, 3));
+ assert_eq!(
+ result.as_primitive::<Decimal128Type>().values(),
+ &[15, 0, -577, 34, -78, 3]
+ );
+
+ let a = Decimal128Array::from(vec![1])
+ .with_precision_and_scale(3, 3)
+ .unwrap();
+ let b = Decimal128Array::from(vec![1])
+ .with_precision_and_scale(37, 37)
+ .unwrap();
+ let err = mul(&a, &b).unwrap_err().to_string();
+ assert_eq!(err, "Invalid argument error: Output scale of Decimal128(3, 3) * Decimal128(37, 37) would exceed max scale of 38");
+
+ let a = Decimal128Array::from(vec![1])
+ .with_precision_and_scale(3, -2)
+ .unwrap();
+ let err = add(&a, &b).unwrap_err().to_string();
+ assert_eq!(err, "Compute error: Overflow happened on: 10 ^ 39");
+
+ let a = Decimal128Array::from(vec![10])
+ .with_precision_and_scale(3, -1)
+ .unwrap();
+ let err = add(&a, &b).unwrap_err().to_string();
+ assert_eq!(err, "Compute error: Overflow happened on: 10 * 100000000000000000000000000000000000000");
+
+ let b = Decimal128Array::from(vec![0])
+ .with_precision_and_scale(1, 1)
+ .unwrap();
+ let err = div(&a, &b).unwrap_err().to_string();
+ assert_eq!(err, "Divide by zero error");
+ let err = rem(&a, &b).unwrap_err().to_string();
+ assert_eq!(err, "Divide by zero error");
+ }
+
+ fn test_timestamp_impl<T: TimestampOp>() {
+ let a = PrimitiveArray::<T>::new(vec![2000000, 434030324, 53943340].into(), None);
+ let b = PrimitiveArray::<T>::new(vec![329593, 59349, 694994].into(), None);
+
+ let result = sub(&a, &b).unwrap();
+ assert_eq!(
+ result.as_primitive::<T::Duration>().values(),
+ &[1670407, 433970975, 53248346]
+ );
+
+ let r2 = add(&b, &result.as_ref()).unwrap();
+ assert_eq!(r2.as_ref(), &a);
+
+ let r3 = add(&result.as_ref(), &b).unwrap();
+ assert_eq!(r3.as_ref(), &a);
+
+ let format_array = |x: &dyn Array| -> Vec<String> {
+ x.as_primitive::<T>()
+ .values()
+ .into_iter()
+ .map(|x| as_datetime::<T>(*x).unwrap().to_string())
+ .collect()
+ };
+
+ let values = vec![
+ "1970-01-01T00:00:00Z",
+ "2010-04-01T04:00:20Z",
+ "1960-01-30T04:23:20Z",
+ ]
+ .into_iter()
+ .map(|x| {
+ T::make_value(DateTime::parse_from_rfc3339(x).unwrap().naive_utc()).unwrap()
+ })
+ .collect();
+
+ let a = PrimitiveArray::<T>::new(values, None);
+ let b = IntervalYearMonthArray::from(vec![
+ IntervalYearMonthType::make_value(5, 34),
+ IntervalYearMonthType::make_value(-2, 4),
+ IntervalYearMonthType::make_value(7, -4),
+ ]);
+ let r4 = add(&a, &b).unwrap();
+ assert_eq!(
+ &format_array(r4.as_ref()),
+ &[
+ "1977-11-01 00:00:00".to_string(),
+ "2008-08-01 04:00:20".to_string(),
+ "1966-09-30 04:23:20".to_string()
+ ]
+ );
+
+ let r5 = sub(&r4, &b).unwrap();
+ assert_eq!(r5.as_ref(), &a);
+
+ let b = IntervalDayTimeArray::from(vec![
+ IntervalDayTimeType::make_value(5, 454000),
+ IntervalDayTimeType::make_value(-34, 0),
+ IntervalDayTimeType::make_value(7, -4000),
+ ]);
+ let r6 = add(&a, &b).unwrap();
+ assert_eq!(
+ &format_array(r6.as_ref()),
+ &[
+ "1970-01-06 00:07:34".to_string(),
+ "2010-02-26 04:00:20".to_string(),
+ "1960-02-06 04:23:16".to_string()
+ ]
+ );
+
+ let r7 = sub(&r6, &b).unwrap();
+ assert_eq!(r7.as_ref(), &a);
+
+ let b = IntervalMonthDayNanoArray::from(vec![
+ IntervalMonthDayNanoType::make_value(344, 34, -43_000_000_000),
+ IntervalMonthDayNanoType::make_value(-593, -33, 13_000_000_000),
+ IntervalMonthDayNanoType::make_value(5, 2, 493_000_000_000),
+ ]);
+ let r8 = add(&a, &b).unwrap();
+ assert_eq!(
+ &format_array(r8.as_ref()),
+ &[
+ "1998-10-04 23:59:17".to_string(),
+ "1960-09-29 04:00:33".to_string(),
+ "1960-07-02 04:31:33".to_string()
+ ]
+ );
+
+ let r9 = sub(&r8, &b).unwrap();
+ // Note: subtraction is not the inverse of addition for intervals
+ assert_eq!(
+ &format_array(r9.as_ref()),
+ &[
+ "1970-01-02 00:00:00".to_string(),
+ "2010-04-02 04:00:20".to_string(),
+ "1960-01-31 04:23:20".to_string()
+ ]
+ );
+ }
+
+ #[test]
+ fn test_timestamp() {
+ test_timestamp_impl::<TimestampSecondType>();
+ test_timestamp_impl::<TimestampMillisecondType>();
+ test_timestamp_impl::<TimestampMicrosecondType>();
+ test_timestamp_impl::<TimestampNanosecondType>();
+ }
+
+ #[test]
+ fn test_interval() {
+ let a = IntervalYearMonthArray::from(vec![
+ IntervalYearMonthType::make_value(32, 4),
+ IntervalYearMonthType::make_value(32, 4),
+ ]);
+ let b = IntervalYearMonthArray::from(vec![
+ IntervalYearMonthType::make_value(-4, 6),
+ IntervalYearMonthType::make_value(-3, 23),
+ ]);
+ let result = add(&a, &b).unwrap();
+ assert_eq!(
+ result.as_ref(),
+ &IntervalYearMonthArray::from(vec![
+ IntervalYearMonthType::make_value(28, 10),
+ IntervalYearMonthType::make_value(29, 27)
+ ])
+ );
+ let result = sub(&a, &b).unwrap();
+ assert_eq!(
+ result.as_ref(),
+ &IntervalYearMonthArray::from(vec![
+ IntervalYearMonthType::make_value(36, -2),
+ IntervalYearMonthType::make_value(35, -19)
+ ])
+ );
+
+ let a = IntervalDayTimeArray::from(vec![
+ IntervalDayTimeType::make_value(32, 4),
+ IntervalDayTimeType::make_value(32, 4),
+ ]);
+ let b = IntervalDayTimeArray::from(vec![
+ IntervalDayTimeType::make_value(-4, 6),
+ IntervalDayTimeType::make_value(-3, 23),
+ ]);
+ let result = add(&a, &b).unwrap();
+ assert_eq!(
+ result.as_ref(),
+ &IntervalDayTimeArray::from(vec![
+ IntervalDayTimeType::make_value(28, 10),
+ IntervalDayTimeType::make_value(29, 27)
+ ])
+ );
+ let result = sub(&a, &b).unwrap();
+ assert_eq!(
+ result.as_ref(),
+ &IntervalDayTimeArray::from(vec![
+ IntervalDayTimeType::make_value(36, -2),
+ IntervalDayTimeType::make_value(35, -19)
+ ])
+ );
+ let a = IntervalMonthDayNanoArray::from(vec![
+ IntervalMonthDayNanoType::make_value(32, 4, 4000000000000),
+ IntervalMonthDayNanoType::make_value(32, 4, 45463000000000000),
+ ]);
+ let b = IntervalMonthDayNanoArray::from(vec![
+ IntervalMonthDayNanoType::make_value(-4, 6, 46000000000000),
+ IntervalMonthDayNanoType::make_value(-3, 23, 3564000000000000),
+ ]);
+ let result = add(&a, &b).unwrap();
+ assert_eq!(
+ result.as_ref(),
+ &IntervalMonthDayNanoArray::from(vec![
+ IntervalMonthDayNanoType::make_value(28, 10, 50000000000000),
+ IntervalMonthDayNanoType::make_value(29, 27, 49027000000000000)
+ ])
+ );
+ let result = sub(&a, &b).unwrap();
+ assert_eq!(
+ result.as_ref(),
+ &IntervalMonthDayNanoArray::from(vec![
+ IntervalMonthDayNanoType::make_value(36, -2, -42000000000000),
+ IntervalMonthDayNanoType::make_value(35, -19, 41899000000000000)
+ ])
+ );
+ let a = IntervalMonthDayNanoArray::from(vec![i64::MAX as i128]);
+ let b = IntervalMonthDayNanoArray::from(vec![1]);
+ let err = add(&a, &b).unwrap_err().to_string();
+ assert_eq!(
+ err,
+ "Compute error: Overflow happened on: 9223372036854775807 + 1"
+ );
+ }
+
+ fn test_duration_impl<T: ArrowPrimitiveType<Native = i64>>() {
+ let a = PrimitiveArray::<T>::new(vec![1000, 4394, -3944].into(), None);
+ let b = PrimitiveArray::<T>::new(vec![4, -5, -243].into(), None);
+
+ let result = add(&a, &b).unwrap();
+ assert_eq!(result.as_primitive::<T>().values(), &[1004, 4389, -4187]);
+ let result = sub(&a, &b).unwrap();
+ assert_eq!(result.as_primitive::<T>().values(), &[996, 4399, -3701]);
+
+ let err = mul(&a, &b).unwrap_err().to_string();
+ assert!(
+ err.contains("Invalid duration arithmetic operation"),
+ "{err}"
+ );
+
+ let err = div(&a, &b).unwrap_err().to_string();
+ assert!(
+ err.contains("Invalid duration arithmetic operation"),
+ "{err}"
+ );
+
+ let err = rem(&a, &b).unwrap_err().to_string();
+ assert!(
+ err.contains("Invalid duration arithmetic operation"),
+ "{err}"
+ );
+
+ let a = PrimitiveArray::<T>::new(vec![i64::MAX].into(), None);
+ let b = PrimitiveArray::<T>::new(vec![1].into(), None);
+ let err = add(&a, &b).unwrap_err().to_string();
+ assert_eq!(
+ err,
+ "Compute error: Overflow happened on: 9223372036854775807 + 1"
+ );
+ }
+
+ #[test]
+ fn test_duration() {
+ test_duration_impl::<DurationSecondType>();
+ test_duration_impl::<DurationMillisecondType>();
+ test_duration_impl::<DurationMicrosecondType>();
+ test_duration_impl::<DurationNanosecondType>();
+ }
+
+ fn test_date_impl<T: ArrowPrimitiveType, F>(f: F)
+ where
+ F: Fn(NaiveDate) -> T::Native,
+ T::Native: TryInto<i64>,
+ {
+ let a = PrimitiveArray::<T>::new(
+ vec![
+ f(NaiveDate::from_ymd_opt(1979, 1, 30).unwrap()),
+ f(NaiveDate::from_ymd_opt(2010, 4, 3).unwrap()),
+ f(NaiveDate::from_ymd_opt(2008, 2, 29).unwrap()),
+ ]
+ .into(),
+ None,
+ );
+
+ let b = IntervalYearMonthArray::from(vec![
+ IntervalYearMonthType::make_value(34, 2),
+ IntervalYearMonthType::make_value(3, -3),
+ IntervalYearMonthType::make_value(-12, 4),
+ ]);
+
+ let format_array = |x: &dyn Array| -> Vec<String> {
+ x.as_primitive::<T>()
+ .values()
+ .into_iter()
+ .map(|x| {
+ as_date::<T>((*x).try_into().ok().unwrap())
+ .unwrap()
+ .to_string()
+ })
+ .collect()
+ };
+
+ let result = add(&a, &b).unwrap();
+ assert_eq!(
+ &format_array(result.as_ref()),
+ &[
+ "2013-03-30".to_string(),
+ "2013-01-03".to_string(),
+ "1996-06-29".to_string(),
+ ]
+ );
+ let result = sub(&result, &b).unwrap();
+ assert_eq!(result.as_ref(), &a);
+
+ let b = IntervalDayTimeArray::from(vec![
+ IntervalDayTimeType::make_value(34, 2),
+ IntervalDayTimeType::make_value(3, -3),
+ IntervalDayTimeType::make_value(-12, 4),
+ ]);
+
+ let result = add(&a, &b).unwrap();
+ assert_eq!(
+ &format_array(result.as_ref()),
+ &[
+ "1979-03-05".to_string(),
+ "2010-04-06".to_string(),
+ "2008-02-17".to_string(),
+ ]
+ );
+ let result = sub(&result, &b).unwrap();
+ assert_eq!(result.as_ref(), &a);
+
+ let b = IntervalMonthDayNanoArray::from(vec![
+ IntervalMonthDayNanoType::make_value(34, 2, -34353534),
+ IntervalMonthDayNanoType::make_value(3, -3, 2443),
+ IntervalMonthDayNanoType::make_value(-12, 4, 2323242423232),
+ ]);
+
+ let result = add(&a, &b).unwrap();
+ assert_eq!(
+ &format_array(result.as_ref()),
+ &[
+ "1981-12-02".to_string(),
+ "2010-06-30".to_string(),
+ "2007-03-04".to_string(),
+ ]
+ );
+ let result = sub(&result, &b).unwrap();
+ assert_eq!(
+ &format_array(result.as_ref()),
+ &[
+ "1979-01-31".to_string(),
+ "2010-04-02".to_string(),
+ "2008-02-29".to_string(),
+ ]
+ );
+ }
+
+ #[test]
+ fn test_date() {
+ test_date_impl::<Date32Type, _>(Date32Type::from_naive_date);
+ test_date_impl::<Date64Type, _>(Date64Type::from_naive_date);
+ }
}
diff --git a/arrow-array/src/arithmetic.rs b/arrow-array/src/arithmetic.rs
index abeb46b99..b0ecef70e 100644
--- a/arrow-array/src/arithmetic.rs
+++ b/arrow-array/src/arithmetic.rs
@@ -229,7 +229,10 @@ macro_rules! native_type_op {
#[inline]
fn pow_checked(self, exp: u32) -> Result<Self, ArrowError> {
self.checked_pow(exp).ok_or_else(|| {
- ArrowError::ComputeError(format!("Overflow happened on: {:?}", self))
+ ArrowError::ComputeError(format!(
+ "Overflow happened on: {:?} ^ {exp:?}",
+ self
+ ))
})
}
diff --git a/arrow-array/src/types.rs b/arrow-array/src/types.rs
index 0a65c64ad..3d14cff38 100644
--- a/arrow-array/src/types.rs
+++ b/arrow-array/src/types.rs
@@ -457,17 +457,17 @@ impl TimestampSecondType {
timestamp: <TimestampSecondType as ArrowPrimitiveType>::Native,
delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
) -> Result<<TimestampSecondType as ArrowPrimitiveType>::Native, ArrowError> {
- let (days, ms) = IntervalDayTimeType::to_parts(-delta);
+ let (days, ms) = IntervalDayTimeType::to_parts(delta);
let res = NaiveDateTime::from_timestamp_opt(timestamp, 0).ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
let res = res
- .checked_add_signed(Duration::days(days as i64))
+ .checked_sub_signed(Duration::days(days as i64))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
let res = res
- .checked_add_signed(Duration::microseconds(ms as i64))
+ .checked_sub_signed(Duration::milliseconds(ms as i64))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
@@ -491,12 +491,12 @@ impl TimestampSecondType {
})?;
let res = shift_months(res, -months);
let res = res
- .checked_add_signed(Duration::days(-days as i64))
+ .checked_sub_signed(Duration::days(days as i64))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
let res = res
- .checked_add_signed(Duration::nanoseconds(-nanos))
+ .checked_sub_signed(Duration::nanoseconds(nanos))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
@@ -616,17 +616,17 @@ impl TimestampMicrosecondType {
delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
) -> Result<<TimestampMicrosecondType as ArrowPrimitiveType>::Native, ArrowError>
{
- let (days, ms) = IntervalDayTimeType::to_parts(-delta);
+ let (days, ms) = IntervalDayTimeType::to_parts(delta);
let res = NaiveDateTime::from_timestamp_micros(timestamp).ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
let res = res
- .checked_add_signed(Duration::days(days as i64))
+ .checked_sub_signed(Duration::days(days as i64))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
let res = res
- .checked_add_signed(Duration::milliseconds(ms as i64))
+ .checked_sub_signed(Duration::milliseconds(ms as i64))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
@@ -651,12 +651,12 @@ impl TimestampMicrosecondType {
})?;
let res = shift_months(res, -months);
let res = res
- .checked_add_signed(Duration::days(-days as i64))
+ .checked_sub_signed(Duration::days(days as i64))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
let res = res
- .checked_add_signed(Duration::nanoseconds(-nanos))
+ .checked_sub_signed(Duration::nanoseconds(nanos))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
@@ -776,17 +776,17 @@ impl TimestampMillisecondType {
delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
) -> Result<<TimestampMillisecondType as ArrowPrimitiveType>::Native, ArrowError>
{
- let (days, ms) = IntervalDayTimeType::to_parts(-delta);
+ let (days, ms) = IntervalDayTimeType::to_parts(delta);
let res = NaiveDateTime::from_timestamp_millis(timestamp).ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
let res = res
- .checked_add_signed(Duration::days(days as i64))
+ .checked_sub_signed(Duration::days(days as i64))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
let res = res
- .checked_add_signed(Duration::milliseconds(ms as i64))
+ .checked_sub_signed(Duration::milliseconds(ms as i64))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
@@ -811,12 +811,12 @@ impl TimestampMillisecondType {
})?;
let res = shift_months(res, -months);
let res = res
- .checked_add_signed(Duration::days(-days as i64))
+ .checked_sub_signed(Duration::days(days as i64))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
let res = res
- .checked_add_signed(Duration::nanoseconds(-nanos))
+ .checked_sub_signed(Duration::nanoseconds(nanos))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
@@ -946,14 +946,14 @@ impl TimestampNanosecondType {
|| ArrowError::ComputeError("Timestamp out of range".to_string()),
)?;
- let (days, ms) = IntervalDayTimeType::to_parts(-delta);
+ let (days, ms) = IntervalDayTimeType::to_parts(delta);
let res = res
- .checked_add_signed(Duration::days(days as i64))
+ .checked_sub_signed(Duration::days(days as i64))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
let res = res
- .checked_add_signed(Duration::milliseconds(ms as i64))
+ .checked_sub_signed(Duration::milliseconds(ms as i64))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
@@ -980,12 +980,12 @@ impl TimestampNanosecondType {
let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
let res = shift_months(res, -months);
let res = res
- .checked_add_signed(Duration::days(-days as i64))
+ .checked_sub_signed(Duration::days(days as i64))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
let res = res
- .checked_add_signed(Duration::nanoseconds(-nanos))
+ .checked_sub_signed(Duration::nanoseconds(nanos))
.ok_or_else(|| {
ArrowError::ComputeError("Timestamp out of range".to_string())
})?;
@@ -1212,10 +1212,10 @@ impl Date32Type {
date: <Date32Type as ArrowPrimitiveType>::Native,
delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
) -> <Date32Type as ArrowPrimitiveType>::Native {
- let (days, ms) = IntervalDayTimeType::to_parts(-delta);
+ let (days, ms) = IntervalDayTimeType::to_parts(delta);
let res = Date32Type::to_naive_date(date);
- let res = res.add(Duration::days(days as i64));
- let res = res.add(Duration::milliseconds(ms as i64));
+ let res = res.sub(Duration::days(days as i64));
+ let res = res.sub(Duration::milliseconds(ms as i64));
Date32Type::from_naive_date(res)
}
@@ -1232,8 +1232,8 @@ impl Date32Type {
let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
let res = Date32Type::to_naive_date(date);
let res = shift_months(res, -months);
- let res = res.add(Duration::days(-days as i64));
- let res = res.add(Duration::nanoseconds(-nanos));
+ let res = res.sub(Duration::days(days as i64));
+ let res = res.sub(Duration::nanoseconds(nanos));
Date32Type::from_naive_date(res)
}
}
@@ -1336,10 +1336,10 @@ impl Date64Type {
date: <Date64Type as ArrowPrimitiveType>::Native,
delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
) -> <Date64Type as ArrowPrimitiveType>::Native {
- let (days, ms) = IntervalDayTimeType::to_parts(-delta);
+ let (days, ms) = IntervalDayTimeType::to_parts(delta);
let res = Date64Type::to_naive_date(date);
- let res = res.add(Duration::days(days as i64));
- let res = res.add(Duration::milliseconds(ms as i64));
+ let res = res.sub(Duration::days(days as i64));
+ let res = res.sub(Duration::milliseconds(ms as i64));
Date64Type::from_naive_date(res)
}
@@ -1356,8 +1356,8 @@ impl Date64Type {
let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
let res = Date64Type::to_naive_date(date);
let res = shift_months(res, -months);
- let res = res.add(Duration::days(-days as i64));
- let res = res.add(Duration::nanoseconds(-nanos));
+ let res = res.sub(Duration::days(days as i64));
+ let res = res.sub(Duration::nanoseconds(nanos));
Date64Type::from_naive_date(res)
}
}