vitessio/vitess · error
unexpected: %v
Error message
unexpected: %v
What it means
buildTablePlan parses the source query of a vreplication rule and requires it to be a plain SELECT statement, since vdiff builds source/target selects from it. If the parsed statement is any other AST node type (INSERT, UPDATE, SET, etc.), it fails with `unexpected: <sql>`.
Source
Thrown at go/vt/vttablet/tabletmanager/vdiff/table_plan.go:83
table *tabletmanagerdatapb.TableDefinition
orderBy sqlparser.OrderBy
aggregates []*engine.AggregateParams
WorkflowConfig **vttablet.VReplicationConfig
}
func (td *tableDiffer) buildTablePlan(dbClient binlogplayer.DBClient, dbName string, collationEnv *collations.Environment) (*tablePlan, error) {
tp := &tablePlan{
table: td.table,
dbName: dbName,
WorkflowConfig: td.wd.WorkflowConfig,
}
statement, err := td.wd.ct.vde.parser.Parse(td.sourceQuery)
if err != nil {
return nil, err
}
sel, ok := statement.(*sqlparser.Select)
if !ok {
return nil, fmt.Errorf("unexpected: %v", sqlparser.String(statement))
}
sourceSelect := &sqlparser.Select{}
targetSelect := &sqlparser.Select{}
// Aggregates is the list of Aggregate functions, if any.
var aggregates []*engine.AggregateParams
for _, selExpr := range sel.GetColumns() {
switch selExpr := selExpr.(type) {
case *sqlparser.StarExpr:
// If it's a '*' expression, expand column list from the schema.
for _, fld := range tp.table.Fields {
aliased := &sqlparser.AliasedExpr{Expr: &sqlparser.ColName{Name: sqlparser.NewIdentifierCI(fld.Name)}}
sourceSelect.AddSelectExpr(aliased)
targetSelect.AddSelectExpr(aliased)
}
case *sqlparser.AliasedExpr:
var targetCol *sqlparser.ColName
if selExpr.As.IsEmpty() {View on GitHub (pinned to 01a25a7d17)
Solutions
- Inspect the workflow's filter rules and fix the offending rule to be a single valid SELECT statement
- If the rule was meant to be a keyrange/table filter, use the supported forms (table name, keyrange string, or SELECT) instead of arbitrary SQL
- Re-create the workflow (MoveTables/Migrate) with correct filter rules rather than editing stored SQL by hand
Example fix
// before (invalid filter rule) rule.Filter = "update t set a=1" // after rule.Filter = "select * from t"
Defensive patterns
Strategy: validation
Validate before calling
stmt, err := parser.Parse(rule.Filter)
if err != nil { return err }
if _, ok := stmt.(*sqlparser.Select); !ok {
return fmt.Errorf("filter rule must be a SELECT: %s", rule.Filter)
} Type guard
func isSelectStatement(sqlText string) bool {
stmt, err := sqlparser.Parse(sqlText)
if err != nil { return false }
_, ok := stmt.(*sqlparser.Select)
return ok
} Try / catch
td, err := buildTablePlan(...)
if err != nil {
if strings.Contains(err.Error(), "unexpected:") {
log.Errorf("filter rule for %s is not a SELECT; fix the rule", tableName)
}
return err
} Prevention
- Never hand-edit stored filter rules to non-SELECT SQL
- Use supported filter forms: table name, keyrange, or SELECT
- Test new workflows' rules with vdiff show/plan before production runs
When it happens
Trigger: A binlog filter rule's Filter value, when parsed by sqlparser, is not a *sqlparser.Select — e.g. the rule was written as a full non-select statement or contains multiple/invalid statements.
Common situations: Hand-edited vreplication filter rules (e.g. in the _vt.vreplication workflow column or MoveTables rules) that accidentally contain non-SELECT text; copying a rule from another tool; version differences in the filter stored for legacy workflows.
Related errors
- unexpected: %+v
- found target SelectExpr which was neither ColName nor FuncEx
- expression needs an alias: %v
- table expression is complex
- VDiff not implemented in vtcombo
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/99e8f9fd5ee97fd6.
Report an issue: GitHub.