David Mollitor created SPARK-59639:
--------------------------------------

             Summary: Tighten nullability of Divide, Remainder, IntegralDivide, 
and Pmod under ANSI mode
                 Key: SPARK-59639
                 URL: https://issues.apache.org/jira/browse/SPARK-59639
             Project: Spark
          Issue Type: Improvement
          Components: SQL
    Affects Versions: 4.1.0
            Reporter: David Mollitor


h2. Summary

{{{}Divide{}}}, {{{}Remainder{}}}, {{IntegralDivide}} (which share the 
{{DivModLike}} trait) and {{Pmod}}
hardcode {{{}override def nullable: Boolean = true{}}}. Under ANSI mode (the 
default), divide-by-zero and integral overflow *throw* instead of returning 
{{{}null{}}}, so these operators can only be {{{}null }}when one of their 
inputs is \{{{}null{}}}. The unconditional {{nullable = true}} is therefore 
over-broad under ANSI.
h2. Why it matters

Whole-stage and expression codegen 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, one spuriously-nullable {{%}} cascades into the 
comparisons, {{{}CASE WHEN{}}}'s and predicates built on top of it, generating 
dead {{if (!isNull)}} branches that can never be taken.

For example, {{(id % 2) = 0}} used inside a {{CASE WHEN}} generated:
{code: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
{code}
h2. Root cause

{{DivModLike.nullable}} and {{Pmod.nullable}} return {{true}} unconditionally. 
But {{eval}} returns {{null}} (beyond its children) only when {{{}!failOnError 
&& isZero(divisor){}}}; integral overflow throws, and {{failOnError}} is 
{{{}evalMode == ANSI{}}}. So under ANSI the result is {{null}} iff an input is.
h2. User-facing change

No behavioral change. Runtime evaluation and code generation are unchanged – 
ANSI
divide/remainder/pmod-by-zero and integral overflow still throw, and LEGACY/TRY 
still return {{{}null{}}}. Only the declared nullability tightens, and only 
under ANSI: an output column that is a division or modulo of non-nullable 
inputs may now be reported as non-nullable, which is more accurate.
h2. Effect
 * Under ANSI, {{{}/{}}}, {{{}%{}}}, {{div}} and {{pmod}} over non-null inputs 
are now correctly
non-nullable, eliminating the dead null-guard branches and their materialized 
flags at the source, and feeding more accurate nullability to the optimizer.
 * On representative codegen-heavy queries, total generated class bytecode 
dropped ~9.6% (fewer branches and StackMapTable frames; e.g. a filter with 30 
modulo predicates shrank ~36%). The reduction scales with how heavily a query 
uses these operators.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

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

Reply via email to