vitessio/vitess · error
[BUG] tried to replace 'Qualifier' on 'TableName'
Error message
[BUG] tried to replace 'Qualifier' on 'TableName'
What it means
Same invariant as TableName.Name: the generated rewriter for TableName.Qualifier panics if the callback attempts to replace the qualifier identifier in place. Identifier components (qualifier, name) are case-insensitive identifiers managed outside the node-replacement mechanism.
Source
Thrown at go/vt/sqlparser/ast_rewrite.go:13150
}
if kontinue {
return true
}
}
if a.collectPaths {
a.cur.current.AddStep(uint16(TableNameName))
}
if !a.rewriteIdentifierCS(node, node.Name, func(newNode, parent SQLNode) {
panic("[BUG] tried to replace 'Name' on 'TableName'")
}) {
return false
}
if a.collectPaths {
a.cur.current.Pop()
a.cur.current.AddStep(uint16(TableNameQualifier))
}
if !a.rewriteIdentifierCS(node, node.Qualifier, func(newNode, parent SQLNode) {
panic("[BUG] tried to replace 'Qualifier' on 'TableName'")
}) {
return false
}
if a.collectPaths {
a.cur.current.Pop()
}
if a.post != nil {
a.cur.replacer = replacer
a.cur.parent = parent
a.cur.node = node
if !a.post(&a.cur) {
return false
}
}
return true
}
// Function Generation Source: SliceMethodView on GitHub (pinned to 01a25a7d17)
Solutions
- Rewrite qualifiers by assigning node.Qualifier = sqlparser.NewTableIdent(...) on the *TableName itself, not by returning a replacement from the callback
- Skip identifier nodes (return node, false) in your rewrite callback
- If you need systematic qualifier rewriting, walk statements and edit TableName nodes directly
- Regenerate the parser code if the local copy is out of sync
Example fix
// before
// callback: return newQualifierNode, true on TableName.Qualifier
// after
func repl(node, parent sqlparser.SQLNode) (sqlparser.SQLNode, bool) {
if tbl, ok := node.(*sqlparser.TableName); ok {
tbl.Qualifier = sqlparser.NewTableIdent(newQualifier)
}
return node, false
} Defensive patterns
Strategy: validation
Validate before calling
func repl(node, parent sqlparser.SQLNode) (sqlparser.SQLNode, bool) {
if tbl, ok := node.(*sqlparser.TableName); ok {
// edit identifiers in place instead of replacing
_ = tbl.Qualifier
return node, false
}
return replaceIfMatched(node)
} Try / catch
err := func() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("rewrite panicked on TableName.Qualifier: %v", r)
}
}()
return sqlparser.SafeRewrite(stmt, opts, repl)
}() Prevention
- Assign tbl.Qualifier = sqlparser.NewTableIdent(...) instead of node replacement
- Return (node, false) for identifier children in callbacks
- Test rewrites on queries with db.table qualified names
When it happens
Trigger: A rewrite callback that returns a replacement when visiting the Qualifier field of *TableName (identifiers.ColIdent), typically from a generic 'replace every child' rewriter.
Common situations: Blanket rewriters doing search/replace over all AST children; code written against the expression-only rewriter being applied to full statements; qualified table name rewrites implemented the wrong way.
Related errors
- [BUG] tried to replace 'Name' on 'TableName'
- [BUG] tried to replace 'Key' on 'VindexParam'
- [BUG] tried to replace 'SQLNode' on 'RootNode'
- [BUG] tried to replace 'VisitThis' on 'Visitable'
- [BUG] tried to replace 'ASTType' on 'ValueContainer'
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/a4e93288f8777e25.
Report an issue: GitHub.