Skip to content

parser: parse IS [NOT] DISTINCT FROM (null-safe comparison)#47

Merged
chiradip merged 1 commit into
mainfrom
parser-is-distinct-from
Jul 27, 2026
Merged

parser: parse IS [NOT] DISTINCT FROM (null-safe comparison)#47
chiradip merged 1 commit into
mainfrom
parser-is-distinct-from

Conversation

@chiradip

@chiradip chiradip commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

What

x IS [NOT] DISTINCT FROM y is valid, null-safe-comparison SQL, but the IS handler only recognized IS [NOT] NULL / TRUE / FALSE / UNKNOWN and silently dropped the DISTINCT FROM tail — leaving the predicate as its bare left operand x, so the WHERE effectively vanished (a silent wrong result).

The parser leg of full IS [NOT] DISTINCT FROM support (the user asked for end-to-end support; analyzer and binder legs follow in their repos).

How

  • Parse it into a BinaryExpr whose operator text is "IS DISTINCT FROM" / "IS NOT DISTINCT FROM" — the same text-carried-operator convention the binder's map_binary_op already uses for every other comparison.
  • Append two ast::BinaryOp enum values, IsDistinctFrom / IsNotDistinctFrom (existing values preserved), for the downstream legs.
  • A malformed IS DISTINCT with no FROM now errors instead of dropping the predicate.

Before / after

WHERE x IS DISTINCT FROM y — before: WhereClause → ColumnRef('x') (3 trailing tokens, DISTINCT FROM y gone); after: WhereClause → BinaryExpr('IS DISTINCT FROM') → [x, y].

Tests

test_parser_expr_hardening: IS DISTINCT FROM / IS NOT DISTINCT FROM build the binary comparison over both operands; IS DISTINCT without FROM is rejected; IS NULL / IS TRUE regressions intact. Full suite 37/37 green.

Downstream

The analyzer will type IS [NOT] DISTINCT FROM as Boolean, never-NULL; the binder's map_binary_op will map the operator text to the new enum values and lower it. Those PRs bump their parser pin to this commit.

Part of the parser-layer robustness pass (CASE fixes are in #46; IN-without-parens and the set-operation drops follow).

`x IS [NOT] DISTINCT FROM y` is a null-safe comparison and valid SQL, but the IS
handler only recognized IS [NOT] NULL / TRUE / FALSE / UNKNOWN and silently
dropped the DISTINCT FROM tail, leaving the predicate as its bare left operand -
a silent wrong result (the WHERE effectively vanished).

Parse it into a BinaryExpr whose operator text is "IS DISTINCT FROM" /
"IS NOT DISTINCT FROM" (the same text-carried-operator convention the binder
already maps for other comparisons). Two new ast::BinaryOp enum values,
IsDistinctFrom / IsNotDistinctFrom, are appended (preserving existing values)
for the analyzer/binder legs that follow. A malformed `IS DISTINCT` with no FROM
now errors instead of dropping the predicate.

This is the parser leg of full IS [NOT] DISTINCT FROM support; the analyzer
(type Boolean, never NULL) and binder (map + lower) legs follow in their repos.

Tests (test_parser_expr_hardening): IS DISTINCT FROM and IS NOT DISTINCT FROM
build the binary comparison over both operands; IS DISTINCT without FROM is
rejected; IS NULL / IS TRUE regressions intact. Full suite 37/37 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FyKDAbMWGwiZq4iJx3imsg
@chiradip
chiradip merged commit 348b88e into main Jul 27, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants