Coverage Report

Created: 2025-08-26 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/Users/andrewlamb/Software/arrow-rs/arrow-cast/src/cast/decimal.rs
Line
Count
Source
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
18
use crate::cast::*;
19
20
/// A utility trait that provides checked conversions between
21
/// decimal types inspired by [`NumCast`]
22
pub(crate) trait DecimalCast: Sized {
23
    fn to_i32(self) -> Option<i32>;
24
25
    fn to_i64(self) -> Option<i64>;
26
27
    fn to_i128(self) -> Option<i128>;
28
29
    fn to_i256(self) -> Option<i256>;
30
31
    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self>;
32
33
    fn from_f64(n: f64) -> Option<Self>;
34
}
35
36
impl DecimalCast for i32 {
37
0
    fn to_i32(self) -> Option<i32> {
38
0
        Some(self)
39
0
    }
40
41
0
    fn to_i64(self) -> Option<i64> {
42
0
        Some(self as i64)
43
0
    }
44
45
0
    fn to_i128(self) -> Option<i128> {
46
0
        Some(self as i128)
47
0
    }
48
49
0
    fn to_i256(self) -> Option<i256> {
50
0
        Some(i256::from_i128(self as i128))
51
0
    }
52
53
0
    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
54
0
        n.to_i32()
55
0
    }
56
57
0
    fn from_f64(n: f64) -> Option<Self> {
58
0
        n.to_i32()
59
0
    }
60
}
61
62
impl DecimalCast for i64 {
63
0
    fn to_i32(self) -> Option<i32> {
64
0
        i32::try_from(self).ok()
65
0
    }
66
67
0
    fn to_i64(self) -> Option<i64> {
68
0
        Some(self)
69
0
    }
70
71
0
    fn to_i128(self) -> Option<i128> {
72
0
        Some(self as i128)
73
0
    }
74
75
0
    fn to_i256(self) -> Option<i256> {
76
0
        Some(i256::from_i128(self as i128))
77
0
    }
78
79
0
    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
80
0
        n.to_i64()
81
0
    }
82
83
0
    fn from_f64(n: f64) -> Option<Self> {
84
0
        n.to_i64()
85
0
    }
86
}
87
88
impl DecimalCast for i128 {
89
0
    fn to_i32(self) -> Option<i32> {
90
0
        i32::try_from(self).ok()
91
0
    }
92
93
0
    fn to_i64(self) -> Option<i64> {
94
0
        i64::try_from(self).ok()
95
0
    }
96
97
0
    fn to_i128(self) -> Option<i128> {
98
0
        Some(self)
99
0
    }
100
101
0
    fn to_i256(self) -> Option<i256> {
102
0
        Some(i256::from_i128(self))
103
0
    }
104
105
0
    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
106
0
        n.to_i128()
107
0
    }
108
109
0
    fn from_f64(n: f64) -> Option<Self> {
110
0
        n.to_i128()
111
0
    }
112
}
113
114
impl DecimalCast for i256 {
115
0
    fn to_i32(self) -> Option<i32> {
116
0
        self.to_i128().map(|x| i32::try_from(x).ok())?
117
0
    }
118
119
0
    fn to_i64(self) -> Option<i64> {
120
0
        self.to_i128().map(|x| i64::try_from(x).ok())?
121
0
    }
122
123
0
    fn to_i128(self) -> Option<i128> {
124
0
        self.to_i128()
125
0
    }
126
127
0
    fn to_i256(self) -> Option<i256> {
128
0
        Some(self)
129
0
    }
130
131
0
    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
132
0
        n.to_i256()
133
0
    }
134
135
0
    fn from_f64(n: f64) -> Option<Self> {
136
0
        i256::from_f64(n)
137
0
    }
138
}
139
140
0
pub(crate) fn cast_decimal_to_decimal_error<I, O>(
141
0
    output_precision: u8,
142
0
    output_scale: i8,
143
0
) -> impl Fn(<I as ArrowPrimitiveType>::Native) -> ArrowError
144
0
where
145
0
    I: DecimalType,
146
0
    O: DecimalType,
147
0
    I::Native: DecimalCast + ArrowNativeTypeOp,
148
0
    O::Native: DecimalCast + ArrowNativeTypeOp,
