@@ -1967,9 +1967,88 @@ fn cast_string_to_int_with_range_check(
19671967/// Equivalent to
19681968/// - org.apache.spark.unsafe.types.UTF8String.toInt(IntWrapper intWrapper, boolean allowDecimal)
19691969/// - org.apache.spark.unsafe.types.UTF8String.toLong(LongWrapper longWrapper, boolean allowDecimal)
1970- fn do_cast_string_to_int < T : Integer + CheckedSub + CheckedNeg + From < u8 > + Copy > (
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 ( ) ;
1975+ let mut start = 0 ;
1976+ let mut end = bytes. len ( ) ;
1977+
1978+ while start < end && bytes[ start] . is_ascii_whitespace ( ) {
1979+ start += 1 ;
1980+ }
1981+ while end > start && bytes[ end - 1 ] . is_ascii_whitespace ( ) {
1982+ end -= 1 ;
1983+ }
1984+
1985+ if start == end {
1986+ return Ok ( None ) ;
1987+ }
1988+ let trimmed_bytes = & bytes[ start..end] ;
1989+ let len = trimmed_bytes. len ( ) ;
1990+ let mut result: T = T :: zero ( ) ;
1991+ let mut idx = 0 ;
1992+ let first_char = trimmed_bytes[ 0 ] ;
1993+ let negative = first_char == b'-' ;
1994+ if negative || first_char == b'+' {
1995+ idx = 1 ;
1996+ if len == 1 {
1997+ return Ok ( None ) ;
1998+ }
1999+ }
2000+
2001+ let radix = T :: from ( 10_u8 ) ;
2002+ let stop_value = min_value / radix;
2003+ let mut parse_sign_and_digits = true ;
2004+
2005+ for & ch in & trimmed_bytes[ idx..] {
2006+ if parse_sign_and_digits {
2007+ if ch == b'.' {
2008+ // truncate decimal in legacy mode
2009+ parse_sign_and_digits = false ;
2010+ continue ;
2011+ }
2012+
2013+ if !ch. is_ascii_digit ( ) {
2014+ return Ok ( None ) ;
2015+ }
2016+
2017+ let digit: T = T :: from ( ch - b'0' ) ;
2018+
2019+ if result < stop_value {
2020+ return Ok ( None ) ;
2021+ }
2022+ let v = result * radix;
2023+ match v. checked_sub ( & digit) {
2024+ Some ( x) if x <= T :: zero ( ) => result = x,
2025+ _ => {
2026+ return Ok ( None ) ;
2027+ }
2028+ }
2029+ } else {
2030+ if !ch. is_ascii_digit ( ) {
2031+ return Ok ( None ) ;
2032+ }
2033+ }
2034+ }
2035+
2036+ if !negative {
2037+ if let Some ( neg) = result. checked_neg ( ) {
2038+ if neg < T :: zero ( ) {
2039+ return Ok ( None ) ;
2040+ }
2041+ result = neg;
2042+ } else {
2043+ return Ok ( None ) ;
2044+ }
2045+ }
2046+
2047+ Ok ( Some ( result) )
2048+ }
2049+
2050+ fn do_parse_string_to_int_ansi < T : Integer + CheckedSub + CheckedNeg + From < u8 > + Copy > (
19712051 str : & str ,
1972- eval_mode : EvalMode ,
19732052 type_name : & str ,
19742053 min_value : T ,
19752054) -> SparkResult < Option < T > > {
@@ -1985,7 +2064,7 @@ fn do_cast_string_to_int<T: Integer + CheckedSub + CheckedNeg + From<u8> + Copy>
19852064 }
19862065
19872066 if start == end {
1988- return none_or_err ( eval_mode , type_name , str ) ;
2067+ return Err ( invalid_value ( str , "STRING" , type_name ) ) ;
19892068 }
19902069 let trimmed_bytes = & bytes[ start..end] ;
19912070 let len = trimmed_bytes. len ( ) ;
@@ -1996,7 +2075,7 @@ fn do_cast_string_to_int<T: Integer + CheckedSub + CheckedNeg + From<u8> + Copy>
19962075 if negative || first_char == b'+' {
19972076 idx = 1 ;
19982077 if len == 1 {
1999- return none_or_err ( eval_mode , type_name , str ) ;
2078+ return Err ( invalid_value ( str , "STRING" , type_name ) ) ;
20002079 }
20012080 }
20022081
@@ -2007,61 +2086,137 @@ fn do_cast_string_to_int<T: Integer + CheckedSub + CheckedNeg + From<u8> + Copy>
20072086 for & ch in & trimmed_bytes[ idx..] {
20082087 if parse_sign_and_digits {
20092088 if ch == b'.' {
2010- if eval_mode == EvalMode :: Legacy {
2011- // truncate decimal in legacy mode
2012- parse_sign_and_digits = false ;
2013- continue ;
2014- } else {
2015- return none_or_err ( eval_mode, type_name, str) ;
2089+ return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2090+ }
2091+
2092+ if !ch. is_ascii_digit ( ) {
2093+ return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2094+ }
2095+
2096+ let digit: T = T :: from ( ch - b'0' ) ;
2097+
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) ) ;
20162106 }
20172107 }
2108+ } else {
2109+ if !ch. is_ascii_digit ( ) {
2110+ return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2111+ }
2112+ }
2113+ }
2114+
2115+ if !negative {
2116+ if let Some ( neg) = result. checked_neg ( ) {
2117+ if neg < T :: zero ( ) {
2118+ return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2119+ }
2120+ result = neg;
2121+ } else {
2122+ return Err ( invalid_value ( str, "STRING" , type_name) ) ;
2123+ }
2124+ }
2125+
2126+ Ok ( Some ( result) )
2127+ }
2128+
2129+ fn do_parse_string_to_int_try < T : Integer + CheckedSub + CheckedNeg + From < u8 > + Copy > (
2130+ str : & str ,
2131+ min_value : T ,
2132+ ) -> SparkResult < Option < T > > {
2133+ 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+ }
2143+
2144+ if start == end {
2145+ return Ok ( None ) ;
2146+ }
2147+ let trimmed_bytes = & bytes[ start..end] ;
2148+ let len = trimmed_bytes. len ( ) ;
2149+ 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+ }
2159+
2160+ let radix = T :: from ( 10_u8 ) ;
2161+ let stop_value = min_value / radix;
2162+ let mut parse_sign_and_digits = true ;
2163+
2164+ for & ch in & trimmed_bytes[ idx..] {
2165+ if parse_sign_and_digits {
2166+ if ch == b'.' {
2167+ return Ok ( None ) ;
2168+ }
20182169
20192170 if !ch. is_ascii_digit ( ) {
2020- return none_or_err ( eval_mode , type_name , str ) ;
2171+ return Ok ( None ) ;
20212172 }
20222173
2023- // Direct conversion: u8 digit (0-9) → T
20242174 let digit: T = T :: from ( ch - b'0' ) ;
20252175
2026- // We are going to process the new digit and accumulate the result. However, before
2027- // doing this, if the result is already smaller than the
2028- // stopValue(Integer.MIN_VALUE / radix), then result * 10 will definitely be
2029- // smaller than minValue, and we can stop
20302176 if result < stop_value {
2031- return none_or_err ( eval_mode , type_name , str ) ;
2177+ return Ok ( None ) ;
20322178 }
2033- // Since the previous result is greater than or equal to stopValue(Integer.MIN_VALUE /
2034- // radix), we can just use `result > 0` to check overflow. If result
2035- // overflows, we should stop
20362179 let v = result * radix;
20372180 match v. checked_sub ( & digit) {
20382181 Some ( x) if x <= T :: zero ( ) => result = x,
20392182 _ => {
2040- return none_or_err ( eval_mode , type_name , str ) ;
2183+ return Ok ( None ) ;
20412184 }
20422185 }
20432186 } else {
2044- // make sure fractional digits are valid digits but ignore them
20452187 if !ch. is_ascii_digit ( ) {
2046- return none_or_err ( eval_mode , type_name , str ) ;
2188+ return Ok ( None ) ;
20472189 }
20482190 }
20492191 }
20502192
20512193 if !negative {
20522194 if let Some ( neg) = result. checked_neg ( ) {
20532195 if neg < T :: zero ( ) {
2054- return none_or_err ( eval_mode , type_name , str ) ;
2196+ return Ok ( None ) ;
20552197 }
20562198 result = neg;
20572199 } else {
2058- return none_or_err ( eval_mode , type_name , str ) ;
2200+ return Ok ( None ) ;
20592201 }
20602202 }
20612203
20622204 Ok ( Some ( result) )
20632205}
20642206
2207+ fn do_cast_string_to_int < T : Integer + CheckedSub + CheckedNeg + From < u8 > + Copy > (
2208+ str : & str ,
2209+ eval_mode : EvalMode ,
2210+ type_name : & str ,
2211+ min_value : T ,
2212+ ) -> SparkResult < Option < T > > {
2213+ match eval_mode {
2214+ EvalMode :: Legacy => do_parse_string_to_int_legacy ( str, min_value) ,
2215+ EvalMode :: Ansi => do_parse_string_to_int_ansi ( str, type_name, min_value) ,
2216+ EvalMode :: Try => do_parse_string_to_int_try ( str, min_value) ,
2217+ }
2218+ }
2219+
20652220fn cast_string_to_decimal (
20662221 array : & ArrayRef ,
20672222 to_type : & DataType ,
0 commit comments