sunchao commented on code in PR #5452:
URL: https://github.com/apache/datafusion-comet/pull/5452#discussion_r3874895480


##########
spark/src/main/scala/org/apache/comet/serde/arrays.scala:
##########
@@ -634,6 +651,42 @@ object CometElementAt extends 
CometExpressionSerde[ElementAt] {
           None
         }
     }
+
+    // Spark's ElementAt is a BinaryExpression: for a NULL map/array it 
returns NULL WITHOUT
+    // evaluating the key/index child. Native scalar functions evaluate the 
key eagerly over the
+    // whole batch, so under ANSI (failOnError) a throwing key (e.g. a 
divide-by-zero) fires even on
+    // rows whose map/array is NULL, where Spark short-circuits. When the left 
can actually be NULL,
+    // guard the lookup with CASE WHEN left IS NOT NULL THEN <lookup> ELSE 
null so the key is only
+    // evaluated on the selected rows (DataFusion's CaseExpr filters the batch 
before the THEN
+    // branch), reproducing the short-circuit. Mirrors the CASE-WHEN idiom in 
CometArrayAppend /
+    // CometSize; the ELSE null literal carries the result type, as in 
CometArraysZip.
+    if (expr.failOnError && expr.left.nullable) {
+      val isNotNullExpr = createUnaryExpr(
+        expr,
+        expr.left,

Review Comment:
   [P2] Evaluate a stateful lookup operand only once
   
   `expr.left` was already serialized into `baseExpr`, so serializing it again 
here creates a second native instance of stateful descendants. CASE evaluates 
this condition over the full batch, but evaluates the lookup only over selected 
rows. With ANSI enabled and ordered INT `id = [1, 2]` in the first batch of 
partition 0:
   
   ```sql
   SELECT element_at(
     element_at(
       array(CAST(NULL AS ARRAY<BIGINT>),
             array(monotonically_increasing_id(), CAST(NULL AS BIGINT))),
       id),
     id - 1)
   FROM t
   ```
   
   The source-derived results are `[NULL, 1]` for Spark and the previous 
serializer, but `[NULL, 0]` here: only `id = 2` reaches the independent 
THEN-side counter, which starts at zero. Spark's Project optimizer does not 
hoist this nested nondeterminism, and native planning/projection does not 
memoize the two serialized instances. The NULL first row also short-circuits 
before index-zero validation in the previous native path, so that control does 
not throw.
   
   This is source-verified, not a fresh runtime reproduction. Please 
materialize the left operand once, or retain a safe evaluation path when this 
guard would duplicate stateful descendants.



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