149
{
150
0
    move |x: I::Native| {
151
0
        ArrowError::CastError(format!(
152
0
            "Cannot cast to {}({}, {}). Overflowing on {:?}",
153
0
            O::PREFIX,
154
0
            output_precision,
155
0
            output_scale,
156
0
            x
157
0
        ))
158
0
    }
159
0
}
160
161
0
pub(crate) fn convert_to_smaller_scale_decimal<I, O>(
162
0
    array: &PrimitiveArray<I>,
163
0
    input_precision: u8,
164
0
    input_scale: i8,
165
0
    output_precision: u8,
166
0
    output_scale: i8,
167
0
    cast_options: &CastOptions,
168
0
) -> Result<PrimitiveArray<O>, ArrowError>
169
0
where
170
0
    I: DecimalType,
171
0
    O: DecimalType,
172
0
    I::Native: DecimalCast + ArrowNativeTypeOp,
173
0
    O::Native: DecimalCast + ArrowNativeTypeOp,
174
{
175
0
    let error = cast_decimal_to_decimal_error::<I, O>(output_precision, output_scale);
176
0
    let delta_scale = input_scale - output_scale;
177
    // if the reduction of the input number through scaling (dividing) is greater
178
    // than a possible precision loss (plus potential increase via rounding)
179
    // every input number will fit into the output type
180
    // Example: If we are starting with any number of precision 5 [xxxxx],
181
    // then and decrease the scale by 3 will have the following effect on the representation:
182
    // [xxxxx] -> [xx] (+ 1 possibly, due to rounding).
183
    // The rounding may add an additional digit, so the cast to be infallible,
184
    // the output type needs to have at least 3 digits of precision.
185
    // e.g. Decimal(5, 3) 99.999 to Decimal(3, 0) will result in 100:
186
    // [99999] -> [99] + 1 = [100], a cast to Decimal(2, 0) would not be possible
187
0
    let is_infallible_cast = (input_precision as i8) - delta_scale < (output_precision as i8);
188
189
0
    let div = I::Native::from_decimal(10_i128)
190
0
        .unwrap()
191
0
        .pow_checked(delta_scale as u32)?;
192
193
0
    let half = div.div_wrapping(I::Native::from_usize(2).unwrap());
194
0
    let half_neg = half.neg_wrapping();
195
196
0
    let f = |x: I::Native| {
197
        // div is >= 10 and so this cannot overflow
198
0
        let d = x.div_wrapping(div);
199
0
        let r = x.mod_wrapping(div);
200
201
        // Round result
202
0
        let adjusted = match x >= I::Native::ZERO {
203
0
            true if r >= half => d.add_wrapping(I::Native::ONE),
204
0
            false if r <= half_neg => d.sub_wrapping(I::Native::ONE),
205
0
            _ => d,
206
        };
207
0
        O::Native::from_decimal(adjusted)
208
0
    };
209
210
0
    Ok(if is_infallible_cast {
211
        // make sure we don't perform calculations that don't make sense w/o validation
212
0
        validate_decimal_precision_and_scale::<O>(output_precision, output_scale)?;
213
0
        let g = |x: I::Native| f(x).unwrap(); // unwrapping is safe since the result is guaranteed
214
                                              // to fit into the target type
215
0
        array.unary(g)
216
0
    } else if cast_options.safe {
217
0
        array.unary_opt(|x| f(x).filter(|v| O::is_valid_decimal_precision(*v, output_precision)))
218
    } else {
219
0
        array.try_unary(|x| {
220
0
            f(x).ok_or_else(|| error(x))
221
0
                .and_then(|v| O::validate_decimal_precision(v, output_precision).map(|_| v))
222
0
        })?
223
    })
224
0
}
225
226
0
pub(crate) fn convert_to_bigger_or_equal_scale_decimal<I, O>(
227
0
    array: &PrimitiveArray<I>,
228
0
    input_precision: u8,
229
0
    input_scale: i8,
230
0
    output_precision: u8,
231
0
    output_scale: i8,
232
0
    cast_options: &CastOptions,
233
0
) -> Result<PrimitiveArray<O>, ArrowError>
234
0
where
235
0
    I: DecimalType,
