#8380·sqlglot

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

Author: Rodrigo-PalmaCreated Sep 17, 2026Updated Sep 17, 2026

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

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

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

Expected:

sql
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