Skip to content

Commit 522bd01

Browse files
authored
Fix string '0' cast to decimal with scale 0 (#6547)
* Fix string '0' cast to decimal with scale 0 Before the change, the cast used to fail or return null, depending on cast options. * Add more test cases
1 parent 9633f14 commit 522bd01

File tree

2 files changed

+131
-1
lines changed

2 files changed

+131
-1
lines changed

arrow-cast/src/cast/decimal.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ where
250250
}
251251
};
252252

253-
let integers = first_part.trim_start_matches('0');
253+
let integers = first_part;
254254
let decimals = if parts.len() == 2 { parts[1] } else { "" };
255255

256256
if !integers.is_empty() && !integers.as_bytes()[0].is_ascii_digit() {
@@ -567,3 +567,48 @@ where
567567
let array = array.unary::<_, T>(op);
568568
Ok(Arc::new(array))
569569
}
570+
571+
#[cfg(test)]
572+
mod tests {
573+
use super::*;
574+
575+
#[test]
576+
fn test_parse_string_to_decimal_native() -> Result<(), ArrowError> {
577+
assert_eq!(
578+
parse_string_to_decimal_native::<Decimal128Type>("0", 0)?,
579+
0_i128
580+
);
581+
assert_eq!(
582+
parse_string_to_decimal_native::<Decimal128Type>("0", 5)?,
583+
0_i128
584+
);
585+
586+
assert_eq!(
587+
parse_string_to_decimal_native::<Decimal128Type>("123", 0)?,
588+
123_i128
589+
);
590+
assert_eq!(
591+
parse_string_to_decimal_native::<Decimal128Type>("123", 5)?,
592+
12300000_i128
593+
);
594+
595+
assert_eq!(
596+
parse_string_to_decimal_native::<Decimal128Type>("123.45", 0)?,
597+
123_i128
598+
);
599+
assert_eq!(
600+
parse_string_to_decimal_native::<Decimal128Type>("123.45", 5)?,
601+
12345000_i128
602+
);
603+
604+
assert_eq!(
605+
parse_string_to_decimal_native::<Decimal128Type>("123.4567891", 0)?,
606+
123_i128
607+
);
608+
assert_eq!(
609+
parse_string_to_decimal_native::<Decimal128Type>("123.4567891", 5)?,
610+
12345679_i128
611+
);
612+
Ok(())
613+
}
614+
}

arrow-cast/src/cast/mod.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8481,6 +8481,10 @@ mod tests {
84818481
assert!(decimal_arr.is_null(25));
84828482
assert!(decimal_arr.is_null(26));
84838483
assert!(decimal_arr.is_null(27));
8484+
assert_eq!("0.00", decimal_arr.value_as_string(28));
8485+
assert_eq!("0.00", decimal_arr.value_as_string(29));
8486+
assert_eq!("12345.00", decimal_arr.value_as_string(30));
8487+
assert_eq!(decimal_arr.len(), 31);
84848488

84858489
// Decimal256
84868490
let output_type = DataType::Decimal256(76, 3);
@@ -8517,6 +8521,10 @@ mod tests {
85178521
assert!(decimal_arr.is_null(25));
85188522
assert!(decimal_arr.is_null(26));
85198523
assert!(decimal_arr.is_null(27));
8524+
assert_eq!("0.000", decimal_arr.value_as_string(28));
8525+
assert_eq!("0.000", decimal_arr.value_as_string(29));
8526+
assert_eq!("12345.000", decimal_arr.value_as_string(30));
8527+
assert_eq!(decimal_arr.len(), 31);
85208528
}
85218529

85228530
#[test]
@@ -8550,10 +8558,30 @@ mod tests {
85508558
Some("1.-23499999"),
85518559
Some("-1.-23499999"),
85528560
Some("--1.23499999"),
8561+
Some("0"),
8562+
Some("000.000"),
8563+
Some("0000000000000000012345.000"),
85538564
]);
85548565
let array = Arc::new(str_array) as ArrayRef;
85558566

