1use crate::cast::*;
6use crate::parse::{DecimalParseError, parse_decimal_checked};
7
8pub trait DecimalCast: Sized {
11 fn to_i32(self) -> Option<i32>;
13
14 fn to_i64(self) -> Option<i64>;
16
17 fn to_i128(self) -> Option<i128>;
19
20 fn to_i256(self) -> Option<i256>;
22
23 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self>;
25
26 fn from_f64(n: f64) -> Option<Self>;
28}
29
30impl DecimalCast for i32 {
31 fn to_i32(self) -> Option<i32> {
32 Some(self)
33 }
34
35 fn to_i64(self) -> Option<i64> {
36 Some(self as i64)
37 }
38
39 fn to_i128(self) -> Option<i128> {
40 Some(self as i128)
41 }
42
43 fn to_i256(self) -> Option<i256> {
44 Some(i256::from_i128(self as i128))
45 }
46
47 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
48 n.to_i32()
49 }
50
51 fn from_f64(n: f64) -> Option<Self> {
52 n.to_i32()
53 }
54}
55
56impl DecimalCast for i64 {
57 fn to_i32(self) -> Option<i32> {
58 i32::try_from(self).ok()
59 }
60
61 fn to_i64(self) -> Option<i64> {
62 Some(self)
63 }
64
65 fn to_i128(self) -> Option<i128> {
66 Some(self as i128)
67 }
68
69 fn to_i256(self) -> Option<i256> {
70 Some(i256::from_i128(self as i128))
71 }
72
73 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
74 n.to_i64()
75 }
76
77 fn from_f64(n: f64) -> Option<Self> {
78 num_traits::ToPrimitive::to_i64(&n)
81 }
82}
83
84impl DecimalCast for i128 {
85 fn to_i32(self) -> Option<i32> {
86 i32::try_from(self).ok()
87 }
88
89 fn to_i64(self) -> Option<i64> {
90 i64::try_from(self).ok()
91 }
92
93 fn to_i128(self) -> Option<i128> {
94 Some(self)
95 }
96
97 fn to_i256(self) -> Option<i256> {
98 Some(i256::from_i128(self))
99 }
100
101 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
102 n.to_i128()
103 }
104
105 fn from_f64(n: f64) -> Option<Self> {
106 n.to_i128()
107 }
108}
109
110impl DecimalCast for i256 {
111 fn to_i32(self) -> Option<i32> {
112 self.to_i128().map(|x| i32::try_from(x).ok())?
113 }
114
115 fn to_i64(self) -> Option<i64> {
116 self.to_i128().map(|x| i64::try_from(x).ok())?
117 }
118
119 fn to_i128(self) -> Option<i128> {
120 self.to_i128()
121 }
122
123 fn to_i256(self) -> Option<i256> {
124 Some(self)
125 }
126
127 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
128 n.to_i256()
129 }
130
131 fn from_f64(n: f64) -> Option<Self> {
132 i256::from_f64(n)
133 }
134}
135
136#[expect(clippy::type_complexity)]
149fn make_upscaler<I: DecimalType, O: DecimalType>(
150 input_precision: u8,
151 input_scale: i8,
152 output_precision: u8,
153 output_scale: i8,
154) -> Option<(
155 impl Fn(I::Native) -> Option<O::Native>,
156 Option<impl Fn(I::Native) -> O::Native>,
157)>
158where
159 I::Native: DecimalCast + ArrowNativeTypeOp,
160 O::Native: DecimalCast + ArrowNativeTypeOp,
161{
162 let delta_scale = output_scale as i16 - input_scale as i16;
163
164 let max = O::MAX_FOR_EACH_PRECISION.get(delta_scale as usize)?;
169 let mul = max.add_wrapping(O::Native::ONE);
170 let f_fallible = move |x| O::Native::from_decimal(x)?.mul_checked(mul).ok();
171
172 let is_infallible_cast = (input_precision as i16) + delta_scale <= (output_precision as i16);
179 let f_infallible = is_infallible_cast
180 .then_some(move |x| O::Native::from_decimal(x).unwrap().mul_wrapping(mul));
181 Some((f_fallible, f_infallible))
182}
183
184#[expect(clippy::type_complexity)]
198fn make_downscaler<I: DecimalType, O: DecimalType>(
199 input_precision: u8,
200 input_scale: i8,
201 output_precision: u8,
202 output_scale: i8,
203) -> Option<(
204 impl Fn(I::Native) -> Option<O::Native>,
205 Option<impl Fn(I::Native) -> O::Native>,
206)>
207where
208 I::Native: DecimalCast + ArrowNativeTypeOp,
209 O::Native: DecimalCast + ArrowNativeTypeOp,
210{
211 let delta_scale = input_scale as i16 - output_scale as i16;
212
213 let max = I::MAX_FOR_EACH_PRECISION.get(delta_scale as usize)?;
220
221 let div = max.add_wrapping(I::Native::ONE);
222 let half = div.div_wrapping(I::Native::ONE.add_wrapping(I::Native::ONE));
223 let half_neg = half.neg_wrapping();
224
225 let f_fallible = move |x: I::Native| {
226 let (d, r) = x.div_rem_wrapping(div);
228
229 let adjusted = match x >= I::Native::ZERO {
231 true if r >= half => d.add_wrapping(I::Native::ONE),
232 false if r <= half_neg => d.sub_wrapping(I::Native::ONE),
233 _ => d,
234 };
235 O::Native::from_decimal(adjusted)
236 };
237
238 let is_infallible_cast = (input_precision as i16) - delta_scale < (output_precision as i16);
249 let f_infallible = is_infallible_cast.then_some(move |x| f_fallible(x).unwrap());
250 Some((f_fallible, f_infallible))
251}
252
253fn apply_rescaler<I: DecimalType, O: DecimalType>(
257 value: I::Native,
258 output_precision: u8,
259 f: impl Fn(I::Native) -> Option<O::Native>,
260 f_infallible: Option<impl Fn(I::Native) -> O::Native>,
261) -> Option<O::Native>
262where
263 I::Native: DecimalCast,
264 O::Native: DecimalCast,
265{
266 if let Some(f_infallible) = f_infallible {
267 Some(f_infallible(value))
268 } else {
269 f(value).filter(|v| O::is_valid_decimal_precision(*v, output_precision))
270 }
271}
272
273pub fn rescale_decimal<I: DecimalType, O: DecimalType>(
289 value: I::Native,
290 input_precision: u8,
291 input_scale: i8,
292 output_precision: u8,
293 output_scale: i8,
294) -> Option<O::Native>
295where
296 I::Native: DecimalCast + ArrowNativeTypeOp,
297 O::Native: DecimalCast + ArrowNativeTypeOp,
298{
299 validate_decimal_precision_and_scale::<I>(input_precision, input_scale).ok()?;
300 validate_decimal_precision_and_scale::<O>(output_precision, output_scale).ok()?;
301
302 if input_scale <= output_scale {
303 let (f, f_infallible) =
304 make_upscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)?;
305 apply_rescaler::<I, O>(value, output_precision, f, f_infallible)
306 } else {
307 let Some((f, f_infallible)) =
308 make_downscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)
309 else {
310 return Some(O::Native::ZERO);
312 };
313 apply_rescaler::<I, O>(value, output_precision, f, f_infallible)
314 }
315}
316
317fn cast_decimal_to_decimal_error<I, O>(
318 output_precision: u8,
319 output_scale: i8,
320) -> impl Fn(<I as ArrowPrimitiveType>::Native) -> ArrowError
321where
322 I: DecimalType,
323 O: DecimalType,
324 I::Native: DecimalCast + ArrowNativeTypeOp,
325 O::Native: DecimalCast + ArrowNativeTypeOp,
326{
327 move |x: I::Native| {
328 ArrowError::CastError(format!(
329 "Cannot cast to {}({}, {}). Overflowing on {:?}",
330 O::PREFIX,
331 output_precision,
332 output_scale,
333 x
334 ))
335 }
336}
337
338fn apply_decimal_cast<I: DecimalType, O: DecimalType>(
339 array: &PrimitiveArray<I>,
340 output_precision: u8,
341 output_scale: i8,
342 f_fallible: impl Fn(I::Native) -> Option<O::Native>,
343 f_infallible: Option<impl Fn(I::Native) -> O::Native>,
344 cast_options: &CastOptions,
345) -> Result<PrimitiveArray<O>, ArrowError>
346where
347 I::Native: DecimalCast + ArrowNativeTypeOp,
348 O::Native: DecimalCast + ArrowNativeTypeOp,
349{
350 let array = if let Some(f_infallible) = f_infallible {
351 array.unary(f_infallible)
352 } else if cast_options.safe {
353 array.unary_opt(|x| {
354 f_fallible(x).filter(|v| O::is_valid_decimal_precision(*v, output_precision))
355 })
356 } else {
357 let error = cast_decimal_to_decimal_error::<I, O>(output_precision, output_scale);
358 array.try_unary(|x| {
359 let v = f_fallible(x).ok_or_else(|| error(x))?;
360 O::validate_decimal_precision(v, output_precision, output_scale).map(|()| v)
361 })?
362 };
363 Ok(array)
364}
365
366fn convert_to_smaller_scale_decimal<I, O>(
367 array: &PrimitiveArray<I>,
368 input_precision: u8,
369 input_scale: i8,
370 output_precision: u8,
371 output_scale: i8,
372 cast_options: &CastOptions,
373) -> Result<PrimitiveArray<O>, ArrowError>
374where
375 I: DecimalType,
376 O: DecimalType,
377 I::Native: DecimalCast + ArrowNativeTypeOp,
378 O::Native: DecimalCast + ArrowNativeTypeOp,
379{
380 if let Some((f_fallible, f_infallible)) =
381 make_downscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)
382 {
383 apply_decimal_cast(
384 array,
385 output_precision,
386 output_scale,
387 f_fallible,
388 f_infallible,
389 cast_options,
390 )
391 } else {
392 let zeros = vec![O::Native::ZERO; array.len()];
394 Ok(PrimitiveArray::new(zeros.into(), array.nulls().cloned()))
395 }
396}
397
398fn convert_to_bigger_or_equal_scale_decimal<I, O>(
399 array: &PrimitiveArray<I>,
400 input_precision: u8,
401 input_scale: i8,
402 output_precision: u8,
403 output_scale: i8,
404 cast_options: &CastOptions,
405) -> Result<PrimitiveArray<O>, ArrowError>
406where
407 I: DecimalType,
408 O: DecimalType,
409 I::Native: DecimalCast + ArrowNativeTypeOp,
410 O::Native: DecimalCast + ArrowNativeTypeOp,
411{
412 if let Some((f, f_infallible)) =
413 make_upscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)
414 {
415 apply_decimal_cast(
416 array,
417 output_precision,
418 output_scale,
419 f,
420 f_infallible,
421 cast_options,
422 )
423 } else {
424 Err(ArrowError::CastError(format!(
426 "Cannot cast to {}({}, {}). Value overflows for output scale",
427 O::PREFIX,
428 output_precision,
429 output_scale
430 )))
431 }
432}
433
434pub(crate) fn cast_decimal_to_decimal_same_type<T>(
436 array: &PrimitiveArray<T>,
437 input_precision: u8,
438 input_scale: i8,
439 output_precision: u8,
440 output_scale: i8,
441 cast_options: &CastOptions,
442) -> Result<ArrayRef, ArrowError>
443where
444 T: DecimalType,
445 T::Native: DecimalCast + ArrowNativeTypeOp,
446{
447 let array: PrimitiveArray<T> =
448 if input_scale == output_scale && input_precision <= output_precision {
449 array.clone()
450 } else if input_scale <= output_scale {
451 convert_to_bigger_or_equal_scale_decimal::<T, T>(
452 array,
453 input_precision,
454 input_scale,
455 output_precision,
456 output_scale,
457 cast_options,
458 )?
459 } else {
460 convert_to_smaller_scale_decimal::<T, T>(
462 array,
463 input_precision,
464 input_scale,
465 output_precision,
466 output_scale,
467 cast_options,
468 )?
469 };
470
471 Ok(Arc::new(array.with_precision_and_scale(
472 output_precision,
473 output_scale,
474 )?))
475}
476
477pub(crate) fn cast_decimal_to_decimal<I, O>(
479 array: &PrimitiveArray<I>,
480 input_precision: u8,
481 input_scale: i8,
482 output_precision: u8,
483 output_scale: i8,
484 cast_options: &CastOptions,
485) -> Result<ArrayRef, ArrowError>
486where
487 I: DecimalType,
488 O: DecimalType,
489 I::Native: DecimalCast + ArrowNativeTypeOp,
490 O::Native: DecimalCast + ArrowNativeTypeOp,
491{
492 let array: PrimitiveArray<O> = if input_scale > output_scale {
493 convert_to_smaller_scale_decimal::<I, O>(
494 array,
495 input_precision,
496 input_scale,
497 output_precision,
498 output_scale,
499 cast_options,
500 )?
501 } else {
502 convert_to_bigger_or_equal_scale_decimal::<I, O>(
503 array,
504 input_precision,
505 input_scale,
506 output_precision,
507 output_scale,
508 cast_options,
509 )?
510 };
511
512 Ok(Arc::new(array.with_precision_and_scale(
513 output_precision,
514 output_scale,
515 )?))
516}
517
518#[deprecated(
527 since = "60.0.0",
528 note = "Use `arrow_cast::parse::parse_decimal` instead"
529)]
530pub fn parse_string_to_decimal_native<T: DecimalType>(
531 value_str: &str,
532 scale: usize,
533) -> Result<T::Native, ArrowError> {
534 let overflow = || {
535 ArrowError::InvalidArgumentError(format!(
536 "Cannot convert {value_str} to {}: Overflow",
537 T::PREFIX
538 ))
539 };
540 let scale = i8::try_from(scale).map_err(|_| overflow())?;
541 parse_decimal_checked::<T>(value_str, T::MAX_PRECISION, scale).map_err(|e| match e {
542 DecimalParseError::InvalidFormat => {
543 ArrowError::InvalidArgumentError(format!("Invalid decimal format: {value_str:?}"))
544 }
545 DecimalParseError::Overflow => overflow(),
546 })
547}
548
549pub(crate) fn generic_string_to_decimal_cast<'a, T, S>(
550 from: &'a S,
551 precision: u8,
552 scale: i8,
553 cast_options: &CastOptions,
554) -> Result<PrimitiveArray<T>, ArrowError>
555where
556 T: DecimalType,
557 &'a S: StringArrayType<'a>,
558{
559 if cast_options.safe {
560 let iter = from
561 .iter()
562 .map(|v| parse_decimal_checked::<T>(v?, precision, scale).ok());
563 Ok(unsafe {
569 PrimitiveArray::<T>::from_trusted_len_iter(iter)
570 .with_precision_and_scale(precision, scale)?
571 })
572 } else {
573 let mut builder = PrimitiveBuilder::<T>::with_capacity(from.len());
574 for v in from.iter() {
575 match v {
576 Some(v) => {
577 let v = parse_decimal_checked::<T>(v, precision, scale).map_err(|e| {
578 let reason = match e {
579 DecimalParseError::InvalidFormat => "invalid decimal format",
580 DecimalParseError::Overflow => "value does not fit",
581 };
582 ArrowError::CastError(format!(
583 "Cannot cast string '{v}' to value of {}({precision}, {scale}) type: {reason}",
584 T::PREFIX,
585 ))
586 })?;
587 builder.append_value(v);
588 }
589 None => builder.append_null(),
590 }
591 }
592 builder.finish().with_precision_and_scale(precision, scale)
593 }
594}
595
596pub(crate) fn string_to_decimal_cast<T: DecimalType, Offset: OffsetSizeTrait>(
597 from: &GenericStringArray<Offset>,
598 precision: u8,
599 scale: i8,
600 cast_options: &CastOptions,
601) -> Result<PrimitiveArray<T>, ArrowError> {
602 generic_string_to_decimal_cast::<T, GenericStringArray<Offset>>(
603 from,
604 precision,
605 scale,
606 cast_options,
607 )
608}
609
610pub(crate) fn string_view_to_decimal_cast<T: DecimalType>(
611 from: &StringViewArray,
612 precision: u8,
613 scale: i8,
614 cast_options: &CastOptions,
615) -> Result<PrimitiveArray<T>, ArrowError> {
616 generic_string_to_decimal_cast::<T, StringViewArray>(from, precision, scale, cast_options)
617}
618
619pub(crate) fn cast_string_to_decimal<T: DecimalType, Offset: OffsetSizeTrait>(
621 from: &dyn Array,
622 precision: u8,
623 scale: i8,
624 cast_options: &CastOptions,
625) -> Result<ArrayRef, ArrowError> {
626 validate_decimal_precision_and_scale::<T>(precision, scale)?;
627
628 let result = match from.data_type() {
629 DataType::Utf8View => string_view_to_decimal_cast::<T>(
630 from.as_any().downcast_ref::<StringViewArray>().unwrap(),
631 precision,
632 scale,
633 cast_options,
634 )?,
635 DataType::Utf8 | DataType::LargeUtf8 => string_to_decimal_cast::<T, Offset>(
636 from.as_any()
637 .downcast_ref::<GenericStringArray<Offset>>()
638 .unwrap(),
639 precision,
640 scale,
641 cast_options,
642 )?,
643 _ => unreachable!(
644 "cast_to_decimal only calls cast_string_to_decimal for Utf8, LargeUtf8, or Utf8View"
645 ),
646 };
647
648 Ok(Arc::new(result))
649}
650
651pub(crate) fn cast_floating_point_to_decimal<T: ArrowPrimitiveType, D>(
652 array: &PrimitiveArray<T>,
653 precision: u8,
654 scale: i8,
655 cast_options: &CastOptions,
656) -> Result<ArrayRef, ArrowError>
657where
658 <T as ArrowPrimitiveType>::Native: AsPrimitive<f64>,
659 D: DecimalType + ArrowPrimitiveType,
660 <D as ArrowPrimitiveType>::Native: DecimalCast,
661{
662 validate_decimal_precision_and_scale::<D>(precision, scale)?;
663 let mul = 10_f64.powi(scale as i32);
664
665 if cast_options.safe {
666 array
667 .unary_opt::<_, D>(|v| float_to_decimal_checked::<D>(v.as_(), mul, precision).ok())
668 .with_precision_and_scale(precision, scale)
669 .map(|a| Arc::new(a) as ArrayRef)
670 } else {
671 array
672 .try_unary::<_, D, _>(|v| {
673 float_to_decimal_checked::<D>(v.as_(), mul, precision)
674 .map_err(|e| e.into_arrow_error(v, precision, scale))
675 })?
676 .with_precision_and_scale(precision, scale)
677 .map(|a| Arc::new(a) as ArrayRef)
678 }
679}
680
681#[inline]
696pub fn float_to_decimal<D>(input: f64, precision: u8, scale: i8) -> Result<D::Native, ArrowError>
697where
698 D: DecimalType,
699 D::Native: DecimalCast,
700{
701 validate_decimal_precision_and_scale::<D>(precision, scale)?;
702 float_to_decimal_checked::<D>(input, 10_f64.powi(scale as i32), precision)
703 .map_err(|e| e.into_arrow_error(input, precision, scale))
704}
705
706pub fn float_to_decimal_opt<D>(input: f64, precision: u8, scale: i8) -> Option<D::Native>
717where
718 D: DecimalType,
719 D::Native: DecimalCast,
720{
721 validate_decimal_precision_and_scale::<D>(precision, scale).ok()?;
722 float_to_decimal_checked::<D>(input, 10_f64.powi(scale as i32), precision).ok()
723}
724
725enum FloatToDecimalError<D: DecimalType> {
727 NativeOverflow,
728 PrecisionOverflow(D::Native),
729}
730
731impl<D: DecimalType> FloatToDecimalError<D> {
732 fn into_arrow_error(self, input: impl std::fmt::Debug, precision: u8, scale: i8) -> ArrowError {
733 match self {
734 Self::NativeOverflow => ArrowError::CastError(format!(
735 "Cannot cast to {}({}, {}). Overflowing on {:?}",
736 D::PREFIX,
737 precision,
738 scale,
739 input
740 )),
741 Self::PrecisionOverflow(value) => {
742 D::validate_decimal_precision(value, precision, scale).unwrap_err()
743 }
744 }
745 }
746}
747
748#[inline]
750fn float_to_decimal_checked<D>(
751 input: f64,
752 mul: f64,
753 precision: u8,
754) -> Result<D::Native, FloatToDecimalError<D>>
755where
756 D: DecimalType,
757 D::Native: DecimalCast,
758{
759 let value =
760 D::Native::from_f64((mul * input).round()).ok_or(FloatToDecimalError::NativeOverflow)?;
761 if D::is_valid_decimal_precision(value, precision) {
762 Ok(value)
763 } else {
764 Err(FloatToDecimalError::PrecisionOverflow(value))
765 }
766}
767
768#[deprecated(since = "60.0.0", note = "Use `float_to_decimal` instead")]
772#[inline(always)]
773pub fn single_float_to_decimal<D>(input: f64, mul: f64) -> Option<D::Native>
774where
775 D: DecimalType + ArrowPrimitiveType,
776 <D as ArrowPrimitiveType>::Native: DecimalCast,
777{
778 D::Native::from_f64((mul * input).round())
779}
780
781pub(crate) fn cast_decimal_to_integer<D, T>(
782 array: &dyn Array,
783 scale: i8,
784 cast_options: &CastOptions,
785) -> Result<ArrayRef, ArrowError>
786where
787 T: ArrowPrimitiveType,
788 <T as ArrowPrimitiveType>::Native: NumCast,
789 D: DecimalType + ArrowPrimitiveType,
790 <D as ArrowPrimitiveType>::Native: ToPrimitive,
791{
792 let array = array.as_primitive::<D>();
793
794 let div: D::Native = D::Native::usize_as(10)
796 .pow_checked(scale.unsigned_abs() as u32)
797 .map_err(|_| {
798 ArrowError::CastError(format!(
799 "Cannot cast to {:?}. The scale {} causes overflow.",
800 D::PREFIX,
801 scale,
802 ))
803 })?;
804
805 let mut value_builder = PrimitiveBuilder::<T>::with_capacity(array.len());
806
807 if scale < 0 {
808 match cast_options.safe {
809 true => {
810 for i in 0..array.len() {
811 if array.is_null(i) {
812 value_builder.append_null();
813 } else {
814 let v = array
815 .value(i)
816 .mul_checked(div)
817 .ok()
818 .and_then(<T::Native as NumCast>::from::<D::Native>);
819 value_builder.append_option(v);
820 }
821 }
822 }
823 false => {
824 for i in 0..array.len() {
825 if array.is_null(i) {
826 value_builder.append_null();
827 } else {
828 let v = array.value(i).mul_checked(div)?;
829
830 let value =
831 <T::Native as NumCast>::from::<D::Native>(v).ok_or_else(|| {
832 ArrowError::CastError(format!(
833 "value of {:?} is out of range {}",
834 v,
835 T::DATA_TYPE
836 ))
837 })?;
838
839 value_builder.append_value(value);
840 }
841 }
842 }
843 }
844 } else {
845 match cast_options.safe {
846 true => {
847 for i in 0..array.len() {
848 if array.is_null(i) {
849 value_builder.append_null();
850 } else {
851 let v = array.value(i).div_wrapping(div);
852 let v = <T::Native as NumCast>::from(v);
853 value_builder.append_option(v);
854 }
855 }
856 }
857 false => {
858 for i in 0..array.len() {
859 if array.is_null(i) {
860 value_builder.append_null();
861 } else {
862 let v = array.value(i).div_wrapping(div);
863
864 let value =
865 <T::Native as NumCast>::from::<D::Native>(v).ok_or_else(|| {
866 ArrowError::CastError(format!(
867 "value of {:?} is out of range {}",
868 v,
869 T::DATA_TYPE
870 ))
871 })?;
872
873 value_builder.append_value(value);
874 }
875 }
876 }
877 }
878 }
879 Ok(Arc::new(value_builder.finish()))
880}
881
882pub(crate) fn cast_decimal_to_float<D: DecimalType, T: ArrowPrimitiveType, F>(
888 array: &dyn Array,
889 op: F,
890) -> Result<ArrayRef, ArrowError>
891where
892 F: Fn(D::Native) -> T::Native,
893{
894 let array = array.as_primitive::<D>();
895 let array = array.unary::<_, T>(op);
896 Ok(Arc::new(array))
897}
898
899fn integer_to_decimal_native<I, M>(value: I) -> Option<M>
907where
908 I: Into<i128>,
909 M: DecimalCast,
910{
911 M::from_decimal(value.into())
912}
913
914fn integers_fit_digits<T>(array: &PrimitiveArray<T>, digits: u32) -> bool
920where
921 T: ArrowPrimitiveType,
922 T::Native: ArrowNativeTypeOp,
923{
924 let Ok(bound) = T::Native::usize_as(10).pow_checked(digits) else {
925 return true;
928 };
929 let hi = bound.sub_wrapping(T::Native::ONE);
930 let lo = T::Native::ZERO.sub_checked(hi).unwrap_or(T::Native::ZERO);
932
933 array
937 .values()
938 .chunks(64)
939 .all(|chunk| chunk.iter().fold(true, |ok, &v| ok & (v >= lo) & (v <= hi)))
940}
941
942fn cast_integer_to_decimal<
943 T: ArrowPrimitiveType,
944 D: DecimalType + ArrowPrimitiveType<Native = M>,
945 M,
946>(
947 array: &PrimitiveArray<T>,
948 precision: u8,
949 scale: i8,
950 cast_options: &CastOptions,
951) -> Result<ArrayRef, ArrowError>
952where
953 <T as ArrowPrimitiveType>::Native: ArrowNativeTypeOp + Into<i128>,
954 M: ArrowNativeTypeOp + DecimalCast,
955{
956 let overflow = |v: T::Native| {
957 ArrowError::CastError(format!(
958 "Cannot cast to {}({precision}, {scale}). Overflowing on {v:?}",
959 D::PREFIX,
960 ))
961 };
962
963 validate_decimal_precision_and_scale::<D>(precision, scale)?;
966
967 let digits = (precision as i32 - scale as i32) as u32;
978 let all_fit = integers_fit_digits(array, digits);
979
980 let array = if scale < 0 {
981 let scale_factor = T::Native::usize_as(10)
988 .pow_checked(scale.unsigned_abs() as u32)
989 .ok();
990
991 match (scale_factor, cast_options.safe) {
992 (Some(scale_factor), _) if all_fit => array.unary::<_, D>(|v| {
993 integer_to_decimal_native::<_, M>(v.div_wrapping(scale_factor))
994 .expect("value fits the decimal")
995 }),
996 (Some(scale_factor), true) => array.unary_opt::<_, D>(|v| {
997 let v = integer_to_decimal_native::<_, M>(v.div_wrapping(scale_factor))?;
998 (D::is_valid_decimal_precision(v, precision)).then_some(v)
999 }),
1000 (Some(scale_factor), false) => array.try_unary::<_, D, _>(|v| {
1001 let v = integer_to_decimal_native::<_, M>(v.div_wrapping(scale_factor))
1002 .ok_or_else(|| overflow(v))?;
1003 D::validate_decimal_precision(v, precision, scale).map(|()| v)
1004 })?,
1005 (None, _) => array.unary::<_, D>(|_| M::ZERO),
1010 }
1011 } else {
1012 let scale_factor = M::usize_as(10)
1013 .pow_checked(scale.unsigned_abs() as u32)
1014 .map_err(|_| {
1015 ArrowError::CastError(format!(
1016 "Cannot cast to {:?}({}, {}). The scale causes overflow.",
1017 D::PREFIX,
1018 precision,
1019 scale,
1020 ))
1021 })?;
1022
1023 if all_fit {
1024 array.unary::<_, D>(|v| {
1025 integer_to_decimal_native::<_, M>(v)
1026 .expect("value fits the decimal")
1027 .mul_wrapping(scale_factor)
1028 })
1029 } else if cast_options.safe {
1030 array.unary_opt::<_, D>(|v| {
1031 let v = integer_to_decimal_native::<_, M>(v)
1032 .and_then(|v| v.mul_checked(scale_factor).ok())?;
1033 (D::is_valid_decimal_precision(v, precision)).then_some(v)
1034 })
1035 } else {
1036 array.try_unary::<_, D, _>(|v| {
1037 let v = integer_to_decimal_native::<_, M>(v)
1038 .ok_or_else(|| overflow(v))
1039 .and_then(|v| v.mul_checked(scale_factor))?;
1040 D::validate_decimal_precision(v, precision, scale).map(|()| v)
1041 })?
1042 }
1043 };
1044
1045 Ok(Arc::new(array.with_precision_and_scale(precision, scale)?))
1046}
1047
1048pub(crate) fn cast_from_decimal<D, F>(
1049 array: &dyn Array,
1050 scale: &i8,
1051 from_type: &DataType,
1052 to_type: &DataType,
1053 as_float: F,
1054 cast_options: &CastOptions,
1055) -> Result<ArrayRef, ArrowError>
1056where
1057 D: DecimalType + ArrowPrimitiveType,
1058 <D as ArrowPrimitiveType>::Native: ToPrimitive,
1059 F: Fn(D::Native) -> f64,
1060{
1061 use DataType::*;
1062 match to_type {
1064 UInt8 => cast_decimal_to_integer::<D, UInt8Type>(array, *scale, cast_options),
1065 UInt16 => cast_decimal_to_integer::<D, UInt16Type>(array, *scale, cast_options),
1066 UInt32 => cast_decimal_to_integer::<D, UInt32Type>(array, *scale, cast_options),
1067 UInt64 => cast_decimal_to_integer::<D, UInt64Type>(array, *scale, cast_options),
1068 Int8 => cast_decimal_to_integer::<D, Int8Type>(array, *scale, cast_options),
1069 Int16 => cast_decimal_to_integer::<D, Int16Type>(array, *scale, cast_options),
1070 Int32 => cast_decimal_to_integer::<D, Int32Type>(array, *scale, cast_options),
1071 Int64 => cast_decimal_to_integer::<D, Int64Type>(array, *scale, cast_options),
1072 Float16 => cast_decimal_to_float::<D, Float16Type, _>(array, |x| {
1073 half::f16::from_f64(single_decimal_to_float_lossy::<D, F>(
1074 &as_float,
1075 x,
1076 <i32 as From<i8>>::from(*scale),
1077 ))
1078 }),
1079 Float32 => cast_decimal_to_f32::<D, F>(array, &as_float, *scale),
1080 Float64 => cast_decimal_to_f64::<D, F>(array, &as_float, *scale),
1081 Utf8View => value_to_string_view(array, cast_options),
1082 Utf8 => value_to_string::<i32>(array, cast_options),
1083 LargeUtf8 => value_to_string::<i64>(array, cast_options),
1084 Null => Ok(new_null_array(to_type, array.len())),
1085 _ => Err(ArrowError::InvalidArgumentError(format!(
1086 "Casting from {from_type} to {to_type} not supported"
1087 ))),
1088 }
1089}
1090
1091pub(crate) fn cast_to_decimal<D, M>(
1092 array: &dyn Array,
1093 precision: &u8,
1094 scale: &i8,
1095 from_type: &DataType,
1096 to_type: &DataType,
1097 cast_options: &CastOptions,
1098) -> Result<ArrayRef, ArrowError>
1099where
1100 D: DecimalType + ArrowPrimitiveType<Native = M>,
1101 M: ArrowNativeTypeOp + DecimalCast,
1102{
1103 use DataType::*;
1104 match from_type {
1106 UInt8 => cast_integer_to_decimal::<_, D, M>(
1107 array.as_primitive::<UInt8Type>(),
1108 *precision,
1109 *scale,
1110 cast_options,
1111 ),
1112 UInt16 => cast_integer_to_decimal::<_, D, _>(
1113 array.as_primitive::<UInt16Type>(),
1114 *precision,
1115 *scale,
1116 cast_options,
1117 ),
1118 UInt32 => cast_integer_to_decimal::<_, D, _>(
1119 array.as_primitive::<UInt32Type>(),
1120 *precision,
1121 *scale,
1122 cast_options,
1123 ),
1124 UInt64 => cast_integer_to_decimal::<_, D, _>(
1125 array.as_primitive::<UInt64Type>(),
1126 *precision,
1127 *scale,
1128 cast_options,
1129 ),
1130 Int8 => cast_integer_to_decimal::<_, D, _>(
1131 array.as_primitive::<Int8Type>(),
1132 *precision,
1133 *scale,
1134 cast_options,
1135 ),
1136 Int16 => cast_integer_to_decimal::<_, D, _>(
1137 array.as_primitive::<Int16Type>(),
1138 *precision,
1139 *scale,
1140 cast_options,
1141 ),
1142 Int32 => cast_integer_to_decimal::<_, D, _>(
1143 array.as_primitive::<Int32Type>(),
1144 *precision,
1145 *scale,
1146 cast_options,
1147 ),
1148 Int64 => cast_integer_to_decimal::<_, D, _>(
1149 array.as_primitive::<Int64Type>(),
1150 *precision,
1151 *scale,
1152 cast_options,
1153 ),
1154 Float16 => cast_floating_point_to_decimal::<_, D>(
1155 array.as_primitive::<Float16Type>(),
1156 *precision,
1157 *scale,
1158 cast_options,
1159 ),
1160 Float32 => cast_floating_point_to_decimal::<_, D>(
1161 array.as_primitive::<Float32Type>(),
1162 *precision,
1163 *scale,
1164 cast_options,
1165 ),
1166 Float64 => cast_floating_point_to_decimal::<_, D>(
1167 array.as_primitive::<Float64Type>(),
1168 *precision,
1169 *scale,
1170 cast_options,
1171 ),
1172 Utf8View | Utf8 => {
1173 cast_string_to_decimal::<D, i32>(array, *precision, *scale, cast_options)
1174 }
1175 LargeUtf8 => cast_string_to_decimal::<D, i64>(array, *precision, *scale, cast_options),
1176 Null => Ok(new_null_array(to_type, array.len())),
1177 _ => Err(ArrowError::InvalidArgumentError(format!(
1178 "Casting from {from_type} to {to_type} not supported"
1179 ))),
1180 }
1181}
1182
1183#[cfg(test)]
1184mod tests {
1185 use super::*;
1186
1187 #[test]
1188 fn test_float_to_decimal() {
1189 fn check<D: DecimalType>()
1190 where
1191 D::Native: DecimalCast,
1192 {
1193 for (input, scale, expected) in [
1194 (12.345, 2, 1235),
1195 (-12.345, 2, -1235),
1196 (999.99, 2, 99999),
1197 (-999.99, 2, -99999),
1198 (99999.25, 0, 99999),
1199 (999992.5, -1, 99999),
1200 (0.0, 2, 0),
1201 ] {
1202 assert_eq!(
1203 float_to_decimal_opt::<D>(input, 5, scale),
1204 float_to_decimal::<D>(input, 5, scale).ok(),
1205 );
1206 assert_eq!(
1207 float_to_decimal::<D>(input, 5, scale)
1208 .unwrap()
1209 .to_i128()
1210 .unwrap(),
1211 expected,
1212 );
1213 }
1214 for (input, scale) in [
1215 (12345.678, 2),
1216 (-12345.678, 2),
1217 (999.9975, 2),
1218 (-999.9975, 2),
1219 (99999.75, 0),
1220 (-99999.75, 0),
1221 (999997.5, -1),
1222 (-999997.5, -1),
1223 (f64::NAN, 0),
1224 (f64::INFINITY, 0),
1225 (f64::NEG_INFINITY, 0),
1226 (f64::MAX, 0),
1227 ] {
1228 assert!(float_to_decimal::<D>(input, 5, scale).is_err());
1229 assert!(float_to_decimal_opt::<D>(input, 5, scale).is_none());
1230 }
1231 for (precision, scale) in [(0, 0), (D::MAX_PRECISION + 1, 0), (5, 6)] {
1232 assert!(float_to_decimal::<D>(0.0, precision, scale).is_err());
1233 assert!(float_to_decimal_opt::<D>(0.0, precision, scale).is_none());
1234 }
1235 }
1236 check::<Decimal32Type>();
1237 check::<Decimal64Type>();
1238 check::<Decimal128Type>();
1239 check::<Decimal256Type>();
1240 }
1241
1242 #[test]
1243 #[expect(deprecated)]
1244 fn test_single_float_to_decimal_preserves_native_range() {
1245 for (input, expected) in [
1248 (1_000_000_000.0, 1_000_000_000),
1249 (-1_000_000_000.0, -1_000_000_000),
1250 (999_999_999.75, 1_000_000_000),
1251 (-999_999_999.75, -1_000_000_000),
1252 ] {
1253 assert_eq!(
1254 single_float_to_decimal::<Decimal32Type>(input, 1.0),
1255 Some(expected)
1256 );
1257 assert!(float_to_decimal::<Decimal32Type>(input, 9, 0).is_err());
1258 }
1259 assert_eq!(
1260 single_float_to_decimal::<Decimal32Type>(999_999_999.0, 1.0),
1261 Some(999_999_999),
1262 );
1263 assert_eq!(
1264 single_float_to_decimal::<Decimal32Type>(12.345, 100.0),
1265 Some(1235)
1266 );
1267 assert_eq!(
1268 single_float_to_decimal::<Decimal32Type>(f64::MAX, 1.0),
1269 None
1270 );
1271 }
1272
1273 #[test]
1274 #[expect(deprecated)]
1275 fn test_parse_string_to_decimal_native() {
1276 assert_eq!(
1277 parse_string_to_decimal_native::<Decimal128Type>("123.456", 2).unwrap(),
1278 12346
1279 );
1280 assert_eq!(
1283 parse_string_to_decimal_native::<Decimal128Type>(&"9".repeat(38), 0).unwrap(),
1284 10_i128.pow(38) - 1
1285 );
1286 assert!(
1287 parse_string_to_decimal_native::<Decimal128Type>(&i128::MAX.to_string(), 0).is_err()
1288 );
1289 assert_eq!(
1290 parse_string_to_decimal_native::<Decimal128Type>("abc", 2)
1291 .unwrap_err()
1292 .to_string(),
1293 "Invalid argument error: Invalid decimal format: \"abc\""
1294 );
1295 assert_eq!(
1296 parse_string_to_decimal_native::<Decimal32Type>("1", 10)
1297 .unwrap_err()
1298 .to_string(),
1299 "Invalid argument error: Cannot convert 1 to Decimal32: Overflow"
1300 );
1301 }
1302
1303 #[test]
1304 fn test_rescale_decimal_upscale_within_precision() {
1305 let result = rescale_decimal::<Decimal128Type, Decimal128Type>(
1306 12_345_i128, 5,
1308 2,
1309 8,
1310 5,
1311 );
1312 assert_eq!(result, Some(12_345_000_i128));
1313 }
1314
1315 #[test]
1316 fn test_rescale_decimal_downscale_rounds_half_away_from_zero() {
1317 let positive = rescale_decimal::<Decimal128Type, Decimal128Type>(
1318 1_050_i128, 5, 3, 5, 1,
1320 );
1321 assert_eq!(positive, Some(11_i128)); let negative = rescale_decimal::<Decimal128Type, Decimal128Type>(
1324 -1_050_i128, 5,
1326 3,
1327 5,
1328 1,
1329 );
1330 assert_eq!(negative, Some(-11_i128)); }
1332
1333 #[test]
1334 fn test_rescale_decimal_downscale_large_delta_returns_zero() {
1335 let result = rescale_decimal::<Decimal32Type, Decimal32Type>(12_345_i32, 9, 9, 9, 4);
1336 assert_eq!(result, Some(0_i32));
1337 }
1338
1339 #[test]
1340 fn test_rescale_decimal_upscale_overflow_returns_none() {
1341 let result = rescale_decimal::<Decimal32Type, Decimal32Type>(9_999_i32, 4, 0, 5, 2);
1342 assert_eq!(result, None);
1343 }
1344
1345 #[test]
1346 fn test_rescale_decimal256_extreme_scales() {
1347 assert_eq!(
1348 rescale_decimal::<Decimal256Type, Decimal256Type>(i256::ONE, 76, -76, 76, 0),
1349 None
1350 );
1351 assert_eq!(
1352 rescale_decimal::<Decimal256Type, Decimal256Type>(i256::ZERO, 76, -76, 76, 0),
1353 Some(i256::ZERO)
1354 );
1355 assert_eq!(
1356 rescale_decimal::<Decimal256Type, Decimal256Type>(i256::ONE, 76, i8::MIN, 76, 76),
1357 None
1358 );
1359 assert_eq!(
1360 rescale_decimal::<Decimal256Type, Decimal256Type>(i256::ONE, 76, 76, 76, i8::MIN),
1361 Some(i256::ZERO)
1362 );
1363 }
1364
1365 #[test]
1366 fn test_rescale_decimal_invalid_input_precision_scale_returns_none() {
1367 let result = rescale_decimal::<Decimal128Type, Decimal128Type>(123_i128, 39, 39, 38, 38);
1368 assert_eq!(result, None);
1369 }
1370
1371 #[test]
1372 fn test_rescale_decimal_invalid_output_precision_scale_returns_none() {
1373 let result = rescale_decimal::<Decimal128Type, Decimal128Type>(123_i128, 38, 38, 39, 39);
1374 assert_eq!(result, None);
1375 }
1376}