Skip to content

Casting decimals that exceed declared precision is buggy #10947

Description

@neilconway

Describe the bug

A decimal can hold values that exceed its declared precision. Casting such a value to another decimal type either panics — even with safe = true, which is documented to replace unrepresentable values with null — or silently returns a wrapped value in both safe and strict modes, depending on the target type.

To Reproduce

use arrow_array::Decimal128Array;
use arrow_cast::{cast_with_options, CastOptions};
use arrow_schema::DataType;

let array = Decimal128Array::from(vec![10_i128.pow(37)])
    .with_precision_and_scale(5, 0)
    .unwrap();

let result = cast_with_options(&array, &DataType::Decimal128(9, 2), &options).unwrap();
// safe=true and safe=false both return incorrect raw value
//   -20847100762815390390123822295304634368
// and do not indicate an error

Related / similar:

use arrow_array::Decimal128Array;
use arrow_cast::{cast_with_options, CastOptions};
use arrow_schema::DataType;

// Declared precision 2; the stored value has 31 digits
let array = Decimal128Array::from(vec![10_i128.pow(30)])
    .with_precision_and_scale(2, 1)
    .unwrap();

let result = cast_with_options(
    &array,
    &DataType::Decimal32(9, 2),
    &CastOptions { safe: true, ..Default::default() },
);

with safe=true, this results in

thread 'main' panicked at arrow-cast/src/cast/decimal.rs:193:56:
called `Option::unwrap()` on a `None` value

Expected behavior

Values that cannot be represented in the target type become null in safe mode and an error with safe = false.

Additional context

No response

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions