{"record":{"id":"728780614abb721c","repo":"vitessio/vitess","slug":"foreign-key-found","errorCode":null,"errorMessage":"Foreign key found","messagePattern":"Foreign key found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/schema/online_ddl.go","lineNumber":55,"sourceCode":"\tptOSCGeneratedTableNameRegexp     = regexp.MustCompile(`^_.*_old$`)\n\tmigrationContextValidatorRegexp   = regexp.MustCompile(`^[\\w:-]*$`)\n)\n\nvar onlineDDLInternalTableHintsMap = map[string]bool{\n\t\"vrp\": true, // vreplication\n\t\"gho\": true, // gh-ost\n\t\"ghc\": true, // gh-ost\n\t\"del\": true, // gh-ost\n\t\"new\": true, // pt-osc\n}\n\nvar (\n\t// ErrDirectDDLDisabled is returned when direct DDL is disabled, and a user attempts to run a DDL statement\n\tErrDirectDDLDisabled = errors.New(\"direct DDL is disabled\")\n\t// ErrOnlineDDLDisabled is returned when online DDL is disabled, and a user attempts to run an online DDL operation (submit, review, control)\n\tErrOnlineDDLDisabled = errors.New(\"online DDL is disabled\")\n\t// ErrForeignKeyFound indicates any finding of FOREIGN KEY clause in a DDL statement\n\tErrForeignKeyFound = errors.New(\"Foreign key found\")\n\t// ErrRenameTableFound indicates finding of ALTER TABLE...RENAME in ddl statement\n\tErrRenameTableFound = errors.New(\"RENAME clause found\")\n)\n\nconst (\n\tSchemaMigrationsTableName = \"schema_migrations\"\n\tRevertActionStr           = \"revert\"\n)\n\n// ValidateMigrationContext validates that the given migration context only uses valid characters\nfunc ValidateMigrationContext(migrationContext string) error {\n\tif migrationContextValidatorRegexp.MatchString(migrationContext) {\n\t\treturn nil\n\t}\n\treturn vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, \"invalid characters in migration_context %v. Use alphanumeric, dash, underscore and colon only\", migrationContext)\n}\n\n// when validateWalk returns true, then the child nodes are also visited","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/schema/online_ddl.go#L37-L73","documentation":"ErrForeignKeyFound is a sentinel error indicating a FOREIGN KEY clause was found in a DDL statement during online-DDL validation (validateWalk / onlineDDLStatementSanity). Online migration tools (gh-ost/pt-osc) cannot safely handle foreign keys, so statements containing them are rejected up front.","triggerScenarios":"Submitting an ALTER TABLE ... ADD/DROP FOREIGN KEY (or CREATE TABLE with FK) through the online-DDL path; the AST walk encounters a ForeignKey definition and returns ErrForeignKeyFound.","commonSituations":"Schemas relying on referential integrity being changed via gh-ost; users unaware of the online-DDL foreign-key limitation; generated migrations (ORM autogen) including FK constraints.","solutions":["Remove the FOREIGN KEY clause and manage constraints outside online DDL (apply that specific statement with ddl_strategy=direct if allowed)","Drop the FK separately with a direct DDL statement, then run the main ALTER online","If FKs are essential to the migration, avoid online-DDL strategy for this statement","Detect errors.Is(err, schema.ErrForeignKeyFound) in tooling to split the migration"],"exampleFix":"-- before\nALTER /*vt+ ddl_strategy=gh-ost */ TABLE t ADD CONSTRAINT fk FOREIGN KEY (a) REFERENCES p(id);\n// after: split statements\nALTER /*vt+ ddl_strategy=direct */ TABLE t ADD CONSTRAINT fk FOREIGN KEY (a) REFERENCES p(id);\nALTER /*vt+ ddl_strategy=gh-ost */ TABLE t ADD COLUMN c INT;","handlingStrategy":"validation","validationCode":"ast, _ := sqlparser.Parse(stmt)\nfound := false\nsqlparser.Walk(func(node sqlparser.SQLNode) (bool) { return true }, ast) // use validateWalk equivalent\nif containsForeignKeyClause(ast) {\n    return fmt.Errorf(\"split FK change out of online DDL statement\")\n}","typeGuard":null,"tryCatchPattern":"_, err := tm.TryExecute(ctx, stmt)\nif errors.Is(err, schema.ErrForeignKeyFound) {\n    return fmt.Errorf(\"rewrite %s without FOREIGN KEY or use direct DDL for it\", stmt)\n}","preventionTips":["Audit schemas for FKs before enabling online DDL workflows","Generate migrations that separate constraint changes from column/table changes","Prefer dropping FKs if the app enforces integrity, simplifying online DDL","Lint migration files for FOREIGN KEY clauses targeting online strategy"],"tags":["ddl","online-ddl","foreign-key","mysql"],"backgroundTag":"ddl-foreign-key-unsupported","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}