vitessio/vitess · error

vindex column cannot be a complex expression: %v

Error message

vindex column cannot be a complex expression: %v

What it means

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.

Source

Thrown at go/vt/vtctl/workflow/utils.go:315

func matchColInSelect(col sqlparser.IdentifierCI, sel *sqlparser.Select) (*sqlparser.ColName, error) {
	for _, selExpr := range sel.GetColumns() {
		switch selExpr := selExpr.(type) {
		case *sqlparser.StarExpr:
			return &sqlparser.ColName{Name: col}, nil
		case *sqlparser.AliasedExpr:
			match := selExpr.As
			if match.IsEmpty() {
				if colExpr, ok := selExpr.Expr.(*sqlparser.ColName); ok {
					match = colExpr.Name
				} else {
					// Cannot match against a complex expression.
					continue
				}
			}
			if match.Equal(col) {
				colExpr, ok := selExpr.Expr.(*sqlparser.ColName)
				if !ok {
					return nil, fmt.Errorf("vindex column cannot be a complex expression: %v", sqlparser.String(selExpr))
				}
				return colExpr, nil
			}
		default:
			return nil, fmt.Errorf("unsupported select expression: %v", sqlparser.String(selExpr))
		}
	}
	return nil, fmt.Errorf("could not find vindex column %v", sqlparser.String(col))
}

func shouldInclude(table string, excludes []string) bool {
	// We filter out internal tables elsewhere when processing SchemaDefinition
	// structures built from the GetSchema database related API calls. In this
	// case, however, the table list comes from the user via the -tables flag
	// so we need to filter out internal table names here in case a user has
	// explicitly specified some.
	// This could happen if there's some automated tooling that creates the list of
	// tables to explicitly specify.

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. 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`).
  2. If a transformed value is needed, compute it downstream or use a generated column in the schema and select that column directly.
  3. Check your filter rules / -tables specification for expressions and simplify them to bare column names for vindex columns.

Example fix

// before
rule := "select id + 1 from t"
// error: vindex column cannot be a complex expression: id + 1
// after
rule := "select id from t"
Defensive patterns

Strategy: validation

Validate before calling

// ensure vindex columns are plain column refs in your workflow select
for _, sel := range rule.SelectExpressions() {
    if _, ok := sqlparser.String(sel); ok && !strings.Contains(sqlparser.String(sel), "(") {
        continue
    }
    return fmt.Errorf("select list must be plain columns: %s", sqlparser.String(sel))
}

Type guard

func isPlainColumn(e sqlparser.SelectExpr) bool {
    ae, ok := e.(*sqlparser.AliasedExpr)
    if !ok { return false }
    _, isCol := ae.Expr.(*sqlparser.ColName)
    return isCol
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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