Skip to main content

arrow_cast/cast/
decimal.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::cast::*;
19
20/// A utility trait that provides checked conversions between
21/// decimal types inspired by [`NumCast`]
22pub trait DecimalCast: Sized {
23    /// Convert the decimal to an i32
24    fn to_i32(self) -> Option<i32>;
25
26    /// Convert the decimal to an i64
27    fn to_i64(self) -> Option<i64>;
28
29    /// Convert the decimal to an i128
30    fn to_i128(self) -> Option<i128>;
31
32    /// Convert the decimal to an i256
33    fn to_i256(self) -> Option<i256>;
34
35    /// Convert a decimal from a decimal
36    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self>;
37
38    /// Convert a decimal from a f64
39    fn from_f64(n: f64) -> Option<Self>;
40}
41
42impl DecimalCast for i32 {
43    fn to_i32(self) -> Option<i32> {
44        Some(self)
45    }
46
47    fn to_i64(self) -> Option<i64> {
48        Some(self as i64)
49    }
50
51    fn to_i128(self) -> Option<i128> {
52        Some(self as i128)
53    }
54
55    fn to_i256(self) -> Option<i256> {
56        Some(i256::from_i128(self as i128))
57    }
58
59    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
60        n.to_i32()
61    }
62
63    fn from_f64(n: f64) -> Option<Self> {
64        n.to_i32()
65    }
66}
67
68impl DecimalCast for i64 {
69    fn to_i32(self) -> Option<i32> {
70        i32::try_from(self).ok()
71    }
72
73    fn to_i64(self) -> Option<i64> {
74        Some(self)
75    }
76
77    fn to_i128(self) -> Option<i128> {
78        Some(self as i128)
79    }
80
81    fn to_i256(self) -> Option<i256> {
82        Some(i256::from_i128(self as i128))
83    }
84
85    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
86        n.to_i64()
87    }
88
89    fn from_f64(n: f64) -> Option<Self> {
90        // Call implementation explicitly otherwise this resolves to `to_i64`
91        // in arrow-buffer that behaves differently.
92        num_traits::ToPrimitive::to_i64(&n)
93    }
94}
95
96impl DecimalCast for i128 {
97    fn to_i32(self) -> Option<i32> {
98        i32::try_from(self).ok()
99    }
100
101    fn to_i64(self) -> Option<i64> {
102        i64::try_from(self).ok()
103    }
104
105    fn to_i128(self) -> Option<i128> {
106        Some(self)
107    }
108
109    fn to_i256(self) -> Option<i256> {
110        Some(i256::from_i128(self))
111    }
112
113    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
114        n.to_i128()
115    }
116
117    fn from_f64(n: f64) -> Option<Self> {
118        n.to_i128()
119    }
120}
121
122impl DecimalCast for i256 {
123    fn to_i32(self) -> Option<i32> {
124        self.to_i128().map(|x| i32::try_from(x).ok())?
125    }
126
127    fn to_i64(self) -> Option<i64> {
128        self.to_i128().map(|x| i64::try_from(x).ok())?
129    }
130
131    fn to_i128(self) -> Option<i128> {
132        self.to_i128()
133    }
134
135    fn to_i256(self) -> Option<i256> {
136        Some(self)
137    }
138
139    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
140        n.to_i256()
141    }
142
143    fn from_f64(n: f64) -> Option<Self> {
144        i256::from_f64(n)
145    }
146}
147
148/// Construct closures to upscale decimals from `(input_precision, input_scale)` to
149/// `(output_precision, output_scale)`.
150///
151/// Returns `(f_fallible, f_infallible)` where:
152/// * `f_fallible` yields `None` when the requested cast would overflow
153/// * `f_infallible` is present only when every input is guaranteed to succeed; otherwise it is `None`
154///   and callers must fall back to `f_fallible`
155///
156/// Returns `None` if the required scale increase `delta_scale = output_scale - input_scale`
157/// exceeds the supported precomputed precision table `O::MAX_FOR_EACH_PRECISION`.
158/// In that case, the caller should treat this as an overflow for the output scale
159/// and handle it accordingly (e.g., return a cast error).
160#[expect(clippy::type_complexity)]
161fn make_upscaler<I: DecimalType, O: DecimalType>(
162    input_precision: u8,
163    input_scale: i8,
164    output_precision: u8,
165    output_scale: i8,
166) -> Option<(
167    impl Fn(I::Native) -> Option<O::Native>,
168    Option<impl Fn(I::Native) -> O::Native>,
169)>
170where
171    I::Native: DecimalCast + ArrowNativeTypeOp,
172    O::Native: DecimalCast + ArrowNativeTypeOp,
173{
174    let delta_scale = output_scale - input_scale;
175
176    // O::MAX_FOR_EACH_PRECISION[k] stores 10^k - 1 (e.g., 9, 99, 999, ...).
177    // Adding 1 yields exactly 10^k without computing a power at runtime.
178    // Using the precomputed table avoids pow(10, k) and its checked/overflow
179    // handling, which is faster and simpler for scaling by 10^delta_scale.
180    let max = O::MAX_FOR_EACH_PRECISION.get(delta_scale as usize)?;
181    let mul = max.add_wrapping(O::Native::ONE);
182    let f_fallible = move |x| O::Native::from_decimal(x).and_then(|x| x.mul_checked(mul).ok());
183
184    // if the gain in precision (digits) is greater than the multiplication due to scaling
185    // every number will fit into the output type
186    // Example: If we are starting with any number of precision 5 [xxxxx],
187    // then an increase of scale by 3 will have the following effect on the representation:
188    // [xxxxx] -> [xxxxx000], so for the cast to be infallible, the output type
189    // needs to provide at least 8 digits precision
190    let is_infallible_cast = (input_precision as i8) + delta_scale <= (output_precision as i8);
191    let f_infallible = is_infallible_cast
192        .then_some(move |x| O::Native::from_decimal(x).unwrap().mul_wrapping(mul));
193    Some((f_fallible, f_infallible))
194}
195
196/// Construct closures to downscale decimals from `(input_precision, input_scale)` to
197/// `(output_precision, output_scale)`.
198///
199/// Returns `(f_fallible, f_infallible)` where:
200/// * `f_fallible` yields `None` when the requested cast would overflow
201/// * `f_infallible` is present only when every input is guaranteed to succeed; otherwise it is `None`
202///   and callers must fall back to `f_fallible`
203///
204/// Returns `None` if the required scale reduction `delta_scale = input_scale - output_scale`
205/// exceeds the supported precomputed precision table `I::MAX_FOR_EACH_PRECISION`.
206/// In this scenario, any value would round to zero (e.g., dividing by 10^k where k exceeds the
207/// available precision). Callers should therefore produce zero values (preserving nulls) rather
208/// than returning an error.
209#[expect(clippy::type_complexity)]
210fn make_downscaler<I: DecimalType, O: DecimalType>(
211    input_precision: u8,
212    input_scale: i8,
213    output_precision: u8,
214    output_scale: i8,
215) -> Option<(
216    impl Fn(I::Native) -> Option<O::Native>,
217    Option<impl Fn(I::Native) -> O::Native>,
218)>
219where
220    I::Native: DecimalCast + ArrowNativeTypeOp,
221    O::Native: DecimalCast + ArrowNativeTypeOp,
222{
223    let delta_scale = input_scale - output_scale;
224
225    // delta_scale is guaranteed to be > 0, but may also be larger than I::MAX_PRECISION. If so, the
226    // scale change divides out more digits than the input has precision and the result of the cast
227    // is always zero. For example, if we try to apply delta_scale=10 a decimal32 value, the largest
228    // possible result is 999999999/10000000000 = 0.0999999999, which rounds to zero. Smaller values
229    // (e.g. 1/10000000000) or larger delta_scale (e.g. 999999999/10000000000000) produce even
230    // smaller results, which also round to zero. In that case, just return an array of zeros.
231    let max = I::MAX_FOR_EACH_PRECISION.get(delta_scale as usize)?;
232
233    let div = max.add_wrapping(I::Native::ONE);
234    let half = div.div_wrapping(I::Native::ONE.add_wrapping(I::Native::ONE));
235    let half_neg = half.neg_wrapping();
236
237    let f_fallible = move |x: I::Native| {
238        // div is >= 10 and so this cannot overflow
239        let d = x.div_wrapping(div);
240        let r = x.mod_wrapping(div);
241
242        // Round result
243        let adjusted = match x >= I::Native::ZERO {
244            true if r >= half => d.add_wrapping(I::Native::ONE),
245            false if r <= half_neg => d.sub_wrapping(I::Native::ONE),
246            _ => d,
247        };
248        O::Native::from_decimal(adjusted)
249    };
250
251    // if the reduction of the input number through scaling (dividing) is greater
252    // than a possible precision loss (plus potential increase via rounding)
253    // every input number will fit into the output type
254    // Example: If we are starting with any number of precision 5 [xxxxx],
255    // then and decrease the scale by 3 will have the following effect on the representation:
256    // [xxxxx] -> [xx] (+ 1 possibly, due to rounding).
257    // The rounding may add a digit, so the cast to be infallible,
258    // the output type needs to have at least 3 digits of precision.
259    // e.g. Decimal(5, 3) 99.999 to Decimal(3, 0) will result in 100:
260    // [99999] -> [99] + 1 = [100], a cast to Decimal(2, 0) would not be possible
261    let is_infallible_cast = (input_precision as i8) - delta_scale < (output_precision as i8);
262    let f_infallible = is_infallible_cast.then_some(move |x| f_fallible(x).unwrap());
263    Some((f_fallible, f_infallible))
264}
265
266/// Apply the rescaler function to the value.
267/// If the rescaler is infallible, use the infallible function.
268/// Otherwise, use the fallible function and validate the precision.
269fn apply_rescaler<I: DecimalType, O: DecimalType>(
270    value: I::Native,
271    output_precision: u8,
272    f: impl Fn(I::Native) -> Option<O::Native>,
273    f_infallible: Option<impl Fn(I::Native) -> O::Native>,
274) -> Option<O::Native>
275where
276    I::Native: DecimalCast,
277    O::Native: DecimalCast,
278{
279    if let Some(f_infallible) = f_infallible {
280        Some(f_infallible(value))
281    } else {
282        f(value).filter(|v| O::is_valid_decimal_precision(*v, output_precision))
283    }
284}
285
286/// Rescales a decimal value from `(input_precision, input_scale)` to
287/// `(output_precision, output_scale)` and returns the converted number when it fits
288/// within the output precision.
289///
290/// The function first validates that the requested precision and scale are supported for
291/// both the source and destination decimal types. It then either upscales (multiplying
292/// by an appropriate power of ten) or downscales (dividing with rounding) the input value.
293/// When the scaling factor exceeds the precision table of the destination type, the value
294/// is treated as an overflow for upscaling, or rounded to zero for downscaling (as any
295/// possible result would be zero at the requested scale).
296///
297/// This mirrors the column-oriented helpers of decimal casting but operates on a single value
298/// (row-level) instead of an entire array.
299///
300/// Returns `None` if the value cannot be represented with the requested precision.
301pub fn rescale_decimal<I: DecimalType, O: DecimalType>(
302    value: I::Native,
303    input_precision: u8,
304    input_scale: i8,
305    output_precision: u8,
306    output_scale: i8,
307) -> Option<O::Native>
308where
309    I::Native: DecimalCast + ArrowNativeTypeOp,
310    O::Native: DecimalCast + ArrowNativeTypeOp,
311{
312    validate_decimal_precision_and_scale::<I>(input_precision, input_scale).ok()?;
313    validate_decimal_precision_and_scale::<O>(output_precision, output_scale).ok()?;
314
315    if input_scale <= output_scale {
316        let (f, f_infallible) =
317            make_upscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)?;
318        apply_rescaler::<I, O>(value, output_precision, f, f_infallible)
319    } else {
320        let Some((f, f_infallible)) =
321            make_downscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)
322        else {
323            // Scale reduction exceeds supported precision; result mathematically rounds to zero
324            return Some(O::Native::ZERO);
325        };
326        apply_rescaler::<I, O>(value, output_precision, f, f_infallible)
327    }
328}
329
330fn cast_decimal_to_decimal_error<I, O>(
331    output_precision: u8,
332    output_scale: i8,
333) -> impl Fn(<I as ArrowPrimitiveType>::Native) -> ArrowError
334where
335    I: DecimalType,
336    O: DecimalType,
337    I::Native: DecimalCast + ArrowNativeTypeOp,
338    O::Native: DecimalCast + ArrowNativeTypeOp,
339{
340    move |x: I::Native| {
341        ArrowError::CastError(format!(
342            "Cannot cast to {}({}, {}). Overflowing on {:?}",
343            O::PREFIX,
344            output_precision,
345            output_scale,
346            x
347        ))
348    }
349}
350
351fn apply_decimal_cast<I: DecimalType, O: DecimalType>(
352    array: &PrimitiveArray<I>,
353    output_precision: u8,
354    output_scale: i8,
355    f_fallible: impl Fn(I::Native) -> Option<O::Native>,
356    f_infallible: Option<impl Fn(I::Native) -> O::Native>,
357    cast_options: &CastOptions,
358) -> Result<PrimitiveArray<O>, ArrowError>
359where
360    I::Native: DecimalCast + ArrowNativeTypeOp,
361    O::Native: DecimalCast + ArrowNativeTypeOp,
362{
363    let array = if let Some(f_infallible) = f_infallible {
364        array.unary(f_infallible)
365    } else if cast_options.safe {
366        array.unary_opt(|x| {
367            f_fallible(x).filter(|v| O::is_valid_decimal_precision(*v, output_precision))
368        })
369    } else {
370        let error = cast_decimal_to_decimal_error::<I, O>(output_precision, output_scale);
371        array.try_unary(|x| {
372            f_fallible(x).ok_or_else(|| error(x)).and_then(|v| {
373                O::validate_decimal_precision(v, output_precision, output_scale).map(|()| v)
374            })
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        // Scale reduction exceeds supported precision; result mathematically rounds to zero
407        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        // Scale increase exceeds supported precision; return overflow error
439        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
448// Only support one type of decimal cast operations
449pub(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            // input_scale > output_scale
475            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
491// Support two different types of decimal cast operations
492pub(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/// Parses given string to specified decimal native (i128/i256) based on given
533/// scale. Returns an `Err` if it cannot parse given string.
534pub fn parse_string_to_decimal_native<T: DecimalType>(
535    value_str: &str,
536    scale: usize,
537) -> Result<T::Native, ArrowError>
538where
539    T::Native: DecimalCast + ArrowNativeTypeOp,
540{
541    let value_str = value_str.trim();
542    let parts: Vec<&str> = value_str.split('.').collect();
543    if parts.len() > 2 {
544        return Err(ArrowError::InvalidArgumentError(format!(
545            "Invalid decimal format: {value_str:?}"
546        )));
547    }
548
549    let (negative, first_part) = if parts[0].is_empty() {
550        (false, parts[0])
551    } else {
552        match parts[0].as_bytes()[0] {
553            b'-' => (true, &parts[0][1..]),
554            b'+' => (false, &parts[0][1..]),
555            _ => (false, parts[0]),
556        }
557    };
558
559    let integers = first_part;
560    let decimals = if parts.len() == 2 { parts[1] } else { "" };
561
562    if integers.is_empty() && decimals.is_empty() {
563        return Err(ArrowError::InvalidArgumentError(format!(
564            "Invalid decimal format: {value_str:?}"
565        )));
566    }
567
568    if !integers.is_empty() && !integers.as_bytes()[0].is_ascii_digit() {
569        return Err(ArrowError::InvalidArgumentError(format!(
570            "Invalid decimal format: {value_str:?}"
571        )));
572    }
573
574    if !decimals.is_empty() && !decimals.as_bytes()[0].is_ascii_digit() {
575        return Err(ArrowError::InvalidArgumentError(format!(
576            "Invalid decimal format: {value_str:?}"
577        )));
578    }
579
580    // Adjust decimal based on scale
581    let mut number_decimals = if decimals.len() > scale {
582        let decimal_number = i256::from_string(decimals).ok_or_else(|| {
583            ArrowError::InvalidArgumentError(format!("Cannot parse decimal format: {value_str}"))
584        })?;
585
586        let div = i256::from_i128(10_i128).pow_checked((decimals.len() - scale) as u32)?;
587
588        let half = div.div_wrapping(i256::from_i128(2));
589        let half_neg = half.neg_wrapping();
590
591        let d = decimal_number.div_wrapping(div);
592        let r = decimal_number.mod_wrapping(div);
593
594        // Round result
595        let adjusted = match decimal_number >= i256::ZERO {
596            true if r >= half => d.add_wrapping(i256::ONE),
597            false if r <= half_neg => d.sub_wrapping(i256::ONE),
598            _ => d,
599        };
600
601        let integers = if !integers.is_empty() {
602            i256::from_string(integers)
603                .ok_or_else(|| {
604                    ArrowError::InvalidArgumentError(format!(
605                        "Cannot parse decimal format: {value_str}"
606                    ))
607                })
608                .map(|v| v.mul_wrapping(i256::from_i128(10_i128).pow_wrapping(scale as u32)))?
609        } else {
610            i256::ZERO
611        };
612
613        format!("{}", integers.add_wrapping(adjusted))
614    } else {
615        let padding = if scale > decimals.len() { scale } else { 0 };
616
617        let decimals = format!("{decimals:0<padding$}");
618        format!("{integers}{decimals}")
619    };
620
621    if negative {
622        number_decimals.insert(0, '-');
623    }
624
625    let value = i256::from_string(number_decimals.as_str()).ok_or_else(|| {
626        ArrowError::InvalidArgumentError(format!(
627            "Cannot convert {} to {}: Overflow",
628            value_str,
629            T::PREFIX
630        ))
631    })?;
632
633    T::Native::from_decimal(value).ok_or_else(|| {
634        ArrowError::InvalidArgumentError(format!("Cannot convert {} to {}", value_str, T::PREFIX))
635    })
636}
637
638pub(crate) fn generic_string_to_decimal_cast<'a, T, S>(
639    from: &'a S,
640    precision: u8,
641    scale: i8,
642    cast_options: &CastOptions,
643) -> Result<PrimitiveArray<T>, ArrowError>
644where
645    T: DecimalType,
646    T::Native: DecimalCast + ArrowNativeTypeOp,
647    &'a S: StringArrayType<'a>,
648{
649    if cast_options.safe {
650        let iter = from.iter().map(|v| {
651            v.and_then(|v| parse_string_to_decimal_native::<T>(v, scale as usize).ok())
652                .and_then(|v| T::is_valid_decimal_precision(v, precision).then_some(v))
653        });
654        // Benefit:
655        //     20% performance improvement
656        // Soundness:
657        //     The iterator is trustedLen because it comes from an `StringArray`.
658        Ok(unsafe {
659            PrimitiveArray::<T>::from_trusted_len_iter(iter)
660                .with_precision_and_scale(precision, scale)?
661        })
662    } else {
663        let vec = from
664            .iter()
665            .map(|v| {
666                v.map(|v| {
667                    parse_string_to_decimal_native::<T>(v, scale as usize)
668                        .map_err(|_| {
669                            ArrowError::CastError(format!(
670                                "Cannot cast string '{v}' to value of {} type",
671                                T::DATA_TYPE,
672                            ))
673                        })
674                        .and_then(|v| {
675                            T::validate_decimal_precision(v, precision, scale).map(|()| v)
676                        })
677                })
678                .transpose()
679            })
680            .collect::<Result<Vec<_>, _>>()?;
681        // Benefit:
682        //     20% performance improvement
683        // Soundness:
684        //     The iterator is trustedLen because it comes from an `StringArray`.
685        Ok(unsafe {
686            PrimitiveArray::<T>::from_trusted_len_iter(vec.iter())
687                .with_precision_and_scale(precision, scale)?
688        })
689    }
690}
691
692pub(crate) fn string_to_decimal_cast<T, Offset: OffsetSizeTrait>(
693    from: &GenericStringArray<Offset>,
694    precision: u8,
695    scale: i8,
696    cast_options: &CastOptions,
697) -> Result<PrimitiveArray<T>, ArrowError>
698where
699    T: DecimalType,
700    T::Native: DecimalCast + ArrowNativeTypeOp,
701{
702    generic_string_to_decimal_cast::<T, GenericStringArray<Offset>>(
703        from,
704        precision,
705        scale,
706        cast_options,
707    )
708}
709
710pub(crate) fn string_view_to_decimal_cast<T>(
711    from: &StringViewArray,
712    precision: u8,
713    scale: i8,
714    cast_options: &CastOptions,
715) -> Result<PrimitiveArray<T>, ArrowError>
716where
717    T: DecimalType,
718    T::Native: DecimalCast + ArrowNativeTypeOp,
719{
720    generic_string_to_decimal_cast::<T, StringViewArray>(from, precision, scale, cast_options)
721}
722
723/// Cast Utf8 to decimal
724pub(crate) fn cast_string_to_decimal<T, Offset: OffsetSizeTrait>(
725    from: &dyn Array,
726    precision: u8,
727    scale: i8,
728    cast_options: &CastOptions,
729) -> Result<ArrayRef, ArrowError>
730where
731    T: DecimalType,
732    T::Native: DecimalCast + ArrowNativeTypeOp,
733{
734    if scale < 0 {
735        return Err(ArrowError::InvalidArgumentError(format!(
736            "Cannot cast string to decimal with negative scale {scale}"
737        )));
738    }
739
740    if scale > T::MAX_SCALE {
741        return Err(ArrowError::InvalidArgumentError(format!(
742            "Cannot cast string to decimal greater than maximum scale {}",
743            T::MAX_SCALE
744        )));
745    }
746
747    let result = match from.data_type() {
748        DataType::Utf8View => string_view_to_decimal_cast::<T>(
749            from.as_any().downcast_ref::<StringViewArray>().unwrap(),
750            precision,
751            scale,
752            cast_options,
753        )?,
754        DataType::Utf8 | DataType::LargeUtf8 => string_to_decimal_cast::<T, Offset>(
755            from.as_any()
756                .downcast_ref::<GenericStringArray<Offset>>()
757                .unwrap(),
758            precision,
759            scale,
760            cast_options,
761        )?,
762        other => {
763            return Err(ArrowError::ComputeError(format!(
764                "Cannot cast {other:?} to decimal",
765            )));
766        }
767    };
768
769    Ok(Arc::new(result))
770}
771
772pub(crate) fn cast_floating_point_to_decimal<T: ArrowPrimitiveType, D>(
773    array: &PrimitiveArray<T>,
774    precision: u8,
775    scale: i8,
776    cast_options: &CastOptions,
777) -> Result<ArrayRef, ArrowError>
778where
779    <T as ArrowPrimitiveType>::Native: AsPrimitive<f64>,
780    D: DecimalType + ArrowPrimitiveType,
781    <D as ArrowPrimitiveType>::Native: DecimalCast,
782{
783    let mul = 10_f64.powi(scale as i32);
784
785    if cast_options.safe {
786        array
787            .unary_opt::<_, D>(|v| {
788                single_float_to_decimal::<D>(v.as_(), mul)
789                    .filter(|v| D::is_valid_decimal_precision(*v, precision))
790            })
791            .with_precision_and_scale(precision, scale)
792            .map(|a| Arc::new(a) as ArrayRef)
793    } else {
794        array
795            .try_unary::<_, D, _>(|v| {
796                single_float_to_decimal::<D>(v.as_(), mul)
797                    .ok_or_else(|| {
798                        ArrowError::CastError(format!(
799                            "Cannot cast to {}({}, {}). Overflowing on {:?}",
800                            D::PREFIX,
801                            precision,
802                            scale,
803                            v
804                        ))
805                    })
806                    .and_then(|v| D::validate_decimal_precision(v, precision, scale).map(|()| v))
807            })?
808            .with_precision_and_scale(precision, scale)
809            .map(|a| Arc::new(a) as ArrayRef)
810    }
811}
812
813/// Cast a single floating point value to a decimal native with the given multiple.
814/// Returns `None` if the value cannot be represented with the requested precision.
815#[inline(always)]
816pub fn single_float_to_decimal<D>(input: f64, mul: f64) -> Option<D::Native>
817where
818    D: DecimalType + ArrowPrimitiveType,
819    <D as ArrowPrimitiveType>::Native: DecimalCast,
820{
821    D::Native::from_f64((mul * input).round())
822}
823
824pub(crate) fn cast_decimal_to_integer<D, T>(
825    array: &dyn Array,
826    base: D::Native,
827    scale: i8,
828    cast_options: &CastOptions,
829) -> Result<ArrayRef, ArrowError>
830where
831    T: ArrowPrimitiveType,
832    <T as ArrowPrimitiveType>::Native: NumCast,
833    D: DecimalType + ArrowPrimitiveType,
834    <D as ArrowPrimitiveType>::Native: ToPrimitive,
835{
836    let array = array.as_primitive::<D>();
837
838    let div: D::Native = base.pow_checked(scale.unsigned_abs() as u32).map_err(|_| {
839        ArrowError::CastError(format!(
840            "Cannot cast to {:?}. The scale {} causes overflow.",
841            D::PREFIX,
842            scale,
843        ))
844    })?;
845
846    let mut value_builder = PrimitiveBuilder::<T>::with_capacity(array.len());
847
848    if scale < 0 {
849        match cast_options.safe {
850            true => {
851                for i in 0..array.len() {
852                    if array.is_null(i) {
853                        value_builder.append_null();
854                    } else {
855                        let v = array
856                            .value(i)
857                            .mul_checked(div)
858                            .ok()
859                            .and_then(<T::Native as NumCast>::from::<D::Native>);
860                        value_builder.append_option(v);
861                    }
862                }
863            }
864            false => {
865                for i in 0..array.len() {
866                    if array.is_null(i) {
867                        value_builder.append_null();
868                    } else {
869                        let v = array.value(i).mul_checked(div)?;
870
871                        let value =
872                            <T::Native as NumCast>::from::<D::Native>(v).ok_or_else(|| {
873                                ArrowError::CastError(format!(
874                                    "value of {:?} is out of range {}",
875                                    v,
876                                    T::DATA_TYPE
877                                ))
878                            })?;
879
880                        value_builder.append_value(value);
881                    }
882                }
883            }
884        }
885    } else {
886        match cast_options.safe {
887            true => {
888                for i in 0..array.len() {
889                    if array.is_null(i) {
890                        value_builder.append_null();
891                    } else {
892                        let v = array
893                            .value(i)
894                            .div_checked(div)
895                            .ok()
896                            .and_then(<T::Native as NumCast>::from::<D::Native>);
897                        value_builder.append_option(v);
898                    }
899                }
900            }
901            false => {
902                for i in 0..array.len() {
903                    if array.is_null(i) {
904                        value_builder.append_null();
905                    } else {
906                        let v = array.value(i).div_checked(div)?;
907
908                        let value =
909                            <T::Native as NumCast>::from::<D::Native>(v).ok_or_else(|| {
910                                ArrowError::CastError(format!(
911                                    "value of {:?} is out of range {}",
912                                    v,
913                                    T::DATA_TYPE
914                                ))
915                            })?;
916
917                        value_builder.append_value(value);
918                    }
919                }
920            }
921        }
922    }
923    Ok(Arc::new(value_builder.finish()))
924}
925
926/// Cast a decimal array to a floating point array.
927///
928/// Conversion is lossy and follows standard floating point semantics. Values
929/// that exceed the representable range become `INFINITY` or `-INFINITY` without
930/// returning an error.
931pub(crate) fn cast_decimal_to_float<D: DecimalType, T: ArrowPrimitiveType, F>(
932    array: &dyn Array,
933    op: F,
934) -> Result<ArrayRef, ArrowError>
935where
936    F: Fn(D::Native) -> T::Native,
937{
938    let array = array.as_primitive::<D>();
939    let array = array.unary::<_, T>(op);
940    Ok(Arc::new(array))
941}
942
943#[cfg(test)]
944mod tests {
945    use super::*;
946
947    #[test]
948    fn test_parse_string_to_decimal_native() -> Result<(), ArrowError> {
949        assert_eq!(
950            parse_string_to_decimal_native::<Decimal128Type>("0", 0)?,
951            0_i128
952        );
953        assert_eq!(
954            parse_string_to_decimal_native::<Decimal128Type>("0", 5)?,
955            0_i128
956        );
957
958        assert_eq!(
959            parse_string_to_decimal_native::<Decimal128Type>("123", 0)?,
960            123_i128
961        );
962        assert_eq!(
963            parse_string_to_decimal_native::<Decimal128Type>("123", 5)?,
964            12300000_i128
965        );
966
967        assert_eq!(
968            parse_string_to_decimal_native::<Decimal128Type>("123.45", 0)?,
969            123_i128
970        );
971        assert_eq!(
972            parse_string_to_decimal_native::<Decimal128Type>("123.45", 5)?,
973            12345000_i128
974        );
975
976        assert_eq!(
977            parse_string_to_decimal_native::<Decimal128Type>("123.4567891", 0)?,
978            123_i128
979        );
980        assert_eq!(
981            parse_string_to_decimal_native::<Decimal128Type>("123.4567891", 5)?,
982            12345679_i128
983        );
984
985        for value in ["", " ", ".", "+", "-", "+.", "-."] {
986            assert!(
987                parse_string_to_decimal_native::<Decimal128Type>(value, 2).is_err(),
988                "expected {value:?} to fail parsing as Decimal128"
989            );
990            assert!(
991                parse_string_to_decimal_native::<Decimal256Type>(value, 2).is_err(),
992                "expected {value:?} to fail parsing as Decimal256"
993            );
994        }
995        Ok(())
996    }
997
998    #[test]
999    fn test_rescale_decimal_upscale_within_precision() {
1000        let result = rescale_decimal::<Decimal128Type, Decimal128Type>(
1001            12_345_i128, // 123.45 with scale 2
1002            5,
1003            2,
1004            8,
1005            5,
1006        );
1007        assert_eq!(result, Some(12_345_000_i128));
1008    }
1009
1010    #[test]
1011    fn test_rescale_decimal_downscale_rounds_half_away_from_zero() {
1012        let positive = rescale_decimal::<Decimal128Type, Decimal128Type>(
1013            1_050_i128, // 1.050 with scale 3
1014            5, 3, 5, 1,
1015        );
1016        assert_eq!(positive, Some(11_i128)); // 1.1 with scale 1
1017
1018        let negative = rescale_decimal::<Decimal128Type, Decimal128Type>(
1019            -1_050_i128, // -1.050 with scale 3
1020            5,
1021            3,
1022            5,
1023            1,
1024        );
1025        assert_eq!(negative, Some(-11_i128)); // -1.1 with scale 1
1026    }
1027
1028    #[test]
1029    fn test_rescale_decimal_downscale_large_delta_returns_zero() {
1030        let result = rescale_decimal::<Decimal32Type, Decimal32Type>(12_345_i32, 9, 9, 9, 4);
1031        assert_eq!(result, Some(0_i32));
1032    }
1033
1034    #[test]
1035    fn test_rescale_decimal_upscale_overflow_returns_none() {
1036        let result = rescale_decimal::<Decimal32Type, Decimal32Type>(9_999_i32, 4, 0, 5, 2);
1037        assert_eq!(result, None);
1038    }
1039
1040    #[test]
1041    fn test_rescale_decimal_invalid_input_precision_scale_returns_none() {
1042        let result = rescale_decimal::<Decimal128Type, Decimal128Type>(123_i128, 39, 39, 38, 38);
1043        assert_eq!(result, None);
1044    }
1045
1046    #[test]
1047    fn test_rescale_decimal_invalid_output_precision_scale_returns_none() {
1048        let result = rescale_decimal::<Decimal128Type, Decimal128Type>(123_i128, 38, 38, 39, 39);
1049        assert_eq!(result, None);
1050    }
1051}