236
0
    O: DecimalType,
237
0
    I::Native: DecimalCast + ArrowNativeTypeOp,
238
0
    O::Native: DecimalCast + ArrowNativeTypeOp,
239
{
240
0
    let error = cast_decimal_to_decimal_error::<I, O>(output_precision, output_scale);
241
0
    let delta_scale = output_scale - input_scale;
242
0
    let mul = O::Native::from_decimal(10_i128)
243
0
        .unwrap()
244
0
        .pow_checked(delta_scale as u32)?;
245
246
    // if the gain in precision (digits) is greater than the multiplication due to scaling
247
    // every number will fit into the output type
248
    // Example: If we are starting with any number of precision 5 [xxxxx],
249
    // then an increase of scale by 3 will have the following effect on the representation:
250
    // [xxxxx] -> [xxxxx000], so for the cast to be infallible, the output type
251
    // needs to provide at least 8 digits precision
252
0
    let is_infallible_cast = (input_precision as i8) + delta_scale <= (output_precision as i8);
253
0
    let f = |x| O::Native::from_decimal(x).and_then(|x| x.mul_checked(mul).ok());
254
255
0
    Ok(if is_infallible_cast {
256
        // make sure we don't perform calculations that don't make sense w/o validation
257
0
        validate_decimal_precision_and_scale::<O>(output_precision, output_scale)?;
258
        // unwrapping is safe since the result is guaranteed to fit into the target type
259
0
        let f = |x| O::Native::from_decimal(x).unwrap().mul_wrapping(mul);
260
0
        array.unary(f)
261
0
    } else if cast_options.safe {
262
0
        array.unary_opt(|x| f(x).filter(|v| O::is_valid_decimal_precision(*v, output_precision)))
263
    } else {
264
0
        array.try_unary(|x| {
265
0
            f(x).ok_or_else(|| error(x))
266
0
                .and_then(|v| O::validate_decimal_precision(v, output_precision).map(|_| v))
267
0
        })?
268
    })
269
0
}
270
271
// Only support one type of decimal cast operations
272
0
pub(crate) fn cast_decimal_to_decimal_same_type<T>(
273
0
    array: &PrimitiveArray<T>,
274
0
    input_precision: u8,
275
0
    input_scale: i8,
276
0
    output_precision: u8,
277
0
    output_scale: i8,
278
0
    cast_options: &CastOptions,
279
0
) -> Result<ArrayRef, ArrowError>
280
0
where
281
0
    T: DecimalType,
282
0
    T::Native: DecimalCast + ArrowNativeTypeOp,
283
{
284
0
    let array: PrimitiveArray<T> =
285
0
        if input_scale == output_scale && input_precision <= output_precision {
286
0
            array.clone()
287
0
        } else if input_scale <= output_scale {
288
0
            convert_to_bigger_or_equal_scale_decimal::<T, T>(
289
0
                array,
290
0
                input_precision,
291
0
                input_scale,
292
0
                output_precision,
293
0
                output_scale,
294
0
                cast_options,
295
0
            )?
296
        } else {
297
            // input_scale > output_scale
298
0
            convert_to_smaller_scale_decimal::<T, T>(
299
0
                array,
300
0
                input_precision,
301
0
                input_scale,
302
0
                output_precision,
303
0
                output_scale,
304
0
                cast_options,
305
0
            )?
306
        };
307
308
0
    Ok(Arc::new(array.with_precision_and_scale(
309
0
        output_precision,
310
0
        output_scale,
311
0
    )?))
312
0
}
313
314
// Support two different types of decimal cast operations
315
0
pub(crate) fn cast_decimal_to_decimal<I, O>(
316
0
    array: &PrimitiveArray<I>,
317
0
    input_precision: u8,
318
0
    input_scale: i8,
319
0
    output_precision: u8,
320
0
    output_scale: i8,
321
0
    cast_options: &CastOptions,
322
0
) -> Result<ArrayRef, ArrowError>
323
0
where
324
0
    I: DecimalType,
325
0
    O: DecimalType,
326
0
    I::Native: DecimalCast + ArrowNativeTypeOp,
327
0
    O::Native: DecimalCast + ArrowNativeTypeOp,