85568567
test_cast_string_to_decimal(array);
8568+
8569+
let test_cases = [
8570+
(None, None),
8571+
// (Some(""), None),
8572+
// (Some(" "), None),
8573+
(Some("0"), Some("0")),
8574+
(Some("000.000"), Some("0")),
8575+
(Some("12345"), Some("12345")),
8576+
(Some("000000000000000000000000000012345"), Some("12345")),
8577+
(Some("-123"), Some("-123")),
8578+
(Some("+123"), Some("123")),
8579+
];
8580+
let inputs = test_cases.iter().map(|entry| entry.0).collect::<Vec<_>>();
8581+
let expected = test_cases.iter().map(|entry| entry.1).collect::<Vec<_>>();
8582+
8583+
let array = Arc::new(StringArray::from(inputs)) as ArrayRef;
8584+
test_cast_string_to_decimal_scale_zero(array, &expected);
85578585
}
85588586

85598587
#[test]
@@ -8587,10 +8615,67 @@ mod tests {
85878615
Some("1.-23499999"),
85888616
Some("-1.-23499999"),
85898617
Some("--1.23499999"),
8618+
Some("0"),
8619+
Some("000.000"),
8620+
Some("0000000000000000012345.000"),
85908621
]);
85918622
let array = Arc::new(str_array) as ArrayRef;
85928623

85938624
test_cast_string_to_decimal(array);
8625+
8626+
let test_cases = [
8627+
(None, None),
8628+
(Some(""), None),
8629+
(Some(" "), None),
8630+
(Some("0"), Some("0")),
8631+
(Some("000.000"), Some("0")),
8632+
(Some("12345"), Some("12345")),
8633+
(Some("000000000000000000000000000012345"), Some("12345")),
8634+
(Some("-123"), Some("-123")),
8635+
(Some("+123"), Some("123")),
8636+
];
8637+
let inputs = test_cases.iter().map(|entry| entry.0).collect::<Vec<_>>();
8638+
let expected = test_cases.iter().map(|entry| entry.1).collect::<Vec<_>>();
8639+
8640+
let array = Arc::new(LargeStringArray::from(inputs)) as ArrayRef;
8641+
test_cast_string_to_decimal_scale_zero(array, &expected);
8642+
}
8643+
8644+
fn test_cast_string_to_decimal_scale_zero(
8645+
array: ArrayRef,
8646+
expected_as_string: &[Option<&str>],
8647+
) {
8648+
// Decimal128
8649+
let output_type = DataType::Decimal128(38, 0);
8650+
assert!(can_cast_types(array.data_type(), &output_type));
8651+
let casted_array = cast(&array, &output_type).unwrap();
8652+
let decimal_arr = casted_array.as_primitive::<Decimal128Type>();
8653+
assert_decimal_array_contents(decimal_arr, expected_as_string);
8654+
8655+
// Decimal256
8656+
let output_type = DataType::Decimal256(76, 0);
8657+
assert!(can_cast_types(array.data_type(), &output_type));
8658+
let casted_array = cast(&array, &output_type).unwrap();
8659+
let decimal_arr = casted_array.as_primitive::<Decimal256Type>();
8660+
assert_decimal_array_contents(decimal_arr, expected_as_string);
8661+
}
8662+
8663+
fn assert_decimal_array_contents<T>(
8664+
array: &PrimitiveArray<T>,
8665+
expected_as_string: &[Option<&str>],
8666+
) where
8667+
T: DecimalType + ArrowPrimitiveType,
8668+
{
8669+
assert_eq!(array.len(), expected_as_string.len());
8670+
for (i, expected) in expected_as_string.iter().enumerate() {
8671+
let actual = if array.is_null(i) {
8672+
None
8673+
} else {
8674+
Some(array.value_as_string(i))
8675+
};
8676+
let actual = actual.as_ref().map(|s| s.as_ref());
8677+
assert_eq!(*expected, actual, "Expected at position {}", i);
8678+
}
85948679
}
85958680

85968681
#[test]

0 commit comments

Comments
 (0)