Skip to content

Commit 8b85aa0

Browse files
authored
support cast decimal to decimal (#1084)
* support cast decimal to decimal * add test case * remove meaningless code
1 parent e8cc39e commit 8b85aa0

File tree

1 file changed

+67
-1
lines changed

1 file changed

+67
-1
lines changed

arrow/src/compute/kernels/cast.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ pub fn can_cast_types(from_type: &DataType, to_type: &DataType) -> bool {
6969

7070
match (from_type, to_type) {
7171
// TODO UTF8/unsigned numeric to decimal
72-
// TODO decimal to decimal type
72+
// cast one decimal type to another decimal type
73+
(Decimal(_, _), Decimal(_, _)) => true,
7374
// signed numeric to decimal
7475
(Int8 | Int16 | Int32 | Int64 | Float32 | Float64, Decimal(_, _)) |
7576
// decimal to signed numeric
@@ -371,6 +372,7 @@ pub fn cast_with_options(
371372
return Ok(array.clone());
372373
}
373374
match (from_type, to_type) {
375+
(Decimal(_, s1), Decimal(p2, s2)) => cast_decimal_to_decimal(array, s1, p2, s2),
374376
(Decimal(_, scale), _) => {
375377
// cast decimal to other type
376378
match to_type {
@@ -1139,6 +1141,42 @@ const MILLISECONDS_IN_DAY: i64 = SECONDS_IN_DAY * MILLISECONDS;
11391141
/// Number of days between 0001-01-01 and 1970-01-01
11401142
const EPOCH_DAYS_FROM_CE: i32 = 719_163;
11411143

1144+
/// Cast one type of decimal array to another type of decimal array
1145+
fn cast_decimal_to_decimal(
1146+
array: &ArrayRef,
1147+
input_scale: &usize,
1148+
output_precision: &usize,
1149+
output_scale: &usize,
1150+
) -> Result<ArrayRef> {
1151+
let mut decimal_builder =
1152+
DecimalBuilder::new(array.len(), *output_precision, *output_scale);
1153+
let array = array.as_any().downcast_ref::<DecimalArray>().unwrap();
1154+
if input_scale > output_scale {
1155+
// For example, input_scale is 4 and output_scale is 3;
1156+
// Original value is 11234_i128, and will be cast to 1123_i128.
1157+
let div = 10_i128.pow((input_scale - output_scale) as u32);
1158+
for i in 0..array.len() {
1159+
if array.is_null(i) {
1160+
decimal_builder.append_null()?;
1161+
} else {
1162+
decimal_builder.append_value(array.value(i) / div)?;
1163+
}
1164+
}
1165+
} else {
1166+
// For example, input_scale is 3 and output_scale is 4;
1167+
// Original value is 1123_i128, and will be cast to 11230_i128.
1168+
let mul = 10_i128.pow((output_scale - input_scale) as u32);
1169+
for i in 0..array.len() {
1170+
if array.is_null(i) {
1171+
decimal_builder.append_null()?;
1172+
} else {
1173+
decimal_builder.append_value(array.value(i) * mul)?;
1174+
}
1175+
}
1176+
}
1177+
Ok(Arc::new(decimal_builder.finish()))
1178+
}
1179+
11421180
/// Cast an array by changing its array_data type to the desired type
11431181
///
11441182
/// Arrays should have the same primitive data type, otherwise this should fail.
@@ -2035,6 +2073,34 @@ mod tests {
20352073
Ok(decimal_builder.finish())
20362074
}
20372075

2076+
#[test]
2077+
fn test_cast_decimal_to_decimal() {
2078+
let input_type = DataType::Decimal(20, 3);
2079+
let output_type = DataType::Decimal(20, 4);
2080+
assert!(can_cast_types(&input_type, &output_type));
2081+
let array = vec![Some(1123456), Some(2123456), Some(3123456), None];
2082+
let input_decimal_array = create_decimal_array(&array, 20, 3).unwrap();
2083+
let array = Arc::new(input_decimal_array) as ArrayRef;
2084+
generate_cast_test_case!(
2085+
&array,
2086+
DecimalArray,
2087+
&output_type,
2088+
vec![
2089+
Some(11234560_i128),
2090+
Some(21234560_i128),
2091+
Some(31234560_i128),
2092+
None
2093+
]
2094+
);
2095+
// negative test
2096+
let array = vec![Some(123456), None];
2097+
let input_decimal_array = create_decimal_array(&array, 10, 0).unwrap();
2098+
let array = Arc::new(input_decimal_array) as ArrayRef;
2099+
let result = cast(&array, &DataType::Decimal(2, 2));
2100+
assert!(result.is_err());
2101+
assert_eq!("Invalid argument error: The value of 12345600 i128 is not compatible with Decimal(2,2)".to_string(), result.unwrap_err().to_string());
2102+
}
2103+
20382104
#[test]
20392105
fn test_cast_decimal_to_numeric() {
20402106
let decimal_type = DataType::Decimal(38, 2);

0 commit comments

Comments
 (0)