328
{
329
0
    let array: PrimitiveArray<O> = if input_scale > output_scale {
330
0
        convert_to_smaller_scale_decimal::<I, O>(
331
0
            array,
332
0
            input_precision,
333
0
            input_scale,
334
0
            output_precision,
335
0
            output_scale,
336
0
            cast_options,
337
0
        )?
338
    } else {
339
0
        convert_to_bigger_or_equal_scale_decimal::<I, O>(
340
0
            array,
341
0
            input_precision,
342
0
            input_scale,
343
0
            output_precision,
344
0
            output_scale,
345
0
            cast_options,
346
0
        )?
347
    };
348
349
0
    Ok(Arc::new(array.with_precision_and_scale(
350
0
        output_precision,
351
0
        output_scale,
352
0
    )?))
353
0
}
354
355
/// Parses given string to specified decimal native (i128/i256) based on given
356
/// scale. Returns an `Err` if it cannot parse given string.
357
0
pub(crate) fn parse_string_to_decimal_native<T: DecimalType>(
358
0
    value_str: &str,
359
0
    scale: usize,
360
0
) -> Result<T::Native, ArrowError>
361
0
where
362
0
    T::Native: DecimalCast + ArrowNativeTypeOp,
363
{
364
0
    let value_str = value_str.trim();
365
0
    let parts: Vec<&str> = value_str.split('.').collect();
366
0
    if parts.len() > 2 {
367
0
        return Err(ArrowError::InvalidArgumentError(format!(
368
0
            "Invalid decimal format: {value_str:?}"
369
0
        )));
370
0
    }
371
372
0
    let (negative, first_part) = if parts[0].is_empty() {
373
0
        (false, parts[0])
374
    } else {
375
0
        match parts[0].as_bytes()[0] {
376
0
            b'-' => (true, &parts[0][1..]),
377
0
            b'+' => (false, &parts[0][1..]),
378
0
            _ => (false, parts[0]),
379
        }
380
    };
381
382
0
    let integers = first_part;
383
0
    let decimals = if parts.len() == 2 { parts[1] } else { "" };
384
385
0
    if !integers.is_empty() && !integers.as_bytes()[0].is_ascii_digit() {
386
0
        return Err(ArrowError::InvalidArgumentError(format!(
387
0
            "Invalid decimal format: {value_str:?}"
388
0
        )));
389
0
    }
390
391
0
    if !decimals.is_empty() && !decimals.as_bytes()[0].is_ascii_digit() {
392
0
        return Err(ArrowError::InvalidArgumentError(format!(
393
0
            "Invalid decimal format: {value_str:?}"
394
0
        )));
395
0
    }
396
397
    // Adjust decimal based on scale
398
0
    let mut number_decimals = if decimals.len() > scale {
399
0
        let decimal_number = i256::from_string(decimals).ok_or_else(|| {
400
0
            ArrowError::InvalidArgumentError(format!("Cannot parse decimal format: {value_str}"))
401
0
        })?;
402
403
0
        let div = i256::from_i128(10_i128).pow_checked((decimals.len() - scale) as u32)?;
404
405
0
        let half = div.div_wrapping(i256::from_i128(2));
406
0
        let half_neg = half.neg_wrapping();
407
408
0
        let d = decimal_number.div_wrapping(div);
409
0
        let r = decimal_number.mod_wrapping(div);
410
411
        // Round result
412
0
        let adjusted = match decimal_number >= i256::ZERO {
413
0
            true if r >= half => d.add_wrapping(i256::ONE),
414
0
            false if r <= half_neg => d.sub_wrapping(i256::ONE),
415
0
            _ => d,
416
        };
417
418
0
        let integers = if !integers.is_empty() {
419
0
            i256::from_string(integers)
420
0
                .ok_or_else(|| {
421
0
                    ArrowError::InvalidArgumentError(format!(
422
0
                        "Cannot parse decimal format: {value_str}"
423
0
                    ))
424
0
                })
425
0
                .map(|v| v.mul_wrapping(i256::from_i128(10_i128).pow_wrapping(scale as u32)))?
426
        } else {
427
0
            i256::ZERO
428
        };
429
430
0
        format!("{}", integers.add_wrapping(adjusted))
431
    } else {
432
0
        let padding = if scale > decimals.len() { scale } else { 0 };
433
434
0
        let decimals = format!("{decimals:0<padding$}");
435
0
        format!("{integers}{decimals}")
436
    };
437
438
0
    if negative {
439
0
        number_decimals.insert(0, '-');
440
0
    }
441
442
0
    let value = i256::from_string(number_decimals.as_str()).ok_or_else(|| {
443
0
        ArrowError::InvalidArgumentError(format!(
444
0
            "Cannot convert {} to {}: Overflow",
445
0
            value_str,
446
0
            T::PREFIX
447
0
        ))
448
0
    })?;
449
450
0
    T::Native::from_decimal(value).ok_or_else(|| {
451
0
        ArrowError::InvalidArgumentError(format!("Cannot convert {} to {}", value_str, T::PREFIX))
452
0
    })
453
0
}
454
455
0
pub(crate) fn generic_string_to_decimal_cast<'a, T, S>(
456
0
    from: &'a S,
