Skip to content

parser: switch desired-side parse to ParseStrictDDL (close vitess partial-DDL silent swallow)#83

Merged
winebarrel merged 2 commits into
mainfrom
parser-detect-vitess-partial-parse
May 5, 2026
Merged

parser: switch desired-side parse to ParseStrictDDL (close vitess partial-DDL silent swallow)#83
winebarrel merged 2 commits into
mainfrom
parser-detect-vitess-partial-parse

Conversation

@winebarrel

Copy link
Copy Markdown
Owner

Summary

Closes the High-severity TODO item from the PR #81 silent-drop audit. vitess's Parser.Parse calls Parse2, which has a partial-DDL fallback: when yacc fails but the tokenizer has a half-built AST, Parse2 logs WARN ignoring error parsing DDL …, sets FullyParsed(false) on the partial DDL, and returns err=nil with that partial AST.

myschema's desired-side parse loop (parser/parser.go) treated err=nil as success and handed the half-built AST to parseCreateTable. For inputs like CREATE TABLE t (id INT STORAGE DISK, PRIMARY KEY (id)); (NDB-only STORAGE DISK that vitess doesn't grammar-parse) the partial AST was effectively empty, and myschema plan emitted CREATE TABLE t ();. apply against a multi-statement plan would half-apply: tables ahead of the bad one succeed, the bad one fails on a MySQL syntax error, leaving the database in an inconsistent state. Silent data loss, not just feature drop.

vitess provides ParseStrictDDL for exactly this case — same parse path but no partial-DDL fallback; yacc failure surfaces as the tokenizer's LastError. One-line swap in the main desired-side parse loop:

- stmt, err := p.Parse(piece)
+ stmt, err := p.ParseStrictDDL(piece)

Other call sites stay on Parse:

  • The -- myschema:execute <check-sql> validator parses a SELECT, not DDL — partial-DDL fallback never fires there.
  • partition.go and view.go parse SQL myschema synthesises; we control the input shape, so no user-supplied syntax error can reach them.

TODO.md: drop the High item now that the fix is in.

Test plan

  • TestParseSQL_PartialDDLSurfacesAsError — single-statement repro: CREATE TABLE bad (id INT NOT NULL STORAGE DISK, …); must error and the wrapped error preserves vitess's STORAGE diagnostic.
  • TestParseSQL_PartialDDLAbortsBatch — multi-statement repro: good, bad, good batch aborts the whole parse (nil ParseResult, no partial leak). This is the realistic shape of the half-apply risk.
  • Both tests fail without the ParseStrictDDL swap — verified by running them against the previous Parse shape during development.
  • make lint clean, make test all six packages green (no existing fixture relied on the partial-DDL tolerance).
  • Manual repro: the previous WARN ignoring error parsing DDL slog noise is gone; the same input now exits non-zero with a usable error message at plan time.

🤖 Generated with Claude Code

winebarrel and others added 2 commits May 5, 2026 22:24
Closes the High-severity TODO item from the PR #81 silent-drop
audit. vitess's `Parser.Parse` calls `Parse2`, which has a
partial-DDL fallback: when yacc fails but the tokenizer has a
half-built AST, Parse2 logs `WARN ignoring error parsing DDL …`,
sets `FullyParsed(false)` on the partial DDL, and returns
**err=nil** with that partial AST.

myschema's desired-side parse loop (parser.go) treated err=nil as
success and handed the half-built AST to `parseCreateTable`. For
inputs like `CREATE TABLE t (id INT STORAGE DISK, PRIMARY KEY
(id));` (NDB-only `STORAGE DISK` that vitess doesn't grammar-parse)
the partial AST was effectively empty, and `myschema plan` emitted
`CREATE TABLE t ();`. apply against a multi-statement plan would
half-apply: tables ahead of the bad one succeed, the bad one fails
on MySQL syntax error, leaving the database in an inconsistent
state. Silent data loss, not just feature drop.

vitess provides `ParseStrictDDL` for exactly this case — same
parse path but no partial-DDL fallback; yacc failure surfaces as
the tokenizer's LastError. One-line swap in parser.go's main
desired-side parse loop:

  - stmt, err := p.Parse(piece)
  + stmt, err := p.ParseStrictDDL(piece)

Other call sites stay on `Parse`:
- The `-- myschema:execute <check-sql>` validator parses a SELECT,
  not DDL — partial-DDL fallback never fires there.
- partition.go and view.go parse SQL myschema *synthesises*; we
  control the input shape, no user-supplied syntax error can
  reach them.

Test: TestParseSQL_PartialDDLSurfacesAsError pins the new
behaviour against the `STORAGE DISK` repro from the audit. The
previous-WARN noise is gone; the same input now returns a real
Go error that includes vitess's syntax-error diagnostic.

TODO.md: drop the High item now that the fix is in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TestParseSQL_PartialDDLAbortsBatch pins the realistic shape of the
audit's silent-data-loss concern: a `good, bad, good` batch must
abort the whole parse rather than returning a ParseResult with
the two good tables and a half-built `bad`. Before the
ParseStrictDDL switch, the loop would register `good_a`,
swallow the syntax error in `bad`, register `good_c`, and
`apply` would half-apply the batch. Asserts:

- err is non-nil
- ParseResult is nil (no partial leak)
- the wrapped error preserves vitess's `STORAGE` diagnostic so the
  user can find the offending statement

Complements TestParseSQL_PartialDDLSurfacesAsError (single-stmt
case). Both fail without the ParseStrictDDL fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 5, 2026 13:27
@codecov

codecov Bot commented May 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.62%. Comparing base (23c3b4b) to head (42f1f0b).

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #83   +/-   ##
=======================================
  Coverage   86.62%   86.62%           
=======================================
  Files          30       30           
  Lines        3268     3268           
=======================================
  Hits         2831     2831           
  Misses        269      269           
  Partials      168      168           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens desired-schema SQL parsing by switching the main parse loop to vitess’s ParseStrictDDL, preventing vitess’s partial-DDL fallback from silently returning an incomplete AST (and thereby avoiding incorrect DDL like CREATE TABLE t (); being emitted/applied).

Changes:

  • Use Parser.ParseStrictDDL in ParseSQL’s statement loop to surface partial-DDL conditions as errors.
  • Add regression tests covering both single-statement and multi-statement “partial DDL” repro cases to ensure parsing fails fast and returns no partial ParseResult.
  • Remove the corresponding “High” TODO entry now that the issue is addressed.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
TODO.md Removes the “High” TODO item describing vitess partial-DDL swallow now that it’s fixed.
parser/parser.go Switches desired-side parsing from Parse to ParseStrictDDL with rationale in comments.
parser/parser_test.go Adds tests to ensure partial-DDL becomes a hard error and aborts multi-statement batches.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@winebarrel winebarrel merged commit 8264344 into main May 5, 2026
9 checks passed
@winebarrel winebarrel deleted the parser-detect-vitess-partial-parse branch May 5, 2026 13:33
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