{"record":{"id":"8161c053ae53270d","repo":"vitessio/vitess","slug":"unsupported-from-source-t","errorCode":null,"errorMessage":"unsupported from source (%T)","messagePattern":"unsupported from source \\(%T\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go","lineNumber":413,"sourceCode":"\tif !ok {\n\t\treturn nil, \"\", errors.New(\"unsupported non-select statement\")\n\t}\n\tif sel.Distinct {\n\t\treturn nil, \"\", errors.New(\"unsupported distinct clause\")\n\t}\n\tif len(sel.From) == 0 {\n\t\treturn nil, \"\", errors.New(\"unsupported select from dual\")\n\t}\n\tif len(sel.From) > 1 {\n\t\treturn nil, \"\", errors.New(\"unsupported multi-table usage\")\n\t}\n\tnode, ok := sel.From[0].(*sqlparser.AliasedTableExpr)\n\tif !ok {\n\t\treturn nil, \"\", fmt.Errorf(\"unsupported from expression (%T)\", sel.From[0])\n\t}\n\tfromTable := sqlparser.GetTableName(node.Expr)\n\tif fromTable.IsEmpty() {\n\t\treturn nil, \"\", fmt.Errorf(\"unsupported from source (%T)\", node.Expr)\n\t}\n\treturn sel, fromTable.String(), nil\n}\n\nfunc (tpb *tablePlanBuilder) analyzeExprs(selExprs []sqlparser.SelectExpr) error {\n\tfor _, selExpr := range selExprs {\n\t\tcexpr, err := tpb.analyzeExpr(selExpr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttpb.colExprs = append(tpb.colExprs, cexpr)\n\t}\n\treturn nil\n}\n\nfunc (tpb *tablePlanBuilder) analyzeExpr(selExpr sqlparser.SelectExpr) (*colExpr, error) {\n\taliased, ok := selExpr.(*sqlparser.AliasedExpr)\n\tif !ok {","sourceCodeStart":395,"sourceCodeEnd":431,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go#L395-L431","documentation":"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.","triggerScenarios":"FROM contains a parenthesized subquery such as `FROM (SELECT * FROM t) AS x`, so GetTableName returns an empty TableName.","commonSituations":"Auto-generated or hand-written filter SELECTs using derived tables; migration tooling translating complex queries into vreplication plans.","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"],"exampleFix":"// before\nFROM (SELECT * FROM sales) AS s\n// after\nFROM sales","handlingStrategy":"validation","validationCode":"node, ok := sel.From[0].(*sqlparser.AliasedTableExpr)\nif !ok { return errors.New(\"not a table expr\") }\nif sqlparser.GetTableName(node.Expr).IsEmpty() {\n    return errors.New(\"FROM must name a real table, not a derived table\")\n}","typeGuard":"func fromNamesRealTable(sel *sqlparser.Select) bool {\n    node, ok := sel.From[0].(*sqlparser.AliasedTableExpr)\n    return ok && !sqlparser.GetTableName(node.Expr).IsEmpty()\n}","tryCatchPattern":"plan, err := buildTablePlan(...)\nif err != nil && strings.Contains(err.Error(), \"unsupported from source\") {\n    log.Warn(\"derived-table FROM is not vstreamable; rewrite to base table\")\n}","preventionTips":["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"],"tags":["sqlparser","vreplication","unsupported-sql","from-clause"],"backgroundTag":"unsupported-sql-construct","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}