Skip to content

tidb: match TiDB v8.5.5 block-comment lexing#372

Open
vsai12 wants to merge 2 commits into
mainfrom
fix/tidb-comment-lexer
Open

tidb: match TiDB v8.5.5 block-comment lexing#372
vsai12 wants to merge 2 commits into
mainfrom
fix/tidb-comment-lexer

Conversation

@vsai12

@vsai12 vsai12 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Wave 0 of the TiDB GAP burn-down (full-compat directive): container-verified rewrite of the block-comment lexer against the pinned pingcap/tidb:v8.5.5 oracle.

What was wrong

TiDB block comments do not nest — the first */ closes — and unclosed regular comments are parse errors. omni's nesting depth-scan diverged in both directions, plus a crash:

  • GAP / silent statement loss: /* a /* b */ SELECT 1 — TiDB executes the SELECT; omni swallowed it as comment (parsed to 0 statements, invisible to every downstream consumer).
  • OVER: SELECT 1 /* unterminated accepted; TiDB rejects 1064.
  • P1 panic on main: SELECT /*! 1 (unclosed /*!) → slice bounds out of range in the splice tail.
  • Wrong AST: /*T![global_index] GLOBAL */ and /*T![affinity] ... content silently dropped — the v8.5 feature list was missing force_inc, global_index, affinity.

What changed

  • Regular comments end at the first */; unclosed → tokInvalid parse error (TiDB 1064 parity). Same non-nesting fix in Split's skipBlockCommentMySQL, and a segment holding an unclosed comment now reaches the lexer instead of being dropped as empty.
  • Bang comments (/*!, supported /*T!): content spliced as SQL. The closing */ is found with a string/backtick/line-comment/inner-comment-aware scan (those hide */ in TiDB since content is lexed as SQL); unclosed constructs are tolerated exactly like TiDB's EOF-in-bang-mode (fixes the panic). Inner bang markers and the closing */ splice to a single space so butted tokens stay separate (x*/YZ lexes as x then YZ; markers can't reconstruct phantom /* openers).
  • /*!NNNNN consumes exactly five version digits or none (scanVersionDigits(5,5) parity).
  • /*T![...] feature lists follow scanFeatureIDs byte-for-byte: malformed lists reset and lex from [ as content; well-formed lists with an unsupported ID downgrade to a regular comment. Feature map synced to v8.5.5.

Deliberate parked leniencies (documented in TestBangCommentParkedLeniencies, all reject-only on TiDB's side, fail-safe)

  1. TiDB's Scanner.Lex lookahead for AS OF/TO TIMESTAMP/MEMBER OF corrupts inBangComment when AS/TO/MEMBER precedes the closing */ (getNextToken restores the reader but not the flag) — an upstream bug we don't emulate.
  2. omni's lexer-wide unclosed-string tolerance (SELECT '1 parses) — pre-existing, tracked separately.
  3. SELECT * / 1-class select-grammar acceptance — pre-existing, surfaces as trailing */ after a closed comment.

Verification

  • Container-grounded TDD: 35 RED subtests written from live-probe ground truth, all GREEN after the fix.
  • 520-probe generated reject-space sweep + 57-probe hand battery vs the live oracle: zero non-parked parse-level divergences, zero panics.
  • Adversarial splice audit: backtrack snapshots capture l.input (splices are restore-safe), all raw-text captures go through clamped inputText, splicing is idempotent; the audit's one real finding (zero-byte marker strip could reconstruct a phantom /*) is fixed by the space-padding and regression-tested.
  • go vet ./tidb/... clean; go test ./tidb/... green except the pre-existing tidb/catalog non-short container tests (collation-rendering mismatch, fails identically on main; -short lane green).

Scoreboard delta (adjudicated sweep, committed)

  • AGREE_REJECT +3: both unterminated-comment OVER clusters now correctly reject.
  • AGREE_ACCEPT −4 / GAP +4: the feature-list sync un-masks four wrong-AST acceptances (GLOBAL index option, AFFINITY table option previously dropped silently). Honest accounting — the grammar gaps are owned by wave-3 burn-down items (index-global-local-options, tidb-table-options-parity).
  • OVER clusters 46 → 44.

Mirror-back note: omni/mysql shares the fork-parent nesting comment scanner and the missing ||-era gaps do not apply here, but the non-nesting + unclosed-comment behavior is MySQL semantics too — candidate for the mysql lane (Jonathan), minus the TiDB-only /*T! machinery.

🤖 Generated with Claude Code

vsai12 added 2 commits July 8, 2026 17:51
Container-verified rewrite of the comment lexer (pingcap/tidb:v8.5.5
oracle). Block comments do not nest in TiDB — the first */ closes — and
the old nesting scan diverged in both directions: it swallowed valid SQL
after pseudo-nested comments (GAP, including silent whole-statement loss)
and accepted unclosed comments TiDB rejects (OVER). Unclosed /*! also
panicked (slice bounds out of range) on the splice tail.

- Regular comments end at the first */; an unclosed one is a parse error
  surfaced as a tokInvalid token (TiDB 1064 parity).
- Bang comments (/*!, supported /*T!) splice their content as SQL. The
  closing */ is found with a string/backtick/line-comment/inner-comment
  aware scan (those constructs hide */ in TiDB because content is lexed
  as SQL), and an unclosed construct is tolerated exactly like TiDB's
  EOF-in-bang-mode. Inner bang markers are replaced with a space —
  inBangComment is a bool upstream, nested markers do not stack — and
  the closing */ splices to a space so butted tokens stay separate
  (x*/YZ lexes as x then YZ).
- /*!NNNNN consumes exactly five version digits or none, mirroring
  scanVersionDigits(5,5); shorter or longer digit runs are SQL content.
- /*T![...] feature lists follow scanFeatureIDs byte-for-byte: malformed
  lists reset and lex from '[' as bang content; well-formed lists with
  an unsupported ID downgrade to a regular comment. Feature map synced
  to v8.5.5 (adds force_inc, global_index, affinity — global_index
  content was previously dropped from the AST silently).
- Split's skipBlockCommentMySQL is non-nesting, and a segment holding an
  unclosed comment reaches the lexer instead of being dropped as empty.

Deliberate, documented leniencies (all reject-only on the TiDB side):
TiDB's Lex lookahead corrupts inBangComment when AS/TO/MEMBER precedes
the closing */ (upstream bug, not emulated); omni's lexer-wide unclosed
string tolerance; SELECT * / 1 select-grammar acceptance.

Verified: 520-probe generated sweep + 57-probe hand battery against the
live v8.5.5 container — zero non-parked divergences, zero panics;
adversarial splice audit confirms backtrack snapshots capture l.input,
raw-text captures go through clamped inputText, and splicing stays
idempotent.
AGREE_REJECT +3: the two unterminated-comment OVER clusters
(1=1/*' and b = 'p' x2, select 1/*) now correctly reject.
AGREE_ACCEPT -4 / GAP +4: feature-list sync (global_index, affinity)
un-masks four wrong-AST acceptances — omni used to skip the whole
/*T![...] comment and silently drop GLOBAL / AFFINITY = '...' from the
AST; the content now injects correctly and rejects on the two known
grammar gaps owned by wave-3 burn-down items (index-global-local-options,
tidb-table-options-parity). Honest accounting, not a parser regression.
@vsai12
vsai12 requested a review from rebelice as a code owner July 9, 2026 00:54

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9417c1c37f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread tidb/parser/lexer.go
Comment on lines +2052 to +2053
case ch == '#':
i = appendLineComment(&sb, l.input, i)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep Split aligned with bang-comment scanning

When this path lets a */ inside a bang-comment line comment be ignored, the top-level Split scanner still uses skipBlockCommentMySQL and resumes at that hidden */. If the bang content then contains a semicolon before the real close, Split cuts the input before the lexer can splice the whole comment; for example SELECT /*!50000 1 # */\n; SELECT 2 */; SELECT 3; is split into a second segment ending in SELECT 2 */ and is rejected, even though the new lexer semantics treat the hidden */ as part of the line comment. Please update Split's conditional-comment scan to use the same bang-aware closing rules.

Useful? React with 👍 / 👎.

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