[
https://issues.apache.org/jira/browse/SPARK-58211?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Wenchen Fan reassigned SPARK-58211:
-----------------------------------
Assignee: Ganesha S
> [SQL] Subexpression elimination evaluates short-circuited AND/OR operands,
> causing spurious errors
> --------------------------------------------------------------------------------------------------
>
> Key: SPARK-58211
> URL: https://issues.apache.org/jira/browse/SPARK-58211
> Project: Spark
> Issue Type: Bug
> Components: SQL
> Affects Versions: 4.2.0
> Reporter: Ganesha S
> Assignee: Ganesha S
> Priority: Major
> Labels: pull-request-available
>
> Subexpression elimination (SEE) can evaluate a common subexpression that
> lives in a short-circuited operand of an AND/OR, defeating the short-circuit
> semantics. When evaluating that operand would raise an exception (e.g.
> NullPointerException, DIVIDE_BY_ZERO), the query fails even though, per SQL
> semantics, the operand should never have been evaluated.
> This is a correctness bug: a query that should succeed instead throws at
> runtime.
> *How to reproduce*
> With default configs (`spark.sql.ansi.enabled=true`,
> `spark.sql.subexpressionElimination.enabled=true`):
> {code:java}
> SELECT id != 0 AND 1 / id > 0 AND 1 / id < 1 FROM range(0, 1, 1, 1); {code}
> For id = 0, id != 0 is false, so short-circuit evaluation should stop and
> never compute 1 / id. Instead the query fails with:
>
> The two-operand form `SELECT id != 0 AND 1 / id > 0 FROM range(0, 1, 1, 1)`
> works correctly; the failure appears once there are three or more AND
> operands and a common subexpression (1 / id) is shared across the
> short-circuited operands.
> Disabling subexpression elimination
> (`spark.sql.subexpressionElimination.enabled=false`) avoids the error,
> confirming SEE as the cause.
> *Root cause*
> `EquivalentExpressions.skipForShortcut` strips only a single And/Or operand:
>
> {code:java}
> expr match {
> case and: And => and.left
> case or: Or => or.left
> case other => other
> }
> {code}
> A chained predicate a AND b AND c is parsed left-deep as And(And(a, b), c).
> Peeling one level yields And(a, b), whose children [a, b] are then recursed
> into as if both are always evaluated. But under short-circuit semantics only
> the first operand (a) is always evaluated; b and c are conditional. A
> subexpression shared with b or c is therefore hoisted and evaluated eagerly.
> By contrast, If/CaseWhen model this correctly via the ConditionalExpression
> trait (recursing only alwaysEvaluatedInputs, with branchGroups for safe
> cross-branch common subexpressions). And/Or do not.
> *Note:* `spark.sql.subexpressionElimination.skipForShortcutExpr=true` (added
> in SPARK-42815) only partially mitigates this; it handles simple cases, but
> still fails when the shared subexpression is inside a non-first operand of a
> 3+-way AND, because it too peels only one level.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]