457
0
    precision: u8,
458
0
    scale: i8,
459
0
    cast_options: &CastOptions,
460
0
) -> Result<PrimitiveArray<T>, ArrowError>
461
0
where
462
0
    T: DecimalType,
463
0
    T::Native: DecimalCast + ArrowNativeTypeOp,
464
0
    &'a S: StringArrayType<'a>,
465
{
466
0
    if cast_options.safe {
467
0
        let iter = from.iter().map(|v| {
468
0
            v.and_then(|v| parse_string_to_decimal_native::<T>(v, scale as usize).ok())
469
0
                .and_then(|v| T::is_valid_decimal_precision(v, precision).then_some(v))
470
0
        });
471
        // Benefit:
472
        //     20% performance improvement
473
        // Soundness:
474
        //     The iterator is trustedLen because it comes from an `StringArray`.
475
        Ok(unsafe {
476
0
            PrimitiveArray::<T>::from_trusted_len_iter(iter)
477
0
                .with_precision_and_scale(precision, scale)?
478
        })
479
    } else {
480
0
        let vec = from
481
0
            .iter()
482
0
            .map(|v| {
483
0
                v.map(|v| {
484
0
                    parse_string_to_decimal_native::<T>(v, scale as usize)
485
0
                        .map_err(|_| {
486
0
                            ArrowError::CastError(format!(
487
0
                                "Cannot cast string '{}' to value of {:?} type",
488
0
                                v,
489
0
                                T::DATA_TYPE,
490
0
                            ))
491
0
                        })
492
0
                        .and_then(|v| T::validate_decimal_precision(v, precision).map(|_| v))
493
0
                })
494
0
                .transpose()
495
0
            })
496
0
            .collect::<Result<Vec<_>, _>>()?;
497
        // Benefit:
498
        //     20% performance improvement
499
        // Soundness:
500
        //     The iterator is trustedLen because it comes from an `StringArray`.
501
        Ok(unsafe {
502
0
            PrimitiveArray::<T>::from_trusted_len_iter(vec.iter())
503
0
                .with_precision_and_scale(precision, scale)?
504
        })
505
    }
506
0
}
507
508
0
pub(crate) fn string_to_decimal_cast<T, Offset: OffsetSizeTrait>(
509
0
    from: &GenericStringArray<Offset>,
510
0
    precision: u8,
511
0
    scale: i8,
512
0
    cast_options: &CastOptions,
513
0
) -> Result<PrimitiveArray<T>, ArrowError>
514
0
where
515
0
    T: DecimalType,
516
0
    T::Native: DecimalCast + ArrowNativeTypeOp,
517
{
518
0
    generic_string_to_decimal_cast::<T, GenericStringArray<Offset>>(
519
0
        from,
520
0
        precision,
521
0
        scale,
522
0
        cast_options,
523
    )
524
0
}
525
526
0
pub(crate) fn string_view_to_decimal_cast<T>(
527
0
    from: &StringViewArray,
528
0
    precision: u8,
529
0
    scale: i8,
530
0
    cast_options: &CastOptions,
531
0
) -> Result<PrimitiveArray<T>, ArrowError>
532
0
where
533
0
    T: DecimalType,
534
0
    T::Native: DecimalCast + ArrowNativeTypeOp,
