You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by tu...@apache.org on 2023/04/01 18:27:35 UTC
[arrow-rs] branch master updated: Handle precision overflow when casting from integer to decimal (#3996)
This is an automated email from the ASF dual-hosted git repository.
tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
The following commit(s) were added to refs/heads/master by this push:
new 4e7bb4505 Handle precision overflow when casting from integer to decimal (#3996)
4e7bb4505 is described below
commit 4e7bb45050622d5b43505aa64dacf410cb329941
Author: Liang-Chi Hsieh <vi...@gmail.com>
AuthorDate: Sat Apr 1 11:27:29 2023 -0700
Handle precision overflow when casting from integer to decimal (#3996)
* Handle overflow precision when casting from integer to decimal
* fix clippy
* Update test
* Update test
---
arrow-cast/src/cast.rs | 73 +++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 63 insertions(+), 10 deletions(-)
diff --git a/arrow-cast/src/cast.rs b/arrow-cast/src/cast.rs
index 9886decd9..e4f4370fd 100644
--- a/arrow-cast/src/cast.rs
+++ b/arrow-cast/src/cast.rs
@@ -358,13 +358,29 @@ where
let array = if scale < 0 {
match cast_options.safe {
- true => array.unary_opt::<_, D>(|v| v.as_().div_checked(scale_factor).ok()),
- false => array.try_unary::<_, D, _>(|v| v.as_().div_checked(scale_factor))?,
+ true => array.unary_opt::<_, D>(|v| {
+ v.as_().div_checked(scale_factor).ok().and_then(|v| {
+ (D::validate_decimal_precision(v, precision).is_ok()).then_some(v)
+ })
+ }),
+ false => array.try_unary::<_, D, _>(|v| {
+ v.as_()
+ .div_checked(scale_factor)
+ .and_then(|v| D::validate_decimal_precision(v, precision).map(|_| v))
+ })?,
}
} else {
match cast_options.safe {
- true => array.unary_opt::<_, D>(|v| v.as_().mul_checked(scale_factor).ok()),
- false => array.try_unary::<_, D, _>(|v| v.as_().mul_checked(scale_factor))?,
+ true => array.unary_opt::<_, D>(|v| {
+ v.as_().mul_checked(scale_factor).ok().and_then(|v| {
+ (D::validate_decimal_precision(v, precision).is_ok()).then_some(v)
+ })
+ }),
+ false => array.try_unary::<_, D, _>(|v| {
+ v.as_()
+ .mul_checked(scale_factor)
+ .and_then(|v| D::validate_decimal_precision(v, precision).map(|_| v))
+ })?,
}
};
@@ -4375,8 +4391,7 @@ mod tests {
assert!(casted_array.is_ok());
let array = casted_array.unwrap();
let array: &Decimal128Array = array.as_primitive();
- let err = array.validate_decimal_precision(3);
- assert_eq!("Invalid argument error: 1000 is too large to store in a Decimal128 of precision 3. Max is 999", err.unwrap_err().to_string());
+ assert!(array.is_null(4));
// test i8 to decimal type with overflow the result type
// the 100 will be converted to 1000_i128, but it is out of range for max value in the precision 3.
@@ -4385,8 +4400,7 @@ mod tests {
assert!(casted_array.is_ok());
let array = casted_array.unwrap();
let array: &Decimal128Array = array.as_primitive();
- let err = array.validate_decimal_precision(3);
- assert_eq!("Invalid argument error: 1000 is too large to store in a Decimal128 of precision 3. Max is 999", err.unwrap_err().to_string());
+ assert!(array.is_null(4));
// test f32 to decimal type
let array = Float32Array::from(vec![
@@ -4544,8 +4558,7 @@ mod tests {
assert!(casted_array.is_ok());
let array = casted_array.unwrap();
let array: &Decimal256Array = array.as_primitive();
- let err = array.validate_decimal_precision(3);
- assert_eq!("Invalid argument error: 1000 is too large to store in a Decimal256 of precision 3. Max is 999", err.unwrap_err().to_string());
+ assert!(array.is_null(4));
// test f32 to decimal type
let array = Float32Array::from(vec![
@@ -8132,4 +8145,44 @@ mod tests {
.unwrap();
assert_eq!(1672531200000000000, c.value(0));
}
+
+ #[test]
+ fn test_cast_numeric_to_decimal128_precision_overflow() {
+ let array = Int64Array::from(vec![1234567]);
+ let array = Arc::new(array) as ArrayRef;
+ let casted_array = cast_with_options(
+ &array,
+ &DataType::Decimal128(7, 3),
+ &CastOptions { safe: true },
+ );
+ assert!(casted_array.is_ok());
+ assert!(casted_array.unwrap().is_null(0));
+
+ let err = cast_with_options(
+ &array,
+ &DataType::Decimal128(7, 3),
+ &CastOptions { safe: false },
+ );
+ assert_eq!("Invalid argument error: 1234567000 is too large to store in a Decimal128 of precision 7. Max is 9999999", err.unwrap_err().to_string());
+ }
+
+ #[test]
+ fn test_cast_numeric_to_decimal256_precision_overflow() {
+ let array = Int64Array::from(vec![1234567]);
+ let array = Arc::new(array) as ArrayRef;
+ let casted_array = cast_with_options(
+ &array,
+ &DataType::Decimal256(7, 3),
+ &CastOptions { safe: true },
+ );
+ assert!(casted_array.is_ok());
+ assert!(casted_array.unwrap().is_null(0));
+
+ let err = cast_with_options(
+ &array,
+ &DataType::Decimal256(7, 3),
+ &CastOptions { safe: false },
+ );
+ assert_eq!("Invalid argument error: 1234567000 is too large to store in a Decimal256 of precision 7. Max is 9999999", err.unwrap_err().to_string());
+ }
}