vitessio/vitess · error
unsupported from expression (%T)
Error message
unsupported from expression (%T)
What it means
analyzeSelectFrom validates that the SELECT's FROM clause is a single simple table expression before generating a vstream filter plan. If the FROM element is not an *sqlparser.AliasedTableExpr (e.g. a derived table/subquery or join), the planner rejects it because vstreaming can only replicate from plain tables.
Source
Thrown at go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go:409
if err != nil {
return nil, "", err
}
sel, ok := statement.(*sqlparser.Select)
if !ok {
return nil, "", errors.New("unsupported non-select statement")
}
if sel.Distinct {
return nil, "", errors.New("unsupported distinct clause")
}
if len(sel.From) == 0 {
return nil, "", errors.New("unsupported select from dual")
}
if len(sel.From) > 1 {
return nil, "", errors.New("unsupported multi-table usage")
}
node, ok := sel.From[0].(*sqlparser.AliasedTableExpr)
if !ok {
return nil, "", fmt.Errorf("unsupported from expression (%T)", sel.From[0])
}
fromTable := sqlparser.GetTableName(node.Expr)
if fromTable.IsEmpty() {
return nil, "", fmt.Errorf("unsupported from source (%T)", node.Expr)
}
return sel, fromTable.String(), nil
}
func (tpb *tablePlanBuilder) analyzeExprs(selExprs []sqlparser.SelectExpr) error {
for _, selExpr := range selExprs {
cexpr, err := tpb.analyzeExpr(selExpr)
if err != nil {
return err
}
tpb.colExprs = append(tpb.colExprs, cexpr)
}
return nil
}View on GitHub (pinned to 01a25a7d17)
Solutions
- Rewrite the FROM clause to reference a single base table by name
- Compute joins upstream and replicate from the resulting materialized table
- If a derived table is needed, create a real table and point the plan at it
Example fix
// before sel.From = "(SELECT * FROM t JOIN u ON ...)" // after sel.From = "t"
Defensive patterns
Strategy: validation
Validate before calling
stmt, err := sqlparser.Parse(selSQL)
if err != nil { return err }
sel := stmt.(*sqlparser.Select)
if len(sel.From) != 1 { return errors.New("plan requires a single FROM table") }
if _, ok := sel.From[0].(*sqlparser.AliasedTableExpr); !ok {
return errors.New("FROM must be a plain table expression")
} Type guard
func isSimpleFrom(sel *sqlparser.Select) bool {
if len(sel.From) != 1 { return false }
_, ok := sel.From[0].(*sqlparser.AliasedTableExpr)
return ok
} Try / catch
plan, err := buildReplicatorPlan(...)
if err != nil && strings.Contains(err.Error(), "unsupported") {
// fall back to plain table-to-table select
} Prevention
- Keep vreplication filter SELECTs limited to single-table queries
- Never feed JOIN/subquery SQL into plan generation
- Review generated plans for unexpected FROM shapes
When it happens
Trigger: A filter/plan input whose FROM clause contains a subquery `(SELECT ...)` , a JOIN expression, or any non-AliasedTableExpr node as the first FROM item.
Common situations: Hand-edited or generated replication plans embedding joins; tooling that passes query templates instead of plain table selects into the vreplication plan builder.
Related errors
- unsupported from source (%T)
- invalid expression: %v
- expression needs an alias: %v
- unsupported qualifier for column: %v
- failed to find column name for convert using expression: %v,
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/5475cb52156e255a.
Report an issue: GitHub.