{"record":{"id":"a4e93288f8777e25","repo":"vitessio/vitess","slug":"bug-tried-to-replace-qualifier-on-tablename","errorCode":null,"errorMessage":"[BUG] tried to replace 'Qualifier' on 'TableName'","messagePattern":"\\[BUG\\] tried to replace 'Qualifier' on 'TableName'","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/sqlparser/ast_rewrite.go","lineNumber":13150,"sourceCode":"\t\t}\n\t\tif kontinue {\n\t\t\treturn true\n\t\t}\n\t}\n\tif a.collectPaths {\n\t\ta.cur.current.AddStep(uint16(TableNameName))\n\t}\n\tif !a.rewriteIdentifierCS(node, node.Name, func(newNode, parent SQLNode) {\n\t\tpanic(\"[BUG] tried to replace 'Name' on 'TableName'\")\n\t}) {\n\t\treturn false\n\t}\n\tif a.collectPaths {\n\t\ta.cur.current.Pop()\n\t\ta.cur.current.AddStep(uint16(TableNameQualifier))\n\t}\n\tif !a.rewriteIdentifierCS(node, node.Qualifier, func(newNode, parent SQLNode) {\n\t\tpanic(\"[BUG] tried to replace 'Qualifier' on 'TableName'\")\n\t}) {\n\t\treturn false\n\t}\n\tif a.collectPaths {\n\t\ta.cur.current.Pop()\n\t}\n\tif a.post != nil {\n\t\ta.cur.replacer = replacer\n\t\ta.cur.parent = parent\n\t\ta.cur.node = node\n\t\tif !a.post(&a.cur) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Function Generation Source: SliceMethod","sourceCodeStart":13132,"sourceCodeEnd":13168,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/sqlparser/ast_rewrite.go#L13132-L13168","documentation":"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.","triggerScenarios":"A rewrite callback that returns a replacement when visiting the Qualifier field of *TableName (identifiers.ColIdent), typically from a generic 'replace every child' rewriter.","commonSituations":"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.","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"],"exampleFix":"// before\n// callback: return newQualifierNode, true on TableName.Qualifier\n// after\nfunc repl(node, parent sqlparser.SQLNode) (sqlparser.SQLNode, bool) {\n    if tbl, ok := node.(*sqlparser.TableName); ok {\n        tbl.Qualifier = sqlparser.NewTableIdent(newQualifier)\n    }\n    return node, false\n}","handlingStrategy":"validation","validationCode":"func repl(node, parent sqlparser.SQLNode) (sqlparser.SQLNode, bool) {\n    if tbl, ok := node.(*sqlparser.TableName); ok {\n        // edit identifiers in place instead of replacing\n        _ = tbl.Qualifier\n        return node, false\n    }\n    return replaceIfMatched(node)\n}","typeGuard":null,"tryCatchPattern":"err := func() (err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"rewrite panicked on TableName.Qualifier: %v\", r)\n        }\n    }()\n    return sqlparser.SafeRewrite(stmt, opts, repl)\n}()","preventionTips":["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"],"tags":["sqlparser","ast-rewrite","codegen","panic","identifiers"],"backgroundTag":"invalid-ast-rewrite-replacement","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}