Max Gekk created SPARK-60329:
--------------------------------

             Summary: Whole-stage codegen of a filter checks a plain-column IS 
NOT NULL later than its position in the condition
                 Key: SPARK-60329
                 URL: https://issues.apache.org/jira/browse/SPARK-60329
             Project: Spark
          Issue Type: Bug
          Components: SQL
    Affects Versions: 4.2.0, 4.1.3, 4.0.4, 4.3.0, 4.4.0, 5.0.0
            Reporter: Max Gekk


With whole-stage codegen enabled, {{FilterExec}} (and the {{FILTER}} clause of 
an aggregate function, through 
{{GeneratePredicateHelper.generatePredicateCode}}) emits a plain-column 
{{IsNotNull}} check lazily: just before the first conjunct that references its 
column, or after all conjuncts if none does. With whole-stage codegen disabled, 
{{And.eval}} evaluates the conjuncts in the order of the condition, where 
{{InferFiltersFromConstraints}} puts the inferred {{isnotnull(...)}} checks 
first and a user-written {{IS NOT NULL}} stays where it was written.

So when a plain-column {{IS NOT NULL}} check is FALSE on a row and a conjunct 
that comes *later* in the condition but does not reference that column raises 
an error on the same row, codegen reaches the error that the interpreted path 
never evaluates. The query fails only with whole-stage codegen on.

Reproducer (ANSI mode):

{code:sql}
CREATE TABLE t USING parquet AS
  SELECT * FROM VALUES (NULL, 1, 2147483647, NULL), (0, 1, 0, 1) AS v(a, b, y, 
x);
SET spark.sql.parquet.filterPushdown=false;

SET spark.sql.codegen.wholeStage=true;
SELECT a FROM t WHERE x IS NOT NULL AND y + 1 > 0 AND x > 0;
-- [ARITHMETIC_OVERFLOW] overflow. ... SQLSTATE: 22003

SET spark.sql.codegen.wholeStage=false;
SELECT a FROM t WHERE x IS NOT NULL AND y + 1 > 0 AND x > 0;
-- 0
{code}

The optimized condition is {{isnotnull(y) AND isnotnull(x) AND (y + 1) > 0 AND 
x > 0}}. With codegen off, {{isnotnull(x)}} is FALSE on the first row and the 
row is dropped. With codegen on, {{isnotnull(x)}} is emitted just before {{x > 
0}}, after {{(y + 1) > 0}}, so the overflow is raised.

This is independent of SPARK-60315, which is about a NULL conjunct stopping the 
evaluation with codegen on. The fix for SPARK-60315 keeps the lazy placement 
when no conjunct can be NULL, and only preserves the position of the pending 
checks once a NULL conjunct flag is generated, so this shape still differs 
between the two modes after it.

A fix should emit each plain-column {{IsNotNull}} check at its position in the 
condition, in both {{generatePredicateCode}} and the CSE branch of 
{{FilterExec.doConsume}}, which mirror each other by hand. The lazy placement 
exists so that columns are not loaded until a conjunct needs them, so the 
performance effect of the change on filters with many inferred checks should be 
measured. Complex {{IsNotNull(expr)}} checks, whose inner expression can itself 
raise, need the same care.




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