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

  1. Replace the derived table with the actual base table name in FROM
  2. Create and populate a materialized table if intermediate processing is required
  3. 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

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


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