1use crate::cast::*;
19use crate::parse::{DecimalParseError, parse_decimal_checked};
20
21pub trait DecimalCast: Sized {
24 fn to_i32(self) -> Option<i32>;
26
27 fn to_i64(self) -> Option<i64>;
29
30 fn to_i128(self) -> Option<i128>;
32
33 fn to_i256(self) -> Option<i256>;
35
36 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self>;
38
39 fn from_f64(n: f64) -> Option<Self>;
41}
42
43impl DecimalCast for i32 {
44 fn to_i32(self) -> Option<i32> {
45 Some(self)
46 }
47
48 fn to_i64(self) -> Option<i64> {
49 Some(self as i64)
50 }
51
52 fn to_i128(self) -> Option<i128> {
53 Some(self as i128)
54 }
55
56 fn to_i256(self) -> Option<i256> {
57 Some(i256::from_i128(self as i128))
58 }
59
60 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
61 n.to_i32()
62 }
63
64 fn from_f64(n: f64) -> Option<Self> {
65 n.to_i32()
66 }
67}
68
69impl DecimalCast for i64 {
70 fn to_i32(self) -> Option<i32> {
71 i32::try_from(self).ok()
72 }
73
74 fn to_i64(self) -> Option<i64> {
75 Some(self)
76 }
77
78 fn to_i128(self) -> Option<i128> {
79 Some(self as i128)
80 }
81
82 fn to_i256(self) -> Option<i256> {
83 Some(i256::from_i128(self as i128))
84 }
85
86 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
87 n.to_i64()
88 }
89
90 fn from_f64(n: f64) -> Option<Self> {
91 num_traits::ToPrimitive::to_i64(&n)
94 }
95}
96
97impl DecimalCast for i128 {
98 fn to_i32(self) -> Option<i32> {
99 i32::try_from(self).ok()
100 }
101
102 fn to_i64(self) -> Option<i64> {
103 i64::try_from(self).ok()
104 }
105
106 fn to_i128(self) -> Option<i128> {
107 Some(self)
108 }
109
110 fn to_i256(self) -> Option<i256> {
111 Some(i256::from_i128(self))
112 }
113
114 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
115 n.to_i128()
116 }
117
118 fn from_f64(n: f64) -> Option<Self> {
119 n.to_i128()
120 }
121}
122
123impl DecimalCast for i256 {
124 fn to_i32(self) -> Option<i32> {
125 self.to_i128().map(|x| i32::try_from(x).ok())?
126 }
127
128 fn to_i64(self) -> Option<i64> {
129 self.to_i128().map(|x| i64::try_from(x).ok())?
130 }
131
132 fn to_i128(self) -> Option<i128> {
133 self.to_i128()
134 }
135
136 fn to_i256(self) -> Option<i256> {
137 Some(self)
138 }
139
140 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
141 n.to_i256()
142 }
143
144 fn from_f64(n: f64) -> Option<Self> {
145 i256::from_f64(n)
146 }
147}
148
149#[expect(clippy::type_complexity)]
162fn make_upscaler<I: DecimalType, O: DecimalType>(
163 input_precision: u8,
164 input_scale: i8,
165 output_precision: u8,
166 output_scale: i8,
167) -> Option<(
168 impl Fn(I::Native) -> Option<O::Native>,
169 Option<impl Fn(I::Native) -> O::Native>,
170)>
171where
172 I::Native: DecimalCast + ArrowNativeTypeOp,
173 O::Native: DecimalCast + ArrowNativeTypeOp,
174{
175 let delta_scale = output_scale - input_scale;
176
177 let max = O::MAX_FOR_EACH_PRECISION.get(delta_scale as usize)?;
182 let mul = max.add_wrapping(O::Native::ONE);
183 let f_fallible = move |x| O::Native::from_decimal(x)?.mul_checked(mul).ok();
184
185 let is_infallible_cast = (input_precision as i8) + delta_scale <= (output_precision as i8);
192 let f_infallible = is_infallible_cast
193 .then_some(move |x| O::Native::from_decimal(x).unwrap().mul_wrapping(mul));
194 Some((f_fallible, f_infallible))
195}
196
197#[expect(clippy::type_complexity)]
211fn make_downscaler<I: DecimalType, O: DecimalType>(
212 input_precision: u8,
213 input_scale: i8,
214 output_precision: u8,
215 output_scale: i8,
216) -> Option<(
217 impl Fn(I::Native) -> Option<O::Native>,
218 Option<impl Fn(I::Native) -> O::Native>,
219)>
220where
221 I::Native: DecimalCast + ArrowNativeTypeOp,
222 O::Native: DecimalCast + ArrowNativeTypeOp,
223{
224 let delta_scale = input_scale - output_scale;
225
226 let max = I::MAX_FOR_EACH_PRECISION.get(delta_scale as usize)?;
233
234 let div = max.add_wrapping(I::Native::ONE);
235 let half = div.div_wrapping(I::Native::ONE.add_wrapping(I::Native::ONE));
236 let half_neg = half.neg_wrapping();
237
238 let f_fallible = move |x: I::Native| {
239 let d = x.div_wrapping(div);
241 let r = x.mod_wrapping(div);
242
243 let adjusted = match x >= I::Native::ZERO {
245 true if r >= half => d.add_wrapping(I::Native::ONE),
246 false if r <= half_neg => d.sub_wrapping(I::Native::ONE),
247 _ => d,
248 };
249 O::Native::from_decimal(adjusted)
250 };
251
252 let is_infallible_cast = (input_precision as i8) - delta_scale < (output_precision as i8);
263 let f_infallible = is_infallible_cast.then_some(move |x| f_fallible(x).unwrap());
264 Some((f_fallible, f_infallible))
265}
266
267fn apply_rescaler<I: DecimalType, O: DecimalType>(
271 value: I::Native,
272 output_precision: u8,
273 f: impl Fn(I::Native) -> Option<O::Native>,
274 f_infallible: Option<impl Fn(I::Native) -> O::Native>,
275) -> Option<O::Native>
276where
277 I::Native: DecimalCast,
278 O::Native: DecimalCast,
279{
280 if let Some(f_infallible) = f_infallible {
281 Some(f_infallible(value))
282 } else {
283 f(value).filter(|v| O::is_valid_decimal_precision(*v, output_precision))
284 }
285}
286
287pub fn rescale_decimal<I: DecimalType, O: DecimalType>(
303 value: I::Native,
304 input_precision: u8,
305 input_scale: i8,
306 output_precision: u8,
307 output_scale: i8,
308) -> Option<O::Native>
309where
310 I::Native: DecimalCast + ArrowNativeTypeOp,
311 O::Native: DecimalCast + ArrowNativeTypeOp,
312{
313 validate_decimal_precision_and_scale::<I>(input_precision, input_scale).ok()?;
314 validate_decimal_precision_and_scale::<O>(output_precision, output_scale).ok()?;
315
316 if input_scale <= output_scale {
317 let (f, f_infallible) =
318 make_upscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)?;
319 apply_rescaler::<I, O>(value, output_precision, f, f_infallible)
320 } else {
321 let Some((f, f_infallible)) =
322 make_downscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)
323 else {
324 return Some(O::Native::ZERO);
326 };
327 apply_rescaler::<I, O>(value, output_precision, f, f_infallible)
328 }
329}
330
331fn cast_decimal_to_decimal_error<I, O>(
332 output_precision: u8,
333 output_scale: i8,
334) -> impl Fn(<I as ArrowPrimitiveType>::Native) -> ArrowError
335where
336 I: DecimalType,
337 O: DecimalType,
338 I::Native: DecimalCast + ArrowNativeTypeOp,
339 O::Native: DecimalCast + ArrowNativeTypeOp,
340{
341 move |x: I::Native| {
342 ArrowError::CastError(format!(
343 "Cannot cast to {}({}, {}). Overflowing on {:?}",
344 O::PREFIX,
345 output_precision,
346 output_scale,
347 x
348 ))
349 }
350}
351
352fn apply_decimal_cast<I: DecimalType, O: DecimalType>(
353 array: &PrimitiveArray<I>,
354 output_precision: u8,
355 output_scale: i8,
356 f_fallible: impl Fn(I::Native) -> Option<O::Native>,
357 f_infallible: Option<impl Fn(I::Native) -> O::Native>,
358 cast_options: &CastOptions,
359) -> Result<PrimitiveArray<O>, ArrowError>
360where
361 I::Native: DecimalCast + ArrowNativeTypeOp,
362 O::Native: DecimalCast + ArrowNativeTypeOp,
363{
364 let array = if let Some(f_infallible) = f_infallible {
365 array.unary(f_infallible)
366 } else if cast_options.safe {
367 array.unary_opt(|x| {
368 f_fallible(x).filter(|v| O::is_valid_decimal_precision(*v, output_precision))
369 })
370 } else {
371 let error = cast_decimal_to_decimal_error::<I, O>(output_precision, output_scale);
372 array.try_unary(|x| {
373 let v = f_fallible(x).ok_or_else(|| error(x))?;
374 O::validate_decimal_precision(v, output_precision, output_scale).map(|()| v)
375 })?
376 };
377 Ok(array)
378}
379
380fn convert_to_smaller_scale_decimal<I, O>(
381 array: &PrimitiveArray<I>,
382 input_precision: u8,
383 input_scale: i8,
384 output_precision: u8,
385 output_scale: i8,
386 cast_options: &CastOptions,
387) -> Result<PrimitiveArray<O>, ArrowError>
388where
389 I: DecimalType,
390 O: DecimalType,
391 I::Native: DecimalCast + ArrowNativeTypeOp,
392 O::Native: DecimalCast + ArrowNativeTypeOp,
393{
394 if let Some((f_fallible, f_infallible)) =
395 make_downscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)
396 {
397 apply_decimal_cast(
398 array,
399 output_precision,
400 output_scale,
401 f_fallible,
402 f_infallible,
403 cast_options,
404 )
405 } else {
406 let zeros = vec![O::Native::ZERO; array.len()];
408 Ok(PrimitiveArray::new(zeros.into(), array.nulls().cloned()))
409 }
410}
411
412fn convert_to_bigger_or_equal_scale_decimal<I, O>(
413 array: &PrimitiveArray<I>,
414 input_precision: u8,
415 input_scale: i8,
416 output_precision: u8,
417 output_scale: i8,
418 cast_options: &CastOptions,
419) -> Result<PrimitiveArray<O>, ArrowError>
420where
421 I: DecimalType,
422 O: DecimalType,
423 I::Native: DecimalCast + ArrowNativeTypeOp,
424 O::Native: DecimalCast + ArrowNativeTypeOp,
425{
426 if let Some((f, f_infallible)) =
427 make_upscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)
428 {
429 apply_decimal_cast(
430 array,
431 output_precision,
432 output_scale,
433 f,
434 f_infallible,
435 cast_options,
436 )
437 } else {
438 Err(ArrowError::CastError(format!(
440 "Cannot cast to {}({}, {}). Value overflows for output scale",
441 O::PREFIX,
442 output_precision,
443 output_scale
444 )))
445 }
446}
447
448pub(crate) fn cast_decimal_to_decimal_same_type<T>(
450 array: &PrimitiveArray<T>,
451 input_precision: u8,
452 input_scale: i8,
453 output_precision: u8,
454 output_scale: i8,
455 cast_options: &CastOptions,
456) -> Result<ArrayRef, ArrowError>
457where
458 T: DecimalType,
459 T::Native: DecimalCast + ArrowNativeTypeOp,
460{
461 let array: PrimitiveArray<T> =
462 if input_scale == output_scale && input_precision <= output_precision {
463 array.clone()
464 } else if input_scale <= output_scale {
465 convert_to_bigger_or_equal_scale_decimal::<T, T>(
466 array,
467 input_precision,
468 input_scale,
469 output_precision,
470 output_scale,
471 cast_options,
472 )?
473 } else {
474 convert_to_smaller_scale_decimal::<T, T>(
476 array,
477 input_precision,
478 input_scale,
479 output_precision,
480 output_scale,
481 cast_options,
482 )?
483 };
484
485 Ok(Arc::new(array.with_precision_and_scale(
486 output_precision,
487 output_scale,
488 )?))
489}
490
491pub(crate) fn cast_decimal_to_decimal<I, O>(
493 array: &PrimitiveArray<I>,
494 input_precision: u8,
495 input_scale: i8,
496 output_precision: u8,
497 output_scale: i8,
498 cast_options: &CastOptions,
499) -> Result<ArrayRef, ArrowError>
500where
501 I: DecimalType,
502 O: DecimalType,
503 I::Native: DecimalCast + ArrowNativeTypeOp,
504 O::Native: DecimalCast + ArrowNativeTypeOp,
505{
506 let array: PrimitiveArray<O> = if input_scale > output_scale {
507 convert_to_smaller_scale_decimal::<I, O>(
508 array,
509 input_precision,
510 input_scale,
511 output_precision,
512 output_scale,
513 cast_options,
514 )?
515 } else {
516 convert_to_bigger_or_equal_scale_decimal::<I, O>(
517 array,
518 input_precision,
519 input_scale,
520 output_precision,
521 output_scale,
522 cast_options,
523 )?
524 };
525
526 Ok(Arc::new(array.with_precision_and_scale(
527 output_precision,
528 output_scale,
529 )?))
530}
531
532#[deprecated(
541 since = "60.0.0",
542 note = "Use `arrow_cast::parse::parse_decimal` instead"
543)]
544pub fn parse_string_to_decimal_native<T: DecimalType>(
545 value_str: &str,
546 scale: usize,
547) -> Result<T::Native, ArrowError> {
548 let overflow = || {
549 ArrowError::InvalidArgumentError(format!(
550 "Cannot convert {value_str} to {}: Overflow",
551 T::PREFIX
552 ))
553 };
554 let scale = i8::try_from(scale).map_err(|_| overflow())?;
555 parse_decimal_checked::<T>(value_str, T::MAX_PRECISION, scale).map_err(|e| match e {
556 DecimalParseError::InvalidFormat => {
557 ArrowError::InvalidArgumentError(format!("Invalid decimal format: {value_str:?}"))
558 }
559 DecimalParseError::Overflow => overflow(),
560 })
561}
562
563pub(crate) fn generic_string_to_decimal_cast<'a, T, S>(
564 from: &'a S,
565 precision: u8,
566 scale: i8,
567 cast_options: &CastOptions,
568) -> Result<PrimitiveArray<T>, ArrowError>
569where
570 T: DecimalType,
571 &'a S: StringArrayType<'a>,
572{
573 if cast_options.safe {
574 let iter = from
575 .iter()
576 .map(|v| parse_decimal_checked::<T>(v?, precision, scale).ok());
577 Ok(unsafe {
583 PrimitiveArray::<T>::from_trusted_len_iter(iter)
584 .with_precision_and_scale(precision, scale)?
585 })
586 } else {
587 let mut builder = PrimitiveBuilder::<T>::with_capacity(from.len());
588 for v in from.iter() {
589 match v {
590 Some(v) => {
591 let v = parse_decimal_checked::<T>(v, precision, scale).map_err(|e| {
592 let reason = match e {
593 DecimalParseError::InvalidFormat => "invalid decimal format",
594 DecimalParseError::Overflow => "value does not fit",
595 };
596 ArrowError::CastError(format!(
597 "Cannot cast string '{v}' to value of {}({precision}, {scale}) type: {reason}",
598 T::PREFIX,
599 ))
600 })?;
601 builder.append_value(v);
602 }
603 None => builder.append_null(),
604 }
605 }
606 builder.finish().with_precision_and_scale(precision, scale)
607 }
608}
609
610pub(crate) fn string_to_decimal_cast<T: DecimalType, Offset: OffsetSizeTrait>(
611 from: &GenericStringArray<Offset>,
612 precision: u8,
613 scale: i8,
614 cast_options: &CastOptions,
615) -> Result<PrimitiveArray<T>, ArrowError> {
616 generic_string_to_decimal_cast::<T, GenericStringArray<Offset>>(
617 from,
618 precision,
619 scale,
620 cast_options,
621 )
622}
623
624pub(crate) fn string_view_to_decimal_cast<T: DecimalType>(
625 from: &StringViewArray,
626 precision: u8,
627 scale: i8,
628 cast_options: &CastOptions,
629) -> Result<PrimitiveArray<T>, ArrowError> {
630 generic_string_to_decimal_cast::<T, StringViewArray>(from, precision, scale, cast_options)
631}
632
633pub(crate) fn cast_string_to_decimal<T: DecimalType, Offset: OffsetSizeTrait>(
635 from: &dyn Array,
636 precision: u8,
637 scale: i8,
638 cast_options: &CastOptions,
639) -> Result<ArrayRef, ArrowError> {
640 validate_decimal_precision_and_scale::<T>(precision, scale)?;
641
642 let result = match from.data_type() {
643 DataType::Utf8View => string_view_to_decimal_cast::<T>(
644 from.as_any().downcast_ref::<StringViewArray>().unwrap(),
645 precision,
646 scale,
647 cast_options,
648 )?,
649 DataType::Utf8 | DataType::LargeUtf8 => string_to_decimal_cast::<T, Offset>(
650 from.as_any()
651 .downcast_ref::<GenericStringArray<Offset>>()
652 .unwrap(),
653 precision,
654 scale,
655 cast_options,
656 )?,
657 other => {
658 return Err(ArrowError::ComputeError(format!(
659 "Cannot cast {other:?} to decimal",
660 )));
661 }
662 };
663
664 Ok(Arc::new(result))
665}
666
667pub(crate) fn cast_floating_point_to_decimal<T: ArrowPrimitiveType, D>(
668 array: &PrimitiveArray<T>,
669 precision: u8,
670 scale: i8,
671 cast_options: &CastOptions,
672) -> Result<ArrayRef, ArrowError>
673where
674 <T as ArrowPrimitiveType>::Native: AsPrimitive<f64>,
675 D: DecimalType + ArrowPrimitiveType,
676 <D as ArrowPrimitiveType>::Native: DecimalCast,
677{
678 let mul = 10_f64.powi(scale as i32);
679
680 if cast_options.safe {
681 array
682 .unary_opt::<_, D>(|v| {
683 single_float_to_decimal::<D>(v.as_(), mul)
684 .filter(|v| D::is_valid_decimal_precision(*v, precision))
685 })
686 .with_precision_and_scale(precision, scale)
687 .map(|a| Arc::new(a) as ArrayRef)
688 } else {
689 array
690 .try_unary::<_, D, _>(|v| {
691 let v = single_float_to_decimal::<D>(v.as_(), mul).ok_or_else(|| {
692 ArrowError::CastError(format!(
693 "Cannot cast to {}({}, {}). Overflowing on {:?}",
694 D::PREFIX,
695 precision,
696 scale,
697 v
698 ))
699 })?;
700 D::validate_decimal_precision(v, precision, scale).map(|()| v)
701 })?
702 .with_precision_and_scale(precision, scale)
703 .map(|a| Arc::new(a) as ArrayRef)
704 }
705}
706
707#[inline(always)]
710pub fn single_float_to_decimal<D>(input: f64, mul: f64) -> Option<D::Native>
711where
712 D: DecimalType + ArrowPrimitiveType,
713 <D as ArrowPrimitiveType>::Native: DecimalCast,
714{
715 D::Native::from_f64((mul * input).round())
716}
717
718pub(crate) fn cast_decimal_to_integer<D, T>(
719 array: &dyn Array,
720 base: D::Native,
721 scale: i8,
722 cast_options: &CastOptions,
723) -> Result<ArrayRef, ArrowError>
724where
725 T: ArrowPrimitiveType,
726 <T as ArrowPrimitiveType>::Native: NumCast,
727 D: DecimalType + ArrowPrimitiveType,
728 <D as ArrowPrimitiveType>::Native: ToPrimitive,
729{
730 let array = array.as_primitive::<D>();
731
732 let div: D::Native = base.pow_checked(scale.unsigned_abs() as u32).map_err(|_| {
733 ArrowError::CastError(format!(
734 "Cannot cast to {:?}. The scale {} causes overflow.",
735 D::PREFIX,
736 scale,
737 ))
738 })?;
739
740 let mut value_builder = PrimitiveBuilder::<T>::with_capacity(array.len());
741
742 if scale < 0 {
743 match cast_options.safe {
744 true => {
745 for i in 0..array.len() {
746 if array.is_null(i) {
747 value_builder.append_null();
748 } else {
749 let v = array
750 .value(i)
751 .mul_checked(div)
752 .ok()
753 .and_then(<T::Native as NumCast>::from::<D::Native>);
754 value_builder.append_option(v);
755 }
756 }
757 }
758 false => {
759 for i in 0..array.len() {
760 if array.is_null(i) {
761 value_builder.append_null();
762 } else {
763 let v = array.value(i).mul_checked(div)?;
764
765 let value =
766 <T::Native as NumCast>::from::<D::Native>(v).ok_or_else(|| {
767 ArrowError::CastError(format!(
768 "value of {:?} is out of range {}",
769 v,
770 T::DATA_TYPE
771 ))
772 })?;
773
774 value_builder.append_value(value);
775 }
776 }
777 }
778 }
779 } else {
780 match cast_options.safe {
781 true => {
782 for i in 0..array.len() {
783 if array.is_null(i) {
784 value_builder.append_null();
785 } else {
786 let v = array
787 .value(i)
788 .div_checked(div)
789 .ok()
790 .and_then(<T::Native as NumCast>::from::<D::Native>);
791 value_builder.append_option(v);
792 }
793 }
794 }
795 false => {
796 for i in 0..array.len() {
797 if array.is_null(i) {
798 value_builder.append_null();
799 } else {
800 let v = array.value(i).div_checked(div)?;
801
802 let value =
803 <T::Native as NumCast>::from::<D::Native>(v).ok_or_else(|| {
804 ArrowError::CastError(format!(
805 "value of {:?} is out of range {}",
806 v,
807 T::DATA_TYPE
808 ))
809 })?;
810
811 value_builder.append_value(value);
812 }
813 }
814 }
815 }
816 }
817 Ok(Arc::new(value_builder.finish()))
818}
819
820pub(crate) fn cast_decimal_to_float<D: DecimalType, T: ArrowPrimitiveType, F>(
826 array: &dyn Array,
827 op: F,
828) -> Result<ArrayRef, ArrowError>
829where
830 F: Fn(D::Native) -> T::Native,
831{
832 let array = array.as_primitive::<D>();
833 let array = array.unary::<_, T>(op);
834 Ok(Arc::new(array))
835}
836
837#[cfg(test)]
838mod tests {
839 use super::*;
840
841 #[test]
842 #[expect(deprecated)]
843 fn test_parse_string_to_decimal_native() {
844 assert_eq!(
845 parse_string_to_decimal_native::<Decimal128Type>("123.456", 2).unwrap(),
846 12346
847 );
848 assert_eq!(
851 parse_string_to_decimal_native::<Decimal128Type>(&"9".repeat(38), 0).unwrap(),
852 10_i128.pow(38) - 1
853 );
854 assert!(
855 parse_string_to_decimal_native::<Decimal128Type>(&i128::MAX.to_string(), 0).is_err()
856 );
857 assert_eq!(
858 parse_string_to_decimal_native::<Decimal128Type>("abc", 2)
859 .unwrap_err()
860 .to_string(),
861 "Invalid argument error: Invalid decimal format: \"abc\""
862 );
863 assert_eq!(
864 parse_string_to_decimal_native::<Decimal32Type>("1", 10)
865 .unwrap_err()
866 .to_string(),
867 "Invalid argument error: Cannot convert 1 to Decimal32: Overflow"
868 );
869 }
870
871 #[test]
872 fn test_rescale_decimal_upscale_within_precision() {
873 let result = rescale_decimal::<Decimal128Type, Decimal128Type>(
874 12_345_i128, 5,
876 2,
877 8,
878 5,
879 );
880 assert_eq!(result, Some(12_345_000_i128));
881 }
882
883 #[test]
884 fn test_rescale_decimal_downscale_rounds_half_away_from_zero() {
885 let positive = rescale_decimal::<Decimal128Type, Decimal128Type>(
886 1_050_i128, 5, 3, 5, 1,
888 );
889 assert_eq!(positive, Some(11_i128)); let negative = rescale_decimal::<Decimal128Type, Decimal128Type>(
892 -1_050_i128, 5,
894 3,
895 5,
896 1,
897 );
898 assert_eq!(negative, Some(-11_i128)); }
900
901 #[test]
902 fn test_rescale_decimal_downscale_large_delta_returns_zero() {
903 let result = rescale_decimal::<Decimal32Type, Decimal32Type>(12_345_i32, 9, 9, 9, 4);
904 assert_eq!(result, Some(0_i32));
905 }
906
907 #[test]
908 fn test_rescale_decimal_upscale_overflow_returns_none() {
909 let result = rescale_decimal::<Decimal32Type, Decimal32Type>(9_999_i32, 4, 0, 5, 2);
910 assert_eq!(result, None);
911 }
912
913 #[test]
914 fn test_rescale_decimal_invalid_input_precision_scale_returns_none() {
915 let result = rescale_decimal::<Decimal128Type, Decimal128Type>(123_i128, 39, 39, 38, 38);
916 assert_eq!(result, None);
917 }
918
919 #[test]
920 fn test_rescale_decimal_invalid_output_precision_scale_returns_none() {
921 let result = rescale_decimal::<Decimal128Type, Decimal128Type>(123_i128, 38, 38, 39, 39);
922 assert_eq!(result, None);
923 }
924}