-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Platform-aware schema comparison #4746
Conversation
ff2f807
to
5be15a1
Compare
8fe95db
to
eb000ad
Compare
$diff = (new Comparator())->diffTable($table, $diffTable); | ||
self::assertNotFalse($diff); | ||
|
||
self::assertEmpty($this->platform->getAlterTableSQL($diff)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two assertions cover the bug in the current implementation rather than the expected behavior. The original test and assertions read as follows:
- Make some schema changes that don't change the resulting SQL.
- Assert that the diff is not empty. This is the bug being fixed.
- Assert that even though the diff is not empty, the resulting diff SQL is. This is a workaround of the bug.
924f0ab
to
489d210
Compare
$diff = (new Comparator())->diffTable($table1, $table2); | ||
self::assertNotFalse($diff); | ||
self::assertEmpty($this->platform->getAlterTableSQL($diff)); | ||
self::assertFalse((new Comparator($this->platform))->diffTable($table1, $table2)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above, the existing logic covers a bug, not the expected behavior.
489d210
to
badc300
Compare
src/Schema/Comparator.php
Outdated
Schema $toSchema, | ||
?AbstractPlatform $platform = null | ||
) { | ||
$comparator = new self($platform); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this still work, now that we have Comparator
implementations for the individual drivers? This constructor call will yield a generic Comparator
and not the implementation specific to the passed platform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. The generic comparator supplied with a platform can cause false-positive diffs. It shouldn't be used this way. I'll also adjust the deprecation message in the comparator constructor accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rolled this back and deprecated Schema::getMigrateFromSql()
and ::getMigrateToSql()
that instantiate a comparator internally. Introducing a dependency of Schema
on SchemaManager
would be incorrect, and they are just convenience methods that can be replaced with their implementation.
/** @var AbstractPlatform */ | ||
private $platform; | ||
|
||
/** @var AbstractSchemaManager */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** @var AbstractSchemaManager */ | |
/** @var AbstractSchemaManager<MySQLPlatform> */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will require Connection::createSchemaManager()
and Driver::createSchemaManager()
to be parametrized which will lead to the same issues as in #4746 (comment).
|
||
protected function setUp(): void | ||
{ | ||
$this->comparator = new Comparator(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have deprecated not passing a platform to the constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it still has to be covered with a test until the deprecated code is removed.
@@ -42,7 +51,7 @@ public function testCompareSame1(): void | |||
|
|||
$expected = new SchemaDiff(); | |||
$expected->fromSchema = $schema1; | |||
self::assertEquals($expected, Comparator::compareSchemas($schema1, $schema2)); | |||
self::assertEquals($expected, $this->comparator->compareSchemas($schema1, $schema2)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this change? The method is still static, isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is but now the comparator class is extended by platform-specific classes and I want to reuse the same test case to test all implementations. See the classes extending this one.
7ed68e7
to
26f64f4
Compare
26f64f4
to
8374a83
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, looks like this is going to be a huge improvement given the number of linked issues 🎉
8374a83
to
1809a91
Compare
The remaining four linked issues are indeed fixed by this PR (the details are in the comments to each issue). I unlinked two since they appear to be unrelated and need to be addressed separately. One more is not reproducible even on |
This PR was merged into the 4.4 branch. Discussion ---------- [Messenger] Fix calls to deprecated DBAL methods | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | N/A | License | MIT | Doc PR | N/A Follows doctrine/dbal#4746 and doctrine/dbal#4707. Commits ------- 2f834f2 [Messenger] Fix calls to deprecated DBAL methods
This code was relevant before the improvement in doctrine#4746 was implemented. The new comparator API is enforced in doctrine#4771. Now, the comparator will not produce a false-positive diff which is enforced by the new test.
This code was relevant before the improvement in doctrine#4746 was implemented. The new comparator API is enforced in doctrine#4771. Now, the comparator will not produce a false-positive diff which is enforced by the new test.
State
The existing property-based column definition comparison has quite some flaws (see all the associated issues):
Proposal
The idea is to replace property-based comparison with the comparison of the SQL that the given column definition generates on the target platform. This way, the diff will be only generated if the column SQL representations on a given platform are different.
API changes
This is quite an invasive change but it's meant to be safe for a minor release. The changes in the test logic identify the existing bugs rather than breaking changes.
Comparator
class now takes an optionalAbstractPlatform
argument. If it's passed, column comparison will be performed in the context of the platform. Not passing a platform is now deprecated.AbstractPlatform::columnsEqual()
andComparator::columnsEqual()
have been added to compare column definitions in the context of the given platform. Depending on the platform, the comparison logic may be more complex than comparing the immediate column SQL. E.g. the default value on SQL Server is represented as a separate constraint.AbstractSchemaManager::createComparator()
has been added to create the comparator. Besides the schemas being compared and the target platform, the comparison logic may depend on the data available only at runtime (current database collation on SQL Server).Comparator
class and the subclasses are marked as internal to their schema managers. This will allow potentially improving the comparison logic without introducing breaking changes. The same is already implemented in driver-level connections and statements.Platform-specific comparators clone the tables, if necessary, and normalize them before passing them to the base implementation. This implementation is far from perfect but it allows to avoid the introduction of a which may be not extensible enough and require a breaking change in the future.
Testing
Most of the changes in the tests are temporary and are needed to guarantee the parity between the old and the new implementation. The data providers will be removed in the next major release when the dependency of
Comparator
onAbstractPlatform
becomes mandatory.$schemaManager->createSchemaManager()
.Comparator
outside of the context of a platform have been extended to cover all sub-classes.Future scope
Column $column
, others expectarray<string,mixed> $column
. The latter is an archaism, relies on some magic defaults (seeAbstractPlatfrom::columnToArray()
) and requires unnecessary type conversion back and forth.Comparator::diffColumn()
andColumnDiff::$changedProperties
. In light of the above changes, the meaning and the purpose of the changed properties become ambiguous. If a property has changed but it doesn't result in a diff in the context of the current platform, should it be included in the changed properties?SQLServer2012Platform::getDefaultConstraintDeclarationSQL()
and other similar methods internal and then protected. See Inconsistent SchemaManager API across platforms #4503 for reference.