commit order must consider non-nullability of join columns as prioritised over nullable join columns: Take 2#7180
commit order must consider non-nullability of join columns as prioritised over nullable join columns: Take 2#7180bendavies wants to merge 14 commits intodoctrine:2.6from bendavies:commit-order-join-column-nullability
Conversation
lib/Doctrine/ORM/UnitOfWork.php
Outdated
| $calc->addDependency( | ||
| $targetClass->name, | ||
| $class->name, | ||
| (int) (($joinColumns['nullable'] ?? true) === false) |
There was a problem hiding this comment.
I think I wrote this ternary, and honestly I should be ashamed :-\
This should be split.
There was a problem hiding this comment.
afraid so ;).
I can split it out, but feel free to push to my branch if you have a preferred style.
|
Restarted zeh build |
|
taking a look at the commit order of the entities in |
| /** @Id @Column(type="integer") @GeneratedValue */ | ||
| public $id; | ||
|
|
||
| /** @ManyToOne(targetEntity="DDC6499A") */ |
| use Doctrine\Tests\OrmFunctionalTestCase; | ||
|
|
||
| /** | ||
| * @group #6499 |
lib/Doctrine/ORM/UnitOfWork.php
Outdated
| $calc->addDependency( | ||
| $targetClass->name, | ||
| $class->name, | ||
| (int) $joinColumnsNotNullable |
There was a problem hiding this comment.
I'd say that $joinColumnsNullable === false ? 1 : 0 is already readable enough, but its just my machine brain talking 😂
| /** @Id @Column(type="integer") @GeneratedValue */ | ||
| public $id; | ||
|
|
||
| /** @ManyToOne(targetEntity="DDC6499A") */ |
| } | ||
|
|
||
| $joinColumns = reset($assoc['joinColumns']); | ||
| $joinColumnsNullable = $joinColumns['nullable'] ?? true; |
There was a problem hiding this comment.
Your test and CascadeRemoveOrderEntityG are kind of mutually exclusive... yours expects that the lack of a @ORM\JoinColumn annotation will lead to 0 as weight and the existing one expects it to be 1. However both scenarios seem to be valid...
@guilhermeblanco what do you think about this?
There was a problem hiding this comment.
the original thinking is in the description of #6533
There was a problem hiding this comment.
Taking a look (confirming your comment), the patch introduces a difference to the weight for when nullable is not defined. old weight would be 1, new is 0.
so the fix seems to correct the bug. a not defined nullable should be created as true.
why this breaks cascade removals is obvious, but how do we fix it?
There was a problem hiding this comment.
btw, the original flawed (?) nullable check was introduced by @guilhermeblanco here: #1570, which was fixing a cascade remove bug!
| use Doctrine\Tests\OrmFunctionalTestCase; | ||
|
|
||
| /** | ||
| * @group #6499 |
There was a problem hiding this comment.
It should be @group 6499 instead
|
Ok, I've taken a deeper look at why
This results in the i.e this graph as 2 valid commit orders. It just so happens that with the fix in this PR, the "wrong" one is chosen, but we have no way of knowing it's the wrong/right one. It seems that So it seems we need to do something about the test. the fix in this PR seems valid. Guidance appreciated @Ocramius @lcobucci @guilhermeblanco |
|
I believe that the changes proposed in the PR are not enough to fix the problem, unfortunately. With this PR applied, the code at https://gist.github.com/arnaud-lb/2ac4a3007bba08ca5f8fa78cfc353cf5 will generate the following graph: The output of The problems seems to arise from the fact that we are trying to sort a cyclic graph topologically. When the execution reaches a state that shouldn't happen in the orignal algorithm ( Ignoring the nullable references when building the graph seems to creates an acyclic graph in my case (Is a cycle of non-null references possible?), and the commit order is correct (on a quite complex schema). The order may not be optimal, though, and I don't know if it works for entity removal. |
|
thanks for that @arnaud-lb. |
|
@bendavies I think that we might consider to have a more complicated logic to calculate the weight of the dependencies. I mean, the $weight = 0;
if ($joinColumnIsNullable) {
++$weight;
}
if ($isSomethingElse) {
++$weight;
}
// ...
return $weight; |
|
hah, i managed to hack in a fix for @arnaud-lb test/problem. |
|
@lcobucci I'm thinking that O's FK could be populated, which means we need to delete O first, then G. EDIT: I've just seen that The only think i can think of (but i'm not very familiar with UOW) is to add |
Awesome! Will it handle cycles involving more than 2 nodes, though? |
|
It's probably not robust at all. Like I said, a quick hack 😁. can you
provide an example for a more complex graph?
|
|
Sure, here is a new failing test with a 3 nodes cycle: arnaud-lb@c7facac |
|
Thanks @arnaud-ld. What is the correct commit order for that, to save me trying to work it out? |
|
F, E, D, G would work as a commit order. Removing the addDependency() call when $joinColumnsNullable is true yields this order. However, I believe that doing this would cause more |
@lcobucci that opens a can of worms IMO, as we start defining weights without having a clear idea of what the weights actually are.
These are scenarios in which the ORM should probably kick in at metadata validation time... Would be interesting to see how far we can go with defining the commit order before runtime: possibly a completely different project?
In short: no. Not all RDBMSs in use support |
|
@Ocramius thanks for that. Do you have any opinion on |
It seems like the test was faulty due to missing explicit |
|
I've taken in @arnaud-lb failing tests and added the same scenario to the commit order calculator. |
|
As above, at the moment the Basically:
Edit: I'm not sure this is appropriate actually. MST is for unidirected graphs while ours is directed. However, there is a slight complication in that the The sorts of these unconnected graphs can then be returned in any order as they are not dependent. The above is a not a trivial undertaking so I don't want pursue it if someone thinks it's a bad idea or can see a really simple fix to the current impl that I've overlooked. If @guilhermeblanco could provide some feedback before I try and take this any further, that would be great, since he implemented the |
@bendavies @Ocramius the commit order calculator knows only about the It may indeed open a can of worms, but if we have issues with the logic which is sending data to the commit order calculator we shouldn't simply change the class ( |
|
@lcobucci i think the data provided to the calculator is fine. This graph is simple but isn't solved correctly: #7180 (comment) |
|
What's the status of this bug ? @bendavies you seemed to be on the right path. |
|
My solution wasn't good because I was waiting for @guilhermeblanco's feedback/help, really. |
|
maybe fixed by #7260 |
-- Processed Ocramius' feedback.
|
rebased after #7260 was merged |
|
thought i'd take a look at how hibernate handles this. They do it a bit differently. Doctrine deduces table insert ordering from class metadata, while hibernate orders the inserted entities themselves. |
|
@bendavies how did you go with this? |
|
no further sorry. I think the proper fix would be to copy Hibernate.
|
|
Still remember what you did 5 years ago? If so, maybe you can help to review #10547. |
Tests suggested in doctrine#7180 (comment) and doctrine#7180 (comment) by @arnaud-lb. Co-authored-by: Arnaud Le Blanc <arnaud.lb@gmail.com>
Add test to show #7180 has been fixed

This is a resubmission of #6533, which attempted to fix #6499 (also #7006)
Note that that I consulted Marco on slack before doing this.
I don't want to step on anyones toes, but the original PR has seemed to have gone stale and come to a halt. Maybe the original submitter has lost interest/moved on (his fork appears to be gone) or whatever (not judging), but regardless, I'd like to pick this back again and try and get it over the line.
I've maintained the original commits from the PR as well as some of Marco's styling fixes.
#6533 was merged and then reverted (#6533 (comment)).
The Tests reveal the problem,
CascadeRemoveOrderTest. If anyone has any idea how this can be fixed before I start poking around, please give me a heads up.