Skip to content

fix(pretty-print): normalize associative boolean expressions before comparison - #802

Open
edjubert wants to merge 1 commit into
supabase-community:mainfrom
edjubert:edjubert/pr-bool-expr-associativity
Open

edjubert wants to merge 1 commit into
supabase-community:mainfrom
edjubert:edjubert/pr-bool-expr-associativity

Conversation

@edjubert

Copy link
Copy Markdown

What

normalize_ast now flattens nested BoolExpr nodes that use the same operator, so that redundant parentheses do not make two semantically identical ASTs compare different.

-- these two parse to different trees but mean the same thing
SELECT * FROM demo WHERE (a OR (b OR c)) AND d;
SELECT * FROM demo WHERE (a OR b OR c) AND d;

Only same-operator AND and OR are flattened. NOT, and any mix of AND and OR, keep their original tree shape - there the parentheses carry meaning.

-- untouched: a AND (b OR c) is not a AND b OR c
SELECT * FROM demo WHERE a AND (b OR c);

Why

PostgreSQL builds a left-leaning tree whose shape depends on where the author put redundant parentheses.
The printer normalizes that away, so the reparsed output legitimately differs in shape from the input AST, and the beta round-trip guard reports Normalized ASTs differ after formatting on a statement the printer handled perfectly.
The result is that a WHERE clause with a few grouped conditions (routine in generated or hand-tuned ETL SQL) is silently never formatted.

Implementation

The pass walks BoolExpr nodes and processes descendants first: flattening a parent moves its children, so child pointers must not be used after their parent has been normalized.

Tests

A multi fixture covering a real-world CREATE TABLE AS with nested same-operator groups, plus the two one-liners above (one flattened, one deliberately not), snapshotted at widths 80 and 100.

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.

1 participant