Disable FK checks during Migrator::dropTables#1074
Closed
dereuromark wants to merge 1 commit into5.xfrom
Closed
Conversation
The test migrator calls ConnectionHelper::dropTables() which drops each
table's outgoing FK constraints and then the tables themselves. With
complex schemas that have many cross-referencing FKs, this can still
fail on MySQL with error 3730 ("Cannot drop table X referenced by FK on
Y") because the drop order doesn't always satisfy MySQL's checks.
Wrap the drop call in $connection->disableConstraints() so FK checks are
suspended for the duration of the bulk drop, matching what projects
routinely do by hand in their bootstrap. No behaviour change on SQLite or
Postgres; MySQL gets robust regardless of FK topology.
Adds a MySQL-only regression test that sets up three cross-referenced
tables and calls dropTables directly.
Contributor
|
I usually just drop the whole database as i dont need to keep other non related tables. |
Member
Author
|
Thats usually much slower I imagine. Current workaround is quite ugly: $migrationSets = [
[],
['plugin' => 'Queue'],
['plugin' => 'DatabaseLog'],
['plugin' => 'Tools'],
];
// The upstream test migrator still only treats legacy `phinxlog` tables as
// migration metadata when it decides to rebuild. In unified-mode setups like
// this app, partial runs can leave real tables and the `cake_migrations` ledger
// out of sync in either direction. Reusing that state makes bootstrap flaky.
//
// Reset the shared `db_test` schema unconditionally and replay migrations from
// scratch. This is slower, but deterministic, and the repo already documents
// that PHPUnit here must run serially through ddev.
$connection = ConnectionManager::get('test');
$tables = $connection->getSchemaCollection()->listTables();
$dropTables = array_values(array_filter($tables, static function (string $table): bool {
return $table !== 'cake_migrations' && !str_contains($table, 'phinxlog');
}));
if ($dropTables !== []) {
$connection->disableConstraints(function ($connection) use ($dropTables): void {
foreach ($dropTables as $table) {
$quotedTable = '`' . str_replace('`', '``', $table) . '`';
$connection->execute("DROP TABLE IF EXISTS {$quotedTable}");
}
});
}
if (in_array('cake_migrations', $tables, true)) {
ConnectionHelper::truncateTables('test', ['cake_migrations']);
}
(new Migrator())->runMany($migrationSets); |
Member
Author
|
Switching to transaction :) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On MySQL,
Migrator::dropTables()(called during test-suite rebuilds whenshouldDropTables()returns true) can fail with error 3730:ConnectionHelper::dropTables()drops each table's outgoing FKs first and then drops the tables. With complex schemas (many cross-referencing FKs across plugins, unified-mode ledgers, and migrations that add/drop FKs in non-trivial patterns) the drop pass can still hit an ordering MySQL refuses.This showed up in a project with ~80 migrations and ~15 FK-linked tables: after clearing the ledger to force a rebuild,
Migrator::dropTablesbombed onDROP TABLE bank_transactionsbecause a FK onrent_paymentspointing at it hadn't been fully resolved by the drop-constraints pass.Projects work around this by dropping tables by hand inside
$connection->disableConstraints(...)in theirtests/bootstrap.php— exactly what this PR moves upstream.Fix
Wrap
$this->helper->dropTables(...)in$connection->disableConstraints(...)so MySQL doesn't enforce FK checks during the bulk drop. Drop order becomes irrelevant.No behaviour change on SQLite or Postgres — they handle the drop order without help.
Test
Adds a MySQL-only regression test (
testDropTablesHandlesCrossTableForeignKeys) that creates three tables with cascading FK references and callsdropTablesdirectly. Skipped on SQLite/Postgres because they don't block on this pattern.Notes
Tagged as draft while folks sanity-check the approach and the minimal test. Happy to iterate on naming / add more coverage if wanted.