Skip to content

fix(compat): handle SQL lexical structure in normalizeSQL - #56

Merged
pablontiv merged 3 commits into
mainfrom
fix/normalize-sql-lexer
Aug 22, 2026
Merged

fix(compat): handle SQL lexical structure in normalizeSQL#56
pablontiv merged 3 commits into
mainfrom
fix/normalize-sql-lexer

Conversation

@pablontiv

Copy link
Copy Markdown
Owner

Follow-up to #52 (merged in #55). The normalizeSQL rewrite shipped there fixes the reported lockout, but left two latent defects that reintroduce the same class of bug on future input. This lands the fix that missed the #55 merge window.

Defect 1 — an apostrophe outside a literal disables normalization for the rest of the statement

The shipped scanner toggles quote state on any ', including one inside a SQL comment. Measured against main:

IN : "CREATE TABLE t ( -- don't\n  a   INTEGER,\n  b   TEXT\n)"
OUT: "CREATE TABLE t ( -- don't\n  a   INTEGER,\n  b   TEXT\n)"   <- nothing collapsed

Whitespace stays load-bearing for that DDL, which is exactly the condition issue #52 exists to eliminate. Not triggered today only because backscroll's current DDL comments happen to contain no apostrophes. A future migration comment reading -- don't or -- the row's id silently reintroduces #52.

Defect 2 — whitespace collapsed inside double-quoted identifiers

IN : `CREATE TABLE "my  table" ( a   INTEGER )`
OUT: `CREATE TABLE "my table" ( a   INTEGER )`

Two genuinely distinct tables produce one signature. This is the opposite and more dangerous failure mode: defect 1 causes false rejection, this one causes false acceptance.

Change

normalizeSQL now scans real SQL lexical structure: -- line comments and /* */ block comments (quotes inside them are text, not delimiters), single-quoted literals with '' escapes, and double-quoted identifiers with "" escapes. Whitespace is collapsed only in code, preserved inside literals and quoted identifiers.

Catalog signatures are unchanged — manifest.json needed no regeneration, confirming both defects were latent rather than active.

Verification

Observed output of just ci:

ok  github.com/pablontiv/backscroll/internal/recovery   2.794s  coverage: 85.4% of statements
ok  github.com/pablontiv/backscroll/internal/storage    6.256s  coverage: 84.4% of statements
ok  github.com/pablontiv/backscroll/internal/templates  2.788s  coverage: 91.8% of statements
Coverage: 85.3%

Behavior after the change:

apostrophe line comment    -> "CREATE TABLE t ( -- don't a INTEGER )"
apostrophe block comment   -> "CREATE TABLE t ( /* row's id */ a INTEGER )"
double quoted ident        -> "CREATE TABLE \"my  table\" ( a INTEGER )"
literal preserved          -> "CREATE TABLE t ( a TEXT DEFAULT 'two  spaces' )"
escaped quote              -> "CREATE TABLE t ( a TEXT DEFAULT 'it''s  here' )"

Relates to #52.

https://claude.ai/code/session_019LDXzStaKrArqJKvy4z3eF

Replace simplistic quote-flipping parser with proper SQL lexical analysis.
Fixes two defects:

1. Apostrophes outside string literals (e.g., in comments) no longer flip
   parser into quote mode, preventing whitespace after the apostrophe from
   being incorrectly preserved. Handles line comments (--) and block
   comments (/* */) with proper boundary detection.

2. Double-quoted identifiers now preserve inner whitespace (e.g.,
   "my  table" remains distinct from "my table"). Handles "" escapes
   within identifiers.

Also adds support for backtick and bracket identifier quoting per SQLite
spec, with consistent whitespace preservation for all quoted contexts.

Implements proper SQL lexer that tracks:
- Line comments (-- until newline)
- Block comments (/* ... */ non-nesting)
- Single-quoted string literals (with '' escapes)
- Double-quoted identifiers (with "" escapes)
- Backtick-quoted identifiers
- Bracket-quoted identifiers [...]

All existing tests pass. Manifest signatures unchanged (backscroll's current
DDL contains no apostrophes in comments, so the whitespace normalization
behavior remains identical for the canonical schemas).

Claude-Session: https://claude.ai/code/session_019LDXzStaKrArqJKvy4z3eF
…istic signatures

When line comments end with CRLF (Windows/mixed line endings), CR was being
included in the normalized output, producing non-deterministic signatures for
identical schemas with different line ending styles. This violates the identity
contract where `normalizeSQL` must produce the same result regardless of line
ending style.

Skip CR characters when copying line comment text — they are trailing whitespace
that will be normalized to a single space anyway. This ensures deterministic
signatures across Unix (LF) and Windows (CRLF) line endings.

Adds test case for CRLF line ending in line comment.

Claude-Session: https://claude.ai/code/session_019LDXzStaKrArqJKvy4z3eF
…c signatures

Comments are cosmetic — internal whitespace differences should not create
different schema signatures. This is the third latent defect in normalizeSQL:
comment content was preserved verbatim, making comment formatting load-bearing.

Example: Two CREATE statements differing only in comment spacing:
  "CREATE TABLE t ( -- note  double\n a INTEGER )"
  "CREATE TABLE t ( -- note double\n a INTEGER )"
were producing different signatures. Now both normalize to the same signature
with single spaces in the comment.

Implementation:
- Line comments: collapse internal whitespace, trim trailing space before newline
- Block comments: collapse internal whitespace, ensure single space before */
- Both: preserve comment delimiters and overall comment structure

Adds comprehensive test suite covering comment whitespace collapsing for both
line and block comments, including edge cases with multiple spaces and newlines.

Manifest.json remains unchanged — defect was latent; this fix applies to future
databases with comments inside CREATE statements.

Claude-Session: https://claude.ai/code/session_019LDXzStaKrArqJKvy4z3eF
@pablontiv
pablontiv merged commit 1f33efe into main Aug 22, 2026
6 checks passed
@pablontiv
pablontiv deleted the fix/normalize-sql-lexer branch August 22, 2026 18:37
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