Skip to content

simplify: collapsing a CASE under an arithmetic operator drops the grouping of the branch #8380

Description

@Rodrigo-Palma

When simplify resolves a CASE to one of its branches, the branch replaces the CASE without parentheses. If the CASE is an operand of an arithmetic operator and the branch is itself a binary expression, the result changes meaning.

Fully reproducible code snippet

Checked on main (19077cc):

from sqlglot import parse_one
from sqlglot.optimizer.simplify import simplify

for sql in [
    "SELECT x * CASE WHEN FALSE THEN NULL ELSE a - b END FROM t",
    "SELECT x * (CASE WHEN 1 IS NULL THEN NULL ELSE 1 - b END) FROM t",
    "SELECT x - CASE WHEN TRUE THEN a + b ELSE c END FROM t",
]:
    print(simplify(parse_one(sql, read="duckdb")).sql(dialect="duckdb"))

Output:

SELECT x * a - b FROM t
SELECT x * 1 - b FROM t
SELECT x - a + b FROM t

Expected:

SELECT x * (a - b) FROM t
SELECT x * (1 - b) FROM t
SELECT x - (a + b) FROM t

With values, DuckDB gives SELECT 2 * CASE WHEN FALSE THEN NULL ELSE 10 - 5 END = 10, while the simplified SELECT 2 * 10 - 5 = 15.

The second case shows that parentheses written around the CASE don't help: they are removed while the node is still a CASE, and the CASE is collapsed afterwards.

Context

Found through SQLMesh's @SAFE_SUB macro, which builds CASE WHEN ... IS NULL THEN NULL ELSE a - b END. When an argument is a literal, the rendered model computes the wrong value (SQLMesh/sqlmesh#5649). SQLMesh can work around it by emitting an explicit paren around the branch (SQLMesh/sqlmesh#6074), but hand written CASE expressions hit the same thing.

Official Documentation

Operator precedence (PostgreSQL, same rules in DuckDB): https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-PRECEDENCE

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions