refactor(advisor/tidb): migrate naming index/UK/FK convention to omni AST (Phase 1.5 §1.5.3 batch 4) by vsai12 · Pull Request #20204 · bytebase/bytebase · GitHub
Skip to content

refactor(advisor/tidb): migrate naming index/UK/FK convention to omni AST (Phase 1.5 §1.5.3 batch 4) - #20204

Merged
vsai12 merged 5 commits into
mainfrom
feat/tidb-omni-advisor-batch-4
May 7, 2026
Merged

vsai12 merged 5 commits into
mainfrom
feat/tidb-omni-advisor-batch-4

Conversation

@vsai12

@vsai12 vsai12 commented May 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Phase 1.5 §1.5.3 batch 4 of the TiDB advisor migration to omni AST.
Migrates 3 naming-convention advisors via Recipe A (top-level type-switch).

Migration progress: 17 of 51 advisors on omni AST.

Migrated advisors

File Recipe Notes
advisor_naming_index_convention.go A Mirrors mysql case ATAddConstraint, ATAddIndex to cover both ALTER syntactic forms
advisor_naming_unique_key_convention.go A Single ConstrUnique arm covers pingcap's 3-way Uniq/UniqKey/UniqIndex split
advisor_naming_foreign_key_convention.go A Uses constraint.RefTable / constraint.RefColumns (omni flat fields)

Pre-batch checklist

Coupling scan

Cross-file helpers in the trio:

Helper Definition site (before) Consumers Action
getTemplateRegexp advisor_naming_index_convention.go:244 all 3 trio files Moved to utils.go
indexMetaData (struct) advisor_naming_index_convention.go:132 all 3 trio files Moved to utils.go

Both helpers are AST-agnostic Go (no pingcap types). Clean move — no Linear cleanup ticket needed (unlike canNull/BYT-9362 or tableNewColumn/BYT-9395, which stay pingcap-typed for un-migrated consumers).

Shape audit findings (NEW divergence — proposed cumulative table item #8)

Advisor / batch Pingcap shape Omni shape Class
naming_*_convention (batch 4) ALTER TABLE ADD INDEX and ALTER TABLE ADD CONSTRAINT name INDEX both produce AlterTableAddConstraint (single enum) omni splits: ATAddIndex (no CONSTRAINT keyword) vs ATAddConstraint (with CONSTRAINT keyword) Latent coverage gap if mechanically ported — case ATAddConstraint only would miss bare ADD INDEX. Mirrored mysql omni case ATAddConstraint, ATAddIndex to cover both. Same applies to UNIQUE forms.

Existing cumulative table items consulted (from plan §1.5.0 invariant #7):

  • Item rename dataSource.go to data_source.go #2 (ConstrUnique unification): omni unifies pingcap's ConstraintUniq/ConstraintUniqKey/ConstraintUniqIndex under single ConstrUnique. UK advisor's switch becomes single arm; covers all 3 syntactic forms.

Spot-check on constraint-name handling per session-baseline review note: constraint.Name is the same field name in both pingcap and omni (verified at omni/tidb/ast/parsenodes.go:459). No divergence.

Fixture pins

New fixtures pin the discovered divergences:

  • naming_index_idx.yaml: ALTER TABLE ... ADD CONSTRAINT name INDEX (pass + violation pair) — pins ATAddConstraint+ConstrIndex path.
  • naming_index_uk.yaml: ALTER TABLE ... ADD UNIQUE INDEX and ADD UNIQUE KEY (4 cases: 2 forms × pass+violation each) — pins ATAddIndex+ConstrUnique path AND the ConstrUnique unification on the ALTER side.

If omni regresses on either split, these fixtures fail loudly.

Behavior preserved (NOT aligned with mysql)

CREATE FULLTEXT INDEX / CREATE SPATIAL INDEX continue to be checked by the index naming convention advisor (matching pingcap behavior of KeyType != IndexKeyTypeUnique). The mysql omni rule additionally excludes Fulltext/Spatial; aligning tidb with mysql on this is a separate intentional behavior change deferred from this AST migration.

Dedup follow-up (commit ce0904a4)

Sonar's first-run review flagged 21.9% duplication across the 3 new files (33.6% / 32.2% / 11.0%). The duplication was real: ~140 LOC of structural overlap in the Check() preamble (payload validation, template-token check, maxLength default) and the advice-emission loop (regex match + length overflow advices).

Extracted runNamingConventionRule(checkCtx, cfg, collect) in utils.go. Each Check() drops to ~17 lines: a namingRuleConfig struct (mismatch code, type noun, internal-error title) plus a closure mapping statement nodes to []*indexMetaData. Collector functions are unchanged.

Net LOC: +93 helper / -167 dedup = -102 LOC. Sonar dup on the trio should drop to ~0%.

Mysql divergence — explicit: the mysql omni naming-convention rules (rule_naming_index_convention.go etc.) left similar duplication inline. That precedent pre-dates Sonar duplication gating on new code. Since the trio is new code, the gate applies; we extract here. The cumulative cost of NOT extracting — naming_auto_increment_column is queued in the handoff doc and would replicate the same ~50 LOC scaffold; any future naming-style rules also benefit — justifies the deviation from mysql precedent. Reviewers who prefer mysql-mirrored structure can push back; the extraction is reversible.

Verification: TestTiDBRules green — advice content is byte-identical to the pre-extraction inline form (helper builds the same "<noun> in table ..." and "<noun> \` in table ..."strings viafmt.Sprintfwithcfg.typeNoun` substitution). Existing fixtures are the safety net.

API mappings used

From the plan §1.5.0 mapping table:

Pingcap Omni
ast.ConstraintUniq/UniqKey/UniqIndex ast.ConstrUnique (single)
ast.ConstraintIndex ast.ConstrIndex
ast.ConstraintForeignKey ast.ConstrForeignKey
constraint.Tp constraint.Type
constraint.Keys[].Column.Name.String() constraint.Columns (simple) / omniIndexColumns(constraint.IndexColumns) (with expressions)
constraint.Refer.Table.Name.String() constraint.RefTable.Name
constraint.Refer.IndexPartSpecifications[].Column.Name.String() constraint.RefColumns
node.KeyType == ast.IndexKeyTypeUnique n.Unique (bool)
node.IndexPartSpecifications (CreateIndexStmt) n.Columns
ast.AlterTableAddConstraint ast.ATAddConstraint AND ast.ATAddIndex (omni split)
ast.AlterTableRenameIndex ast.ATRenameIndex
spec.FromKey.String() / spec.ToKey.String() cmd.Name / cmd.NewName
node.OriginTextPosition() (whole statement) ostmt.FirstTokenLine()
constraint.OriginTextPosition() (sub-node) ostmt.AbsoluteLine(constraint.Loc.Start)

Helper additions to utils.go

  • OmniStmt.TrimmedText() — strings.TrimSpace(s.Text). Used for embedding statement text in advice content.
  • OmniStmt.FirstTokenLine() — 1-based absolute line of first non-whitespace char. Pingcap-parity for whole-statement advice positions (per cumulative item #6).
  • runNamingConventionRule() — shared scaffold for the index/UK/FK rules (see Dedup follow-up above).
  • namingRuleConfig — config struct for the scaffold.

TrimmedText and FirstTokenLine are also defined on PR #20199 (batch 3). Whichever batch merges second drops the duplicate definition. Trivial conflict resolution.

What's NOT in this batch

Test plan

  • go test -count=1 -run "^TestTiDBRules$" ./backend/plugin/advisor/tidb/ — green
  • go test -count=1 ./backend/plugin/advisor/tidb/ ./backend/plugin/advisor/mysql/ — green (cross-engine non-regression)
  • go test -count=1 ./backend/plugin/parser/tidb/ — green
  • golangci-lint run --allow-parallel-runners ./backend/plugin/advisor/tidb/... — 0 issues
  • go build -ldflags "-w -s" -p=16 -o /tmp/bb-batch4 ./backend/bin/server/main.go — clean

🤖 Generated with Claude Code

… AST (Phase 1.5 §1.5.3 batch 4)

Migrate 3 advisors to omni AST via Recipe A (top-level type-switch):
- advisor_naming_index_convention.go
- advisor_naming_unique_key_convention.go
- advisor_naming_foreign_key_convention.go

Pre-batch coupling scan promoted 2 cross-file helpers to utils.go:
- getTemplateRegexp (function)
- indexMetaData (struct)

Both are AST-agnostic Go (no pingcap types in their signatures), so the
move is clean. No Linear cleanup ticket needed (unlike canNull/BYT-9362
or tableNewColumn/BYT-9395, which stayed pingcap-typed for un-migrated
consumers).

Also added OmniStmt.TrimmedText() and OmniStmt.FirstTokenLine() helpers
to utils.go. Same helpers also defined on PR #20199 (batch 3); whichever
batch merges second drops the duplicate definition (trivial conflict).

NEW shape divergence caught and pinned:

omni splits ALTER TABLE ADD INDEX (ATAddIndex) vs ADD CONSTRAINT name
INDEX (ATAddConstraint) where pingcap collapsed both into
AlterTableAddConstraint. Same split applies to UNIQUE: ADD UNIQUE INDEX
goes to ATAddIndex, ADD CONSTRAINT name UNIQUE goes to ATAddConstraint.
Switch arms now cover both, mirroring mysql omni rule.

Fixture pins for the discovered divergence:
- naming_index_idx.yaml: ALTER TABLE ADD CONSTRAINT name INDEX
  (pass + violation pair).
- naming_index_uk.yaml: ALTER TABLE ADD UNIQUE INDEX, ALTER TABLE ADD
  UNIQUE KEY (4 cases: 2 forms x pass+violation each). These also pin
  the existing ConstrUnique unification (cumulative table item #2).

Behavior preserved (NOT aligned with mysql divergence):

CREATE FULLTEXT/SPATIAL INDEX statements continue to be checked by the
index naming convention advisor (matching pingcap behavior). The mysql
omni rule excludes them; aligning tidb with mysql on this is a separate
intentional behavior change deferred from this AST migration.

Migration progress: 17 of 51 advisors on omni AST.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vsai12
vsai12 requested a review from a team as a code owner May 7, 2026 01:28
@cla-bot cla-bot Bot added the cla-signed label May 7, 2026
…up naming trio

Sonar flagged 21.9% duplication across the 3 new naming-convention
advisors (33.6% / 32.2% / 11.0% per file). The duplication was real:
~140 LOC of structural overlap across the trio in the Check() preamble
(payload validation, template-token check, maxLength default) and the
advice-emission loop (regex match + length overflow advices).

Extracted runNamingConventionRule(checkCtx, cfg, collect) in utils.go.
Each advisor's Check() drops to ~17 lines: a config struct (mismatch
code, type noun, internal-error title) plus a closure that maps statement
nodes to []*indexMetaData. Collector functions (collect{Index,UK,FK}*)
unchanged — those carry the rule-specific logic that genuinely differs.

Net LOC: +93 helper / -167 dedup = -102 LOC. Sonar duplication on the
trio should drop to ~0%.

Mysql analogs left similar duplication inline; that precedent pre-dates
Sonar duplication gating on new code. Since the trio is new code, the
gate applies; the cumulative cost — naming_table, naming_column,
naming_auto_increment_column are queued in the handoff doc and would
each replicate the same ~50 LOC scaffold without the helper — justifies
the extraction now.

Verification:
- TestTiDBRules green — advice content is byte-identical to the
  pre-extraction inline form (helper builds the same "<noun> in table
  ..." and "<noun> `<name>` in table ..." strings via fmt.Sprintf with
  cfg.typeNoun substitution). Existing fixtures are the safety net.
- go test ./backend/plugin/advisor/tidb/ ./backend/plugin/advisor/mysql/
  green (cross-engine non-regression).
- golangci-lint 0 issues.
- Server build clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@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: ce0904a4cb

ℹ️ 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".

@d-bytebase
d-bytebase requested a review from a team May 7, 2026 03:35
vsai12 and others added 3 commits May 6, 2026 20:52
Codex P2 round-1 catch on PR #20204. The line argument
ostmt.AbsoluteLine(constraint.Loc.Start) was evaluated BEFORE
buildFKMetaData could nil-check, so a nil entry in n.Constraints would
panic with a nil pointer dereference instead of being skipped. The
helper's internal `if constraint == nil` was vestigial because of Go's
argument-evaluation order.

Sibling collectIndexCreateTable / collectUKCreateTable already
nil-checked at loop top; collectFKCreateTable was the outlier.
collectFKAlterTable was safe by accident — it precomputes stmtLine
outside the loop.

Fix: nil-check at loop top, mirroring siblings exactly.

Added TestCollectFKCreateTableNilConstraintGuard with a synthetic AST
containing a nil constraint slot. Omni's parser doesn't produce nils
from valid SQL so no YAML fixture can reach this branch — the unit test
pins the contract for future refactors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…r-batch-4

# Conflicts:
#	backend/plugin/advisor/tidb/utils.go
Code review pushback on PR #20204 — the runNamingConventionRule
extraction's byte-identity claim only stood for the regex-mismatch and
length-overflow paths the YAML fixtures exercise. The internal-error
advice path (fires when getTemplateRegexp's regexp.Compile fails on the
post-substitution template) had no test coverage; an accidental swap of
cfg.internalErrorTitle vs cfg.typeNoun, or a regression in Status/Code
fields, would go unnoticed.

Added TestRunNamingConventionRuleEmitsInternalErrorAdvice — uses an
unbalanced-paren format that passes payload-token validation but fails
regexp.Compile after no-op substitution. Asserts the advice's Title
comes from cfg.internalErrorTitle (not cfg.typeNoun or rule.Type),
Code is code.Internal, Status mirrors the rule level, and Content shape
matches the pre-extraction inline form.

Also added clarifying comments per the same review:
- runNamingConventionRule docstring: indexMetaData coupling tradeoff
  (4-field LCD shared by all callers; future divergence may require
  widening or replacing the closure shape).
- omniIndexColumns docstring: silent skip of expression-based parts
  (functional indexes contribute no name to column-list substitution;
  matches pingcap behavior).
- Index/UK Check methods: explain the closure-capture pattern for
  originalMetadata (keeps helper signature uniform across rules that
  don't need metadata).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented May 7, 2026

Copy link
Copy Markdown

@vsai12
vsai12 enabled auto-merge (squash) May 7, 2026 06:29
@vsai12
vsai12 merged commit df8b579 into main May 7, 2026
14 of 16 checks passed
@vsai12
vsai12 deleted the feat/tidb-omni-advisor-batch-4 branch May 7, 2026 06:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants