Skip to content

fix(splitter): preserve blank lines in parsable SQL - #788

Open
edjubert wants to merge 4 commits into
supabase-community:mainfrom
edjubert:edjubert/splitter-blank-line-recovery
Open

fix(splitter): preserve blank lines in parsable SQL#788
edjubert wants to merge 4 commits into
supabase-community:mainfrom
edjubert:edjubert/splitter-blank-line-recovery

Conversation

@edjubert

@edjubert edjubert commented Sep 10, 2026

Copy link
Copy Markdown

What kind of change does this PR introduce?

Bug fix.

What is the current behavior?

The statement splitter treats blank lines as statement boundaries. Valid SQL formatted
with blank lines can therefore be split into invalid fragments and reported as syntax
errors.

Fixes #784.

This also addresses the underlying behavior reported in #372, #380, and #780.

What is the new behavior?

Statement splitting now uses two passes:

  1. A coarse pass splits on ; and structural statement starts while ignoring blank lines.
  2. For fragments containing blank lines, a recovery pass checks whether PostgreSQL can
    parse the fragment:
    • parsable fragments remain intact;
    • unparsable fragments are re-split using blank lines as boundaries.

A blank line now splits SQL only when the complete fragment cannot be parsed.

This preserves recovery for incomplete SQL while typing, including cases such as a
missing ; before BEGIN.

Additional context

  • Named parameters are normalized before the parser check, matching workspace parsing.
  • The lexer records whether the source contains blank lines, so files without them avoid
    the recovery-path token scan.
  • Valid fragments can still be split when their blank-line-separated parts are independently
    valid statements.

Added coverage for:

  • blank lines inside a FROM clause;
  • blank lines between CTEs and after the final CTE;
  • blank lines inside string literals;
  • named parameters;
  • parser recovery for invalid SQL;
  • a missing semicolon before BEGIN.

Validated with:

cargo test -p pgls_lexer -p pgls_statement_splitter
cargo clippy --all-targets --all-features -- -D warnings

The named-parameter normalization is purely lexical and is about to be needed by the statement splitter as well. Moving it to pgls_lexer keeps the splitter and the workspace in agreement about what counts as parsable SQL.

Pure move: no signature, body or comment changed.
Splitter::new now splits on ';' and structural statement starts only;
Splitter::with_blank_line_boundaries keeps the historical behaviour.

No behaviour change: split() opts into blank-line boundaries, so the whole
test suite is unchanged. This only puts the switch in place.
split() now runs two passes. The first splits on ';' and on structural
statement starts, ignoring blank lines. The second asks PostgreSQL about each
fragment: a fragment that parses is kept whole, unless splitting it on its
blank lines yields several statements that are each valid on their own, which
is what a missing ';' between two statements looks like.

A blank line inside a statement is a common readability convention and is not
a terminator in PostgreSQL, so valid SQL is no longer cut in two and reported
as a syntax error. Error recovery while typing is preserved, but scoped to the
fragment being edited instead of the whole file.

Fixes supabase-community#784.
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.

Statement splitter breaks statements on blank lines, producing false syntax errors

1 participant