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

  1. Rewrite the FROM clause to reference a single base table by name
  2. Compute joins upstream and replicate from the resulting materialized table
  3. 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

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


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