535
{
536
0
    generic_string_to_decimal_cast::<T, StringViewArray>(from, precision, scale, cast_options)
537
0
}
538
539
/// Cast Utf8 to decimal
540
0
pub(crate) fn cast_string_to_decimal<T, Offset: OffsetSizeTrait>(
541
0
    from: &dyn Array,
542
0
    precision: u8,
543
0
    scale: i8,
544
0
    cast_options: &CastOptions,
545
0
) -> Result<ArrayRef, ArrowError>
546
0
where
547
0
    T: DecimalType,
548
0
    T::Native: DecimalCast + ArrowNativeTypeOp,
549
{
550
0
    if scale < 0 {
551
0
        return Err(ArrowError::InvalidArgumentError(format!(
552
0
            "Cannot cast string to decimal with negative scale {scale}"
553
0
        )));
554
0
    }
555
556
0
    if scale > T::MAX_SCALE {
557
0
        return Err(ArrowError::InvalidArgumentError(format!(
558
0
            "Cannot cast string to decimal greater than maximum scale {}",
559
0
            T::MAX_SCALE
560
0
        )));
561
0
    }
562
563
0
    let result = match from.data_type() {
564
0
        DataType::Utf8View => string_view_to_decimal_cast::<T>(
565
0
            from.as_any().downcast_ref::<StringViewArray>().unwrap(),
566
0
            precision,
567
0
            scale,
568
0
            cast_options,
569
0
        )?,
570
0
        DataType::Utf8 | DataType::LargeUtf8 => string_to_decimal_cast::<T, Offset>(
571
0
            from.as_any()
572
0
                .downcast_ref::<GenericStringArray<Offset>>()
573
0
                .unwrap(),
574
0
            precision,
575
0
            scale,
576
0
            cast_options,
577
0
        )?,
578
0
        other => {
579
0
            return Err(ArrowError::ComputeError(format!(
580
0
                "Cannot cast {other:?} to decimal",
581
0
            )))
582
        }
583
    };
584
585
0
    Ok(Arc::new(result))
586
0
}
587
588
0
pub(crate) fn cast_floating_point_to_decimal<T: ArrowPrimitiveType, D>(
589
0
    array: &PrimitiveArray<T>,
590
0
    precision: u8,
591
0
    scale: i8,
592
0
    cast_options: &CastOptions,
593
0
) -> Result<ArrayRef, ArrowError>
594
0
where
595
0
    <T as ArrowPrimitiveType>::Native: AsPrimitive<f64>,
596
0
    D: DecimalType + ArrowPrimitiveType,
597
0
    <D as ArrowPrimitiveType>::Native: DecimalCast,
598
{
599
0
    let mul = 10_f64.powi(scale as i32);
600
601
0
    if cast_options.safe {
602
0
        array
603
0
            .unary_opt::<_, D>(|v| {
604
0
                D::Native::from_f64((mul * v.as_()).round())
605
0
                    .filter(|v| D::is_valid_decimal_precision(*v, precision))
606
0
            })
607
0
            .with_precision_and_scale(precision, scale)
608
0
            .map(|a| Arc::new(a) as ArrayRef)
609
    } else {
610
0
        array
611
0
            .try_unary::<_, D, _>(|v| {
612
0
                D::Native::from_f64((mul * v.as_()).round())
613
0
                    .ok_or_else(|| {
614
0
                        ArrowError::CastError(format!(
615
0
                            "Cannot cast to {}({}, {}). Overflowing on {:?}",
616
0
                            D::PREFIX,
617
0
                            precision,
618
0
                            scale,
619
0
                            v
620
0
                        ))
621
0
                    })
622
0
                    .and_then(|v| D::validate_decimal_precision(v, precision).map(|_| v))
623
0
            })?
624
0
            .with_precision_and_scale(precision, scale)
625
0
            .map(|a| Arc::new(a) as ArrayRef)
626
    }
627
0
}
628
629
0
pub(crate) fn cast_decimal_to_integer<D, T>(
630
0
    array: &dyn Array,
631
0
    base: D::Native,
632
0
    scale: i8,
633
0
    cast_options: &CastOptions,
634
0
) -> Result<ArrayRef, ArrowError>
635
0
where
636
0
    T: ArrowPrimitiveType,
