vitessio/vitess · error

VT12002

VT12002

Error message

VT12002(sqlparser.String(tblName), fk.Table.String())

What it means

VT12002 (unsupported: cross-shard foreign key constraint) is raised when a DELETE on a parent table has a child foreign key with ON DELETE RESTRICT that Vitess must emulate itself — specifically the cross-shard/cross-keyspace RESTRICT cases that reach createFkCascadeOpForDelete. Since RESTRICT semantics (reject if children exist) cannot be enforced efficiently across shards in this code path, the planner rejects the delete.

Source

Thrown at go/vt/vtgate/planbuilder/operators/delete.go:351

			tbl.Table, _ = tbl.Alias.TableName()
		}
		return op, Rewrote("change query table point to source table")
	}, func(operator Operator) VisitRule {
		_, ok := operator.(*QueryGraph)
		return VisitRule(ok)
	})
	return vTbl
}

func createFkCascadeOpForDelete(ctx *plancontext.PlanningContext, parentOp Operator, delStmt *sqlparser.Delete, childFks []vindexes.ChildFKInfo, deletedTbl *vindexes.BaseTable) Operator {
	fkChildren := make([]*FkChild, 0, len(childFks))
	var selectExprs []sqlparser.SelectExpr
	tblName := delStmt.Targets[0]
	for _, fk := range childFks {
		// Any RESTRICT type foreign keys that arrive here,
		// are cross-shard/cross-keyspace RESTRICT cases, which we don't currently support.
		if fk.OnDelete.IsRestrict() {
			panic(vterrors.VT12002(sqlparser.String(tblName), fk.Table.String()))
		}

		// We need to select all the parent columns for the foreign key constraint, to use in the update of the child table.
		var offsets []int
		offsets, selectExprs = addColumns(ctx, fk.ParentColumns, selectExprs, tblName)

		fkChildren = append(fkChildren,
			createFkChildForDelete(ctx, fk, offsets))
	}
	selectionOp := createSelectionOp(ctx, selectExprs, delStmt.TableExprs, delStmt.Where, nil, nil, getUpdateLock(deletedTbl))

	return &FkCascade{
		Selection: selectionOp,
		Children:  fkChildren,
		Parent:    parentOp,
	}
}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Change the FK to ON DELETE CASCADE or ON DELETE SET NULL if business logic allows, so Vitess can emulate it.
  2. Enforce the delete guard in application logic: check for and delete/NULL child rows before deleting the parent.
  3. Co-locate parent and child tables in the same keyspace/shard (via VReplication move or redesign) so MySQL can enforce RESTRICT natively.
  4. Set foreign_key_mode appropriately (e.g. `disallow` to reject cross-shard FK DDL up front) so the situation is surfaced during migration rather than at delete time.

Example fix

// before
CREATE TABLE child (id INT, parent_id INT, FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE RESTRICT);

// after
CREATE TABLE child (id INT, parent_id INT, FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE);
Defensive patterns

Strategy: validation

Validate before calling

-- detect unsupported RESTRICT child FKs on a parent table before issuing DELETE
SELECT child.table_name AS child_table
FROM information_schema.referential_constraints rc
JOIN information_schema.key_column_usage kcu ON kcu.constraint_name = rc.constraint_name
WHERE rc.unique_constraint_schema = ? AND rc.referenced_table_name = ?
  AND rc.delete_rule = 'RESTRICT';

Type guard

func fkDeleteIsSupported(fk vindexes.ChildFKInfo) bool {
    return !fk.OnDelete.IsRestrict()
}

Try / catch

if err := execDelete(sql); err != nil {
    if strings.Contains(err.Error(), "VT12002") {
        return fmt.Errorf("cross-shard RESTRICT FK blocks this DELETE; delete child rows first or use CASCADE: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: DELETE from a parent table that has a child FK with `ON DELETE RESTRICT` where parent and child are in different keyspaces or the FK is managed by Vitess (managed foreign key mode) rather than pushed down to MySQL.

Common situations: Applications with legacy schemas using ON DELETE RESTRICT migrated to sharded Vitess; cross-keyspace FK relationships; enabling `--foreign_key_mode=managed`/`unmanaged` boundaries where RESTRICT cannot be honored sharded.

Related errors


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