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

   ## Describe the bug
   
   `CometArrayAppend.convert` reproduces Spark's NULL propagation with
   
   ```
   CASE WHEN <array> IS NOT NULL THEN array_append(<array>, <item>) ELSE null 
END
   ```
   
   The item sits inside the `THEN` branch. DataFusion's `CaseExpr` evaluates a 
`THEN` branch through
   `filter_record_batch`, so the item is only evaluated on the rows the guard 
selects. Spark's
   `ArrayAppend.doGenCode` does the opposite:
   
   ```scala
   val nullSafeEval =
     leftGen.code + rightGen.code + ctx.nullSafeExec(left.nullable, 
leftGen.isNull) {
       ...
     }
   ```
   
   `rightGen.code` is emitted *outside* `nullSafeExec`, so Spark evaluates the 
item on every row and
   only the result assignment is guarded. (`ArrayAppend.eval` does 
short-circuit, so the interpreted
   path and the codegen path already disagree with each other in Spark. Under 
whole-stage codegen the
   codegen path is what runs.)
   
   The consequence is that under ANSI mode an item that raises on a row whose 
array is `NULL` raises in
   Spark and does not raise in Comet. A query that Spark fails, Comet answers.
   
   This is separate from the nondeterministic-item half of the same problem, 
which #5781 covers and
   which is being fixed in #5867 by declining a nondeterministic child. A 
deterministic item that
   raises is not nondeterministic, so that decline does not cover it.
   
   ## Steps to reproduce
   
   Reproduced against `main` at `5ca149928`, Spark 3.4.3, as a 
`CometSqlFileTestSuite` fixture:
   
   ```sql
   -- Config: spark.sql.ansi.enabled=true
   
   statement
   CREATE TABLE t(_1 int) USING parquet
   
   statement
   INSERT INTO t VALUES (0), (1), (2), (3)
   
   query expect_error(DIVIDE_BY_ZERO)
   SELECT _1, array_append(IF(_1 % 2 = 0, array(1), CAST(NULL AS ARRAY<INT>)), 
1 / (_1 - 1)) AS a
   FROM t
   ```
   
   At `_1 = 1` the array is `NULL` and the item divides by zero. Result:
   
   ```
   cometError.isDefined was false Expected Comet to throw an error matching 
'DIVIDE_BY_ZERO' but query succeeded
   ```
   
   Spark raises. Comet returns `[NULL]` for that row and completes.
   
   A control query in the same fixture confirms Comet's ANSI divide does raise 
on its own, so the test
   is not vacuous:
   
   ```sql
   query expect_error(DIVIDE_BY_ZERO)
   SELECT _1, 1 / (_1 - 1) AS d FROM t
   ```
   
   That one passes.
   
   ## Expected behavior
   
   Comet raises `DIVIDE_BY_ZERO`, as Spark does.
   
   ## Additional context
   
   `CometArrayAppend.getSupportLevel` currently reports `Compatible()` for this 
case, so nothing in the
   serde or in the generated compatibility guide records the divergence.
   
   Scope: `ArrayAppend` is `RuntimeReplaceable` on Spark 4.x (rewritten to 
`array_insert(-1)`), so
   `CometArrayAppend` is only reachable on Spark 3.4 and 3.5.
   
   Two possible fixes:
   
   1. Evaluate the item outside the guard, so the `CASE` only guards the result 
rather than the
      operand evaluation. This keeps the native path for every item.
   2. Widen the decline in `CometArrayAppend.getSupportLevel` to any 
non-foldable item under ANSI, and
      let `CodegenDispatchFallback` route it to Spark's own `doGenCode`. 
Cheaper, but it gives up the
      native path for a common shape.
   
   I checked the sibling serdes that use the same guard idiom. 
`CometMapFromArrays` builds
   `IsNotNull(left) AND IsNotNull(right)` as its guard, which looked like the 
same bug in the opposite
   direction (Comet raising where Spark's `nullSafeCodeGen` short-circuits). It 
does not reproduce:
   
   ```sql
   query expect_native(map_from_arrays)
   SELECT _1, map_from_arrays(IF(_1 % 2 = 0, array(_1), CAST(NULL AS 
ARRAY<INT>)), array(1 / (_1 - 1))) AS m
   FROM t
   ```
   
   runs natively and matches Spark. `CometSize` has a single child that the 
guard evaluates anyway, and
   `CometArraysZip`'s children are all in the guard condition, so 
`array_append` looks like the only
   one of the four affected.
   


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