Problem/Motivation

As noted in #3186934: Introduce an ExceptionHandler class in the database API, deprecate Connection::handleQueryException, the way query classes are loaded by Connection is preventing static code analysis, besides being 'a magic' in the sense that they're autoloaded by placing files that override the base classes in the same directory where the Connection class resides.

Proposed resolution

While waiting for #3217699: Convert select query extenders to backend-overrideable services, for now let's start changing most Database/Query classes to standard autoloading. This will reduce a lot boilerplate in contrib drivers.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Issue fork drupal-3217531

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mondrake created an issue. See original summary.

mondrake’s picture

Assigned: mondrake » Unassigned
Status: Active » Needs review

A first patch. While overriding the Connection methods that call getDriverClass in the driver-specific classes with direct indication of the class to use is (relatively) straigthforward, the use of the method to replace query extenders with driver-specific classes is not. AFAICS this in more recent times was done with backend-overridable services; could that be applied here too?

andypost’s picture

andypost’s picture

mondrake’s picture

I have opened #3217699: Convert select query extenders to backend-overrideable services for #3. There's a PoC there, comments appreciated.

mondrake’s picture

Status: Needs review » Postponed

#3217699: Convert select query extenders to backend-overrideable services would help here, so postponing on that for the moment.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

mondrake’s picture

Rebased to 9.4 and rerolled

daffie’s picture

mondrake’s picture

Assigned: Unassigned » mondrake

On this

daffie’s picture

Updated the CR.

mondrake’s picture

1) Drupal\FunctionalTests\Installer\InstallerNonDefaultDatabaseDriverTest::testInstalled
Exception: Error: Class "Drupal\mysql\Driver\Database\mysql\ExceptionHandler" not found
Drupal\mysql\Driver\Database\mysql\Connection->exceptionHandler()() (Line: 499)

I think we are hitting #3256642: Introduce database driver extensions and autoload database drivers' dependencies here, we are blocked on it

mondrake’s picture

Assigned: mondrake » Unassigned
mondrake’s picture

Assigned: Unassigned » mondrake
Status: Postponed » Needs work
mondrake’s picture

Status: Needs work » Needs review

With #3256642: Introduce database driver extensions and autoload database drivers' dependencies the D10 patch will be much simpler and cleanup a lot, so I am still keen on holding this (both 9.4 and 10) on that. But the MR towards 9.4 should pass now.

daffie’s picture

Status: Needs review » Postponed

As we need #3256642: Introduce database driver extensions and autoload database drivers' dependencies before we can do the patch for D10, lets postpone this issue until the other lands.

The patch for 9.4 looks good.

mondrake’s picture

Title: Deprecate Connection::getDriverClass mechanism, make its callers abstract, and use standard autoloading » [PP-2] Deprecate Connection::getDriverClass mechanism, make its callers abstract, and use standard autoloading
Assigned: mondrake » Unassigned
Related issues: +#3217699: Convert select query extenders to backend-overrideable services

Unfortunately, #3217699: Convert select query extenders to backend-overrideable services was reverted and reopened so now this is postponed on 2 issues.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

mondrake’s picture

Title: [PP-2] Deprecate Connection::getDriverClass mechanism, make its callers abstract, and use standard autoloading » Deprecate Connection::getDriverClass mechanism, make its callers abstract, and use standard autoloading
Status: Postponed » Active
mondrake’s picture

Title: Deprecate Connection::getDriverClass mechanism, make its callers abstract, and use standard autoloading » Deprecate usage of Connection::getDriverClass for some classes, and use standard autoloading instead
Issue summary: View changes
Status: Active » Needs review

Let's make this issue a bit less boastful. The MR here helps a lot reducing boilerplate classes in database drivers extending from core ones.

daffie’s picture

@mondrake: Thank you for working on this.

Adding the same code to every database drivers Connnection class is a bit of an ugly solution. We are breaking the DRY principle. Would it be possible to put the code in trait? Or maybe another solution?

mondrake’s picture

It’s not the SAME code actually. Each method instantiates a class either in the same namespace of the Connection class, or in the core lib, depending in the use imports at the beginning of each driver’s Connection class. In D11 we might change the core abstract Connection methods to instantiate the classes on Database\Query, and use default inheritance in drivers’ extending Connection classes. We can’t do that now without breaking BC. In any case, most of the classes even in core drivers DO extend from the core classes, so the driver specific methods will stay.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Going to put this in the committers queue. But if I'm understanding correctly this gives more control in the individual drivers?

Change record was clear too for how to update my own driver. Think getting in early gives more time for contrib modules to transition.

daffie’s picture

