vitessio/vitess · error
unsupported from source (%T)
Error message
unsupported from source (%T)
What it means
After confirming the FROM element is an AliasedTableExpr, the builder extracts the table name via sqlparser.GetTableName. If the underlying expression is not a plain TableName (e.g. a subquery inside parentheses), the name is empty and the builder rejects the plan since it cannot identify the source table to stream from.
Source
Thrown at go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go:413
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
}
func (tpb *tablePlanBuilder) analyzeExpr(selExpr sqlparser.SelectExpr) (*colExpr, error) {
aliased, ok := selExpr.(*sqlparser.AliasedExpr)
if !ok {View on GitHub (pinned to 01a25a7d17)
Solutions
- Replace the derived table with the actual base table name in FROM
- Create and populate a materialized table if intermediate processing is required
- Keep table-to-table replication plans limited to plain `SELECT ... FROM <table>` shapes
Example fix
// before FROM (SELECT * FROM sales) AS s // after FROM sales
Defensive patterns
Strategy: validation
Validate before calling
node, ok := sel.From[0].(*sqlparser.AliasedTableExpr)
if !ok { return errors.New("not a table expr") }
if sqlparser.GetTableName(node.Expr).IsEmpty() {
return errors.New("FROM must name a real table, not a derived table")
} Type guard
func fromNamesRealTable(sel *sqlparser.Select) bool {
node, ok := sel.From[0].(*sqlparser.AliasedTableExpr)
return ok && !sqlparser.GetTableName(node.Expr).IsEmpty()
} Try / catch
plan, err := buildTablePlan(...)
if err != nil && strings.Contains(err.Error(), "unsupported from source") {
log.Warn("derived-table FROM is not vstreamable; rewrite to base table")
} Prevention
- Use `FROM <table>` directly; materialize derived tables first
- Run the plan builder in a preflight check before starting workflows
- Keep filter templates to simple table selects
When it happens
Trigger: FROM contains a parenthesized subquery such as `FROM (SELECT * FROM t) AS x`, so GetTableName returns an empty TableName.
Common situations: Auto-generated or hand-written filter SELECTs using derived tables; migration tooling translating complex queries into vreplication plans.
Related errors
- unsupported from expression (%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/8161c053ae53270d.
Report an issue: GitHub.