Skip to content

Commit 24a56b9

Browse files
committed
test: add error asserts for zero scale decimal parsing
1 parent f4f3ff5 commit 24a56b9

File tree

1 file changed

+20
-15
lines changed

1 file changed

+20
-15
lines changed

arrow-cast/src/parse.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -836,8 +836,10 @@ fn parse_e_notation<T: DecimalType>(
836836

837837
/// Parse the string format decimal value to i128/i256 format and checking the precision and scale.
838838
/// Expected behavior:
839-
/// - the result value can't be out of bounds.
840-
/// - when parsing a decimal with scale 0, any digits pass the decimal point will be discarded
839+
/// - The result value can't be out of bounds.
840+
/// - When parsing a decimal with scale 0, all fractional digits will be discarded. The final
841+
/// fractional digits may be a subset or a superset of the digits after the decimal point when
842+
/// e-notation is used.
841843
pub fn parse_decimal<T: DecimalType>(
842844
s: &str,
843845
precision: u8,
@@ -848,19 +850,19 @@ pub fn parse_decimal<T: DecimalType>(
848850
let mut digits: u8 = 0;
849851
let base = T::Native::usize_as(10);
850852

853+
if matches!(s, "" | "-" | "+" | ".") {
854+
return Err(ArrowError::ParseError(format!(
855+
"can't parse the string value {s} to decimal"
856+
)));
857+
}
858+
851859
let bs = s.as_bytes();
852860
let (signed, negative) = match bs.first() {
853861
Some(b'-') => (true, true),
854862
Some(b'+') => (true, false),
855863
_ => (false, false),
856864
};
857865

858-
if bs.is_empty() || signed && bs.len() == 1 {
859-
return Err(ArrowError::ParseError(format!(
860-
"can't parse the string value {s} to decimal"
861-
)));
862-
}
863-
864866
// Iterate over the raw input bytes, skipping the sign if any
865867
let mut bs = bs.iter().enumerate().skip(signed as usize);
866868

@@ -918,13 +920,6 @@ pub fn parse_decimal<T: DecimalType>(
918920
if is_e_notation {
919921
break;
920922
}
921-
922-
// Fail on "."
923-
if digits == 0 && scale != 0 {
924-
return Err(ArrowError::ParseError(format!(
925-
"can't parse the string value {s} to decimal"
926-
)));
927-
}
928923
}
929924
b'e' | b'E' => {
930925
result = parse_e_notation::<T>(
@@ -2742,6 +2737,7 @@ mod tests {
27422737
}
27432738

27442739
let zero_scale_tests = [
2740+
(".123", 0, 3),
27452741
("0.123", 0, 3),
27462742
("1.0", 1, 3),
27472743
("1.2", 1, 3),
@@ -2769,6 +2765,15 @@ mod tests {
27692765
let result_128 = parse_decimal::<Decimal128Type>(s, precision, 0).unwrap();
27702766
assert_eq!(i, result_128);
27712767
}
2768+
2769+
let can_not_parse_zero_scale = [".", "blag", "", "+", "-", "e"];
2770+
for s in can_not_parse_zero_scale {
2771+
let result_128 = parse_decimal::<Decimal128Type>(s, 5, 0);
2772+
assert_eq!(
2773+
format!("Parser error: can't parse the string value {s} to decimal"),
2774+
result_128.unwrap_err().to_string(),
2775+
);
2776+
}
27722777
}
27732778

27742779
#[test]

0 commit comments

Comments
 (0)