From 591f0ef45c8184c2cbf5d46fc1ddf81c207e55ee Mon Sep 17 00:00:00 2001 From: comphead Date: Sat, 1 Apr 2023 13:45:54 -0700 Subject: [PATCH] Support CAST from Decimal datatype to String (#3994) * Support CAST Decimal to Utf8 * remove null * fmt * Apply suggestions from code review Co-authored-by: Raphael Taylor-Davies <1781103+tustvold@users.noreply.github.com> * comments --------- Co-authored-by: Raphael Taylor-Davies <1781103+tustvold@users.noreply.github.com> Co-authored-by: Raphael Taylor-Davies --- arrow-cast/src/cast.rs | 61 +++++++++++++++++++++++++++++++++++++++ arrow/tests/array_cast.rs | 4 +-- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/arrow-cast/src/cast.rs b/arrow-cast/src/cast.rs index e4f4370fdde5..5d7bea0e9d0f 100644 --- a/arrow-cast/src/cast.rs +++ b/arrow-cast/src/cast.rs @@ -145,6 +145,9 @@ pub fn can_cast_types(from_type: &DataType, to_type: &DataType) -> bool { // decimal to signed numeric (Decimal128(_, _), Null | Int8 | Int16 | Int32 | Int64 | Float32 | Float64) | (Decimal256(_, _), Null | Int8 | Int16 | Int32 | Int64 | Float32 | Float64) => true, + // decimal to Utf8 + (Decimal128(_, _), Utf8 | LargeUtf8) => true, + (Decimal256(_, _), Utf8 | LargeUtf8) => true, // Utf8 to decimal (Utf8 | LargeUtf8, Decimal128(_, _)) => true, (Utf8 | LargeUtf8, Decimal256(_, _)) => true, @@ -826,6 +829,8 @@ pub fn cast_with_options( x as f64 / 10_f64.powi(*scale as i32) }) } + Utf8 => value_to_string::(array), + LargeUtf8 => value_to_string::(array), Null => Ok(new_null_array(to_type, array.len())), _ => Err(ArrowError::CastError(format!( "Casting from {from_type:?} to {to_type:?} not supported" @@ -893,6 +898,8 @@ pub fn cast_with_options( x.to_f64().unwrap() / 10_f64.powi(*scale as i32) }) } + Utf8 => value_to_string::(array), + LargeUtf8 => value_to_string::(array), Null => Ok(new_null_array(to_type, array.len())), _ => Err(ArrowError::CastError(format!( "Casting from {from_type:?} to {to_type:?} not supported" @@ -8146,6 +8153,60 @@ mod tests { assert_eq!(1672531200000000000, c.value(0)); } + #[test] + fn test_cast_decimal_to_utf8() { + fn test_decimal_to_string( + output_type: DataType, + array: PrimitiveArray, + ) { + let b = cast(&array, &output_type).unwrap(); + + assert_eq!(b.data_type(), &output_type); + let c = b.as_string::(); + + assert_eq!("1123.454", c.value(0)); + assert_eq!("2123.456", c.value(1)); + assert_eq!("-3123.453", c.value(2)); + assert_eq!("-3123.456", c.value(3)); + assert_eq!("0.000", c.value(4)); + assert_eq!("0.123", c.value(5)); + assert_eq!("1234.567", c.value(6)); + assert_eq!("-1234.567", c.value(7)); + assert!(c.is_null(8)); + } + let array128: Vec> = vec![ + Some(1123454), + Some(2123456), + Some(-3123453), + Some(-3123456), + Some(0), + Some(123), + Some(123456789), + Some(-123456789), + None, + ]; + + let array256: Vec> = + array128.iter().map(|v| v.map(i256::from_i128)).collect(); + + test_decimal_to_string::( + DataType::Utf8, + create_decimal_array(array128.clone(), 7, 3).unwrap(), + ); + test_decimal_to_string::( + DataType::LargeUtf8, + create_decimal_array(array128, 7, 3).unwrap(), + ); + test_decimal_to_string::( + DataType::Utf8, + create_decimal256_array(array256.clone(), 7, 3).unwrap(), + ); + test_decimal_to_string::( + DataType::LargeUtf8, + create_decimal256_array(array256, 7, 3).unwrap(), + ); + } + #[test] fn test_cast_numeric_to_decimal128_precision_overflow() { let array = Int64Array::from(vec![1234567]); diff --git a/arrow/tests/array_cast.rs b/arrow/tests/array_cast.rs index 2807bbd79b83..96a4f2b41f3c 100644 --- a/arrow/tests/array_cast.rs +++ b/arrow/tests/array_cast.rs @@ -185,9 +185,7 @@ fn get_arrays_of_all_types() -> Vec { Arc::new(DurationMillisecondArray::from(vec![1000, 2000])), Arc::new(DurationMicrosecondArray::from(vec![1000, 2000])), Arc::new(DurationNanosecondArray::from(vec![1000, 2000])), - Arc::new( - create_decimal_array(vec![Some(1), Some(2), Some(3), None], 38, 0).unwrap(), - ), + Arc::new(create_decimal_array(vec![Some(1), Some(2), Some(3)], 38, 0).unwrap()), make_dictionary_primitive::(vec![1, 2]), make_dictionary_primitive::(vec![1, 2]), make_dictionary_primitive::(vec![1, 2]),