{"record":{"id":"44e428daecdcc6ee","repo":"vitessio/vitess","slug":"vindex-column-cannot-be-a-complex-expression-v","errorCode":null,"errorMessage":"vindex column cannot be a complex expression: %v","messagePattern":"vindex column cannot be a complex expression: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtctl/workflow/utils.go","lineNumber":315,"sourceCode":"func matchColInSelect(col sqlparser.IdentifierCI, sel *sqlparser.Select) (*sqlparser.ColName, error) {\n\tfor _, selExpr := range sel.GetColumns() {\n\t\tswitch selExpr := selExpr.(type) {\n\t\tcase *sqlparser.StarExpr:\n\t\t\treturn &sqlparser.ColName{Name: col}, nil\n\t\tcase *sqlparser.AliasedExpr:\n\t\t\tmatch := selExpr.As\n\t\t\tif match.IsEmpty() {\n\t\t\t\tif colExpr, ok := selExpr.Expr.(*sqlparser.ColName); ok {\n\t\t\t\t\tmatch = colExpr.Name\n\t\t\t\t} else {\n\t\t\t\t\t// Cannot match against a complex expression.\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tif match.Equal(col) {\n\t\t\t\tcolExpr, ok := selExpr.Expr.(*sqlparser.ColName)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, fmt.Errorf(\"vindex column cannot be a complex expression: %v\", sqlparser.String(selExpr))\n\t\t\t\t}\n\t\t\t\treturn colExpr, nil\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unsupported select expression: %v\", sqlparser.String(selExpr))\n\t\t}\n\t}\n\treturn nil, fmt.Errorf(\"could not find vindex column %v\", sqlparser.String(col))\n}\n\nfunc shouldInclude(table string, excludes []string) bool {\n\t// We filter out internal tables elsewhere when processing SchemaDefinition\n\t// structures built from the GetSchema database related API calls. In this\n\t// case, however, the table list comes from the user via the -tables flag\n\t// so we need to filter out internal table names here in case a user has\n\t// explicitly specified some.\n\t// This could happen if there's some automated tooling that creates the list of\n\t// tables to explicitly specify.","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtctl/workflow/utils.go#L297-L333","documentation":"During workflow (Reshard/MigrateTables) setup, generateRule builds query rewrite rules from the source select statement. matchColInSelect matches a vindex column against the select list, and if the matched select item is anything other than a plain column reference (e.g. an expression, function call, or alias of an expression), it cannot be safely rewritten, so this error is thrown. The library requires vindex columns to be simple column references in the select list.","triggerScenarios":"Running a vexec/MoveTables or Reshard workflow where the source select list (e.g. from a filter rule or -tables spec) selects a vindex column via a complex expression like `select id+1 from t` or `select upper(name) from t` instead of a bare column, and generateRule tries to match that column in the select list.","commonSituations":"Users hand-writing workflow filter/ON DDL rules with computed columns; migrating tables whose primary vindex is derived (function of columns); copying select clauses from application queries into migration rules.","solutions":["Rewrite the select list so the vindex column is selected as a plain column reference (e.g. `select id from t`, not `select id+1 from t`).","If a transformed value is needed, compute it downstream or use a generated column in the schema and select that column directly.","Check your filter rules / -tables specification for expressions and simplify them to bare column names for vindex columns."],"exampleFix":"// before\nrule := \"select id + 1 from t\"\n// error: vindex column cannot be a complex expression: id + 1\n// after\nrule := \"select id from t\"","handlingStrategy":"validation","validationCode":"// ensure vindex columns are plain column refs in your workflow select\nfor _, sel := range rule.SelectExpressions() {\n    if _, ok := sqlparser.String(sel); ok && !strings.Contains(sqlparser.String(sel), \"(\") {\n        continue\n    }\n    return fmt.Errorf(\"select list must be plain columns: %s\", sqlparser.String(sel))\n}","typeGuard":"func isPlainColumn(e sqlparser.SelectExpr) bool {\n    ae, ok := e.(*sqlparser.AliasedExpr)\n    if !ok { return false }\n    _, isCol := ae.Expr.(*sqlparser.ColName)\n    return isCol\n}","tryCatchPattern":null,"preventionTips":["Always select vindex columns as bare column references in workflow filters/rules.","Validate filter rules with the SQL parser before submitting a workflow.","Avoid computed/derived columns in migration select lists; use generated columns in the schema instead."],"tags":["go","vindex","workflow","sql-parsing"],"backgroundTag":"vindex-column-complex-expression","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}