Skip to content

Commit f0cadcd

Browse files
committed
Fix string '0' cast to decimal with scale 0 (apache#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 49e714d commit f0cadcd

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() {
@@ -571,3 +571,48 @@ where
571571
let array = array.unary::<_, T>(op);
572572
Ok(Arc::new(array))
573573
}
574+
575+
#[cfg(test)]
576+
mod tests {
577+
use super::*;
578+
579+
#[test]
580+
fn test_parse_string_to_decimal_native() -> Result<(), ArrowError> {
581+
assert_eq!(
582+
parse_string_to_decimal_native::<Decimal128Type>("0", 0)?,
583+
0_i128
584+
);
585+
assert_eq!(
586+
parse_string_to_decimal_native::<Decimal128Type>("0", 5)?,
587+
0_i128
588+
);
589+
590+
assert_eq!(
591+
parse_string_to_decimal_native::<Decimal128Type>("123", 0)?,
592+
123_i128
593+
);
594+
assert_eq!(
595+
parse_string_to_decimal_native::<Decimal128Type>("123", 5)?,
596+
12300000_i128
597+
);
598+
599+
assert_eq!(
600+
parse_string_to_decimal_native::<Decimal128Type>("123.45", 0)?,
601+
123_i128
602+
);
603+
assert_eq!(
604+
parse_string_to_decimal_native::<Decimal128Type>("123.45", 5)?,
605+
12345000_i128
606+
);
607+
608+
assert_eq!(
609+
parse_string_to_decimal_native::<Decimal128Type>("123.4567891", 0)?,
610+
123_i128
611+
);
612+
assert_eq!(
613+
parse_string_to_decimal_native::<Decimal128Type>("123.4567891", 5)?,
614+
12345679_i128
615+
);
616+
Ok(())
617+
}
618+
}

arrow-cast/src/cast/mod.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8209,6 +8209,10 @@ mod tests {
82098209
assert!(decimal_arr.is_null(25));
82108210
assert!(decimal_arr.is_null(26));
82118211
assert!(decimal_arr.is_null(27));
8212+
assert_eq!("0.00", decimal_arr.value_as_string(28));
8213+
assert_eq!("0.00", decimal_arr.value_as_string(29));
8214+
assert_eq!("12345.00", decimal_arr.value_as_string(30));
8215+
assert_eq!(decimal_arr.len(), 31);
82128216

82138217
// Decimal256
82148218
let output_type = DataType::Decimal256(76, 3);
@@ -8245,6 +8249,10 @@ mod tests {
82458249
assert!(decimal_arr.is_null(25));
82468250
assert!(decimal_arr.is_null(26));
82478251
assert!(decimal_arr.is_null(27));
8252+
assert_eq!("0.000", decimal_arr.value_as_string(28));
8253+
assert_eq!("0.000", decimal_arr.value_as_string(29));
8254+
assert_eq!("12345.000", decimal_arr.value_as_string(30));
8255+
assert_eq!(decimal_arr.len(), 31);
82488256
}
82498257

82508258
#[test]
@@ -8278,10 +8286,30 @@ mod tests {
82788286
Some("1.-23499999"),
82798287
Some("-1.-23499999"),
82808288
Some("--1.23499999"),
8289+
Some("0"),
8290+
Some("000.000"),
8291+
Some("0000000000000000012345.000"),
82818292
]);
82828293
let array = Arc::new(str_array) as ArrayRef;
82838294

82848295
test_cast_string_to_decimal(array);
8296+
8297+
let test_cases = [
8298+
(None, None),
8299+
// (Some(""), None),
8300+
// (Some(" "), None),
8301+
(Some("0"), Some("0")),
8302+
(Some("000.000"), Some("0")),
8303+
(Some("12345"), Some("12345")),
8304+
(Some("000000000000000000000000000012345"), Some("12345")),
8305+
(Some("-123"), Some("-123")),
8306+
(Some("+123"), Some("123")),
8307+
];
8308+
let inputs = test_cases.iter().map(|entry| entry.0).collect::<Vec<_>>();
8309+
let expected = test_cases.iter().map(|entry| entry.1).collect::<Vec<_>>();
8310+
8311+
let array = Arc::new(StringArray::from(inputs)) as ArrayRef;
8312+
test_cast_string_to_decimal_scale_zero(array, &expected);
82858313
}
82868314

82878315
#[test]
@@ -8315,10 +8343,67 @@ mod tests {
83158343
Some("1.-23499999"),
83168344
Some("-1.-23499999"),
83178345
Some("--1.23499999"),
8346+
Some("0"),
8347+
Some("000.000"),
8348+
Some("0000000000000000012345.000"),
83188349
]);
83198350
let array = Arc::new(str_array) as ArrayRef;
83208351

83218352
test_cast_string_to_decimal(array);
8353+
8354+
let test_cases = [
8355+
(None, None),
8356+
(Some(""), None),
8357+
(Some(" "), None),
8358+
(Some("0"), Some("0")),
8359+
(Some("000.000"), Some("0")),
8360+
(Some("12345"), Some("12345")),
8361+
(Some("000000000000000000000000000012345"), Some("12345")),
8362+
(Some("-123"), Some("-123")),
8363+
(Some("+123"), Some("123")),
8364+
];
8365+
let inputs = test_cases.iter().map(|entry| entry.0).collect::<Vec<_>>();
8366+
let expected = test_cases.iter().map(|entry| entry.1).collect::<Vec<_>>();
8367+
8368+
let array = Arc::new(LargeStringArray::from(inputs)) as ArrayRef;
8369+
test_cast_string_to_decimal_scale_zero(array, &expected);
8370+
}
8371+
8372+
fn test_cast_string_to_decimal_scale_zero(
8373+
array: ArrayRef,
8374+
expected_as_string: &[Option<&str>],
8375+
) {
8376+
// Decimal128
8377+
let output_type = DataType::Decimal128(38, 0);
8378+
assert!(can_cast_types(array.data_type(), &output_type));
8379+
let casted_array = cast(&array, &output_type).unwrap();
8380+
let decimal_arr = casted_array.as_primitive::<Decimal128Type>();
8381+
assert_decimal_array_contents(decimal_arr, expected_as_string);
8382+
8383+
// Decimal256
8384+
let output_type = DataType::Decimal256(76, 0);
8385+
assert!(can_cast_types(array.data_type(), &output_type));
8386+
let casted_array = cast(&array, &output_type).unwrap();
8387+
let decimal_arr = casted_array.as_primitive::<Decimal256Type>();
8388+
assert_decimal_array_contents(decimal_arr, expected_as_string);
8389+
}
8390+
8391+
fn assert_decimal_array_contents<T>(
8392+
array: &PrimitiveArray<T>,
8393+
expected_as_string: &[Option<&str>],
8394+
) where
8395+
T: DecimalType + ArrowPrimitiveType,
8396+
{
8397+
assert_eq!(array.len(), expected_as_string.len());
8398+
for (i, expected) in expected_as_string.iter().enumerate() {
8399+
let actual = if array.is_null(i) {
8400+
None
8401+
} else {
8402+
Some(array.value_as_string(i))
8403+
};
8404+
let actual = actual.as_ref().map(|s| s.as_ref());
8405+
assert_eq!(*expected, actual, "Expected at position {}", i);
8406+
}
83228407
}
83238408

83248409
#[test]

0 commit comments

Comments
 (0)