vitessio/vitess · error

[BUG] tried to replace 'Name' on 'TableName'

Error message

[BUG] tried to replace 'Name' on 'TableName'

What it means

Generated rewriter code for TableName.Name uses rewriteIdentifierCS, whose replacement callback panics because TableName.Name cannot be rewritten in place — replacing a column name via the path rewriter would break identifier semantics handled elsewhere (e.g. colVindex rewriting tracks identifiers separately).

Source

Thrown at go/vt/sqlparser/ast_rewrite.go:13141

func (a *application) rewriteTableName(parent SQLNode, node TableName, replacer replacerFunc) bool {
	if a.pre != nil {
		a.cur.replacer = replacer
		a.cur.parent = parent
		a.cur.node = node
		kontinue := !a.pre(&a.cur)
		if a.cur.revisit {
			a.cur.revisit = false
			return a.rewriteSQLNode(parent, a.cur.node, replacer)
		}
		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

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Do not return replacements for identifier (ColIdent/TableIdent) fields in the rewrite callback; handle name rewriting by mutating node.Name directly before/after rewriting
  2. Restrict the rewrite callback to replaceable node types (expressions, table names as whole nodes)
  3. Use a targeted AST manipulation (sqlparser.Rewrite or manual assignment) for identifier changes
  4. Regenerate via `make codegen` if your local generated files are stale

Example fix

// before
func repl(node, parent sqlparser.SQLNode) (sqlparser.SQLNode, bool) {
    if ident, ok := node.(sqlparser.IdentifierCS); ok {
        return newIdent, true // panics on TableName.Name
    }
    return node, false
}
// after
func repl(node, parent sqlparser.SQLNode) (sqlparser.SQLNode, bool) {
    if tbl, ok := node.(*sqlparser.TableName); ok {
        tbl.Name = sqlparser.NewTableIdent(newName)
    }
    return node, false
}
Defensive patterns

Strategy: validation

Validate before calling

func repl(node, parent sqlparser.SQLNode) (sqlparser.SQLNode, bool) {
    switch node.(type) {
    case sqlparser.IdentifierCS, sqlparser.IdentifierCI:
        return node, false // identifiers are not replaceable
    }
    return replaceIfMatched(node)
}

Type guard

func isReplaceableNode(node sqlparser.SQLNode) bool {
    switch node.(type) {
    case sqlparser.IdentifierCS, sqlparser.IdentifierCI:
        return false
    default:
        return true
    }
}

Try / catch

err := func() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("rewrite panicked on identifier: %v", r)
        }
    }()
    return sqlparser.SafeRewrite(stmt, opts, repl)
}()

Prevention

When it happens

Trigger: A custom rewriter's callback returns a replacement node when the visited child is a TableName's Name field (an identifiers.ColIdent), via astRewriteVisitor/RewriteOptions that rewrite identifier nodes.

Common situations: Rewriters that generically replace all SQLNode children including identifier fields; migrating old rewrite logic to the newer path-based rewriter; using a rewriter intended for expression nodes on whole statements.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/9bf21b01eef803fd. Report an issue: GitHub.