mbutrovich commented on code in PR #4939:
URL: https://github.com/apache/datafusion-comet/pull/4939#discussion_r3591242909


##########
native/spark-expr/src/conversion_funcs/numeric.rs:
##########
@@ -1160,6 +1153,60 @@ mod tests {
         assert_eq!(decimal_array.value(1), -10000); // -100 * 10^2
         assert!(decimal_array.is_null(2));
     }
+
+    #[test]
+    fn test_cast_int_to_decimal128_overflow_legacy_nulls() {
+        // 1000 * 10^2 = 100000 does not fit precision 3 -> null (legacy). 
Valid values and the

Review Comment:
   `native/spark-expr/src/conversion_funcs/numeric.rs:1159-1160` (new test 
`test_cast_int_to_decimal128_overflow_legacy_nulls`) says the test exercises 
"the vectorized sentinel + masking path." The new implementation has no 
sentinel value and no second masking pass. `unary_opt` writes nulls directly. 
The comment is left over from an earlier design and will mislead the next 
reader.
   
   Suggested change: replace with something like "exercises the vectorized 
null-on-overflow path with valid values and an input null preserved."



##########
native/spark-expr/src/conversion_funcs/numeric.rs:
##########
@@ -663,48 +663,41 @@ where
     T: ArrowPrimitiveType,
     T::Native: Into<i128>,
 {
-    let mut builder = Decimal128Builder::with_capacity(array.len());
     let multiplier = 10_i128.pow(scale as u32);
 
-    for i in 0..array.len() {
-        if array.is_null(i) {
-            builder.append_null();
-        } else {
-            let v = array.value(i).into();
-            let scaled = v.checked_mul(multiplier);
-            match scaled {
-                Some(scaled) => {
-                    if !is_validate_decimal_precision(scaled, precision) {
-                        match eval_mode {
-                            EvalMode::Ansi => {
-                                return Err(SparkError::NumericValueOutOfRange {
-                                    value: v.to_string(),
-                                    precision,
-                                    scale,
-                                });
-                            }
-                            EvalMode::Try | EvalMode::Legacy => 
builder.append_null(),
-                        }
-                    } else {
-                        builder.append_value(scaled);
-                    }
+    // Single vectorized pass: a value that overflows the multiply or does not 
fit the output
+    // precision maps to null. `unary_opt` only applies the closure to 
non-null slots and carries
+    // the input null buffer over, replacing the per-element builder loop 
without a second pass.
+    let result: Decimal128Array = array.unary_opt::<_, Decimal128Type>(|v| {

Review Comment:
   `native/spark-expr/src/conversion_funcs/numeric.rs`: the fast path closure 
computes `v.checked_mul(multiplier).filter(|scaled| 
is_validate_decimal_precision(*scaled, precision))`, and the ANSI rescan 
recomputes the same thing inverted as `checked_mul(multiplier).map(|scaled| 
!is_validate_decimal_precision(...)).unwrap_or(true)`. Two spellings of one 
predicate invite drift where one is updated and the other is not.
   
   Suggested change: extract a small local closure or helper, for example:
   
   ```rust
   let fits = |v: i128| -> Option<i128> {
       v.checked_mul(multiplier)
           .filter(|scaled| is_validate_decimal_precision(*scaled, precision))
   };
   let result: Decimal128Array = array.unary_opt::<_, Decimal128Type>(|v| 
fits(v.into()));
   // rescan:
   if fits(v).is_none() { return Err(SparkError::NumericValueOutOfRange { .. 
}); }
   ```



##########
native/spark-expr/src/conversion_funcs/numeric.rs:
##########
@@ -1160,6 +1153,60 @@ mod tests {
         assert_eq!(decimal_array.value(1), -10000); // -100 * 10^2
         assert!(decimal_array.is_null(2));
     }
+
+    #[test]

Review Comment:
   The three new tests cover Legacy null-on-overflow, ANSI no-overflow, and 
ANSI overflow-error. None cover `EvalMode::Try`. Try shares the Legacy null 
branch today, so this is untested rather than broken, but Try is a distinct 
enum arm that a future edit could regress silently, and the PR text claims all 
eval modes are covered.
   
   Suggested change: add a `test_cast_int_to_decimal128_overflow_try_nulls` 
mirroring the Legacy test with `EvalMode::Try`, asserting overflow maps to null 
and the input null is preserved.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to