{"record":{"id":"d0206951b4154899","repo":"vitessio/vitess","slug":"vt12001-d02069","errorCode":"VT12001","errorMessage":"'%s' column referenced in update expression '%s' is itself updated","messagePattern":"'(.+?)' column referenced in update expression '(.+?)' is itself updated","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/planbuilder/operators/update.go","lineNumber":233,"sourceCode":"\t\tjc := breakExpressionInLHSandRHS(ctx, ue.Expr, exprDeps.Remove(target))\n\t\tueMap[target] = append(ueMap[target], updColumn{ue.Name, jc})\n\t}\n\n\t// Check if any of the dependent columns are updated in the same query.\n\t// This can result in a mismatch of rows on how MySQL interprets it and how Vitess would have updated those rows.\n\t// It is safe to fail for those cases.\n\terrIfDependentColumnUpdated(ctx, upd, ueMap)\n\n\treturn ueMap\n}\n\nfunc errIfDependentColumnUpdated(ctx *plancontext.PlanningContext, upd *sqlparser.Update, ueMap map[semantics.TableSet]updList) {\n\tfor _, ue := range upd.Exprs {\n\t\tfor _, list := range ueMap {\n\t\t\tfor _, dc := range list {\n\t\t\t\tfor _, bvExpr := range dc.jc.LHSExprs {\n\t\t\t\t\tif ctx.SemTable.EqualsExprWithDeps(ue.Name, bvExpr.Expr) {\n\t\t\t\t\t\tpanic(vterrors.VT12001(\n\t\t\t\t\t\t\tfmt.Sprintf(\"'%s' column referenced in update expression '%s' is itself updated\", sqlparser.String(ue.Name), sqlparser.String(dc.jc.Original))))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc createUpdateOpWithTarget(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update, target semantics.TableSet, uList updList) dmlOp {\n\tif len(uList) == 0 {\n\t\tpanic(vterrors.VT13001(\"no update expression for the target\"))\n\t}\n\n\tti, err := ctx.SemTable.TableInfoFor(target)\n\tif err != nil {\n\t\tpanic(vterrors.VT13001(err.Error()))\n\t}\n\tvTbl := ti.GetVindexTable()","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/planbuilder/operators/update.go#L215-L251","documentation":"VT12001 \"'column' referenced in update expression 'expr' is itself updated\" is panicked by errIfDependentColumnUpdated (go/vt/vtgate/planbuilder/operators/update.go:233), invoked from prepareUpdateExpressionList. For a multi-table UPDATE, each update expression's join-column dependencies are checked; if an expression assigned in the SET clause references (per ctx.SemTable.EqualsExprWithDeps) a column that the same update also modifies, the statement is rejected — MySQL semantics forbid reading a column being updated in the same statement. Vitess turns this into a planning-time error instead of producing a wrong plan.","triggerScenarios":"A multi-table UPDATE like UPDATE t1 JOIN t2 ... SET t1.a = t2.b WHERE ... where t2.b (the join/read column) is itself assigned elsewhere in the same UPDATE's Exprs; the check finds ue.Name equal-by-deps to a bvExpr in dc.jc.LHSExprs of an updated dependent column.","commonSituations":"Bulk updates written as self-referencing joins (SET t.x = t.y while t.y is also SET); migrations rewriting key columns that participate in the join condition; users porting application logic that assumed single-pass semantics.","solutions":["Restructure the UPDATE so the SET expressions do not read columns assigned in the same statement — compute the needed values first (e.g. into a temp table or derived table)","Split the UPDATE into two sequential statements: one to materialize dependent values, one to apply them","If the reference is unintentional, rename/qualify columns so the SET expression reads the pre-update source column from the other table only"],"exampleFix":"-- before (b is both read and written)\nUPDATE t1 JOIN t2 ON t1.id = t2.id SET t1.a = t2.b, t2.b = t2.b + 1;\n-- after (two statements)\nUPDATE t1 JOIN t2 ON t1.id = t2.id SET t1.a = t2.b;\nUPDATE t2 SET b = b + 1;","handlingStrategy":"validation","validationCode":"-- verify no SET expression reads a column assigned in the same multi-table UPDATE\nSELECT column_name FROM information_schema.columns\nWHERE table_name = ? AND column_name IN (columns_read_by_set_expressions);","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"is itself updated\") {\n    return splitIntoSequentialUpdates(ctx, stmt)\n}","preventionTips":["Never SET a column and read that same column in another SET expression of the same UPDATE","Split multi-table updates that feed values between tables into sequential statements","Use a temp table or CTE to materialize values before updating"],"tags":["vitess","planbuilder","update","multi-table","validation"],"backgroundTag":"update-reads-own-written-column","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}