{"record":{"id":"151d8dc8206e5a5a","repo":"vitessio/vitess","slug":"vt03032","errorCode":"VT03032","errorMessage":"the target table %s of the UPDATE is not updatable","messagePattern":"the target table (.+?) of the UPDATE is not updatable","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/planbuilder/operators/update.go","lineNumber":310,"sourceCode":"\t\tvTbl:    vTbl,\n\t\tcols:    cols,\n\t\tupdList: uList,\n\t}\n}\n\nfunc errIfUpdateNotSupported(ctx *plancontext.PlanningContext, stmt *sqlparser.Update) {\n\tfor _, ue := range stmt.Exprs {\n\t\ttblInfo, err := ctx.SemTable.TableInfoForExpr(ue.Name)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tif _, isATable := tblInfo.(*semantics.RealTable); !isATable {\n\t\t\tvar tblName string\n\t\t\tate := tblInfo.GetAliasedTableExpr()\n\t\t\tif ate != nil {\n\t\t\t\ttblName = sqlparser.String(ate)\n\t\t\t}\n\t\t\tpanic(vterrors.VT03032(tblName))\n\t\t}\n\t}\n\n\t// Now we check if any of the foreign key columns that are being updated have dependencies on other updated columns.\n\t// This is unsafe, and we currently don't support this in Vitess.\n\tif err := ctx.SemTable.ErrIfFkDependentColumnUpdated(stmt.Exprs); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc createUpdateOperator(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update) (Operator, TargetTable, *sqlparser.Update) {\n\top := crossJoin(ctx, updStmt.TableExprs)\n\n\tsqc := &SubQueryBuilder{}\n\tif updStmt.Where != nil {\n\t\top = addWherePredsToSubQueryBuilder(ctx, updStmt.Where.Expr, op, sqc)\n\t}\n","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/planbuilder/operators/update.go#L292-L328","documentation":"VT03032: the planner rejects UPDATE statements whose target is not a RealTable (e.g. a derived table, subquery, or view Vitess cannot update). MySQL's own error for non-updatable targets is mirrored here — the UPDATE names something that is not a base table.","triggerScenarios":"UPDATE with SET expressions resolved to a table info that is not *semantics.RealTable — updating a derived table alias, an inline view, or certain routed/virtual tables in errIfUpdateNotSupported.","commonSituations":"Porting MySQL applications that update derived tables; updating a view that Vitess cannot merge; updating a table through an unsupported routing construct.","solutions":["Rewrite the UPDATE to target the underlying base table directly.","If updating a view, update the base tables the view selects from instead.","Push the statement to MySQL directly if it must update a derived construct (e.g. via a passthrough route).","Check the vschema so the intended table is defined as a routable real table."],"exampleFix":"// before\nUPDATE (SELECT * FROM users WHERE id = 1) AS u SET name = 'x';\n// after\nUPDATE users SET name = 'x' WHERE id = 1;","handlingStrategy":"validation","validationCode":"// Only pass UPDATEs whose target is a base table\nif strings.Contains(strings.ToLower(query), \"update (select\") {\n  return errors.New(\"cannot update a derived table through Vitess\")\n}","typeGuard":null,"tryCatchPattern":"res, err := vtgate.Execute(ctx, session, query, vars)\nif err != nil && strings.Contains(err.Error(), \"not updatable\") {\n  // rewrite to update the base table and retry once\n  query = rewriteToBaseTable(query)\n  res, err = vtgate.Execute(ctx, session, query, vars)\n}","preventionTips":["Never issue UPDATE against subqueries or derived tables","Update views only through their base tables","Run queries through EXPLAIN to catch unsupported targets early"],"tags":["vitess","vt03032","update","not-updatable"],"backgroundTag":"target-table-not-updatable","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}