Skip to content

Commit 980667f

Browse files
authored
Add interval multiplication by i64 (#10336)
# Which issue does this PR close? Part of #9030. # Rationale for this change As part of updating Datafusion to better support date / time math operations (apache/datafusion#19022 among others) it was uncovered that `arrow-rs` does not support interval * number ops that should be valid. For example: interval '1 second' * 900 → 00:15:00 interval '1 day' * 21 → 21 days this pr only include the interval multiply integer support because div and floating is too complex in case of #6906 (comment) # What changes are included in this PR? Allow: - (Int64, Interval(YearMonth)) - (Int64, Interval(DayTime)) - (Int64, Interval(MonthDayNano) and their swap to multiple pair by pair. # Are these changes tested? - `cargo test -p arrow-arith` # Are there any user-facing changes? yes, `arithmetic_op` now accept 1. (Interval(YearMonth), Int64) 2. (Interval(DayTime), Int64) 3. (Interval(MonthDayNano), Int64) 4. (Int64, Interval(YearMonth)) 5. (Int64, Interval(DayTime)) 6. (Int64, Interval(MonthDayNano)) as its (lhs, rhs) pair type.
1 parent 30c10d5 commit 980667f

1 file changed

Lines changed: 117 additions & 0 deletions

File tree

arrow-arith/src/numeric.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,12 @@ fn arithmetic_op(op: Op, lhs: &dyn Datum, rhs: &dyn Datum) -> Result<ArrayRef, A
243243
(Duration(Millisecond), Duration(Millisecond)) => duration_op::<DurationMillisecondType>(op, l, l_scalar, r, r_scalar),
244244
(Duration(Microsecond), Duration(Microsecond)) => duration_op::<DurationMicrosecondType>(op, l, l_scalar, r, r_scalar),
245245
(Duration(Nanosecond), Duration(Nanosecond)) => duration_op::<DurationNanosecondType>(op, l, l_scalar, r, r_scalar),
246+
(Interval(YearMonth), Int64) if matches!(op, Op::Mul) => interval_mul_op::<IntervalYearMonthType>(l, l_scalar, r, r_scalar),
247+
(Interval(DayTime), Int64) if matches!(op, Op::Mul) => interval_mul_op::<IntervalDayTimeType>(l, l_scalar, r, r_scalar),
248+
(Interval(MonthDayNano), Int64) if matches!(op, Op::Mul) => interval_mul_op::<IntervalMonthDayNanoType>(l, l_scalar, r, r_scalar),
249+
(Int64, Interval(YearMonth)) if matches!(op, Op::Mul) => interval_mul_op::<IntervalYearMonthType>(r, r_scalar, l, l_scalar),
250+
(Int64, Interval(DayTime)) if matches!(op, Op::Mul) => interval_mul_op::<IntervalDayTimeType>(r, r_scalar, l, l_scalar),
251+
(Int64, Interval(MonthDayNano)) if matches!(op, Op::Mul) => interval_mul_op::<IntervalMonthDayNanoType>(r, r_scalar, l, l_scalar),
246252
(Interval(YearMonth), Interval(YearMonth)) => interval_op::<IntervalYearMonthType>(op, l, l_scalar, r, r_scalar),
247253
(Interval(DayTime), Interval(DayTime)) => interval_op::<IntervalDayTimeType>(op, l, l_scalar, r, r_scalar),
248254
(Interval(MonthDayNano), Interval(MonthDayNano)) => interval_op::<IntervalMonthDayNanoType>(op, l, l_scalar, r, r_scalar),
@@ -621,6 +627,14 @@ date!(Date64Type);
621627
trait IntervalOp: ArrowPrimitiveType {
622628
fn add(left: Self::Native, right: Self::Native) -> Result<Self::Native, ArrowError>;
623629
fn sub(left: Self::Native, right: Self::Native) -> Result<Self::Native, ArrowError>;
630+
fn mul_i64(left: Self::Native, right: i64) -> Result<Self::Native, ArrowError>;
631+
}
632+
633+
fn mul_i32_i64(left: i32, right: i64) -> Result<i32, ArrowError> {
634+
let value = i64::from(left).mul_checked(right)?;
635+
i32::try_from(value).map_err(|_| {
636+
ArrowError::ArithmeticOverflow(format!("Overflow happened on: {left} * {right}"))
637+
})
624638
}
625639

626640
impl IntervalOp for IntervalYearMonthType {
@@ -631,6 +645,10 @@ impl IntervalOp for IntervalYearMonthType {
631645
fn sub(left: Self::Native, right: Self::Native) -> Result<Self::Native, ArrowError> {
632646
left.sub_checked(right)
633647
}
648+
649+
fn mul_i64(left: Self::Native, right: i64) -> Result<Self::Native, ArrowError> {
650+
mul_i32_i64(left, right)
651+
}
634652
}
635653

636654
impl IntervalOp for IntervalDayTimeType {
@@ -649,6 +667,14 @@ impl IntervalOp for IntervalDayTimeType {
649667
let ms = l_ms.sub_checked(r_ms)?;
650668
Ok(Self::make_value(days, ms))
651669
}
670+
671+
fn mul_i64(left: Self::Native, right: i64) -> Result<Self::Native, ArrowError> {
672+
let (days, ms) = Self::to_parts(left);
673+
Ok(Self::make_value(
674+
mul_i32_i64(days, right)?,
675+
mul_i32_i64(ms, right)?,
676+
))
677+
}
652678
}
653679

654680
impl IntervalOp for IntervalMonthDayNanoType {
@@ -669,6 +695,33 @@ impl IntervalOp for IntervalMonthDayNanoType {
669695
let nanos = l_nanos.sub_checked(r_nanos)?;
670696
Ok(Self::make_value(months, days, nanos))
671697
}
698+
699+
fn mul_i64(left: Self::Native, right: i64) -> Result<Self::Native, ArrowError> {
700+
let (months, days, nanos) = Self::to_parts(left);
701+
Ok(Self::make_value(
702+
mul_i32_i64(months, right)?,
703+
mul_i32_i64(days, right)?,
704+
nanos.mul_checked(right)?,
705+
))
706+
}
707+
}
708+
709+
fn interval_mul_op<T: IntervalOp>(
710+
interval: &dyn Array,
711+
interval_scalar: bool,
712+
factor: &dyn Array,
713+
factor_scalar: bool,
714+
) -> Result<ArrayRef, ArrowError> {
715+
let interval = interval.as_primitive::<T>();
716+
let factor = factor.as_primitive::<Int64Type>();
717+
Ok(try_op_ref!(
718+
T,
719+
interval,
720+
interval_scalar,
721+
factor,
722+
factor_scalar,
723+
T::mul_i64(interval, factor)
724+
))
672725
}
673726

674727
/// Perform arithmetic operation on an interval array
@@ -1555,6 +1608,70 @@ mod tests {
15551608
);
15561609
}
15571610

1611+
#[test]
1612+
fn test_interval_mul_i64() {
1613+
let interval = IntervalYearMonthArray::from(vec![16, 5, 0]);
1614+
let factor = Int64Array::from(vec![3, -2, i64::MAX]);
1615+
let expected = IntervalYearMonthArray::from(vec![48, -10, 0]);
1616+
assert_eq!(mul(&interval, &factor).unwrap().as_ref(), &expected);
1617+
assert_eq!(mul(&factor, &interval).unwrap().as_ref(), &expected);
1618+
1619+
let interval = IntervalDayTimeArray::from(vec![
1620+
Some(IntervalDayTimeType::make_value(10, 2 * 60 * 60 * 1000)),
1621+
None,
1622+
]);
1623+
let factor = Int64Array::new_scalar(3);
1624+
let expected = IntervalDayTimeArray::from(vec![
1625+
Some(IntervalDayTimeType::make_value(30, 6 * 60 * 60 * 1000)),
1626+
None,
1627+
]);
1628+
assert_eq!(mul(&interval, &factor).unwrap().as_ref(), &expected);
1629+
assert_eq!(mul(&factor, &interval).unwrap().as_ref(), &expected);
1630+
1631+
let null_factor = Scalar::new(Int64Array::new_null(1));
1632+
let expected = IntervalDayTimeArray::new_null(interval.len());
1633+
assert_eq!(mul(&interval, &null_factor).unwrap().as_ref(), &expected);
1634+
assert_eq!(mul(&null_factor, &interval).unwrap().as_ref(), &expected);
1635+
1636+
let interval = IntervalMonthDayNanoArray::new_scalar(IntervalMonthDayNanoType::make_value(
1637+
12,
1638+
15,
1639+
5_000_000_000,
1640+
));
1641+
let factor = Int64Array::from(vec![2, 0, -1]);
1642+
let expected = IntervalMonthDayNanoArray::from(vec![
1643+
IntervalMonthDayNanoType::make_value(24, 30, 10_000_000_000),
1644+
IntervalMonthDayNanoType::make_value(0, 0, 0),
1645+
IntervalMonthDayNanoType::make_value(-12, -15, -5_000_000_000),
1646+
]);
1647+
assert_eq!(mul(&interval, &factor).unwrap().as_ref(), &expected);
1648+
assert_eq!(mul(&factor, &interval).unwrap().as_ref(), &expected);
1649+
1650+
let float_factor = Float64Array::new_scalar(2.);
1651+
assert!(mul(&interval, &float_factor).is_err());
1652+
assert!(mul_wrapping(&factor, &interval).is_err());
1653+
}
1654+
1655+
#[test]
1656+
fn test_interval_mul_i64_overflow() {
1657+
let interval = IntervalYearMonthArray::from(vec![i32::MAX]);
1658+
let factor = Int64Array::from(vec![2]);
1659+
assert_eq!(
1660+
mul(&interval, &factor).unwrap_err().to_string(),
1661+
"Arithmetic overflow: Overflow happened on: 2147483647 * 2"
1662+
);
1663+
1664+
let interval = IntervalMonthDayNanoArray::from(vec![IntervalMonthDayNanoType::make_value(
1665+
0,
1666+
0,
1667+
i64::MAX,
1668+
)]);
1669+
assert_eq!(
1670+
mul(&interval, &factor).unwrap_err().to_string(),
1671+
"Arithmetic overflow: Overflow happened on: 9223372036854775807 * 2"
1672+
);
1673+
}
1674+
15581675
fn test_duration_impl<T: ArrowPrimitiveType<Native = i64>>() {
15591676
let a = PrimitiveArray::<T>::new(vec![1000, 4394, -3944].into(), None);
15601677
let b = PrimitiveArray::<T>::new(vec![4, -5, -243].into(), None);

0 commit comments

Comments
 (0)