vitessio/vitess · error
unsupported construct: %s
Error message
unsupported construct: %s
What it means
buildControllerPlan parses a SQL statement issued against the vreplication engine and only supports Update, Insert, Delete and Select statements. Any other construct (DDL, SET, transactions, SHOW, etc.) hits the default branch and returns `unsupported construct: <SQL>`.
Source
Thrown at go/vt/vttablet/tabletmanager/vreplication/controller_plan.go:157
// buildControllerPlan parses the input query and returns an appropriate plan.
func buildControllerPlan(query string, parser *sqlparser.Parser) (*controllerPlan, error) {
stmt, err := parser.Parse(query)
if err != nil {
return nil, err
}
var plan *controllerPlan
switch stmt := stmt.(type) {
case *sqlparser.Insert:
plan, err = buildInsertPlan(stmt)
case *sqlparser.Update:
plan, err = buildUpdatePlan(stmt)
case *sqlparser.Delete:
plan, err = buildDeletePlan(stmt)
case *sqlparser.Select:
plan, err = buildSelectPlan(stmt)
default:
return nil, fmt.Errorf("unsupported construct: %s", sqlparser.String(stmt))
}
if err != nil {
return nil, err
}
plan.query = query
return plan, nil
}
func buildInsertPlan(ins *sqlparser.Insert) (*controllerPlan, error) {
// This should never happen.
if ins == nil {
return nil, errors.New("BUG: invalid nil INSERT statement found when building VReplication plan")
}
tableName, err := ins.Table.TableName()
if err != nil {
return nil, err
}
if tableName.Qualifier.String() != sidecar.GetName() && tableName.Qualifier.String() != sidecar.DefaultName {View on GitHub (pinned to 01a25a7d17)
Solutions
- Rewrite the statement as one of UPDATE/INSERT/DELETE/SELECT against _vt.vreplication or _vt.resharding_journal.
- Use vtctldclient workflow commands instead of raw DDL to mutate vreplication state.
- For DDL on the sidecar table, run it directly on the tablet's MySQL, not through the vreplication engine.
- If a legitimate new statement type should be supported, file/see an upstream issue to add a plan builder branch.
Example fix
// before
exec("ALTER TABLE _vt.vreplication ADD COLUMN foo INT")
// after
// run DDL directly on tablet MySQL, or use:
exec("update _vt.vreplication set state='Stopped' where id=1") Defensive patterns
Strategy: validation
Validate before calling
// Only send DML to the vreplication engine:
stmt := strings.TrimSpace(query)
lower := strings.ToLower(stmt)
if !(strings.HasPrefix(lower, "select") || strings.HasPrefix(lower, "insert") ||
strings.HasPrefix(lower, "update") || strings.HasPrefix(lower, "delete")) {
return fmt.Errorf("vexec only supports SELECT/INSERT/UPDATE/DELETE, got: %s", stmt)
} Try / catch
plan, err := buildControllerPlan(stmt, query, sidecar)
if err != nil {
if strings.Contains(err.Error(), "unsupported construct") {
// fall back to running the statement directly on tablet MySQL
}
return err
} Prevention
- Use vtctldclient workflow commands instead of raw DDL/VExec for stream management.
- Restrict scripts to SELECT/INSERT/UPDATE/DELETE against _vt.vreplication.
- Never send DDL, SET, or transactional statements through the vreplication engine.
When it happens
Trigger: Executing a non-DML statement through the vreplication engine's exec path (the code that runs _vt.vreplication queries), e.g. `ALTER TABLE _vt.vreplication ...`, `SET ...`, `BEGIN`, or `TRUNCATE` passed to vexec / UpdateVReplicationWorkflow style APIs.
Common situations: Operators running ad-hoc SQL on _vt.vreplication via vtctldclient VExec with DDL; tooling or scripts sending unsupported statements; newer parser statement types (e.g. source-specific syntax) not mapped to a plan builder.
Related errors
- unsupported construct: %v
- ErrUnsupportedQuery
- ErrUnsupportedQueryConstruct
- ErrCannotUpdateImmutableColumn
- invalid database name: %s
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/60bce17089e1d98c.
Report an issue: GitHub.