637
0
    <T as ArrowPrimitiveType>::Native: NumCast,
638
0
    D: DecimalType + ArrowPrimitiveType,
639
0
    <D as ArrowPrimitiveType>::Native: ArrowNativeTypeOp + ToPrimitive,
640
{
641
0
    let array = array.as_primitive::<D>();
642
643
0
    let div: D::Native = base.pow_checked(scale as u32).map_err(|_| {
644
0
        ArrowError::CastError(format!(
645
0
            "Cannot cast to {:?}. The scale {} causes overflow.",
646
0
            D::PREFIX,
647
0
            scale,
648
0
        ))
649
0
    })?;
650
651
0
    let mut value_builder = PrimitiveBuilder::<T>::with_capacity(array.len());
652
653
0
    if cast_options.safe {
654
0
        for i in 0..array.len() {
655
0
            if array.is_null(i) {
656
0
                value_builder.append_null();
657
0
            } else {
658
0
                let v = array
659
0
                    .value(i)
660
0
                    .div_checked(div)
661
0
                    .ok()
662
0
                    .and_then(<T::Native as NumCast>::from::<D::Native>);
663
0
664
0
                value_builder.append_option(v);
665
0
            }
666
        }
667
    } else {
668
0
        for i in 0..array.len() {
669
0
            if array.is_null(i) {
670
0
                value_builder.append_null();
671
0
            } else {
672
0
                let v = array.value(i).div_checked(div)?;
673
674
0
                let value = <T::Native as NumCast>::from::<D::Native>(v).ok_or_else(|| {
675
0
                    ArrowError::CastError(format!(
676
0
                        "value of {:?} is out of range {}",
677
0
                        v,
678
0
                        T::DATA_TYPE
679
0
                    ))
680
0
                })?;
681
682
0
                value_builder.append_value(value);
683
            }
684
        }
685
    }
686
0
    Ok(Arc::new(value_builder.finish()))
687
0
}
688
689
/// Cast a decimal array to a floating point array.
690
///
691
/// Conversion is lossy and follows standard floating point semantics. Values
692
/// that exceed the representable range become `INFINITY` or `-INFINITY` without
693
/// returning an error.
694
0
pub(crate) fn cast_decimal_to_float<D: DecimalType, T: ArrowPrimitiveType, F>(
695
0
    array: &dyn Array,
696
0
    op: F,
697
0
) -> Result<ArrayRef, ArrowError>
698
0
where
699
0
    F: Fn(D::Native) -> T::Native,
700
{
701
0
    let array = array.as_primitive::<D>();
702
0
    let array = array.unary::<_, T>(op);
703
0
    Ok(Arc::new(array))
704
0
}
705
706
#[cfg(test)]
707
mod tests {
708
    use super::*;
709
710
    #[test]
711
    fn test_parse_string_to_decimal_native() -> Result<(), ArrowError> {
712
        assert_eq!(
713
            parse_string_to_decimal_native::<Decimal128Type>("0", 0)?,
714
            0_i128
715
        );
716
        assert_eq!(
717
            parse_string_to_decimal_native::<Decimal128Type>("0", 5)?,
718
            0_i128
719
        );
720
721
        assert_eq!(
722
            parse_string_to_decimal_native::<Decimal128Type>("123", 0)?,
723
            123_i128
724
        );
725
        assert_eq!(
726
            parse_string_to_decimal_native::<Decimal128Type>("123", 5)?,
727
            12300000_i128
728
        );
729
730
        assert_eq!(
731
            parse_string_to_decimal_native::<Decimal128Type>("123.45", 0)?,
732
            123_i128
733
        );
734
        assert_eq!(
735
            parse_string_to_decimal_native::<Decimal128Type>("123.45", 5)?,
736
            12345000_i128
737
        );
738
739
        assert_eq!(
740
            parse_string_to_decimal_native::<Decimal128Type>("123.4567891", 0)?,
741
            123_i128
742
        );
743
        assert_eq!(
744
            parse_string_to_decimal_native::<Decimal128Type>("123.4567891", 5)?,
745
            12345679_i128
746
        );
747
        Ok(())
748
    }
749
}