From 9f4d76c184794bb033bdc7c9abd51909acfccd0c Mon Sep 17 00:00:00 2001 From: uncaught Date: Fri, 6 Sep 2024 14:29:18 +0200 Subject: [PATCH] Add comparison of foreign key names with option to opt-out of this (fixes #6518). --- src/Configuration.php | 19 ++++++++++++++++++ src/Platforms/MySQL/Comparator.php | 4 +++- src/Platforms/SQLServer/Comparator.php | 10 +++++++--- src/Platforms/SQLite/Comparator.php | 5 +++-- src/Schema/AbstractSchemaManager.php | 2 +- src/Schema/Comparator.php | 14 +++++++++++-- src/Schema/MySQLSchemaManager.php | 2 ++ src/Schema/SQLServerSchemaManager.php | 3 ++- src/Schema/SQLiteSchemaManager.php | 3 ++- tests/Functional/Schema/ComparatorTest.php | 3 ++- .../AbstractMySQLPlatformTestCase.php | 2 ++ tests/Platforms/AbstractPlatformTestCase.php | 3 ++- tests/Platforms/MySQL/ComparatorTest.php | 6 ++++-- .../MySQL/MariaDBJsonComparatorTest.php | 2 ++ tests/Platforms/SQLServer/ComparatorTest.php | 5 +++-- tests/Platforms/SQLServerPlatformTest.php | 3 ++- tests/Platforms/SQLite/ComparatorTest.php | 5 +++-- tests/Platforms/SQLitePlatformTest.php | 3 ++- tests/Schema/AbstractComparatorTestCase.php | 20 ++++++++++++++++++- tests/Schema/Platforms/MySQLSchemaTest.php | 2 ++ 20 files changed, 94 insertions(+), 22 deletions(-) diff --git a/src/Configuration.php b/src/Configuration.php index 9aa001db483..f0513ae6f73 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -36,6 +36,15 @@ class Configuration private ?SchemaManagerFactory $schemaManagerFactory = null; + /** + * Whether changes in the foreign key names should be compared. + * If you opt-out of this, you need to handle name changes of foreign keys yourself. + * Databases created based on the current schema might have different foreign key names + * than those migrated from older schemas if you turn this off. + * This could lead to incompatible migrations that try to drop non-existent foreign keys. + */ + private bool $compareForeignKeyNames = true; + public function __construct() { $this->schemaAssetsFilter = static function (): bool { @@ -153,4 +162,14 @@ public function setDisableTypeComments(bool $disableTypeComments): self return $this; } + + public function getCompareForeignKeyNames(): bool + { + return $this->compareForeignKeyNames; + } + + public function setCompareForeignKeyNames(bool $compareForeignKeyNames): void + { + $this->compareForeignKeyNames = $compareForeignKeyNames; + } } diff --git a/src/Platforms/MySQL/Comparator.php b/src/Platforms/MySQL/Comparator.php index ebe025dc2a9..50e1d380fcc 100644 --- a/src/Platforms/MySQL/Comparator.php +++ b/src/Platforms/MySQL/Comparator.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Platforms\MySQL; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Schema\Comparator as BaseComparator; use Doctrine\DBAL\Schema\Table; @@ -23,11 +24,12 @@ class Comparator extends BaseComparator /** @internal The comparator can be only instantiated by a schema manager. */ public function __construct( AbstractMySQLPlatform $platform, + Configuration $configuration, private readonly CharsetMetadataProvider $charsetMetadataProvider, private readonly CollationMetadataProvider $collationMetadataProvider, private readonly DefaultTableOptions $defaultTableOptions, ) { - parent::__construct($platform); + parent::__construct($platform, $configuration); } public function compareTables(Table $oldTable, Table $newTable): TableDiff diff --git a/src/Platforms/SQLServer/Comparator.php b/src/Platforms/SQLServer/Comparator.php index aa8d9fb5b62..dbb820fe5de 100644 --- a/src/Platforms/SQLServer/Comparator.php +++ b/src/Platforms/SQLServer/Comparator.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Platforms\SQLServer; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\DBAL\Schema\Comparator as BaseComparator; use Doctrine\DBAL\Schema\Table; @@ -17,9 +18,12 @@ class Comparator extends BaseComparator { /** @internal The comparator can be only instantiated by a schema manager. */ - public function __construct(SQLServerPlatform $platform, private readonly string $databaseCollation) - { - parent::__construct($platform); + public function __construct( + SQLServerPlatform $platform, + Configuration $configuration, + private readonly string $databaseCollation, + ) { + parent::__construct($platform, $configuration); } public function compareTables(Table $oldTable, Table $newTable): TableDiff diff --git a/src/Platforms/SQLite/Comparator.php b/src/Platforms/SQLite/Comparator.php index f27e1b4571c..05f9dfc106f 100644 --- a/src/Platforms/SQLite/Comparator.php +++ b/src/Platforms/SQLite/Comparator.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Platforms\SQLite; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Schema\Comparator as BaseComparator; use Doctrine\DBAL\Schema\Table; @@ -19,9 +20,9 @@ class Comparator extends BaseComparator { /** @internal The comparator can be only instantiated by a schema manager. */ - public function __construct(SQLitePlatform $platform) + public function __construct(SQLitePlatform $platform, Configuration $configuration) { - parent::__construct($platform); + parent::__construct($platform, $configuration); } public function compareTables(Table $oldTable, Table $newTable): TableDiff diff --git a/src/Schema/AbstractSchemaManager.php b/src/Schema/AbstractSchemaManager.php index 97307979a96..1c3cd562ec5 100644 --- a/src/Schema/AbstractSchemaManager.php +++ b/src/Schema/AbstractSchemaManager.php @@ -842,7 +842,7 @@ private function getDatabase(string $methodName): string public function createComparator(): Comparator { - return new Comparator($this->platform); + return new Comparator($this->platform, $this->connection->getConfiguration()); } /** diff --git a/src/Schema/Comparator.php b/src/Schema/Comparator.php index 240df8965dd..653030e7fc3 100644 --- a/src/Schema/Comparator.php +++ b/src/Schema/Comparator.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Schema; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Platforms\AbstractPlatform; use function array_map; @@ -17,8 +18,10 @@ class Comparator { /** @internal The comparator can be only instantiated by a schema manager. */ - public function __construct(private readonly AbstractPlatform $platform) - { + public function __construct( + private readonly AbstractPlatform $platform, + private readonly Configuration $configuration, + ) { } /** @@ -389,6 +392,13 @@ private function detectRenamedIndexes(array &$addedIndexes, array &$removedIndex protected function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2): bool { + if ( + $this->configuration->getCompareForeignKeyNames() + && strtolower($key1->getName()) !== strtolower($key2->getName()) + ) { + return true; + } + if ( array_map('strtolower', $key1->getUnquotedLocalColumns()) !== array_map('strtolower', $key2->getUnquotedLocalColumns()) diff --git a/src/Schema/MySQLSchemaManager.php b/src/Schema/MySQLSchemaManager.php index 1c0915905fa..bba725593c6 100644 --- a/src/Schema/MySQLSchemaManager.php +++ b/src/Schema/MySQLSchemaManager.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Schema; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Platforms\MariaDBPlatform; @@ -318,6 +319,7 @@ public function createComparator(): Comparator { return new MySQL\Comparator( $this->platform, + new Configuration(), new CachingCharsetMetadataProvider( new ConnectionCharsetMetadataProvider($this->connection), ), diff --git a/src/Schema/SQLServerSchemaManager.php b/src/Schema/SQLServerSchemaManager.php index e0a74ce2a81..51115d5dcc9 100644 --- a/src/Schema/SQLServerSchemaManager.php +++ b/src/Schema/SQLServerSchemaManager.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Schema; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\SQLServer; use Doctrine\DBAL\Platforms\SQLServerPlatform; @@ -264,7 +265,7 @@ protected function _getPortableViewDefinition(array $view): View /** @throws Exception */ public function createComparator(): Comparator { - return new SQLServer\Comparator($this->platform, $this->getDatabaseCollation()); + return new SQLServer\Comparator($this->platform, new Configuration(), $this->getDatabaseCollation()); } /** @throws Exception */ diff --git a/src/Schema/SQLiteSchemaManager.php b/src/Schema/SQLiteSchemaManager.php index 86f2cbe99aa..59dd3053d7c 100644 --- a/src/Schema/SQLiteSchemaManager.php +++ b/src/Schema/SQLiteSchemaManager.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Schema; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\SQLite; use Doctrine\DBAL\Platforms\SQLitePlatform; @@ -498,7 +499,7 @@ private function getForeignKeyDetails(string $table): array public function createComparator(): Comparator { - return new SQLite\Comparator($this->platform); + return new SQLite\Comparator($this->platform, new Configuration()); } protected function selectTableNames(string $databaseName): Result diff --git a/tests/Functional/Schema/ComparatorTest.php b/tests/Functional/Schema/ComparatorTest.php index da7244aad9c..0fecbcce43c 100644 --- a/tests/Functional/Schema/ComparatorTest.php +++ b/tests/Functional/Schema/ComparatorTest.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Tests\Functional\Schema; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Platforms\MariaDBPlatform; use Doctrine\DBAL\Schema\AbstractSchemaManager; @@ -53,7 +54,7 @@ public function testDefaultValueComparison(string $type, mixed $value): void public function testRenameColumnComparison(): void { $platform = $this->connection->getDatabasePlatform(); - $comparator = new Comparator($platform); + $comparator = new Comparator($platform, new Configuration()); $table = new Table('rename_table'); $table->addColumn('test', Types::STRING, ['default' => 'baz', 'length' => 20]); diff --git a/tests/Platforms/AbstractMySQLPlatformTestCase.php b/tests/Platforms/AbstractMySQLPlatformTestCase.php index 9c569cece8d..a357435b7d0 100644 --- a/tests/Platforms/AbstractMySQLPlatformTestCase.php +++ b/tests/Platforms/AbstractMySQLPlatformTestCase.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Tests\Platforms; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Exception\InvalidColumnDeclaration; use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Platforms\MySQL; @@ -689,6 +690,7 @@ protected function createComparator(): Comparator { return new MySQL\Comparator( $this->platform, + new Configuration(), self::createStub(CharsetMetadataProvider::class), self::createStub(CollationMetadataProvider::class), new DefaultTableOptions('utf8mb4', 'utf8mb4_general_ci'), diff --git a/tests/Platforms/AbstractPlatformTestCase.php b/tests/Platforms/AbstractPlatformTestCase.php index 67b9afdd9a8..81b1dfcbbbc 100644 --- a/tests/Platforms/AbstractPlatformTestCase.php +++ b/tests/Platforms/AbstractPlatformTestCase.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Tests\Platforms; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception\InvalidColumnDeclaration; use Doctrine\DBAL\Platforms\AbstractPlatform; @@ -40,7 +41,7 @@ protected function setUp(): void protected function createComparator(): Comparator { - return new Comparator($this->platform); + return new Comparator($this->platform, new Configuration()); } public function testQuoteIdentifier(): void diff --git a/tests/Platforms/MySQL/ComparatorTest.php b/tests/Platforms/MySQL/ComparatorTest.php index d2d8a25ad21..e8d48fc64e1 100644 --- a/tests/Platforms/MySQL/ComparatorTest.php +++ b/tests/Platforms/MySQL/ComparatorTest.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Tests\Platforms\MySQL; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Platforms\MySQL\CharsetMetadataProvider; use Doctrine\DBAL\Platforms\MySQL\CollationMetadataProvider; use Doctrine\DBAL\Platforms\MySQL\Comparator; @@ -13,10 +14,11 @@ class ComparatorTest extends AbstractComparatorTestCase { - protected function setUp(): void + protected function createComparator(?Configuration $configuration = null): Comparator { - $this->comparator = new Comparator( + return new Comparator( new MySQLPlatform(), + $configuration ?? new Configuration(), self::createStub(CharsetMetadataProvider::class), self::createStub(CollationMetadataProvider::class), new DefaultTableOptions('utf8mb4', 'utf8mb4_general_ci'), diff --git a/tests/Platforms/MySQL/MariaDBJsonComparatorTest.php b/tests/Platforms/MySQL/MariaDBJsonComparatorTest.php index ef9c99d24a4..18e0e5f2577 100644 --- a/tests/Platforms/MySQL/MariaDBJsonComparatorTest.php +++ b/tests/Platforms/MySQL/MariaDBJsonComparatorTest.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Tests\Platforms\MySQL; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Platforms\MariaDBPlatform; use Doctrine\DBAL\Platforms\MySQL\CharsetMetadataProvider; use Doctrine\DBAL\Platforms\MySQL\CollationMetadataProvider; @@ -27,6 +28,7 @@ protected function setUp(): void { $this->comparator = new Comparator( new MariaDBPlatform(), + new Configuration(), new class implements CharsetMetadataProvider { public function getDefaultCharsetCollation(string $charset): ?string { diff --git a/tests/Platforms/SQLServer/ComparatorTest.php b/tests/Platforms/SQLServer/ComparatorTest.php index d93bfcf021a..dd87885c7bb 100644 --- a/tests/Platforms/SQLServer/ComparatorTest.php +++ b/tests/Platforms/SQLServer/ComparatorTest.php @@ -4,14 +4,15 @@ namespace Doctrine\DBAL\Tests\Platforms\SQLServer; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Platforms\SQLServer\Comparator; use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\DBAL\Tests\Schema\AbstractComparatorTestCase; class ComparatorTest extends AbstractComparatorTestCase { - protected function setUp(): void + protected function createComparator(?Configuration $configuration = null): Comparator { - $this->comparator = new Comparator(new SQLServerPlatform(), ''); + return new Comparator(new SQLServerPlatform(), $configuration ?? new Configuration(), ''); } } diff --git a/tests/Platforms/SQLServerPlatformTest.php b/tests/Platforms/SQLServerPlatformTest.php index ac8337344df..3f8bb8af1fb 100644 --- a/tests/Platforms/SQLServerPlatformTest.php +++ b/tests/Platforms/SQLServerPlatformTest.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Tests\Platforms; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception\InvalidColumnDeclaration; use Doctrine\DBAL\LockMode; @@ -32,7 +33,7 @@ public function createPlatform(): AbstractPlatform protected function createComparator(): Comparator { - return new SQLServer\Comparator($this->platform, ''); + return new SQLServer\Comparator($this->platform, new Configuration(), ''); } public function getGenerateTableSql(): string diff --git a/tests/Platforms/SQLite/ComparatorTest.php b/tests/Platforms/SQLite/ComparatorTest.php index 4ed8911b91c..37584c88d5b 100644 --- a/tests/Platforms/SQLite/ComparatorTest.php +++ b/tests/Platforms/SQLite/ComparatorTest.php @@ -4,15 +4,16 @@ namespace Doctrine\DBAL\Tests\Platforms\SQLite; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Platforms\SQLite\Comparator; use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Tests\Schema\AbstractComparatorTestCase; class ComparatorTest extends AbstractComparatorTestCase { - protected function setUp(): void + protected function createComparator(?Configuration $configuration = null): Comparator { - $this->comparator = new Comparator(new SQLitePlatform()); + return new Comparator(new SQLitePlatform(), $configuration ?? new Configuration()); } public function testCompareChangedBinaryColumn(): void diff --git a/tests/Platforms/SQLitePlatformTest.php b/tests/Platforms/SQLitePlatformTest.php index 5d400c37eae..e013196115a 100644 --- a/tests/Platforms/SQLitePlatformTest.php +++ b/tests/Platforms/SQLitePlatformTest.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Tests\Platforms; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\SQLite; @@ -30,7 +31,7 @@ public function createPlatform(): AbstractPlatform protected function createComparator(): Comparator { - return new SQLite\Comparator($this->platform); + return new SQLite\Comparator($this->platform, new Configuration()); } public function getGenerateTableSql(): string diff --git a/tests/Schema/AbstractComparatorTestCase.php b/tests/Schema/AbstractComparatorTestCase.php index 759b6740f13..8f887d083ef 100644 --- a/tests/Schema/AbstractComparatorTestCase.php +++ b/tests/Schema/AbstractComparatorTestCase.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Tests\Schema; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Schema\AbstractAsset; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\ColumnDiff; @@ -29,6 +30,13 @@ abstract class AbstractComparatorTestCase extends TestCase { protected Comparator $comparator; + protected function setUp(): void + { + $this->comparator = $this->createComparator(); + } + + abstract protected function createComparator(?Configuration $configuration = null): Comparator; + public function testCompareSame1(): void { $schema1 = new Schema([ @@ -375,10 +383,20 @@ public function testCompareForeignKeyBasedOnPropertiesNotName(): void $tableB->addColumn('ID', Types::INTEGER); $tableB->addForeignKeyConstraint('bar', ['id'], ['id'], [], 'bar_constraint'); + $fkA = new ForeignKeyConstraint(['id'], 'bar', ['id'], 'foo_constraint'); + $fkB = new ForeignKeyConstraint(['id'], 'bar', ['id'], 'bar_constraint'); self::assertEquals( - new TableDiff($tableA), + new TableDiff(oldTable: $tableA, addedForeignKeys: [$fkB], droppedForeignKeys: [$fkA]), $this->comparator->compareTables($tableA, $tableB), ); + + $configWithDisabledNames = new Configuration(); + $configWithDisabledNames->setCompareForeignKeyNames(false); + $comparatorDisabledNameComparison = $this->createComparator($configWithDisabledNames); + self::assertEquals( + new TableDiff($tableA), + $comparatorDisabledNameComparison->compareTables($tableA, $tableB), + ); } public function testDetectRenameColumn(): void diff --git a/tests/Schema/Platforms/MySQLSchemaTest.php b/tests/Schema/Platforms/MySQLSchemaTest.php index fee785069e2..3dee0baa2bb 100644 --- a/tests/Schema/Platforms/MySQLSchemaTest.php +++ b/tests/Schema/Platforms/MySQLSchemaTest.php @@ -4,6 +4,7 @@ namespace Doctrine\DBAL\Tests\Schema\Platforms; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\MySQL; use Doctrine\DBAL\Platforms\MySQL\CharsetMetadataProvider; @@ -68,6 +69,7 @@ private function createComparator(): Comparator { return new MySQL\Comparator( new MySQLPlatform(), + new Configuration(), self::createStub(CharsetMetadataProvider::class), self::createStub(CollationMetadataProvider::class), new DefaultTableOptions('utf8mb4', 'utf8mb4_general_ci'),