1515// specific language governing permissions and limitations
1616// under the License.
1717
18+ use crate :: utils:: is_valid_decimal_precision;
1819use arrow:: datatypes:: { DataType , Schema } ;
1920use arrow:: {
20- array:: { as_primitive_array, Array , ArrayRef , Decimal128Array } ,
21- datatypes:: { Decimal128Type , DecimalType } ,
21+ array:: { as_primitive_array, Array , Decimal128Array } ,
22+ datatypes:: Decimal128Type ,
2223 record_batch:: RecordBatch ,
2324} ;
2425use datafusion:: common:: { DataFusionError , ScalarValue } ;
@@ -101,8 +102,8 @@ impl PhysicalExpr for CheckOverflow {
101102 ColumnarValue :: Array ( array)
102103 if matches ! ( array. data_type( ) , DataType :: Decimal128 ( _, _) ) =>
103104 {
104- let ( precision , scale ) = match & self . data_type {
105- DataType :: Decimal128 ( p, s) => ( p, s) ,
105+ let ( target_precision , target_scale ) = match & self . data_type {
106+ DataType :: Decimal128 ( p, s) => ( * p, * s) ,
106107 dt => {
107108 return Err ( DataFusionError :: Execution ( format ! (
108109 "CheckOverflow expects only Decimal128, but got {dt:?}"
@@ -112,38 +113,89 @@ impl PhysicalExpr for CheckOverflow {
112113
113114 let decimal_array = as_primitive_array :: < Decimal128Type > ( & array) ;
114115
115- let casted_array = if self . fail_on_error {
116- // Returning error if overflow
117- decimal_array. validate_decimal_precision ( * precision) ?;
116+ // Get input precision to check if we can skip validation
117+ let ( input_precision, input_scale) = match decimal_array. data_type ( ) {
118+ DataType :: Decimal128 ( p, s) => ( * p, * s) ,
119+ _ => unreachable ! ( ) ,
120+ } ;
121+
122+ // Optimization: if input precision <= target precision and scales match,
123+ // no overflow is possible - just update metadata
124+ if input_precision <= target_precision && input_scale == target_scale {
125+ let new_array = decimal_array
126+ . clone ( )
127+ . with_precision_and_scale ( target_precision, target_scale) ?;
128+ return Ok ( ColumnarValue :: Array ( Arc :: new ( new_array) ) ) ;
129+ }
130+
131+ let result_array = if self . fail_on_error {
132+ // ANSI mode: validate and return error on overflow
133+ // Use optimized validation that avoids error string allocation until needed
134+ for i in 0 ..decimal_array. len ( ) {
135+ if decimal_array. is_valid ( i) {
136+ let value = decimal_array. value ( i) ;
137+ if !is_valid_decimal_precision ( value, target_precision) {
138+ return Err ( DataFusionError :: ArrowError (
139+ Box :: new ( arrow:: error:: ArrowError :: InvalidArgumentError (
140+ format ! (
141+ "{} is not a valid Decimal128 value with precision {}" ,
142+ value, target_precision
143+ ) ,
144+ ) ) ,
145+ None ,
146+ ) ) ;
147+ }
148+ }
149+ }
150+ // Validation passed - just update metadata without copying data
118151 decimal_array
152+ . clone ( )
153+ . with_precision_and_scale ( target_precision, target_scale) ?
119154 } else {
120- // Overflowing gets null value
121- & decimal_array. null_if_overflow_precision ( * precision)
155+ // Legacy/Try mode: convert overflows to null
156+ // Use Arrow's optimized null_if_overflow_precision which does a single pass
157+ let result = decimal_array. null_if_overflow_precision ( target_precision) ;
158+ result. with_precision_and_scale ( target_precision, target_scale) ?
122159 } ;
123160
124- let new_array = Decimal128Array :: from ( casted_array. into_data ( ) )
125- . with_precision_and_scale ( * precision, * scale)
126- . map ( |a| Arc :: new ( a) as ArrayRef ) ?;
127-
128- Ok ( ColumnarValue :: Array ( new_array) )
161+ Ok ( ColumnarValue :: Array ( Arc :: new ( result_array) ) )
129162 }
130- ColumnarValue :: Scalar ( ScalarValue :: Decimal128 ( v, precision, scale) ) => {
131- // `fail_on_error` is only true when ANSI is enabled, which we don't support yet
132- // (Java side will simply fallback to Spark when it is enabled)
133- assert ! (
134- !self . fail_on_error,
135- "fail_on_error (ANSI mode) is not supported yet"
136- ) ;
137-
138- let new_v: Option < i128 > = v. and_then ( |v| {
139- Decimal128Type :: validate_decimal_precision ( v, precision, scale)
140- . map ( |_| v)
141- . ok ( )
142- } ) ;
143-
144- Ok ( ColumnarValue :: Scalar ( ScalarValue :: Decimal128 (
145- new_v, precision, scale,
146- ) ) )
163+ ColumnarValue :: Scalar ( ScalarValue :: Decimal128 ( v, _, _) ) => {
164+ let ( target_precision, target_scale) = match & self . data_type {
165+ DataType :: Decimal128 ( p, s) => ( * p, * s) ,
166+ dt => {
167+ return Err ( DataFusionError :: Execution ( format ! (
168+ "CheckOverflow expects only Decimal128 for scalar, but got {dt:?}"
169+ ) ) )
170+ }
171+ } ;
172+
173+ if self . fail_on_error {
174+ if let Some ( value) = v {
175+ if !is_valid_decimal_precision ( value, target_precision) {
176+ return Err ( DataFusionError :: ArrowError (
177+ Box :: new ( arrow:: error:: ArrowError :: InvalidArgumentError ( format ! (
178+ "{} is not a valid Decimal128 value with precision {}" ,
179+ value, target_precision
180+ ) ) ) ,
181+ None ,
182+ ) ) ;
183+ }
184+ }
185+ Ok ( ColumnarValue :: Scalar ( ScalarValue :: Decimal128 (
186+ v,
187+ target_precision,
188+ target_scale,
189+ ) ) )
190+ } else {
191+ // Use optimized bool check instead of Result-returning validation
192+ let new_v = v. filter ( |& val| is_valid_decimal_precision ( val, target_precision) ) ;
193+ Ok ( ColumnarValue :: Scalar ( ScalarValue :: Decimal128 (
194+ new_v,
195+ target_precision,
196+ target_scale,
197+ ) ) )
198+ }
147199 }
148200 v => Err ( DataFusionError :: Execution ( format ! (
149201 "CheckOverflow's child expression should be decimal array, but found {v:?}"
0 commit comments