diff --git a/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php
index 7e405e1b1b..a2e19d1b1b 100644
--- a/core/lib/Drupal/Core/Database/Connection.php
+++ b/core/lib/Drupal/Core/Database/Connection.php
@@ -27,6 +27,13 @@
  */
 abstract class Connection {
 
+  /**
+   * The class for the query builder for SELECT statements.
+   *
+   * @var string
+   */
+  protected $selectClass = Select::class;
+
   /**
    * The database target this connection is for.
    *
@@ -990,8 +997,7 @@ public function exceptionHandler() {
    */
   public function select($table, $alias = NULL, array $options = []) {
     assert(is_string($alias) || $alias === NULL, 'The \'$alias\' argument to ' . __METHOD__ . '() must be a string or NULL');
-    $class = $this->getDriverClass('Select');
-    return new $class($this, $table, $alias, $options);
+    return new $this->selectClass($this, $table, $alias, $options);
   }
 
   /**
diff --git a/core/modules/mysql/src/Driver/Database/mysql/Connection.php b/core/modules/mysql/src/Driver/Database/mysql/Connection.php
index 73239a4536..b80ad9ec07 100644
--- a/core/modules/mysql/src/Driver/Database/mysql/Connection.php
+++ b/core/modules/mysql/src/Driver/Database/mysql/Connection.php
@@ -23,6 +23,11 @@
  */
 class Connection extends DatabaseConnection implements SupportsTemporaryTablesInterface {
 
+  /**
+   * {@inheritdoc}
+   */
+  protected $selectClass = Select::class;
+
   /**
    * Error code for "Unknown database" error.
    */

@mondrake I am not sure my other solution also works with static code analysis. A database driver only needs to set the class variable when it is overriding the class. This solution also works for database drivers that are extending another database driver. The problem with your solution is that every database driver Connnection class needs to add the "same" boilerplate code. What do you think?

mondrake’s picture

@daffie IMHO we should avoid using properties to store the class whereabouts, that replace native autoloading with our magic. That's what protected $statementWrapperClass is doing, and I think we should also change that at some point.

I know this looks like adding boilerplate, but it's actually not. These are methods that are there just to create a proper instance of a class depending on the driver. In our current predicament, we can not leverage inheritance because of BC concerns. But once we can, there will be quite some cleanup possible - the same cleanup that it's possible with this MR in contrib drivers that extend from core ones and do not have to override a specific class (see the test drivers in the MR already).

Better than many words, I'll post here 'how' D11 could look like in this respect.

daffie’s picture

+1 for RTBC.

@mondrake: Thank you for explaining.

mondrake’s picture

@daffie here's the idea per #28; Upsert and Schema are abstract classes in core, so every driver must implement its ones. But the rest of the classes we can inherit from the core Connection in D11, unless there are overrides.

EDIT - I shouldn't have removed MySql Insert class... but here is to illustrate, not to make it work.

daffie’s picture

@mondrake: Lets go with your solution. I think that in some point in the future, we should create a boilerplate module for a custom database driver for maintainer/creators of database driver modules.

longwave’s picture

Should we not deprecate getDriverClass() entirely here? Are there any other calls to it with other arguments?

Also #28/#30 imply that we might not make all those @todo methods abstract?

mondrake’s picture

Assigned: Unassigned » mondrake
Status: Reviewed & tested by the community » Needs work

Should we not deprecate getDriverClass() entirely here? Are there any other calls to it with other arguments?

Unfortunately not, #3217699: Convert select query extenders to backend-overrideable services blocks that deprecation since Query\Select::extend() uses it.

Also #28/#30 imply that we might not make all those @todo methods abstract?

Yes, NW to adjust the @todo(s) - so we need to make schema and upsert abstract since the classes are abstract, all others should be implementations instantiating the corresponding Query\* base class.

mondrake’s picture

Assigned: mondrake » Unassigned
Status: Needs work » Needs review
smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Putting back in front of committer eyes.

mondrake’s picture

Rebased.

longwave’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +ddd2023

Some tiny nitpicks/questions, and the Postgres tests had a random fail, but I have reviewed this twice now and think it is ready to go after this last round is responded to.

mondrake’s picture

Status: Needs work » Needs review

Here it comes, @longwave. Thanks

daffie’s picture

Status: Needs review » Reviewed & tested by the community

The changes look good to me.
Back to RTBC for me.
As long as the testbot is happy too.

  • longwave committed 97db62a1 on 11.x
    Issue #3217531 by mondrake, daffie, longwave: Deprecate usage of...
longwave’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed 97db62a138 to 11.x. Thanks!

Also published the change record.

mondrake’s picture

Thank you. Edited the CR to reflect the approach that was eventually committed.

mondrake’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.