david-mollitor-db opened a new pull request, #58910:
URL: https://github.com/apache/spark/pull/58910

   ### What changes were proposed in this pull request?
   
   `Divide`, `Remainder`, and `IntegralDivide` (which share the `DivModLike` 
trait) and `Pmod` declared `override def nullable: Boolean = true` 
unconditionally. This PR changes `nullable` to reflect what the operators can 
actually produce at runtime:
   
   ```scala
   override def nullable: Boolean = left.nullable || right.nullable || 
!failOnError
   ```
   
   Under ANSI mode (`failOnError`), divide-by-zero and integral overflow throw 
rather than returning `null`, so the result is `null` only when one of the 
inputs is `null`. Under LEGACY/TRY mode the behavior is unchanged (`nullable` 
stays effectively `true`), because divide-by-zero returns `null` there and, for 
decimals, a precision overflow can also return `null` independent of the 
divisor.
   
   The change is limited to the declared `nullable` property; `eval` and code 
generation are untouched.
   
   ### Why are the changes needed?
   
   Whole-stage and expression code generation represent SQL `NULL` with boolean 
`isNull` flags. A `nullable = true` child forces the framework to materialize 
an `isNull` variable and makes every parent expression emit a null-guard branch 
on it (`CodegenContext.nullSafeExec`). Because nullability propagates, a single 
spuriously-nullable `%` / `/` / `pmod` cascades dead `if (!isNull)` branches 
into the comparisons, `CASE WHEN`s, and predicates built on top of it.
   
   For example, `(id % 2) = 0` inside a `CASE WHEN` (ANSI) generated:
   
   ```java
   boolean project_isNull_5 = false;          // id % 2 -- never set to true
   if (!project_isNull_5) { ... }             // dead guard
   ...
   if (!project_isNull_4 && project_value_4)  // dead: !isNull_4 is always true 
here
   ```
   
   Correcting the nullability removes these dead guards at the source and feeds 
more accurate nullability to the optimizer.
   
   ### Does this PR introduce _any_ user-facing change?
   
   Yes (schema metadata only). Under ANSI mode, an output column that is a `/`, 
`%`, `div`, or `pmod` over non-nullable inputs is now reported as non-nullable, 
whereas before it was reported as nullable. Computed values and error behavior 
are unchanged: ANSI divide/remainder/pmod-by-zero and integral overflow still 
throw, and LEGACY/TRY still return `null`.
   
   - Before: `spark.range(1).selectExpr("id % 2").schema.head.nullable` is 
`true`
   - After (ANSI): the same is `false`
   
   ### How was this patch tested?
   
   Existing suites, all pass with no golden-file changes:
   
   - `ArithmeticExpressionSuite`, `PredicateSuite`, `ColumnExpressionSuite`
   - `SQLQueryTestSuite`: `operators.sql`, `try_arithmetic.sql`, 
`decimalArithmeticOperations.sql`
   
   `.sql.out` golden files are unaffected because their schema lines use 
`df.schema.catalogString`, which does not encode nullability. Additionally 
verified by inspecting generated code that the dead `isNull` guards for `%` / 
`/` / `pmod` over non-null inputs are removed under ANSI.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Isaac
   
   This pull request and its description were written by Isaac.
   


-- 
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