Problem/Motivation
Found in #2848952-14: Replace all calls to db_merge(), which is deprecated
Deprecated function db_merge() using to setup default db target but database service implementation don't
if (empty($options['target']) || $options['target'] == 'replica') {
$options['target'] = 'default';
}
other functions use different
if (empty($options['target'])) {
$options['target'] = 'default';
}
Proposed resolution
Move this hunk to \Drupal\Core\Database\Connection::merge()
Remaining tasks
- patch with test for replica & default target usage selection in merge
- agree & commit
User interface changes
no
API changes
\Drupal\Core\Database\Connection::merge() should work the same compatible way
Data model changes
no
Comments
Comment #2
andypostComment #3
andypostThe same happens with
db_transaction()in #2848820: Replace all calls to db_transaction, which is deprecatedComment #4
andypostComment #5
andypostComment #7
volegerI guess we have no work right here because this logic already implemented at \Drupal\Core\Database\Database::getConnection() method.
Comment #8
andypostwe need here to clean-up todo's left in code and try to unify calls like summary tells
Comment #9
volegerThe
targetandkeyproperty of the connection instance can be set only once during::getConnectioncall.So we need to create some helper method to reset the connection property on the Connection instance level that will run the same code from getConnection method in Database class.
Comment #10
mondrakeRaising to major as we discovered in #2873684: Replace all calls to db_select, which is deprecated there are BC implications here. See comments #28 onwards.
Comment #11
voleger#2873684-28: Replace all calls to db_select, which is deprecated
Comment #12
andypostI bet it needs regression test and approval to set it in stone
Comment #13
catchThe differing logic in db_merge() compared to all the other db_* functions has been in core since the original database layer patch landed in 2008.
Choosing deliberately to write to the replica connection would be a bug - it's a replica being replicated to.
Overriding that explicit choice then both feels unnecessary and potentially a bug too.
So I'm not sure we need to provide bc for the db_merge( ) behaviour at all.
Comment #14
mondrake#13 @catch thanks for feedback - that would cover anything 'writing' to the db, merge, insert, update, delete, upsert, truncate, opening transactions. How about 'reading' (select, query, queryTemporary, queryRange)? We may want to address query to replica directly as we are discovering in #2873684: Replace all calls to db_select, which is deprecated.
Comment #15
catchWouldn't you need to use Database::getConnection() for those cases?
Comment #16
longwaveWhile 'replica' is a special case supported by core for a handful of select queries, isn't it up to the caller to determine which database they want to use? If custom code wants to write to a specific connection, be it 'replica' or 'custom' or 'remote', that is up to them.
While we support the 'target' parameter in $options in the db_* calls we have never pretended to support it in the OO world, so I don't see where we need backward compatibility, unless we are to replicate this apparent bug in
db_merge()etc. However, we could try to be helpful by triggering a warning/error if someone does try to use 'target' when blindly using find and replace to convert e.g.db_select()to\Drupal\Core\Database\Connection::select().edit: Connection::defaultOptions() says 'target' is supported in query options, but I don't see where this is actually implemented? By the time you have a Connection object open you cannot change the target?
Comment #17
catchOr to expand a bit there are three possible cases:
1. You just want to read or write from the default connection.
2. You want to read or write from a custom connection which you know exists (an easy core example is a migration source database) via setting $key.
3. You want to read from a replica if it's available, but fall back to the default connection if it's not - what the fallback in Database::getConnection does.
So the question seems to be, should there be another mechanism other than Database::getConnection() to do #2/#3 in that list?
For #2 you could create a new service just for that connection and use that instead of @database and inject it.
For #3 is the issue that it's not injectable? But whatever we inject is going to have to call Database::getConnection() on the fly anyway, since that's what the container definition for @database does anyway. Some kind of factory that can be mocked which does this might be fine though, but I don't think it's a blocker to any of the deprecation issues.
Comment #18
alexpottI agree with #17 and #16. As @longwave says
Yep it is. So calling db_insert() with the replica db as an option is just wrong. It might be worth updating the CR to note this change but as @catch says
I think this issue should be "closed won't fix"
Comment #19
andypostthen edge case is
db_query&db_selectwhich allows to use replica (there was some protection in db layer about restrictions of allowed types of queries/dml)We could add "throw error"/assert to insert, merge, delete and file CR that points that
Comment #20
catch#16:
Yes you're right, that documentation is just completely wrong.
Comment #21
catchOK so I think there are two issues here, but both are pre-existing issues that shouldn't block deprecated code removals:
1. The documentation for Connection::query() and by extension Connection::defaultOptions() is wrong - we should open an issue to correct this.
2. There's no injectable way to use different database targets (for different database keys you can add a different connection as a separate service though) - we could possibly repurpose this issue for that.
Tagging as 'needs issue summary update' but for me it's fine if conversions use Database::getConnection() when they need to specify target.
Comment #22
longwaveOpened #3000931: Connection::query() does not support 'target' option
Comment #23
voleger#21.2
https://symfony.com/doc/3.4/service_container/parameters.html
For me, it sounds like we need to define a service (something like
@database.factory) that has the method that can generate the connection instance with the target that different to 'default'.Comment #24
mondrakeI suppose what will be left to do here then would be just removing theif (empty($options['target'])) {$options['target'] = 'default';
}
blocks from the alreAdy deprecated db_* legacy functions?Edit - ignore, this is wrong.
Comment #25
mondrakeI suggest to won’t fix this - we won’t be doing what the IS says, and we are already addressing in #3000931: Connection::query() does not support 'target' option what to do with the leftover code in the legacy functions.
Just reopen if you disagree.