-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
[GH-1204] Add full support for foreign key constraints for SQLite #3762
Conversation
aaf2410
to
5a6bedc
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.
The code and tests look fine. Just a few questions.
* This algorithm have a linear running time based on nodes (V) and dependency | ||
* between the nodes (E), resulting in a computational complexity of O(V + E). | ||
*/ | ||
class CommitOrderCalculator |
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.
Although this is used for commit order calculation, it's not specialized for that. Would it make sense to rename this class and the related ones in a way that doesn't imply that it's only for commits? E.g. Graph
, Node
, Edge
?
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.
We should make it final
too
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.
Perhaps we could bring this to doctrine/persistence
to be reuse by the ORM too?
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 made it final now, we can move it to persistence in another step, but ORM depends on DBAL, so it can already use 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.
If it's needed in ORM together with the methods like hasNode()
that are not used by DBAL and probably weights as well, why not move it to a shared library instead?
It feels weird that the code is ported from one library to another as is just because it's needed as is for the library it's currently located in.
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.
@morozov i don't see how its weird, DBAL is the common denominator of where this code is needed in terms of dependencies. Introducing yet another library is just adding overhead. In addition it would prevent this fix from being merged into 2.10 per the dependencies are locked rule.
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.
IMO, the "common denominator" approach is wrong and counterproductive. This way, the consumers of the shared logic end up storing and maintaining the logic they need in a place where it doesn't belong.
Eventually, the changes in this logic are driven by the consumers while the owners of the library end up accepting everything "just because it's needed for project X".
In this case, I see two proper solutions:
- Have
@internal
implementations that solve the problems of each of the libraries independently and evolve independently (looks the most reasonable in this case). - Have a shared library that solves this problem in general (look like overkill in this case).
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.
We can keep this code as a 100% copy of the ORM code. This is not an attempt to force the ORM to use the class from here in the future. I just happened to need the exact same logic, because its the same problem, hence suggesting it can be re-used by the ORM in the future.
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 disagree. The commit order resolution is an internal (as the namespace suggests) implementation detail of the SQLite driver which is not part of the database abstraction layer. It cannot be planned for reuse by another component. And this argument cannot be used to keep the pieces of the code that this component doesn't need for its internal purposes.
tests/Doctrine/Tests/DBAL/Internal/CommitOrderCalculatorTest.php
Outdated
Show resolved
Hide resolved
tests/Doctrine/Tests/DBAL/Internal/CommitOrderCalculatorTest.php
Outdated
Show resolved
Hide resolved
/** | ||
* Sorts tables by dependencies so that they are created in the right order. | ||
* | ||
* This is ncessary when one table depends on another while creating foreign key |
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 is ncessary when one table depends on another while creating foreign key | |
* This is necessary when one table depends on another while creating foreign key |
The new one true base branch is 2.10.x, switching to that. |
bdc458d
to
cb21373
Compare
@beberlei the result looks good. Should we squash at least some commits? The “fix typo”-like are definitely irrelevant for the history. |
The ContinuousPHP failure is temporary and expected due to the configuration change in #3915. |
@beberlei to avoid double builds w/o making additional configuration changes across CI platforms, please open future PRs from your fork instead of the upstream repository. |
@morozov |
cb21373
to
e7d04a6
Compare
@morozov if it's just a rebase that involves squashing a bunch of commits, there's nothing to prevent us from doing that ourselves or doing it via the GitHub merge functionality. I've squashed all commits since all follow-up commits were only due to review changes. |
e7d04a6
to
8073d1a
Compare
@alcaeus the newly added test fails on Oracle:
|
950cdac
to
85a983c
Compare
@morozov Fixed by rewriting the assertion logic, I couldn't find out why Oracle had the column ID vs id names because of the default casing. Its not related to what the test is testing, so the adjusted assertion takes care of that. |
$diff = Comparator::compareSchemas($offlineSchema, $schema); | ||
|
||
foreach ($diff->changedTables as $table) { | ||
if (count($table->changedForeignKeys) <= 0 && count($table->addedForeignKeys) <= 0 && count($table->removedForeignKeys) <= 0) { |
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.
Not that it needs to be taken care of immediately but aren't these just three assertions like assertCount(0, $table->changedForeignKeys)
?
Makes sense. Given that on Oracle, DBAL produces unexpected DDL, it looks like a bug (although, unrelated). Right? |
@@ -77,3 +77,9 @@ parameters: | |||
- | |||
message: '~^Access to undefined constant PDO::PGSQL_ATTR_DISABLE_PREPARES\.$~' | |||
path: %currentWorkingDirectory%/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php | |||
|
|||
# False Positive | |||
- '~Strict comparison using === between 1 and 2 will always evaluate to false~' |
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.
@beberlei how is this a false positive?
dbal/lib/Doctrine/DBAL/Internal/DependencyOrderCalculator.php
Lines 106 to 123 in 85a983c
$vertex->state = self::IN_PROGRESS; | |
foreach ($vertex->dependencyList as $edge) { | |
$adjacentVertex = $this->nodeList[$edge->to]; | |
switch ($adjacentVertex->state) { | |
case self::VISITED: | |
case self::IN_PROGRESS: | |
// Do nothing, since node was already visited or is | |
// currently visited | |
break; | |
case self::NOT_VISITED: | |
$this->visit($adjacentVertex); | |
} | |
} | |
if ($vertex->state === self::VISITED) { |
It looks like a bug in the code. How is $vertex->state
expected to change its value from IN_PROGRESS
to anything else between the first and last lines of the block above?
Technically, it's possible if the node is adjacent to (depends on) itself but this doesn't look like a valid case.
Add full support for foreign key in SQLite.
Summary
SQLite has partial foreign key support, which leads to bugs in ORM Schema Tool, because it cannot reliable make use of
AbstractPlatform::supportsForeignKeyConstraints()
and hence always re-creates the same table structure, because the DBAL Comparator it finds a diff in the schema that doesn't exist.This PR does a few things:
SqlitePlatform::supportsForeignKeyConstraints()
to truesupportsCreateDropForeignKeyConstraints()
which is necessary to control inSchemaDiff::toSql()
how to create the SQL.CommitOrderCalculator
from ORM to DBAL because its needed inSchema\Comparator
to calculate the order of new tables to create. The tests are also copied over, the idea is probably that ORM removes the commit order calculator in 2.8 and uses the one from DBAL instead.ForeignKeyConstraintViolationException
whenPRAGMA foreign_keys=On
set and violation occurs. Tests failed after switching supportsForeignKeyConstraints to true.Even though its big, I would argue this is still considered a bugfix, because returning false from
supportsForeignKeyConstraints()
is actually not wrong, since the code supports foreig key constraints.