@@ -1964,14 +1964,8 @@ fn cast_string_to_int_with_range_check(
19641964 }
19651965}
19661966
1967- /// Equivalent to
1968- /// - org.apache.spark.unsafe.types.UTF8String.toInt(IntWrapper intWrapper, boolean allowDecimal)
1969- /// - org.apache.spark.unsafe.types.UTF8String.toLong(LongWrapper longWrapper, boolean allowDecimal)
1970- fn do_parse_string_to_int_legacy < T : Integer + CheckedSub + CheckedNeg + From < u8 > + Copy > (
1971- str : & str ,
1972- min_value : T ,
1973- ) -> SparkResult < Option < T > > {
1974- let bytes = str. as_bytes ( ) ;
1967+ // Returns (start, end) indices after trimming whitespace
1968+ fn trim_whitespace ( bytes : & [ u8 ] ) -> ( usize , usize ) {
19751969 let mut start = 0 ;
19761970 let mut end = bytes. len ( ) ;
19771971
@@ -1982,21 +1976,51 @@ fn do_parse_string_to_int_legacy<T: Integer + CheckedSub + CheckedNeg + From<u8>
19821976 end -= 1 ;
19831977 }
19841978
1985- if start == end {
1986- return Ok ( None ) ;
1987- }
1988- let trimmed_bytes = & bytes[ start..end] ;
1979+ ( start, end)
1980+ }
1981+
1982+ // Parses sign and returns (is_negative, start_idx after sign)
1983+ // Returns None if invalid (e.g., just "+" or "-")
1984+ fn parse_sign ( trimmed_bytes : & [ u8 ] ) -> Option < ( bool , usize ) > {
19891985 let len = trimmed_bytes. len ( ) ;
1990- let mut result: T = T :: zero ( ) ;
1991- let mut idx = 0 ;
1986+ if len == 0 {
1987+ return None ;
1988+ }
1989+
19921990 let first_char = trimmed_bytes[ 0 ] ;
19931991 let negative = first_char == b'-' ;
1992+
19941993 if negative || first_char == b'+' {
1995- idx = 1 ;
19961994 if len == 1 {
1997- return Ok ( None ) ;
1995+ return None ;
19981996 }
1997+ Some ( ( negative, 1 ) )
1998+ } else {
1999+ Some ( ( false , 0 ) )
19992000 }
2001+ }
2002+
2003+ /// Equivalent to
2004+ /// - org.apache.spark.unsafe.types.UTF8String.toInt(IntWrapper intWrapper, boolean allowDecimal)
2005+ /// - org.apache.spark.unsafe.types.UTF8String.toLong(LongWrapper longWrapper, boolean allowDecimal)
2006+ fn do_parse_string_to_int_legacy < T : Integer + CheckedSub + CheckedNeg + From < u8 > + Copy > (
2007+ str : & str ,
2008+ min_value : T ,
2009+ ) -> SparkResult < Option < T > > {
2010+ let bytes = str. as_bytes ( ) ;
2011+ let ( start, end) = trim_whitespace ( bytes) ;
2012+
2013+ if start == end {
2014+ return Ok ( None ) ;
2015+ }
2016+ let trimmed_bytes = & bytes[ start..end] ;
2017+
2018+ let ( negative, idx) = match parse_sign ( trimmed_bytes) {
2019+ Some ( result) => result,
2020+ None => return Ok ( None ) ,
2021+ } ;
2022+
2023+ let mut result: T = T :: zero ( ) ;
20002024
20012025 let radix = T :: from ( 10_u8 ) ;
20022026 let stop_value = min_value / radix;
@@ -2027,6 +2051,7 @@ fn do_parse_string_to_int_legacy<T: Integer + CheckedSub + CheckedNeg + From<u8>
20272051 }
20282052 }
20292053 } else {
2054+ // in legacy mode we still process chars after the dot and make sure the chars are digits
20302055 if !ch. is_ascii_digit ( ) {
20312056 return Ok ( None ) ;
20322057 }
@@ -2053,60 +2078,41 @@ fn do_parse_string_to_int_ansi<T: Integer + CheckedSub + CheckedNeg + From<u8> +
20532078 min_value : T ,
20542079) -> SparkResult < Option < T > > {
20552080 let bytes = str. as_bytes ( ) ;
2056- let mut start = 0 ;
2057- let mut end = bytes. len ( ) ;
2058-
2059- while start < end && bytes[ start] . is_ascii_whitespace ( ) {
2060- start += 1 ;
2061- }
2062- while end > start && bytes[ end - 1 ] . is_ascii_whitespace ( ) {
2063- end -= 1 ;
2064- }
2081+ let ( start, end) = trim_whitespace ( bytes) ;
20652082
20662083 if start == end {
20672084 return Err ( invalid_value ( str, "STRING" , type_name) ) ;
20682085 }
20692086 let trimmed_bytes = & bytes[ start..end] ;
2070- let len = trimmed_bytes. len ( ) ;
2087+
2088+ let ( negative, idx) = match parse_sign ( trimmed_bytes) {
2089+ Some ( result) => result,
2090+ None => return Err ( invalid_value ( str, "STRING" , type_name) ) ,
2091+ } ;
2092+
20712093 let mut result: T = T :: zero ( ) ;
2072- let mut idx = 0 ;
2073- let first_char = trimmed_bytes[ 0 ] ;
2074- let negative = first_char == b'-' ;
2075- if negative || first_char == b'+' {
2076- idx = 1 ;
2077- if len == 1 {
2078- return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2079- }
2080- }
20812094
20822095 let radix = T :: from ( 10_u8 ) ;
20832096 let stop_value = min_value / radix;
2084- let mut parse_sign_and_digits = true ;
20852097
20862098 for & ch in & trimmed_bytes[ idx..] {
2087- if parse_sign_and_digits {
2088- if ch == b'.' {
2089- return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2090- }
2099+ if ch == b'.' {
2100+ return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2101+ }
20912102
2092- if !ch. is_ascii_digit ( ) {
2093- return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2094- }
2103+ if !ch. is_ascii_digit ( ) {
2104+ return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2105+ }
20952106
2096- let digit: T = T :: from ( ch - b'0' ) ;
2107+ let digit: T = T :: from ( ch - b'0' ) ;
20972108
2098- if result < stop_value {
2099- return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2100- }
2101- let v = result * radix;
2102- match v. checked_sub ( & digit) {
2103- Some ( x) if x <= T :: zero ( ) => result = x,
2104- _ => {
2105- return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2106- }
2107- }
2108- } else {
2109- if !ch. is_ascii_digit ( ) {
2109+ if result < stop_value {
2110+ return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2111+ }
2112+ let v = result * radix;
2113+ match v. checked_sub ( & digit) {
2114+ Some ( x) if x <= T :: zero ( ) => result = x,
2115+ _ => {
21102116 return Err ( invalid_value ( str, "STRING" , type_name) ) ;
21112117 }
21122118 }
@@ -2131,60 +2137,42 @@ fn do_parse_string_to_int_try<T: Integer + CheckedSub + CheckedNeg + From<u8> +
21312137 min_value : T ,
21322138) -> SparkResult < Option < T > > {
21332139 let bytes = str. as_bytes ( ) ;
2134- let mut start = 0 ;
2135- let mut end = bytes. len ( ) ;
2136-
2137- while start < end && bytes[ start] . is_ascii_whitespace ( ) {
2138- start += 1 ;
2139- }
2140- while end > start && bytes[ end - 1 ] . is_ascii_whitespace ( ) {
2141- end -= 1 ;
2142- }
2140+ let ( start, end) = trim_whitespace ( bytes) ;
21432141
21442142 if start == end {
21452143 return Ok ( None ) ;
21462144 }
21472145 let trimmed_bytes = & bytes[ start..end] ;
2148- let len = trimmed_bytes. len ( ) ;
2146+
2147+ let ( negative, idx) = match parse_sign ( trimmed_bytes) {
2148+ Some ( result) => result,
2149+ None => return Ok ( None ) ,
2150+ } ;
2151+
21492152 let mut result: T = T :: zero ( ) ;
2150- let mut idx = 0 ;
2151- let first_char = trimmed_bytes[ 0 ] ;
2152- let negative = first_char == b'-' ;
2153- if negative || first_char == b'+' {
2154- idx = 1 ;
2155- if len == 1 {
2156- return Ok ( None ) ;
2157- }
2158- }
21592153
21602154 let radix = T :: from ( 10_u8 ) ;
21612155 let stop_value = min_value / radix;
2162- let mut parse_sign_and_digits = true ;
21632156
2157+ // we don't have to go beyond decimal point in try eval mode - early return NULL
21642158 for & ch in & trimmed_bytes[ idx..] {
2165- if parse_sign_and_digits {
2166- if ch == b'.' {
2167- return Ok ( None ) ;
2168- }
2159+ if ch == b'.' {
2160+ return Ok ( None ) ;
2161+ }
21692162
2170- if !ch. is_ascii_digit ( ) {
2171- return Ok ( None ) ;
2172- }
2163+ if !ch. is_ascii_digit ( ) {
2164+ return Ok ( None ) ;
2165+ }
21732166
2174- let digit: T = T :: from ( ch - b'0' ) ;
2167+ let digit: T = T :: from ( ch - b'0' ) ;
21752168
2176- if result < stop_value {
2177- return Ok ( None ) ;
2178- }
2179- let v = result * radix;
2180- match v. checked_sub ( & digit) {
2181- Some ( x) if x <= T :: zero ( ) => result = x,
2182- _ => {
2183- return Ok ( None ) ;
2184- }
2185- }
2186- } else {
2187- if !ch. is_ascii_digit ( ) {
2169+ if result < stop_value {
2170+ return Ok ( None ) ;
2171+ }
2172+ let v = result * radix;
2173+ match v. checked_sub ( & digit) {
2174+ Some ( x) if x <= T :: zero ( ) => result = x,
2175+ _ => {
21882176 return Ok ( None ) ;
21892177 }
21902178 }
@@ -2517,15 +2505,6 @@ fn parse_decimal_str(s: &str) -> Result<(i128, i32), String> {
25172505 Ok ( ( final_mantissa, final_scale) )
25182506}
25192507
2520- /// Either return Ok(None) or Err(SparkError::CastInvalidValue) depending on the evaluation mode
2521- #[ inline]
2522- fn none_or_err < T > ( eval_mode : EvalMode , type_name : & str , str : & str ) -> SparkResult < Option < T > > {
2523- match eval_mode {
2524- EvalMode :: Ansi => Err ( invalid_value ( str, "STRING" , type_name) ) ,
2525- _ => Ok ( None ) ,
2526- }
2527- }
2528-
25292508#[ inline]
25302509fn invalid_value ( value : & str , from_type : & str , to_type : & str ) -> SparkError {
25312510 SparkError :: CastInvalidValue {
0 commit comments