andygrove opened a new issue, #23894:
URL: https://github.com/apache/datafusion/issues/23894

   
   ### Describe the bug
   
   `datafusion-spark`'s `mod` shares the `try_rem` helper in
   `datafusion/spark/src/function/math/modulus.rs` with `pmod`. That helper has 
two
   gaps in its zero divisor handling. Both are visible through `mod`.
   
   1. **ANSI mode, floating point divisor.** `try_rem` delegates to Arrow's 
`rem`
      kernel when `datafusion.execution.enable_ansi_mode` is `true`. Arrow's 
`rem`
      only reports division by zero for integer and decimal types. Floating 
point
      divisors follow IEEE 754 and quietly produce `NaN`. Spark raises
      `REMAINDER_BY_ZERO` for a zero divisor of any numeric type.
   
   2. **`-0.0` divisor, both modes.** The legacy path nulls out zero divisors by
      comparing with `eq(right, 0)`. Arrow's comparison kernels order floating
      point values totally, so `-0.0` is not equal to `0.0` and a `-0.0` 
divisor is
      not recognised. Spark's `isZero` check is a numeric comparison, so `-0.0`
      counts as zero.
   
   `pmod` was fixed for both cases as part of an audit of that function and no
   longer routes through `try_rem`. `mod` still does.
   
   ### To Reproduce
   
   Spark 4.2.0 (verified with `pyspark==4.2.0`):
   
   ```sql
   SET spark.sql.ansi.enabled = true;
   SELECT MOD(CAST(10.5 AS DOUBLE), CAST(0.0 AS DOUBLE));
   -- [REMAINDER_BY_ZERO] Remainder by zero. ... SQLSTATE: 22012
   SELECT MOD(CAST(10.5 AS DOUBLE), CAST('-0.0' AS DOUBLE));
   -- [REMAINDER_BY_ZERO] Remainder by zero. ... SQLSTATE: 22012
   
   SET spark.sql.ansi.enabled = false;
   SELECT MOD(CAST(10.5 AS DOUBLE), CAST(0.0 AS DOUBLE));   -- NULL
   SELECT MOD(CAST(10.5 AS DOUBLE), CAST('-0.0' AS DOUBLE)); -- NULL
   ```
   
   DataFusion:
   
   ```sql
   set datafusion.execution.enable_ansi_mode = true;
   SELECT mod(10.5::float8, 0.0::float8);   -- NaN, expected an error
   SELECT mod(10.5::float8, -0.0::float8);  -- NaN, expected an error
   
   set datafusion.execution.enable_ansi_mode = false;
   SELECT mod(10.5::float8, -0.0::float8);  -- NaN, expected NULL
   ```
   
   ### Expected behavior
   
   `mod` raises for a zero divisor of any numeric type in ANSI mode and returns
   `NULL` in legacy mode, treating `-0.0` as zero in both.
   
   ### Affected versions
   
   Spark 3.5.8, 4.0.4, 4.1.3 and 4.2.0 all behave the same way here. The 
behaviour
   difference across those versions is only the error class name, which changed
   from `DIVIDE_BY_ZERO` to `REMAINDER_BY_ZERO` in Spark 4.1.
   
   ### Relevant code
   
   `datafusion/spark/src/function/math/modulus.rs`, `try_rem`.
   
   `spark_pmod` in the same file shows one possible shape for the fix. Its
   `is_zero` helper treats `-0.0` as zero and its ANSI branch masks the check by
   the validity of the dividend, because Spark's remainder expressions are null
   intolerant and a NULL dividend short circuits to NULL before the divisor is
   validated.
   
   Surfaced by the audit-datafusion-spark-expression skill.
   


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