Problem/Motivation
There is a bug in the method Drupal\Core\Database\Connection::getDriverClass(). In the line: $this->driverClasses[$class] = class_exists($driver_class) ? $driver_class : $class; we state that when the driver does not implement the class, that we should take the default class. We return only the class name, not with its full namespace. And that is a problem when that class lives in another namespace then the current namespace (Drupal\Core\Database vs Drupal\Core\Database\Query). When we return the class with its full namespace, we can then remove all unnecessary classes from the database drivers.
This "functionality" was designed - see #2461239-8: Database driver class fallback is broken and stubbed instead - however it results in classes like
namespace Drupal\Core\Database\Driver\mysql;
use Drupal\Core\Database\Query\Select as QuerySelect;
/**
* MySQL implementation of \Drupal\Core\Database\Query\Select.
*/
class Select extends QuerySelect {}
Which are pointless.
Proposed resolution
Correctly fallback to a core provided class if the database driver does not provide one.
Remaining tasks
User interface changes
None
API changes
The core database drivers have been cleaned up. Their classes are not API and if contrib or custom code is extending them they should extend the classes in Drupal\Core\Database\Query instead.
Data model changes
None
Release notes snippet
Contributed and custom database drivers no longer need to extend core's database agnostic database classes unless they need to override code provided functionality. The core database drivers have had unnecessary classes removed. Read the change record for the full list of classes removed.
| Comment | File | Size | Author |
|---|---|---|---|
| #29 | 3124354-29.patch | 34.48 KB | daffie |
| #29 | interdiff-3124354-25-29.txt | 4.36 KB | daffie |
Comments
Comment #2
daffie commentedComment #3
daffie commentedComment #4
alexpott@daffie I support this change but I know that there are old issues that have argued that this is the way it is meant to be. I think we need to find and reference the arguments they made here
Comment #5
beakerboyIf the core classes must be implemented by each driver, why wouldn't they be abstract? That would be the correct way to dictate that behavior IMO, not some buried discussion thread from years ago.
Comment #6
alexpottSo this issue is essentially a duplicate of #2461239: Database driver class fallback is broken and stubbed instead. In comment #8 in that issue the maintainer of the DB API at the time said
I agree with both @daffie and @Beakerboy - I not sure I agree with the decision taken early in the D8 cycle. And what @Beakerboy says is entirely correct:
Comment #7
daffie commentedThis issue is now about 2 things: the bug in the method
Drupal\Core\Database\Connection::getDriverClass()and the policy decision whether or not to have empty database driver classes in the by core supported drivers.Should we create a child issue for fixing the bug or can we do it in all one issue?
@catch said in #3120096-82: Support contrib database driver directories in a fixed location in a module in point 4 about the adding of empty driver classes: "So is this necessary for all contrib drivers now?". My conclusion is that @catch also does not like to have the empty driver classes.
Comment #8
alexpott@daffie I think not. The two things are completed entangled and deserve to go together.
Comment #9
catchThis conclusion is correct :)
Comment #10
daffie commentedThis issue still needs testing. After #3120096: Support contrib database driver directories in a fixed location in a module has landed, this will be easy to do.
Comment #11
alexpottThis logic can be more performant. We only need to do the switch if
class_exists($driver_class)is FALSE.We can change this whole function to be
That makes the implementation less special and easier to maintain.
Comment #12
abhisekmazumdarUpdating the patch as per #11.
Comment #13
abhisekmazumdarHere is an updated patch. Kindly review.
Can't add an interdiff as the old patch was not applying.
Comment #15
daffie commentedChanged in this patch:
- Added a new database driver with no custom driver classes.
- Added testing for Connection->getDriverClass() for the default classes.
- Improved the readability of Connection->getDriverClass().
- A couple test fixes.
Comment #16
daffie commentedForgot to do a git pull.
Comment #17
alexpottTo maintain the old behaviour we need a default here too :)
Something like
I'm not convinced that adding this driver is really worthwhile - we can test one of the other test drivers just as well. If the class fallback code was not working everything would be broken. I think it might more important to test that a driver can override the core defaults.
Is this change actually necessary - I can't see why.
How come we do this? As far as I can see this test is db driver independent.
Comment #18
daffie commented@alexpott: Thank you for the review.
For #17.1: Fixed.
For #17.2: With the added driver we can test all default classes. With existing drivers that is not possible.
For #17.3: Fixed.
For #17.4: The test will fail for regular driver because they have one or more custom driver classes. This test will test all default driver classes. We only need to test for one driver, because the method we are testing lives in Drupal\Core\Database\Connection and therefor is driver independent.
Comment #19
alexpottRe #17.4 but say we removed mysql testing... unlikely yes... but this test should be independent of the DB driver. I agree we only need to test this once but that's the same for 99% of unit tests already. And we don't do that. That's not how our orthogonal tests of each DB driver work atm.
Comment #20
alexpottAlso for #17.2 well my point is that
Plus we're missing test coverage of #17.1 - I thought this was important for SelectExtenders but looking at \Drupal\Core\Database\Query\Select::extend() the way in which we allow drivers to override select extenders is completely wrong and pre namespaces. Ho hum. We need an issue for that too :(
Comment #21
alexpottOh compare \Drupal\Core\Database\Query\Select::extend() and \Drupal\Core\Database\Query\SelectExtender::extend() - that's an interesting difference. Anyhow looking for a class of
$this->connectionOptions['namespace'] . '\\' . \Drupal\search\SearchQuery' . ;is amusing...I wonder what actually is happening in the following code:
from \Drupal\help_topics\Plugin\Search\HelpSearch::findResults().
Comment #22
daffie commentedAdded support in Connection->getDriverClass for PagerSelectExtender and TableSortExtender. I did not do the same for SearchQuery, because that class lives in the search module and not in \Drupal\Core. Maybe we should create a followup to fix that.
For #19: You are right. Moved the testing to UnitTesting.
For #20: Fixed. Added testing for: PagerSelectExtender, TableSortExtender and SearchQuery.
For #21: The class PagerSelectExtender overrides the following methods: __construct(), execute(), ensureElement(), setCountQuery(), getCountQuery(), limit() and element()
The class TableSortExtender overrides the following methods: __construct() and orderByHeader().
The __construct() methods only add a "tag" to the query ('pager' and 'tablesort').
There is no overlap between the 2 classes. I think that is way it works.
Comment #23
daffie commentedForgot to remove some testing/development stuff.
Comment #24
johnwebdev commentedJust minor random thoughts:
We could use an array lookup table here instead. It pairs nice with the Null coalescing operator. Although, this is just plainly for the "easier on the eye" reason.
This feels wrong. A getter shouldn't set a value. It is a test class though, so maybe it's fine.
Here we need docblocks. There are more occurences.
Comment #25
daffie commented@johnwebdav: Thank you for the review.
For 24.1: I do not fully understand what you want to do and how it then would be "easier on the eye". Feel free to update the patch and make that it so.
For 24.2: The module "database_statement_monitoring_test" is only used in the test
Drupal\KernelTests\Core\Cache\EndOfTransactionQueriesTest. The whole test including the named module can use an update/rewrite. The reflection part was already part of the test and inDrupal\Core\Database\Connection::__construct()we also use something similar ($connection_options['namespace'] = (new \ReflectionObject($this))->getNamespaceName();). We are trying to remove the reflection stuff. For that we need to have one namespace formula for every database driver. Now we have 3 different ones and trying to get rid of 2 of those #3123461: Deprecate support for database drivers placed in DRUPAL_ROOT/drivers and #3129043: Move core database drivers to modules of their own. Hopefully in Drupal 10.0 will get to remove the reflection stuff.For 24.3: Fixed.
Comment #26
johnwebdev commented#25.1
Something like:
This is just a preference (and why I only suggested it) I don't think we use this pattern that much in core either.
Let's leave it as is :)
#25.2 I'm not that bothered with the reflections parts per se, I'm just pointing out that our getter method is producing side-effects. This is generally a bad practise, in this case it's in a test, but I'll leave it to other reviewers to have their say if that's something that should be changed.
Comment #27
johnwebdev commentedI missed that #24.2 was actually pointed out as a suggested change in earlier reviews, and as I was unsure myself, I feel ok to not pursue the discussion.
Interdiff in #25 looks good.
Comment #28
alexpottIt never gets called with these. We shouldn't add these here. We need to open up a follow-up issue about select extenders and database overrides. There differences between \Drupal\Core\Database\Query\Select::extend() and \Drupal\Core\Database\Query\SelectExtender::extend() are problematic and the implementations are BOTH problematic!
In the case of these classes the calls are always made with ->extend('Drupal\Core\Database\Query\PagerSelectExtender') or ->extend('Drupal\Core\Database\Query\TableSortExtender') so this represents new functionality that's not really supported and makes it look like things will work that don't.
This is not true. It's not BC - it's expected functionality.
There's no need for the NativeUpsert class anymore. It should become postgres's upsert class.
Comment #29
daffie commentedFor #28.1 and #28.2: Fixed.
Comment #30
daffie commentedCreated followup: #3129560: Remove the Upsert implementation for PostgreSQL < 9.5.
Comment #31
daffie commentedCreated followup #3129563: The database query extenders does not allow database drivers to override them.
Comment #32
johnwebdev commentedInterdiff looks good and follow ups has been created. Pre-Re-RTBC.
Comment #33
alexpottFixing the issue summary and adding a change record.
Comment #34
alexpottI’d like to commit this - but it’s removing all the core db driver specific useless classes. Note we can’t deprecate them because they’ll be used by core. I see a couple of options here:
Any thoughts?
Comment #35
daffie commentedMy preference would be to go with the first option. There are only 2 contrib database drivers that are/will extend one of the by core supported database drivers. The first is https://www.drupal.org/project/mysql56 and it is done by @effulgentsia. The second is https://www.drupal.org/project/pgsql_fallback and is done by myself. I think that both @effulgentsia and myself can update our projects before the release of 9.0.0.
@alexpott: Thank you for adding the CR and updating the IS.
Comment #36
catchGiven only two projects are affected let's remove the redundant classes in 9.0.x. The inability to properly deprecate them is not great, and we also can't do that for class_alias.
Comment #37
xjmWe should try to do this before RC though.
Comment #38
alexpottAdded the removed classes to the issue summary.
Comment #39
mondrakewouldn't sth like
be simpler (maybe with additional check that the class exists)?
Comment #40
alexpottCommitted a11ed7a and pushed to 9.1.x. Thanks!
Also cherry-picked back to 9.0.x.
Fixed the coding standards on commit.
Comment #43
daffie commented@mondrake: This does not work, because not all the classes live in the namespace Drupal\Core\Database\Query, like the Schema class that lives in the namespace Drupal\Core\Database.
Thanks to everybody that helped getting this issue to land!