Problem
- #2145041: Allow dynamic routes to be defined via a callback changed the provider of existing routes, which results in the following errors/exceptions when the router is rebuilt:
PDOException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'field_ui.instance_edit_comment' for key 'PRIMARY' in core\lib\Drupal\Core\Database\Statement.php on line 54 {main}( ) drupal_rebuild( ) drupal_flush_all_caches( ) Drupal\Core\Routing\RouteBuilder->rebuild( ) Drupal\Core\Routing\MatcherDumper->dump( ) Drupal\Core\Database\Driver\mysql\Insert->execute( ) Drupal\Core\Database\Connection->query( ) Drupal\Core\Database\Statement->execute( ) execute ( )IntegrityConstraintViolationException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'field_ui.instance_edit_comment' for key 'PRIMARY': INSERT INTO {router} (name, provider, fit, path, pattern_outline, number_parts, route) VALUES (...snip...) {main}( ) drupal_rebuild( ) drupal_flush_all_caches( ) Drupal\Core\Routing\RouteBuilder->rebuild( ) Drupal\Core\Routing\MatcherDumper->dump( )
Proposed solution A
- Adjust
MatcherDumper->dump()to not only delete existing{router}rows with the same'provider', but also all rows having the same route'name'as the new routes to be inserted.
Proposed solution B
- Use a
MergeQueryinstead ofInsertQueryinMatcherDumper->dump().
Proposed solution C
- Make the rebuild script truncate the entire
{router}table before rebuilding the router.
| Comment | File | Size | Author |
|---|---|---|---|
| #19 | interdiff-2160811-19.txt | 2.12 KB | damiankloip |
| #19 | 2160811-19.patch | 6.87 KB | damiankloip |
Comments
Comment #1
sunAttached patch implements proposed solution A.
Comment #3
sunSorry, missed the fact that $this->routes is a RouteCollection.
Comment #4
Crell commentedOf the available options I agree A is the best on the table. Thanks, sun!
Comment #5
webchickI imagine the answer is no, but is there any possible way of providing test coverage for this?
Comment #6
Crell commentedI think when the amount of code needed to test reaches a certain multiplier of the code being tested, it's no longer worth it. I'm pretty sure this would be past that threshold even even I could think of a way to do so (which off hand I can't).
Comment #7
dawehnerWell, if you would apply this logic everywhere all unit tests should be thrown away immediately.
Comment #8
sunbtw:
I wasn't sure whether the dumper gets also invoked in case the RouteCollection is empty.
Thus, I used the defensive approach of checking whether $names is non-empty before adding the condition.
[OT: When I wrote that condition code, I wondered whether we couldn't make Condition smart enough to figure out itself whether the argument array of an IN query condition is empty → if so, do not add a malformed SQL WHERE clause. Just a thought.]
But more importantly:
If the RouteCollection can, in fact, be empty, then I can only assume that the subsequent INSERT query will fail and blow up, too?
That edge-case could happen if a module has removed all routes, after providing some routes previously, no?
In that case, we'd still want to execute the DELETE query, so as to remove the stale/obsolete routes of the provider, but not the INSERT query.
In essence, we'd wrap the INSERT query execution into the same new condition, like so?
Thoughts?
Comment #9
Crell commentedOK, daniel's test-fu is stronger than mine, apparently.
sun: I'd check for an empty RouteCollection right off, and special case that whole code path. I think that would be simpler over-all, since that's a really really short code path (just firing a delete).
Comment #10
dawehnerFor a short moment I thought we should tell the dev that he might want better to return some routes, though I guess there are usecases for modules to provide just an empty collection, so I think it is better to be safe here and just do the changes silently.
@sun
Both how we can fix the Condition object we could also fix the Insert object and not execute something if there are no values to insert.
Comment #11
sunAdded the empty RouteCollection check in the way @Crell suggested.
Sorry for the much larger diff; since $this->routes is ought to be flushed/emptied after dumping, I turned the whole thing into an if/else structure.
Interdiff is pointless. No other changes in that code though. Only adjusted the new test accordingly.
Comment #13
sunAlso created #2161943: Throw a helpful exception for empty IN conditions in Database\Query\Condition
Comment #14
sun11: drupal8.rebuild-router.11.patch queued for re-testing.
Comment #16
sunWeird - I'm not able to reproduce the installation failure locally
Comment #17
dawehner11: drupal8.rebuild-router.11.patch queued for re-testing.
Comment #19
damiankloip commentedI think we also need to take care of the case where an empty route collection is set on the Dumper? Like this.
Comment #20
dawehnerSo yeah empty indeed does not work on objects.
Comment #21
sunAwesome, thanks! :)
Comment #22
alexpottCommitted 4f2d2ab and pushed to 8.x. Thanks!