vitessio/vitess · error
expression needs an alias: %v
Error message
expression needs an alias: %v
What it means
Each target select expression in a vdiff plan must resolve to a column name. When an AliasedExpr has no alias, it is only accepted if the expression is a bare ColName; otherwise vdiff cannot name the target column and throws `expression needs an alias`.
Source
Thrown at go/vt/vttablet/tabletmanager/vdiff/table_plan.go:105
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() {
if colAs, ok := selExpr.Expr.(*sqlparser.ColName); ok {
targetCol = colAs
} else {
return nil, fmt.Errorf("expression needs an alias: %v", sqlparser.String(selExpr))
}
} else {
targetCol = &sqlparser.ColName{Name: selExpr.As}
}
// If the input was "select a as b", then source will use "a" and target will use "b".
sourceSelect.AddSelectExpr(selExpr)
targetSelect.AddSelectExpr(&sqlparser.AliasedExpr{Expr: targetCol})
// Check if it's an aggregate expression
if expr, ok := selExpr.Expr.(sqlparser.AggrFunc); ok {
switch fname := expr.AggrName(); fname {
case "count", "sum":
// this will only work as long as aggregates can be pushed down to tablets
// this won't work: "select count(*) from (select id from t limit 1)"
// since vreplication only handles simple tables (no joins/derived tables) this is fine for now
// but will need to be revisited when we add such support to vreplication
aggregates = append(aggregates, engine.NewAggregateParam(
/*opcode*/ opcode.AggregateSum,View on GitHub (pinned to 01a25a7d17)
Solutions
- Add an explicit alias: wrap every non-column expression with `AS <name>` in the workflow's select/filter rule
- Make the alias match the actual column name on the target table so compareCols validation passes
- Use plain column selects in rules wherever possible, restricting expressions (like convert_tz) to aliased ones
Example fix
// before select convert_tz(ts, 'UTC', 'US/Eastern') from t // after select convert_tz(ts, 'UTC', 'US/Eastern') as ts from t
Defensive patterns
Strategy: validation
Validate before calling
// validate all select expressions are alias-safe before running vdiff
for _, se := range selectExprs {
ae := se.(*sqlparser.AliasedExpr)
if ae.As.IsEmpty() {
if _, ok := ae.Expr.(*sqlparser.ColName); !ok {
return fmt.Errorf("add AS alias to: %s", sqlparser.String(ae))
}
}
} Type guard
func hasRequiredAlias(e sqlparser.SelectExpr) bool {
ae, ok := e.(*sqlparser.AliasedExpr)
if !ok { return false }
if !ae.As.IsEmpty() { return true }
_, isCol := ae.Expr.(*sqlparser.ColName)
return isCol
} Try / catch
tp, err := buildTablePlan(ctx, dbClient, dbName, collationEnv)
if err != nil {
if strings.Contains(err.Error(), "needs an alias") {
return fmt.Errorf("fix filter rule: %w", err)
}
return err
} Prevention
- Alias every non-column expression in select lists
- Match aliases to actual target column names
- Prefer plain column selects in filter rules
When it happens
Trigger: A select list entry like `select convert_tz(ts,...) from t` or `select a+b from t` with no `AS alias`, parsed by buildTablePlan, where the expression is not a simple column reference.
Common situations: Custom filter rules using computed columns without aliases; convert_tz style transformations added for datetime handling but left unaliased; generated rules edited to add expressions.
Related errors
- unexpected: %+v
- found target SelectExpr which was neither ColName nor FuncEx
- unexpected: %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/6c550a1a2311a624.
Report an issue: GitHub.