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: SliceMethod

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Rewrite qualifiers by assigning node.Qualifier = sqlparser.NewTableIdent(...) on the *TableName itself, not by returning a replacement from the callback
  2. Skip identifier nodes (return node, false) in your rewrite callback
  3. If you need systematic qualifier rewriting, walk statements and edit TableName nodes directly
  4. 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

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


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