{"record":{"id":"678302909b392b4d","repo":"vitessio/vitess","slug":"table-expression-is-complex","errorCode":null,"errorMessage":"table expression is complex","messagePattern":"table expression is complex","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/sqlparser/analyzer.go","lineNumber":354,"sourceCode":"\t\treturn true\n\t}\n\n\treturn false\n}\n\n// TableFromStatement returns the qualified table name for the query.\n// This works only for select statements.\nfunc (p *Parser) TableFromStatement(sql string) (TableName, error) {\n\tstmt, err := p.Parse(sql)\n\tif err != nil {\n\t\treturn TableName{}, err\n\t}\n\tsel, ok := stmt.(*Select)\n\tif !ok {\n\t\treturn TableName{}, fmt.Errorf(\"unrecognized statement: %s\", sql)\n\t}\n\tif len(sel.From) != 1 {\n\t\treturn TableName{}, errors.New(\"table expression is complex\")\n\t}\n\taliased, ok := sel.From[0].(*AliasedTableExpr)\n\tif !ok {\n\t\treturn TableName{}, errors.New(\"table expression is complex\")\n\t}\n\ttableName, ok := aliased.Expr.(TableName)\n\tif !ok {\n\t\treturn TableName{}, errors.New(\"table expression is complex\")\n\t}\n\treturn tableName, nil\n}\n\n// GetTableName returns the table name from the SimpleTableExpr\n// only if it's a simple expression. Otherwise, it returns \"\".\nfunc GetTableName(node SimpleTableExpr) IdentifierCS {\n\tif n, ok := node.(TableName); ok && n.Qualifier.IsEmpty() {\n\t\treturn n.Name\n\t}","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/sqlparser/analyzer.go#L336-L372","documentation":"sqlparser.TableFromStatement extracts a single simple table name from a SQL statement. It first requires the statement to be a SELECT with exactly one FROM item; if there are multiple/joined FROM items (len(sel.From) != 1) it cannot identify one table and returns 'table expression is complex'.","triggerScenarios":"TableFromStatement called with SQL whose SELECT has more than one FROM element — joins, comma-separated tables, or subquery-in-FROM constructs.","commonSituations":"VReplication/automation code assuming single-table SELECTs but given joins; users passing 'SELECT * FROM a JOIN b' where only 'SELECT * FROM a' is supported.","solutions":["Pass a SELECT with exactly one table in FROM (no joins, no comma lists)","Parse the statement yourself with sqlparser and handle multi-table FROM explicitly","Simplify the query upstream (e.g. split per-table queries) before extracting the table name"],"exampleFix":"// before\ntn, err := TableFromStatement(\"select * from a join b on a.id=b.id\")\n// after\ntn, err := TableFromStatement(\"select * from a\")","handlingStrategy":"validation","validationCode":"stmt, err := sqlparser.Parse(sql)\nif err != nil { return err }\nsel, ok := stmt.(*sqlparser.Select)\nif !ok || len(sel.From) != 1 { return errors.New(\"query must be a single-table select\") }","typeGuard":"func isSimpleSelect(sql string) bool {\n    stmt, err := sqlparser.Parse(sql)\n    if err != nil { return false }\n    sel, ok := stmt.(*sqlparser.Select)\n    return ok && len(sel.From) == 1\n}","tryCatchPattern":"tn, err := sqlparser.TableFromStatement(sql)\nif err != nil && strings.Contains(err.Error(), \"table expression is complex\") {\n    // fall back to full AST parsing of the query\n}","preventionTips":["Only pass single-table SELECTs to TableFromStatement","Pre-parse and validate query shape before extraction","Reject joins/comma-FROM at the API boundary"],"tags":["go","sqlparser","parsing"],"backgroundTag":"unsupported-sql-construct","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}