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 62bb64cf9f Check precision overflow for casting floating to decimal
(#4866)
62bb64cf9f is described below
commit 62bb64cf9f034a75e4485719653253077eb8efa6
Author: Liang-Chi Hsieh <[email protected]>
AuthorDate: Wed Sep 27 12:17:30 2023 -0700
Check precision overflow for casting floating to decimal (#4866)
* Check precision overflow for casting floating to decimal
* For review
---
arrow-cast/src/cast.rs | 122 +++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 102 insertions(+), 20 deletions(-)
diff --git a/arrow-cast/src/cast.rs b/arrow-cast/src/cast.rs
index e7727565c9..54c500f1ac 100644
--- a/arrow-cast/src/cast.rs
+++ b/arrow-cast/src/cast.rs
@@ -364,21 +364,32 @@ where
if cast_options.safe {
array
- .unary_opt::<_, Decimal128Type>(|v| (mul *
v.as_()).round().to_i128())
+ .unary_opt::<_, Decimal128Type>(|v| {
+ (mul * v.as_()).round().to_i128().filter(|v| {
+ Decimal128Type::validate_decimal_precision(*v,
precision).is_ok()
+ })
+ })
.with_precision_and_scale(precision, scale)
.map(|a| Arc::new(a) as ArrayRef)
} else {
array
.try_unary::<_, Decimal128Type, _>(|v| {
- (mul * v.as_()).round().to_i128().ok_or_else(|| {
- ArrowError::CastError(format!(
- "Cannot cast to {}({}, {}). Overflowing on {:?}",
- Decimal128Type::PREFIX,
- precision,
- scale,
- v
- ))
- })
+ (mul * v.as_())
+ .round()
+ .to_i128()
+ .ok_or_else(|| {
+ ArrowError::CastError(format!(
+ "Cannot cast to {}({}, {}). Overflowing on {:?}",
+ Decimal128Type::PREFIX,
+ precision,
+ scale,
+ v
+ ))
+ })
+ .and_then(|v| {
+ Decimal128Type::validate_decimal_precision(v,
precision)
+ .map(|_| v)
+ })
})?
.with_precision_and_scale(precision, scale)
.map(|a| Arc::new(a) as ArrayRef)
@@ -398,21 +409,30 @@ where
if cast_options.safe {
array
- .unary_opt::<_, Decimal256Type>(|v| i256::from_f64((v.as_() *
mul).round()))
+ .unary_opt::<_, Decimal256Type>(|v| {
+ i256::from_f64((v.as_() * mul).round()).filter(|v| {
+ Decimal256Type::validate_decimal_precision(*v,
precision).is_ok()
+ })
+ })
.with_precision_and_scale(precision, scale)
.map(|a| Arc::new(a) as ArrayRef)
} else {
array
.try_unary::<_, Decimal256Type, _>(|v| {
- i256::from_f64((v.as_() * mul).round()).ok_or_else(|| {
- ArrowError::CastError(format!(
- "Cannot cast to {}({}, {}). Overflowing on {:?}",
- Decimal256Type::PREFIX,
- precision,
- scale,
- v
- ))
- })
+ i256::from_f64((v.as_() * mul).round())
+ .ok_or_else(|| {
+ ArrowError::CastError(format!(
+ "Cannot cast to {}({}, {}). Overflowing on {:?}",
+ Decimal256Type::PREFIX,
+ precision,
+ scale,
+ v
+ ))
+ })
+ .and_then(|v| {
+ Decimal256Type::validate_decimal_precision(v,
precision)
+ .map(|_| v)
+ })
})?
.with_precision_and_scale(precision, scale)
.map(|a| Arc::new(a) as ArrayRef)
@@ -7748,6 +7768,68 @@ mod tests {
assert!(casted_array.is_err());
}
+ #[test]
+ fn test_cast_floating_point_to_decimal128_precision_overflow() {
+ let array = Float64Array::from(vec![1.1]);
+ let array = Arc::new(array) as ArrayRef;
+ let casted_array = cast_with_options(
+ &array,
+ &DataType::Decimal128(2, 2),
+ &CastOptions {
+ safe: true,
+ format_options: FormatOptions::default(),
+ },
+ );
+ assert!(casted_array.is_ok());
+ assert!(casted_array.unwrap().is_null(0));
+
+ let casted_array = cast_with_options(
+ &array,
+ &DataType::Decimal128(2, 2),
+ &CastOptions {
+ safe: false,
+ format_options: FormatOptions::default(),
+ },
+ );
+ let err = casted_array.unwrap_err().to_string();
+ let expected_error = "Invalid argument error: 110 is too large to
store in a Decimal128 of precision 2. Max is 99";
+ assert!(
+ err.contains(expected_error),
+ "did not find expected error '{expected_error}' in actual error
'{err}'"
+ );
+ }
+
+ #[test]
+ fn test_cast_floating_point_to_decimal256_precision_overflow() {
+ let array = Float64Array::from(vec![1.1]);
+ let array = Arc::new(array) as ArrayRef;
+ let casted_array = cast_with_options(
+ &array,
+ &DataType::Decimal256(2, 2),
+ &CastOptions {
+ safe: true,
+ format_options: FormatOptions::default(),
+ },
+ );
+ assert!(casted_array.is_ok());
+ assert!(casted_array.unwrap().is_null(0));
+
+ let casted_array = cast_with_options(
+ &array,
+ &DataType::Decimal256(2, 2),
+ &CastOptions {
+ safe: false,
+ format_options: FormatOptions::default(),
+ },
+ );
+ let err = casted_array.unwrap_err().to_string();
+ let expected_error = "Invalid argument error: 110 is too large to
store in a Decimal256 of precision 2. Max is 99";
+ assert!(
+ err.contains(expected_error),
+ "did not find expected error '{expected_error}' in actual error
'{err}'"
+ );
+ }
+
#[test]
fn test_cast_floating_point_to_decimal128_overflow() {
let array = Float64Array::from(vec![f64::MAX]);