{"record":{"id":"a040d4a1b255604d","repo":"vitessio/vitess","slug":"unsupported-query-construct","errorCode":null,"errorMessage":"unsupported query construct","messagePattern":"unsupported query construct","errorType":"error_code","errorClass":"ErrUnsupportedQueryConstruct","httpStatus":null,"severity":"error","filePath":"go/vt/vtctl/workflow/vexec/query_planner.go","lineNumber":40,"sourceCode":"\t\"strconv\"\n\n\t\"vitess.io/vitess/go/vt/sqlparser\"\n\t\"vitess.io/vitess/go/vt/vttablet/tmclient\"\n)\n\nvar ( // Query planning errors.\n\t// ErrCannotUpdateImmutableColumn is returned when attempting to plan a\n\t// query that updates a column that should be treated as immutable.\n\tErrCannotUpdateImmutableColumn = errors.New(\"cannot update immutable column\")\n\t// ErrUnsupportedQueryConstruct is returned when a particular query\n\t// construct is unsupported by a QueryPlanner, despite the more general kind\n\t// of query being supported.\n\t//\n\t// For example, VReplication supports DELETEs, but does not support DELETEs\n\t// with LIMIT clauses, so planning a \"DELETE ... LIMIT\" will return\n\t// ErrUnsupportedQueryConstruct rather than a \"CREATE TABLE\", which would\n\t// return an ErrUnsupportedQuery.\n\tErrUnsupportedQueryConstruct = errors.New(\"unsupported query construct\")\n)\n\n// Query execution errors.\n// ErrUnpreparedQuery is returned when attempting to execute an unprepared\n// QueryPlan.\nvar ErrUnpreparedQuery = errors.New(\"attempted to execute unprepared query\")\n\n// QueryPlanner defines the interface that VExec uses to build QueryPlans for\n// various vexec workflows. A given vexec table, which is to say a table in the\n// \"_vt\" database, will have at most one QueryPlanner implementation, which is\n// responsible for defining both what queries are supported for that table, as\n// well as how to build plans for those queries.\n//\n// VReplicationQueryPlanner is a good example implementation to refer to.\ntype QueryPlanner interface {\n\t// (NOTE:@ajm188) I don't think this method fits on the query planner. To\n\t// me, especially given that it's only implemented by the vrep query planner\n\t// in the old implementation (the schema migration query planner no-ops this","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtctl/workflow/vexec/query_planner.go#L22-L58","documentation":"ErrUnsupportedQueryConstruct is returned when a query is of a kind the planner could handle in principle, but includes a construct VReplication vexec does not implement — e.g. a DELETE with a LIMIT clause. It is deliberately distinct from ErrUnsupportedQuery (whole query kind unsupported, like CREATE TABLE).","triggerScenarios":"planDelete or planUpdate encounters a syntax node such as a LIMIT/OORDER on a DELETE/UPDATE being planned for a vexec workflow table.","commonSituations":"Operators adding LIMIT to cleanup DELETEs on _vt.vreplication to batch deletion, or generic SQL-migration tooling emitting ORDER BY/LIMIT in maintenance UPDATEs.","solutions":["Remove the LIMIT/ORDER BY clause and run the statement without it (chunk manually by primary key ranges if batching is needed)","Delete rows in batches using WHERE id < X predicates instead of LIMIT"],"exampleFix":"// before\nDELETE FROM _vt.vreplication WHERE workflow = 'sales' LIMIT 100\n// ErrUnsupportedQueryConstruct\n// after\nDELETE FROM _vt.vreplication WHERE workflow = 'sales' AND id <= 1000","handlingStrategy":"type-guard","validationCode":"switch stmt := parsed.(type) {\ncase *sqlparser.Delete:\n    if stmt.Limit != nil {\n        return ErrUnsupportedQueryConstruct\n    }\n}","typeGuard":"func hasLimit(n sqlparser.SQLNode) bool {\n    d, ok := n.(*sqlparser.Delete)\n    return ok && d.Limit != nil\n}","tryCatchPattern":"plan, err := planner.PlanDelete(stmt)\nif errors.Is(err, workflow.ErrUnsupportedQueryConstruct) {\n    return fmt.Errorf(\"re-run query without LIMIT/ORDER BY: %w\", err)\n}","preventionTips":["Avoid LIMIT and ORDER BY in vexec DELETE/UPDATE statements","Batch deletions with WHERE key-range predicates instead of LIMIT","Check the QueryPlanner docs for supported constructs before writing maintenance SQL"],"tags":["vreplication","vexec","query-planner"],"backgroundTag":"unsupported-query-construct","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}