Problem/Motivation

The testing framework is unable to use contributed drivers for KernelTests and FunctionalTests because when calling PHPUnit it passes the connection URL, and there is no available logic to identify and load non-core database drivers' code.

Proposed resolution

Make the needed modifications so that the proper database connection string can be passed between environments so that tests can be run:

  • Ensure that Database::getConnectionInfoAsUrl() contains the all the necessary information that is specified in the connection, also for non-core database drivers
  • Add helper methods to adapt an Uri object with connections options for the username and password
  • Componentize and centralize the conversion between Drupal's connection array and a connection URI and back
  • Add test coverage

Proposed Change Record

https://www.drupal.org/node/2896416

Remaining tasks

RBTC

User interface changes

None

API changes

Added a couple of helper methods to the database abstraction layer.

See proposed change record.

Data model changes

None

CommentFileSizeAuthor
#227 2605284-227.patch26.62 KBmondrake
#227 interdiff_223-227.txt3.99 KBmondrake
#223 interdiff_222-223.txt752 bytesmondrake
#223 2605284-223.patch26.5 KBmondrake
#222 2605284-222.patch26.5 KBmondrake
#222 interdiff_220-222.txt3.01 KBmondrake
#220 interdiff_218-220.txt729 bytesmondrake
#220 2605284-220.patch24.08 KBmondrake
#218 2605284-218.patch24.08 KBmondrake
#218 interdiff_216-218.txt1.59 KBmondrake
#215 interdiff_214-216.txt587 bytesmondrake
#215 2605284-216.patch22.49 KBmondrake
#214 interdiff_210-214.txt3.19 KBmondrake
#214 2605284-214.patch22.49 KBmondrake
#210 2605284-210.patch23.82 KBmondrake
#210 interdiff_207-210.txt7.07 KBmondrake
#207 2605284-207.patch21.44 KBmondrake
#207 interdiff_205-207.txt4.93 KBmondrake
#205 interdiff_202-205.txt5.77 KBmondrake
#205 2605284-205.patch19.49 KBmondrake
#202 2605284-202.patch18.57 KBmondrake
#197 interdiff_194-197.txt2.75 KBmondrake
#197 2605284-197.patch18.55 KBmondrake
#194 2605284-194.patch20.3 KBmondrake
#194 interdiff_193-194.txt729 bytesmondrake
#193 2605284-193.patch20.3 KBmondrake
#193 interdiff_191-193.txt1.36 KBmondrake
#191 interdiff_188-191.txt8.76 KBmondrake
#191 2605284-191.patch19.92 KBmondrake
#188 2605284-188.patch16.2 KBmondrake
#182 2605284-182.patch42.41 KBmondrake
#182 interdiff_167-182.txt2.57 KBmondrake
#169 interdiff_146-167.txt16.6 KBmondrake
#167 interdiff_164-167.txt722 bytesmondrake
#167 2605284-167.patch42.61 KBmondrake
#164 interdiff_163-164.txt776 bytesmondrake
#164 2605284-164.patch42.56 KBmondrake
#163 2605284-163.patch41.8 KBmondrake
#163 interdiff_161-163.txt4.59 KBmondrake
#161 interdiff_159-161.txt4.55 KBmondrake
#161 2605284-161.patch44.25 KBmondrake
#159 interdiff_146-159.txt15.92 KBmondrake
#159 2605284-159.patch43.34 KBmondrake
#154 2605284-154.patch42.91 KBmondrake
#154 interdiff_146-154.txt1.18 KBmondrake
#146 interdiff_143-146.txt4.62 KBmondrake
#146 2605284-146.patch40.8 KBmondrake
#143 2605284-143.patch42.37 KBmondrake
#143 interdiff_137-143.txt3.21 KBmondrake
#138 2605284-137.patch41.44 KBmondrake
#138 interdiff_136-137.txt1.02 KBmondrake
#137 2605284-136.patch41.44 KBmondrake
#137 interdiff_134-136.txt9.77 KBmondrake
#134 2605284-134.patch40.51 KBmondrake
#132 2605284-132.patch40.56 KBmondrake
#132 interdiff_129-132.txt8.63 KBmondrake
#130 interdiff_128-129.txt17.46 KBmondrake
#130 2605284-129.patch35.57 KBmondrake
#128 2605284-128.patch32.92 KBmondrake
#128 interdiff_121-128.txt2.86 KBmondrake
#121 2605284-121.patch29.76 KBjofitz
#121 interdiff-119-121.txt4.27 KBjofitz
#119 2605284-119.patch29.45 KBjofitz
#119 interdiff-116-119.txt1.38 KBjofitz
#116 simpletest_is_broken_on-2605284-115.patch29.92 KBdavid_garcia
#107 simpletest_is_broken_on-2605284-107.patch23.18 KBdavid_garcia
#88 interdiff-81-89.txt621 bytesdavid_garcia
#88 simpletest_is_broken_on-2605284-89.patch22.53 KBdavid_garcia
#81 simpletest_is_broken_on-2605284-81.patch22.47 KBjofitz
#81 interdiff-78-81.txt1.1 KBjofitz
#78 simpletest_is_broken_on-2605284-78.patch23.04 KBjofitz
#78 interdiff-75-78.txt1.58 KBjofitz
#75 simpletest_is_broken_on-2605284-75.patch24.14 KBGoZ
#75 interdiff-2605284-73-75.txt299 bytesGoZ
#73 simpletest_is_broken_on-2605284-73.patch23.9 KBGoZ
#69 interdiff-61-69.txt573 bytesdavid_garcia
#69 simpletest_is_broken_on-2605284-69.patch23.28 KBdavid_garcia
#62 interdiff-56-61.txt1.35 KBdavid_garcia
#61 simpletest_is_broken_on-2605284-61.patch23.28 KBdavid_garcia
#59 interdiff-55-56.txt1.3 KBdavid_garcia
#57 simpletest_is_broken_on-2605284-56.patch23.98 KBdavid_garcia
#55 interdiff-49-56.txt3.79 KBdavid_garcia
#55 simpletest_is_broken_on-2605284-55.patch23.59 KBdavid_garcia
#50 interdiff-46-49.txt1.15 KBdavid_garcia
#49 interdiff-46-49.patch20.75 KBdavid_garcia
#49 simpletest_is_broken_on-2605284-49.patch20.67 KBdavid_garcia
#46 simpletest_is_broken_on-2605284-46.patch20.75 KBdavid_garcia
#45 simpletest_is_broken_on-2605284-45.patch21.33 KBdavid_garcia
#42 simpletest_is_broken_on-2605284-42.patch20.51 KBdavid_garcia
#39 simpletest_is_broken_on-2605284-39.patch18.33 KBdavid_garcia
#39 simpletest_is_broken_on-2605284-38.patch18.33 KBdavid_garcia
#38 simpletest_is_broken_on-2605284-38.patch18.24 KBdavid_garcia
#27 interdiff-25-27.diff684 bytesGoZ
#27 interdiff-15-27.txt10.05 KBGoZ
#27 simpletest_is_broken_on-2605284-27.patch26.25 KBGoZ
#25 interdiff-15-25.txt10.05 KBGoZ
#25 simpletest_is_broken_on-2605284-25.patch26.25 KBGoZ
#22 2605284-22.patch53.1 KBPradnya Pingat
#15 2605284-15.patch22.8 KBalexpott
#15 13-15-interdiff.txt3.94 KBalexpott
#13 2605284-13.patch10.45 KBalexpott
#9 2605284-broken-simpletest.patch2.14 KBdavid_garcia
#7 2605284-broken-simpletest.patch2.82 KBdavid_garcia
#4 2605284-broken-simpletest.patch2.14 KBdavid_garcia
#3 2605284-broken-simpletest.patch1.16 KBdavid_garcia
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

david_garcia created an issue. See original summary.

david_garcia’s picture

So the issue is here:

function simpletest_phpunit_command() {
  // Load the actual autoloader being used and determine its filename using
  // reflection. We can determine the vendor directory based on that filename.
  $autoloader = require \Drupal::root() . '/autoload.php';
  $reflector = new ReflectionClass($autoloader);
  $vendor_dir = dirname(dirname($reflector->getFileName()));

  // Don't use the committed version in composer's bin dir if running on
  // windows.
  if (substr(PHP_OS, 0, 3) == 'WIN') {
    $php_executable_finder = new PhpExecutableFinder();
    $php = $php_executable_finder->find();
    $phpunit_bin = escapeshellarg($php) . ' -f ' . escapeshellarg($vendor_dir . '/phpunit/phpunit/composer/bin/phpunit') . ' --';
  }
  else {
    $phpunit_bin = $vendor_dir . '/phpunit/phpunit/phpunit';
  }
  return $phpunit_bin;
}

There seems to be custom logic for Windows regarding the phpunit path. But the generated path points to a non existent file.

david_garcia’s picture

Status: Active » Needs review
FileSize
1.16 KB

This doesn't really fix the issue, just to make sure it passes tests.

david_garcia’s picture

As if the path was the only broken thing... Database::openConnection() is doing quite a crap job trying to find out the connection class for a driver, combined with the mess inside install's .inc (core/include/install.inc):

drupal_get_database_types()
drupal_detect_database_types()

And of course, int SiteSettingsForm.php this is the elegant and straightforward way of determining the namespace for a driver:

 public function validateForm(array &$form, FormStateInterface $form_state) {
    $driver = $form_state->getValue('driver');
    $database = $form_state->getValue($driver);
    $drivers = drupal_get_database_types();
    $reflection = new \ReflectionClass($drivers[$driver]);
    $install_namespace = $reflection->getNamespaceName();
    // Cut the trailing \Install from namespace.
    $database['namespace'] = substr($install_namespace, 0, strrpos($install_namespace, '\\'));
    $database['driver'] = $driver;

    $form_state->set('database', $database);
    $errors = install_database_errors($database, $form_state->getValue('settings_file'));
    foreach ($errors as $name => $message) {
      $form_state->setErrorByName($name, $message);
    }
  }

Probably for a follow-up issue, driver detection and namespacing should be reviewed and moved to the Database class.

Embeding the namespace in the connection URL as a query string is just a quick workaround, Drupal should be able to - given a driver name - be able to tell what Connection class it is going to use. I tried to implement it but too many things needed to be changed.

david_garcia’s picture

Title: Simpletest is completely broken on Windows » Simpletest is broken on Windows and KernelTests don't work with contributed database drivers
Issue summary: View changes
david_garcia’s picture

Issue tags: +Needs Review
david_garcia’s picture

And when exceptions are thrown that does not necessarily mean that there is nothing meaningful that we can show in the test results...

Status: Needs review » Needs work

The last submitted patch, 7: 2605284-broken-simpletest.patch, failed testing.

david_garcia’s picture

Ok then, turn this 3 bug combo into just a 2 bug combo.

david_garcia’s picture

alexpott’s picture

Issue tags: +Triaged core major

Discussed with @catch and @xjm - we agreed that the that part of this issue about running tests on contributed database drivers is a major bug.

  1. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -489,6 +489,15 @@ public static function convertDbUrlToConnectionInfo($url, $root) {
    +      if (isset($parts['namespace'])) {
    +        $database['namespace'] = $parts['namespace'];
    
    @@ -525,6 +534,9 @@ public static function getConnectionInfoAsUrl($key = 'default') {
    +      $db_url .= '?namespace=' . $db_info['default']['namespace'];
    

    This is problematic... since namespace is SqlServer thing - other databases could have other requirements. Actually I think the bug here is that we should call the method on the selected database driver and not on the abstract base class. Then each db can do it's own thing.

  2. +++ b/core/modules/simpletest/simpletest.module
    @@ -184,7 +184,7 @@ function simpletest_run_phpunit_tests($test_id, array $unescaped_test_classnames
    -      'test_id' => '1',
    +      'test_id' => $test_id,
    

    I guess this is an unrelated change to help debugging? If so can this be filed as a separate issue.

  3. +++ b/core/modules/simpletest/simpletest.module
    @@ -316,7 +316,7 @@ function simpletest_phpunit_command() {
    -    $phpunit_bin = escapeshellarg($php) . ' -f ' . escapeshellarg($vendor_dir . '/phpunit/phpunit/composer/bin/phpunit') . ' --';
    +    $phpunit_bin = escapeshellarg($php) . ' -f ' . escapeshellarg($vendor_dir . '/phpunit/phpunit/phpunit') . ' --';
    

    This should be in a separate issue. Given that phpunit can be run from the command line I think this is a just a normal bug.

alexpott’s picture

So part of #11.1 is wrong - namespace is not an SqlServer concept - it is the namespace of the driver class - which is a Drupal concept. But the point still stands. We need to be able to defer to the real database driver class in these functions.

alexpott’s picture

Here's a patch that allows each database driver to do custom stuff to the URI <-> connection options conversions. And because SQLite is different in core it uses this.

Status: Needs review » Needs work

The last submitted patch, 13: 2605284-13.patch, failed testing.

alexpott’s picture

david_garcia’s picture

This should be in a separate issue. Given that phpunit can be run from the command line I think this is a just a normal bug.

Yep I tried to stuff a 3 bug combo into a single patch. Just being practial.

I'll give this a decent review when I get some time. Your patch is touching more stuff than mine did, but for good :)

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Status: Needs review » Needs work

The last submitted patch, 15: 2605284-15.patch, failed testing.

daffie’s picture

A quick review:

  1. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -6,6 +6,7 @@
     namespace Drupal\Core\Database;
    +use GuzzleHttp\Psr7\Uri;
    

    Nitpick: No black line between the two.

  2. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1433,4 +1434,73 @@ public function __sleep() {
    +   * This method can be overridden in order to customise how a URI is converted.
    ...
    +   * This method can be overridden in order to customise how connection options
    +   * are converted.
    

    Is this necessary? Basic object oriented coding stuff.

  3. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1433,4 +1434,73 @@ public function __sleep() {
    +   * @internal
    +   *   This method should not be called. Use
    +   *   \Drupal\Core\Database\Database::convertDbUrlToConnectionInfo() instead.
    ...
    +  public static function convertDbUrlToConnectionInfoHelper(Uri $uri, $root, array $connection_options) {
    ...
    +   * @internal
    +   *   This method should not be called. Use
    +   *   \Drupal\Core\Database\Database::getConnectionInfoAsUrl() instead.
    ...
    +  public static function getConnectionInfoAsUrlHelper(array $connection_options, Uri $uri) {
    

    I see the problem. But I do think that internal function should be protected.

  4. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1433,4 +1434,73 @@ public function __sleep() {
    +  public static function convertDbUrlToConnectionInfoHelper(Uri $uri, $root, array $connection_options) {
    ...
    +  public static function getConnectionInfoAsUrlHelper(array $connection_options, Uri $uri) {
    
    +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
    @@ -415,4 +416,30 @@ public function getFullQualifiedTableName($table) {
    +  public static function convertDbUrlToConnectionInfoHelper(Uri $uri, $root, array $database) {
    ...
    +  public static function getConnectionInfoAsUrlHelper(array $connection_options, Uri $uri) {
    

    The class GuzzleHttp\Psr7\Uri implements the interface Psr\Http\Message\UriInterface. Should we not use the interface for parameter typehinting.

  5. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1433,4 +1434,73 @@ public function __sleep() {
    +    $user = '';
    +    if ($connection_options['username']) {
    +      $user = $connection_options['username'];
    +    }
    +    $password = '';
    +    if ($connection_options['password']) {
    +      $password = $connection_options['password'];
    +    }
    

    Can we rewrite this to:

    $user = $connection_options['username'] ?: ‘’;
    $password = $connection_options[‘password’] ?: ‘’;
    
  6. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -460,36 +457,33 @@ public static function ignoreTarget($key, $target) {
    +    if (empty($uri->getHost()) || empty($uri->getHost()) || empty($uri->getPath())) {
    

    There are two getHost()'s. One should be getSchema().

  7. +++ b/core/tests/Drupal/Tests/Core/Database/DatabaseTest.php
    @@ -0,0 +1,176 @@
    +  public function testNoConnectionInfo() {
    

    There is no docblock and no @covers data.

  8. +++ b/core/tests/Drupal/Tests/Core/Database/DatabaseTest.php
    @@ -0,0 +1,176 @@
    +
    +/**
    + * @file
    + * Contains \Drupal\Tests\Core\Database\DatabaseTest.
    + */
    

    Nitpick: We do not do this any more.

  9. +++ b/core/tests/Drupal/Tests/Core/Database/DatabaseTest.php
    @@ -0,0 +1,176 @@
    \ No newline at end of file
    

    Nitpick: No new line.

daffie’s picture

Issue tags: +Needs reroll
Pradnya Pingat’s picture

Assigned: Unassigned » Pradnya Pingat
Issue tags: +DrupalMumbaiCodeSprint15

I am working on rerolling patch

Pradnya Pingat’s picture

Assigned: Pradnya Pingat » Unassigned
Status: Needs work » Needs review
FileSize
53.1 KB

I fixed the issues according to comment #19 except point no 2 and 3 , need more explanation about that.

Thanks

Status: Needs review » Needs work

The last submitted patch, 22: 2605284-22.patch, failed testing.

mradcliffe’s picture

Thank you for contributing the patch, @Pradnya Pingat.

The patch in #22 makes many coding standard changes. You may want to confirm your IDE or text editor is configured for the Drupal coding standards. The child pages linked at the bottom of the Development tools page may help as well.

GoZ’s picture

Status: Needs work » Needs review
FileSize
26.25 KB
10.05 KB

Rewrite #22 fixing coding standard issues. I only fix those added by #22.

I think a big clean should be done on all this files but it's not the purpose of this issue.

+++ b/core/lib/Drupal/Core/Database/Connection.php
@@ -1433,4 +1434,73 @@ public function __sleep() {
+   * This method can be overridden in order to customise how a URI is converted.
...
+   * This method can be overridden in order to customise how connection options
+   * are converted.
Is this necessary? Basic object oriented coding stuff.

I agree with #19 2., so i remove this part of comment.

+++ b/core/lib/Drupal/Core/Database/Connection.php
@@ -1433,4 +1434,73 @@ public function __sleep() {
+   * @internal
+   *   This method should not be called. Use
+   *   \Drupal\Core\Database\Database::convertDbUrlToConnectionInfo() instead.
...
+  public static function convertDbUrlToConnectionInfoHelper(Uri $uri, $root, array $connection_options) {
...
+   * @internal
+   *   This method should not be called. Use
+   *   \Drupal\Core\Database\Database::getConnectionInfoAsUrl() instead.
...
+  public static function getConnectionInfoAsUrlHelper(array $connection_options, Uri $uri) {
I see the problem. But I do think that internal function should be protected.

For #19 3. I understand why alexpott put this @internal comment and i think we should keep it, so contributor know this method should not be called directly.

Status: Needs review » Needs work

The last submitted patch, 25: simpletest_is_broken_on-2605284-25.patch, failed testing.

GoZ’s picture

Status: Needs review » Needs work

The last submitted patch, 27: interdiff-25-27.diff, failed testing.

GoZ’s picture

Status: Needs work » Needs review

I cancel test for interdiff. Sorry for that

The last submitted patch, 27: simpletest_is_broken_on-2605284-27.patch, failed testing.

daffie’s picture

Status: Needs review » Needs work

It looks good. Got 2 points left:

  1. In the patch from comment #15 is the file core/tests/Drupal/Tests/Core/Database/Stub/StubConnection.php renamed to core/tests/Drupal/Tests/Core/Database/Stub/Connection.php. Why is that not do?
  2. All my remarks from comment #19 are addressed except for #19.4.

I only can do a code review because I have no windows PC.

GoZ’s picture

+++ b/core/lib/Drupal/Core/Database/Database.php
@@ -455,36 +452,33 @@ public static function ignoreTarget($key, $target) {
+    $connection_opptions = array(
...
+    $driver_class = static::getDatabaseConnectionClass($connection_opptions['driver'], $connection_opptions['namespace']);
...
+    return $driver_class::convertDbUrlToConnectionInfoHelper($uri, $root, $connection_opptions);

Error typing, $connection_opptions should be $connection_options.

In the patch from comment #15 is the file core/tests/Drupal/Tests/Core/Database/Stub/StubConnection.php renamed to core/tests/Drupal/Tests/Core/Database/Stub/Connection.php. Why is that not do?
All my remarks from comment #19 are addressed except for #19.4.

Good question, i rename it, but it seems git diff make them separated as delete and create.
Interdiff make some strange things to. It the first time i use interdiff, i usually use git diff between to patched branchs, but i cannot apply alexpott patch. May be i miss something.

4.

+++ b/core/lib/Drupal/Core/Database/Connection.php
@@ -1433,4 +1434,73 @@ public function __sleep() {
+  public static function convertDbUrlToConnectionInfoHelper(Uri $uri, $root, array $connection_options) {
...
+  public static function getConnectionInfoAsUrlHelper(array $connection_options, Uri $uri) {

+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
@@ -415,4 +416,30 @@ public function getFullQualifiedTableName($table) {
+  public static function convertDbUrlToConnectionInfoHelper(Uri $uri, $root, array $database) {
...
+  public static function getConnectionInfoAsUrlHelper(array $connection_options, Uri $uri) {

The class GuzzleHttp\Psr7\Uri implements the interface Psr\Http\Message\UriInterface. Should we not use the interface for parameter typehinting.

I don't understand what you want to do with Interface.

daffie’s picture

@GoZ: I think you get the git diff rename with the commend git diff -C -M or git diff -C -M HEAD. And a good find with connection_opption.

In Drupal we like to use an interface for typehinting, so that we can change the implementing class with an other one without having to do an API change. We now have only to make sure that the replacing class implements the interface (Psr\Http\Message\UriInterface).
We would like to use the class GuzzleHttp\Psr7\Uri. And if we look at that class we see that that class is implementing the interface Psr\Http\Message\UriInterface. So the code change that I am suggesting is:

At the top of the file we add: use Psr\Http\Message\UriInterface;

+++ b/core/lib/Drupal/Core/Database/Connection.php
@@ -1425,4 +1427,62 @@ public function __sleep() {
-  public static function convertDbUrlToConnectionInfoHelper(Uri $uri, $root, array $connection_options) {
+  public static function convertDbUrlToConnectionInfoHelper(UriInterface $uri, $root, array $connection_options) {
...
-  public static function getConnectionInfoAsUrlHelper(array $connection_options, Uri $uri) {
+  public static function getConnectionInfoAsUrlHelper(array $connection_options, UriInterface $uri) {

+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
@@ -415,4 +416,30 @@ public function getFullQualifiedTableName($table) {
-  public static function convertDbUrlToConnectionInfoHelper(Uri $uri, $root, array $database) {
+  public static function convertDbUrlToConnectionInfoHelper(UriInterface $uri, $root, array $database) {
...
-  public static function getConnectionInfoAsUrlHelper(array $connection_options, Uri $uri) {
+  public static function getConnectionInfoAsUrlHelper(array $connection_options, UriInterface $uri) {
GoZ’s picture

Trying to figure out why tests fail, here is what i found :

Test fail in KernelTestBaseTest::testGetDatabaseConnectionInfoWithOutManualSetDbUrl() comparing prefix database connection.

  /**
   * @covers ::getDatabaseConnectionInfo
   */
  public function testGetDatabaseConnectionInfoWithOutManualSetDbUrl() {
    $options = $this->container->get('database')->getConnectionOptions();
    $this->assertSame($this->databasePrefix, $options['prefix']['default']);
  }

In this test, default prefix from connectionOptions is composed of two concatenated prefixes. Example : simpletest123456simpletest987654. This concatenation is made by KernelTestBase::getDatabaseConnectionInfo().

  /**
   * Returns the Database connection info to be used for this test.
   *
   * This method only exists for tests of the Database component itself, because
   * they require multiple database connections. Each SQLite :memory: connection
   * creates a new/separate database in memory. A shared-memory SQLite file URI
   * triggers PHP open_basedir/allow_url_fopen/allow_url_include restrictions.
   * Due to that, Database tests are running against a SQLite database that is
   * located in an actual file in the system's temporary directory.
   *
   * Other tests should not override this method.
   *
   * @return array
   *   A Database connection info array.
   *
   * @internal
   */
  protected function getDatabaseConnectionInfo() {
    // If the test is run with argument dburl then use it.
    $db_url = getenv('SIMPLETEST_DB');
    if (empty($db_url)) {
      throw new \Exception('There is no database connection so no tests can be run. You must provide a SIMPLETEST_DB environment variable to run PHPUnit based functional tests outside of run-tests.sh. See https://www.drupal.org/node/2116263#skipped-tests for more information.');
    }
    else {
      $database = Database::convertDbUrlToConnectionInfo($db_url, $this->root);
      Database::addConnectionInfo('default', 'default', $database);
    }

    // Clone the current connection and replace the current prefix.
    $connection_info = Database::getConnectionInfo('default');
    if (!empty($connection_info)) {
      Database::renameConnection('default', 'simpletest_original_default');
      foreach ($connection_info as $target => $value) {
        // Replace the full table prefix definition to ensure that no table
        // prefixes of the test runner leak into the test.
        $connection_info[$target]['prefix'] = array(
          'default' => $value['prefix']['default'] . $this->databasePrefix,
        );
      }
    }
    return $connection_info;
  }

Before this patch, in KernelTestBase::getDatabaseConnectionInfo(), default prefix values was empty. It was empty because Database::convertDbUrlToConnectionInfo() never set prefix (get it in $info variable but never put it in $database).

Before patch:

  public static function convertDbUrlToConnectionInfo($url, $root) {
    $info = parse_url($url);
    if (!isset($info['scheme'], $info['host'], $info['path'])) {
      throw new \InvalidArgumentException('Minimum requirement: driver://host/database');
    }
    $info += array(
      'user' => '',
      'pass' => '',
      'fragment' => '',
    );

    // A SQLite database path with two leading slashes indicates a system path.
    // Otherwise the path is relative to the Drupal root.
    if ($info['path'][0] === '/') {
      $info['path'] = substr($info['path'], 1);
    }
    if ($info['scheme'] === 'sqlite' && $info['path'][0] !== '/') {
      $info['path'] = $root . '/' . $info['path'];
    }

    $database = array(
      'driver' => $info['scheme'],
      'username' => $info['user'],
      'password' => $info['pass'],
      'host' => $info['host'],
      'database' => $info['path'],
    );
    if (isset($info['port'])) {
      $database['port'] = $info['port'];
    }
    return $database;
  }

With the patch, prefix is setted.
After patch:

  public static function convertDbUrlToConnectionInfo($url, $root) {
    $uri = new Uri($url);
    if (empty($uri->getHost()) || empty($uri->getScheme()) || empty($uri->getPath())) {
      throw new \InvalidArgumentException('Minimum requirement: driver://host/database');
    }

    // Discover if the URL has a driver namespace.
    $parts = array();
    parse_str($uri->getQuery(), $parts);
    $namespace = isset($parts['namespace']) ? $parts['namespace'] : '';

    $fragment = $uri->getFragment();
    $connection_options = array(
      'driver' => $uri->getScheme(),
      'host' => $uri->getHost(),
      // Strip the first leading slash of the path to get the database name.
      // Note that additional leading slashes have meaning for some database
      // drivers.
      'database' => substr($uri->getPath(), 1),
      'prefix' => $fragment ?: NULL,
      'namespace' => $namespace,
    );

    $driver_class = static::getDatabaseConnectionClass($connection_options['driver'], $connection_options['namespace']);
    if (!class_exists($driver_class)) {
      throw new \RuntimeException("Can not convert $url to a database connection, class $driver_class does not exist");
    }
    return $driver_class::convertDbUrlToConnectionInfoHelper($uri, $root, $connection_options);
  }

We should see this issue with someone who implemented KernelTestBase and Database class (see #2232861: Create BrowserTestBase for web-testing on top of Mink and #2304461: KernelTestBaseTNG™) or with someone who can tell if:

  1. Database::convertDbUrlToConnectionInfo() should return prefix, so tests are wrong.
  2. Database::convertDbUrlToConnectionInfo() should not return prefix, so tests are right and this patch should remove prefix from this method.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

david_garcia’s picture

Title: Simpletest is broken on Windows and KernelTests don't work with contributed database drivers » Testing framework does not work with contributed database drivers
david_garcia’s picture

Issue summary: View changes
david_garcia’s picture

Version: 8.2.x-dev » 8.3.x-dev
Status: Needs work » Needs review
FileSize
18.24 KB

Rerolled as it did not apply anymore.

david_garcia’s picture

The last submitted patch, 38: simpletest_is_broken_on-2605284-38.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 39: simpletest_is_broken_on-2605284-39.patch, failed testing.

david_garcia’s picture

Fixes some tests.

david_garcia’s picture

I am wondering if we should refactor the installer to move driver discovery into a method:

// The internal database driver name is any valid PHP identifier.
  $mask = '/^' . DRUPAL_PHP_FUNCTION_PATTERN . '$/';
  $files = file_scan_directory(DRUPAL_ROOT . '/core/lib/Drupal/Core/Database/Driver', $mask, array('recurse' => FALSE));
  if (is_dir(DRUPAL_ROOT . '/drivers/lib/Drupal/Driver/Database')) {
    $files += file_scan_directory(DRUPAL_ROOT . '/drivers/lib/Drupal/Driver/Database/', $mask, array('recurse' => FALSE));
  }
  foreach ($files as $file) {
    if (file_exists($file->uri . '/Install/Tasks.php')) {
      $drivers[$file->filename] = $file->uri;
    }
  }

And instead of storing the "namespace" in the URL representing the connection, have the convertDbUrlToConnectionInfo() method look for the driver name inside the filesystem in the same way it does during the install process.

And methods like this one look horribly bad to me:

function db_installer_object($driver) {
  // We cannot use Database::getConnection->getDriverClass() here, because
  // the connection object is not yet functional.
  $task_class = "Drupal\\Core\\Database\\Driver\\{$driver}\\Install\\Tasks";
  if (class_exists($task_class)) {
    return new $task_class();
  }
  else {
    $task_class = "Drupal\\Driver\\Database\\{$driver}\\Install\\Tasks";
    return new $task_class();
  }
}

But looking into this might be out of scope...

Status: Needs review » Needs work

The last submitted patch, 42: simpletest_is_broken_on-2605284-42.patch, failed testing.

david_garcia’s picture

Status: Needs work » Needs review
FileSize
21.33 KB

This fixes the issue described in #34.

I'm not sure what the test is really testing, but given that this is a simpletest nested call (simpletest UI being tested from a simpletest itself) I believe we whould be getting nested database prefixes. So updated the test acoordingly.

david_garcia’s picture

The last submitted patch, 45: simpletest_is_broken_on-2605284-45.patch, failed testing.

GoZ’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
@@ -437,4 +438,30 @@ public function getFullQualifiedTableName($table) {
+      // If the database is a absolute path add an additional leading slash to

Just a little thing in comments, replace 'a absolute path' by 'an absolute path'.
Sorry for this insignificant review :/

Everything else looks OK. Good job david_garcia

david_garcia’s picture

david_garcia’s picture

FileSize
1.15 KB

First attempts at an interdiff ever :(

GoZ’s picture

@david_garcia, can you cancel test on interdiff file please ?

I miss the document comment. You are right

Status: Needs review » Needs work

The last submitted patch, 49: interdiff-46-49.patch, failed testing.

GoZ’s picture

Status: Needs work » Needs review

Re-launch tests for #49 fail. Test fails due to random javascript fail see #2837676: Provide a better way to validate all javascript activity is completed

GoZ’s picture

Status: Needs review » Needs work

Tests fail with SQLite

david_garcia’s picture

Let's try with this.

jofitz’s picture

Issue tags: -Needs reroll

No longer appears to require reroll - tag removed.

david_garcia’s picture

GoZ’s picture

@david_garcia Can you add interdiff please so we can see difference between both patchs ? thanks

david_garcia’s picture

FileSize
1.3 KB

This was it.

So doing a little summary:

We should see this issue with someone who implemented KernelTestBase and Database class (see #2232861: Create BrowserTestBase for web-testing on top of Mink and #2304461: KernelTestBaseTNG™) or with someone who can tell if:

Database::convertDbUrlToConnectionInfo() should return prefix, so tests are wrong.
Database::convertDbUrlToConnectionInfo() should not return prefix, so tests are right and this patch should remove prefix from this method.

UrlToConnectionInfo() and ConnectionInfoToUrl() should deal with the prefix. Prefix is an important part to identify an installation/connection string.

In the case of tests, connection info is passed between installs using it's URL form and though the prefix is "not needed" (because each test environment will generate it's own prefix and override whatever prefix comes from the upstream environment), the essence of these methods require the prefix to be part of the information sent back and forth.

My assumptions in #45 were wrong. Nested tests should not have nested database prefixes, as each test environment generates it's own and unique database prefix, hence this change:

         $connection_info[$target]['prefix'] = array(
-           'default' => $value['prefix']['default'] . $this->databasePrefix,
+          'default' => $this->databasePrefix,
         );

The failing tests in #55 were due to KernelTestBaseTest.php assuming $this->databasePrefix was the real prefix being used (which SHOULD be) but the lines I just copied above this text broke that completely now that the prefixes are properly embeded into the URL generated by getConnectionInfoAsUrl:

$result = $connection->query("SELECT name FROM " . $this->databasePrefix . ".sqlite_master WHERE type = :type AND name LIKE :table_name AND name NOT LIKE :pattern", array(
        ':type' => 'table',
        ':table_name' => '%',
        ':pattern' => 'sqlite_%',
      ))->fetchAllKeyed(0, 0);

      $this->assertTrue(empty($result), 'All test tables have been removed.');

Because in this case, $this->databasePrefix() is generated in such a way that it is unique and will ensure no leakage happens between tests, or main site and tests.

Test coverage has also been expanded in the final patch to ensure that the convertConnectionInfoToUrl() and convertDbUrlToConnectionInfo() properly work back and forth, including namespace support.

daffie’s picture

The patch looks good. I do have some remarks:

  1. +++ b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php
    @@ -48,6 +48,9 @@ public function testBootEnvironment() {
    +    if (Database::getConnection()->driver() == 'sqlite') {
    +      $this->markTestSkipped('SQLITE modifies the prefixes so we cannot effectively test it');
    +    }
    

    Is this really necessary! With your comment in #59 can we try the patch without this added.

  2. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -498,29 +492,64 @@ public static function convertDbUrlToConnectionInfo($url, $root) {
    +    $uri = new Uri($db_info['default']['driver'] . '://' . $host . '/' . $db_info['default']['database']);
    

    Can we use the methods withScheme(), withHost() and withPath().

@david_garcia: You are the maintainer for the Microsoft SQLServer. Can you confirm that this patch works for the Microsoft SQLServer driver? And what namespace do you use?

david_garcia’s picture

Depending on how this performs on the tests I'll expand my comments.

david_garcia’s picture

FileSize
1.35 KB

Is this really necessary! With your comment in #59 can we try the patch without this added.

True, this was not needed anymore.

Can we use the methods withScheme(), withHost() and withPath().

Done.

Can you confirm that this patch works for the Microsoft SQLServer driver? And what namespace do you use?

https://ci.appveyor.com/project/david-garcia-garcia/sqlsrv/build/1.0.53

Other things are broken, but the test suite itself is working.

And what namespace do you use?

It's not that I pick a namespace... the way Drupal Core is designed contributed drivers are forced to be place into "Drupal\\Driver\\Database\\contribdrivername"

In the SQL Server case it's "Drupal\\Driver\\Database\\sqlsrv".

The current approach breaks a composer based workflow (you can workaround it with a few xcopy commands in post-install scripts), that is another matter.

daffie’s picture

For me the patch is almost RTBC. I do have one issue left:

  1. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -2,6 +2,8 @@
    +
    
    @@ -498,29 +492,67 @@ public static function convertDbUrlToConnectionInfo($url, $root) {
    +    $uri = $uri->withScheme($db_info['default']['driver'])
    +      ->withHost($host)
    +      ->withPath($db_info['default']['database']);
    +
    +    if (!empty($db_info['default']['prefix']['default'])) {
    +      $uri = $uri->withFragment($db_info['default']['prefix']['default']);
    +    }
    

    Why do we not remove $uri = part? So we will get:

       $uri->withScheme($db_info['default']['driver'])
         ->withHost($host)
         ->withPath($db_info['default']['database']);
    
       if (!empty($db_info['default']['prefix']['default'])) {
         $uri->withFragment($db_info['default']['prefix']['default']);
       }
    
  2. The same for:
    +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1471,4 +1473,62 @@ public function __sleep() {
    +    $uri = $uri->withUserInfo($user, $password);
    +    if (!empty($connection_options['port'])) {
    +      $uri = $uri->withPort($connection_options['port']);
    +    }
    
  3. The same for:
    +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
    @@ -437,4 +438,30 @@ public function getFullQualifiedTableName($table) {
    +      $uri = $uri->withPath('/' . $connection_options['database']);
    

@david_garcia: Can you elaborate on what "Other things are broken". I have written a comment #2209307-47: Convert database drivers into a regular extension type and I was wondering what you think about it. And thank you for your interdiff file!

david_garcia’s picture

Status: Needs work » Needs review

Why do we not remove $uri = part?

Because these methods do not modify the original URI object, but return a new URI object with the requested changes.

Can you elaborate on what "Other things are broken"

I linked to an appveyour CI for the sqlsrv driver (which I'm just setting up) and there are lots of tests failures, mostly due to the build script still having some flaws. It's better now than before, but still too many failures.

[...] I was wondering what you think about it.

Will post my comments there.

daffie’s picture

Status: Needs review » Reviewed & tested by the community

With the information from the previous comment, the patch is for me RTBC.
I do not have a contributed driver to test with and @david_garcia claims/proves that the patch works for MS SQL server driver.
The code looks good to me.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

catch’s picture

Component: simpletest.module » database system
Issue tags: +Needs subsystem maintainer review

This looks good to me (just not sure on the Helper suffix to the new methods on Drupal\Core\Database\Connection), but I think it'd be good to get review from a database subsystem maintainer, so tagging for that. Moving to database since the more serious changes are there. Getting driver-specific logic into drivers is always good.

xjm’s picture

Status: Reviewed & tested by the community » Needs review

NR #67. Thanks @catch!

david_garcia’s picture

Using this patch on a project that has an updated version of Guzzle/Psr7, it crashes when calling ->withPath():

The path of a URI with an authority must start with a slash "/" or be empty

Here's the fix.

daffie’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs subsystem maintainer review

Now that @Crell is gone and @David_Strauss has not been seen in the database issue queue for months, am I removing the tag "Needs subsystem maintainer review" and putting it back to RTBC.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 69: simpletest_is_broken_on-2605284-69.patch, failed testing.

GoZ’s picture

Status: Needs review » Needs work

The last submitted patch, 73: simpletest_is_broken_on-2605284-73.patch, failed testing.

GoZ’s picture

Miss to rename stubConnection.php to Connection.php

david_garcia’s picture

Status: Needs review » Reviewed & tested by the community

Back to RBTC

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/modules/simpletest/simpletest.module
    @@ -337,7 +339,7 @@ function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpun
    -    $escaped_test_classnames = array_map(function($class) {
    +    $escaped_test_classnames = array_map(function ($class) {
    
    +++ b/core/modules/system/tests/src/Kernel/Scripts/DbCommandBaseTest.php
    @@ -1,10 +1,5 @@
    -/**
    - * @file
    - * Contains \Drupal\Tests\system\Kernel\Scripts\DbCommandBaseTest.
    - */
    -
    

    These changes, whilst correct, are out-of-scope. The @file change is especially problematic as this is supposed to be checked by phpcs but apparently the sniff is broken in some way. We need to find out why (it's because there is one than one class in this file). So we need to file an issue against the coder module and also against core to fix it. Fixing it in core might involve just move the test class to its own file like it should be in strict psr-0/4.

  2. +++ b/core/tests/Drupal/Tests/Core/Database/ConnectionTest.php
    @@ -46,10 +46,10 @@ public function providerPrefixRoundTrip() {
    -    $reflection = new \ReflectionClass('Drupal\Tests\Core\Database\Stub\StubConnection');
    +    $reflection = new \ReflectionClass('Drupal\Tests\Core\Database\Stub\Connection');
    
    @@ -214,9 +214,9 @@ public function testSchema($expected, $driver, $namespace) {
    -      'Drupal\Tests\Core\Database\Stub\StubConnection',
    +      'Drupal\Tests\Core\Database\Stub\Connection',
    
    @@ -288,10 +288,10 @@ public function providerFilterComments() {
    -    $reflection = new \ReflectionClass('Drupal\Tests\Core\Database\Stub\StubConnection');
    +    $reflection = new \ReflectionClass('Drupal\Tests\Core\Database\Stub\Connection');
    

    If we're chagning this (and as there are other things to do) Let's do Connection::class instead of the hard-coded fully-qualified-classname.

jofitz’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
1.58 KB
23.04 KB

Replaced 'Drupal\Tests\Core\Database\Stub\Connection' with Connection::class as per 77.2.

jofitz’s picture

Status: Needs review » Needs work

Setting back to Needs Work for 77.1, I tried to open a new ticket against core, but couldn't quite put the problems into words, sorry.

jofitz’s picture

Re-instated @file. Reverted out-of-scope code-style correction.

daffie’s picture

Issue tags: -Novice

@Jo Fitzgerald: Thanks.

daffie’s picture

Status: Needs review » Reviewed & tested by the community

All points of @alexpott are addressed.
Back to RTBC.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs change record, +Needs issue summary update
+++ b/core/tests/Drupal/KernelTests/KernelTestBase.php
@@ -457,7 +457,7 @@ protected function getDatabaseConnectionInfo() {
-          'default' => $value['prefix']['default'] . $this->databasePrefix,
+          'default' => $this->databasePrefix,

This change is odd - how come? Reading back... the answer is in #34. Looks good and makes sense.

Sorry I've realised that there is more work we need - the issue summary doesn't really contain the arrived at solution and we need a CR to tell alternate DB drivers how to be updated for this change.

david_garcia’s picture

Status: Needs work » Needs review

#84 The change was discussed #59.

What happens here is that nested tests generate their own unique prefixes for subsites (at some point in the tests there's a 2nd level nesting), so there is no need to propagate prefixes. Can't recall exactly why, but the additional $value['prefix']['default'] simply made no sense, and did not match the behaviour of KernelTestBase expecting the test ID to be uniquely generated without reference to the parent test.

the issue summary doesn't really contain the arrived at solution

This is not always easy...

dawehner’s picture

Issue summary: View changes
  1. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -2,6 +2,8 @@
    +use GuzzleHttp\Psr7\Uri;
    
    @@ -455,36 +452,33 @@ public static function ignoreTarget($key, $target) {
    +    $uri = new Uri($url);
    

    Am I the only one who is a bit confused about adding a HTTP library as dependency for a database subsystem?

  2. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -455,36 +452,33 @@ public static function ignoreTarget($key, $target) {
       public static function convertDbUrlToConnectionInfo($url, $root) {
    

    I'm wondering where we could document this general capability.

  3. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -455,36 +452,33 @@ public static function ignoreTarget($key, $target) {
    +    $driver_class = static::getDatabaseConnectionClass($connection_options['driver'], $connection_options['namespace']);
    +    if (!class_exists($driver_class)) {
    +      throw new \RuntimeException("Can not convert $url to a database connection, class $driver_class does not exist");
         }
    

    I like checking for misconfigured database connections here. This allows people to debug failures much easier than when the system breaks somewhere later.

  4. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -498,29 +492,67 @@ public static function convertDbUrlToConnectionInfo($url, $root) {
    +  public static function convertConnectionInfoToUrl($db_info) {
    

    We could typehint with array.

  5. +++ b/core/modules/simpletest/simpletest.module
    @@ -308,9 +308,11 @@ function simpletest_phpunit_configuration_filepath() {
       global $base_url;
    -  // Setup an environment variable containing the database connection so that
    -  // functional tests can connect to the database.
    -  putenv('SIMPLETEST_DB=' . Database::getConnectionInfoAsUrl());
    +  if (Database::getConnectionInfo()) {
    

    ... It'd be nice to document why getConnectionInfo could fail here.

the issue summary doesn't really contain the arrived at solution

This is not always easy...

Well, right, it requires you to read the patch and the issue :) I did that now, so here is my approach to it.

+++ b/core/lib/Drupal/Core/Database/Connection.php
@@ -1471,4 +1473,62 @@ public function __sleep() {
+  public static function convertDbUrlToConnectionInfoHelper(UriInterface $uri, $root, array $connection_options) {
...
+  public static function getConnectionInfoAsUrlHelper(array $connection_options, UriInterface $uri) {

Is it just me that "helper" as a method name is kind of not descriptive?

mondrake’s picture

1. Tested the patch in #81 in conjuction with an experimental driver for Doctrine DBAL https://github.com/mondrake/drudbal. Works fine, thank you.

2. In the driver's code, added overrides for Connection::getConnectionInfoAsUrlHelper and for Connection::convertDbUrlToConnectionInfoHelper, in order to add an additional part to the URL querystring. It also work fine. Added this below as an example for a CR if you believe relevant.

  /**
   * {@inheritdoc}
   */
  public static function getConnectionInfoAsUrlHelper(array $connection_options, UriInterface $uri) {
    $uri = parent::getConnectionInfoAsUrlHelper($connection_options, $uri);
    // Add the 'dbal_driver' key to the URI.
    if (!empty($connection_options['dbal_driver'])) {
      $uri = Uri::withQueryValue($uri, 'dbal_driver', $connection_options['dbal_driver']);
    }
    return $uri;
  }
  /**
   * {@inheritdoc}
   */
  public static function convertDbUrlToConnectionInfoHelper(UriInterface $uri, $root, array $connection_options) {
    $connection_options = parent::convertDbUrlToConnectionInfoHelper($uri, $root, $connection_options);
    // Add the 'dbal_driver' key to the connection options.
    $parts = [];
    parse_str($uri->getQuery(), $parts);
    $dbal_driver = isset($parts['dbal_driver']) ? $parts['dbal_driver'] : '';
    $connection_options['dbal_driver'] = $dbal_driver;
    return $connection_options;
  }

Note: if you use the URL in the phpunit.xml file to set the db environment for testing, and the driver adds parts to the querystring, you have to manually convert the querystring separators & to HTML entities counterparts &amp;. So, if you get

dbal://username:password@host:port/database?namespace=Drupal%5CDriver%5CDatabase%5Cdbal&dbal_driver=pdo_mysql#prefix

from call to \Drupal\Core\Database\Database::getConnectionInfoAsUrl, your phpunit.xml entry should be something like

<env name="SIMPLETEST_DB" value="dbal://username:password@host:port/database?namespace=Drupal%5CDriver%5CDatabase%5Cdbal&amp;dbal_driver=pdo_mysql#prefix"/>

3. While trying to get a clean run of phpunit --group Database with the driver, I incurred in a couple of issues that IMO need change in core:
a) Contrib/custom database drivers are not in the Drupal\Core\Database namespace, and in this case Log::findCaller will not report the query caller correctly. Filed #2867788: Log::findCaller fails to report the correct caller function with non-core drivers. for that.
b) If a database driver (contrib) is not implementing a PDO connection as Connection::$connection, the ConnectionUnitTest::testConnectionOpen test fails because it expects that the connction is PDO and PDO methods are available. Filed #2867700: ConnectionUnitTest::testConnectionOpen fails if the driver is not implementing a PDO connection for that.

david_garcia’s picture

About comments in #86

Am I the only one who is a bit confused about adding a HTTP library as dependency for a database subsystem?

We use URI's to move around connection info between installs (convertConnectionInfoToUrl and convertDbUrlToConnectionInfoHelper).

Having tools to reliably manipulate this URI's requires an HTTP library...

[convertDbUrlToConnectionInfo] I'm wondering where we could document this general capability.

Well... passing around connection url's is something very specific to the testing framework (don't see where else it would/could be used). Not sure if documenting this somewhere would really have any impact.

[convertDbUrlToConnectionInfo] We could typehint with array.

Done.

dawehner’s picture

Not sure if documenting this somewhere would really have any impact.

Call me weird, but if you don't know whether it has an impact than it certainly has one :) You know that kind of stuff already, but what if someone stumbles upon on. They wonder why its there etc. The amount of hidden costs in maintaining software is a LOT.

david_garcia’s picture

I'm wondering where we could document this general capability.

But this is nothing to be dealt with in the patch itself right? I mean, this is for the docs in drupal.org or something similar.

mondrake’s picture

Adding a related issue.

Does anybody know is there any Drush issue to allow Drush si with a --db-url option that will be according to the changes in this issue? ATM Drush can only cope with core db drivers AFAICS

amateescu’s picture

I fully agree with @dawehner in #86.1, it is very weird to see a HTTP library in the DB layer. Why can't we continue to use parse_url() and send around the array that results from it?

david_garcia’s picture

@amateescu: Why can't we continue to use parse_url() and send around the array that results from it?

Because it is way more robust to use the URI builder than dealing with magic arrays. I.E. the bug in #69 only showed up thanks to using such a library, that ensures that URI's are well formed. This makes code more maintainable and error-proof.

@mondrake Does anybody know is there any Drush issue to allow Drush si with a --db-url option that will be according to the changes in this issue? ATM Drush can only cope with core db drivers AFAICS

Drush will not work with contributed database drivers. Try with the Drupal Console project, it is way more robust, properly designed and coded, etc. I can confirm it does support contributed database drivers for stuff such as installing a site through console.

amateescu’s picture

@david_garcia, the interdiff from #69 shows, to me at least, that the patch simply ported some code incorrectly:

+++ b/core/lib/Drupal/Core/Database/Database.php
@@ -498,29 +492,67 @@ public static function convertDbUrlToConnectionInfo($url, $root) {
-      $db_url .= '/' . $db_info['default']['database'];
...
+      ->withPath($db_info['default']['database']);

One of the goals of the database layer is to be made available as a generic PHP component outside of Drupal, and, IMO, adding a dependency on a HTTP library hinders that effort.

mondrake’s picture

@david_garcia Drush will not work with contributed database drivers. Try with the Drupal Console...

Nice, thank you. I tried it to establish a test webserver on Travis via drupal server, works fine. Still, it would be nice to support drupal site:install with a kind of --db-url option after this patch is in, it would simplify installation for testing purposes (it would be the same info passed on to PHPUnit in the SIMPLETEST_DBvariable for Kernel tests).

david_garcia’s picture

One of the goals of the database layer is to be made available as a generic PHP component outside of Drupal

Yes, that was the original intention many years ago (https://github.com/Crell/DBTNG).

But given the state of PHP, the state of Drupal, and how things have evolved, IMHO it makes little to no sense to still hold such a statement.

amateescu’s picture

But given the state of PHP, the state of Drupal, and how things have evolved, IMHO it makes little to no sense to still hold such a statement.

I'm not sure what are the states you are referring to, but #1826054: [Meta] Expose Drupal Components outside of Drupal is still open and actively (more or less) discussed.

david_garcia’s picture

@amateescu I believe this discussion is moving out of scope (my fault). Let's focus on getting this done.

database layer is to be made available as a generic PHP component outside of Drupal

The fact that it depends on another library does not mean that it cannot be isolated and distributed outside of Drupal. Ultimately, we are moving everything to composer based packages, and packages can perfectly depend on other packages.

mondrake’s picture

david_garcia’s picture

Issue summary: View changes
david_garcia’s picture

Status: Needs review » Reviewed & tested by the community

Changing back to RTBC, last comments are mostly nitpicks and this has had extensive review and testing, including test coverage.

This issue sort of blocks any contributed database backend work as it prevents Drupal's testing system to work with them.

#2877583: [Meta] Remove database specific logic from core

And most important, the inability of Drupal to scale (without 100% relying on caches) due to the current limited ability to deal with database transactions:

#2572283: Neither REPEATABLE READ nor READ COMMITTED transaction isolation levels are always appropriate

dawehner’s picture

I think a database system maintainer should be asked in this case of conflict.

dawehner’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs subsystem maintainer review

I doubt this can be RTBC with "needs change record" and "needs issue summary update"

david_garcia’s picture

Issue summary: View changes
Status: Needs work » Needs review

I doubt this can be RTBC with "needs change record" and "needs issue summary update"

The issue summary was updated a while ago, but no one removed the "needs issue summary update" :(

I did my best attempt to add a through change record to the issue summary (this also helps understand what changes have been made in the patch).

Needs subsystem maintainer review

There seems to be little (or none) engagement/dedication towards the database abstraction layer. And that is worrying because this is a key component to Drupal :(

See #70:

Comment #70daffie CreditAttribution: daffie commented 2 months ago
Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs subsystem maintainer review
Now that @Crell is gone and @David_Strauss has not been seen in the database issue queue for months, am I removing the tag "Needs subsystem maintainer review" and putting it back to RTBC.

dawehner’s picture

Issue summary: View changes
mondrake’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs change record, -Needs issue summary update

Added a draft Change record https://www.drupal.org/node/2896416, editing from @david_garcia proposal in the IS, and adding some of my points from #87.

This patch is a must have if we want to allow any proper testing of contributed/custom database drivers. An experimental Drupal database driver for Doctrine DBAL has been using the patch in #88 for Travis testing for a couple of months with no problems. Just setting a different environment variable for SIMPLETEST_URL allows to identify the combination of database platform/database driver to execute the tests with (see https://travis-ci.org/mondrake/drudbal).

With regards to discussion about adding dependency from guzzlehttp/psr7 to manipulate URI's, I can only say I found very handy to have its methods available to be able to add an additional URL querystring parameter without having to write additional code (see the example in the draft CR).

Launched a retest on #88 yesterday, it came back green. RTBC from my point of view.

david_garcia’s picture

Last 8.3.x path needed a reroll.

mondrake’s picture

Not sure this can make into 8.3.x, but the 8.4.x failures of the patch in #88 seem related to a commit of #2891911: Random fail in Drupal\Tests\locale\Functional\LocaleTranslationUiTest::testStringTranslation that was reversed. Triggered new tests.

david_garcia’s picture

Not sure this can make into 8.3.x

I know it won't make it, but I need the patch for the CI of several projects.

Hope this gets in before 8.4.0 release.

dawehner’s picture

Applying patches is surprisingly easy these days with composer :) I think we should really not worry anymore, when a specific patch goes in, as long it does at some point.

larowlan’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/lib/Drupal/Core/Database/Database.php
@@ -455,36 +452,33 @@ public static function ignoreTarget($key, $target) {
+      'database' => substr($uri->getPath(), 1),

Should we be using ltrim here instead? Presence of isAbsolutePathReference on Uri class suggests it doesn't always start with a leading / ?

#77.1 references out of scope changes, these still exist in the current patch.

#67 asks for subsystem maintainer review, which hasn't happened yet - pinged david-strauss via his contact form.

Let's give him a week or two to review. If someone is willing to put their hand up to become database subsystem co-maintainer, that'd be great - please open an issue.

david_garcia’s picture

I think we should really not worry anymore, when a specific patch goes in, as long it does at some point.

No please don't.

Complex patches that have to be rerolled over and over again exhaust contributors.

David Strauss’s picture

I'm reviewing this issue at the request of Lee (larowlan).

I'm not a fan of calling it just "namespace." As we can see from Alex's comment (#11), doing so can create confusion. I would at least change it to "php-namespace" or "driver-namespace" if we take the approach of putting it into the query string.

What I'd prefer, though, is moving the driver specification to the scheme. Specifying the Drupal-level driver should imply the underlying (PHP extension-level) driver. Valid characters in a scheme include ".", "-", and "+". Using one of those instead of backslashes (probably periods) would allow specifying the PHP namespace within the scheme. We could abbreviate common namespaces to avoid becoming too verbose in the scheme (if there's objection to a lengthy scheme). Seeing the PHP namespace written in the query string with lots of URI escaping (for the backslashes) is ugly, anyway.

I don't have a strong opinion on adding a dependency on Guzzle for URI parsing, especially because I can't find (via Ctrl-F) any explanation on this issue why it's preferable over parse_url(). I imagine it's because the "namespace" query parameter needs to get parsed out, and parse_url() only returns the whole query string. If so, then that's even more reason to put the driver namespace into the scheme.

david_garcia’s picture

I'm not a fan of calling it just "namespace." As we can see from Alex's comment (#11), doing so can create confusion. I would at least change it to "php-namespace" or "driver-namespace" if we take the approach of putting it into the query string.

This was already there. Changing the naming would either break BC, or create an inconsistent usage where in some places we use "namespace" (for BC) and others "driver-namespaces".

What I'd prefer, though, is moving the driver specification to the scheme. Specifying the Drupal-level driver should imply the underlying (PHP extension-level) driver. Valid characters in a scheme include ".", "-", and "+".

Another possibility is not to pass around namespaces at all in the URI. There is a function somewhere in the installer that does database driver discovery. We could abstract that function and use it during the ConnectionURL to ConnectionArray conversion to obtain the actual driver's namespace:

https://api.drupal.org/api/drupal/core%21includes%21install.inc/function...

Maybe moving that away from the Installer and into something more "core".

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

david_garcia’s picture

Let's see how the test runner behaves with this new approach. Namespaces are not passed around anymore.

All driver discovery logic has been centralized in a component for re-usability.

dawehner’s picture

@david_garcia Please post interdiffs, yes, using patch files is really antique, but at least let's make it not harder than it could be.

Status: Needs review » Needs work

The last submitted patch, 116: simpletest_is_broken_on-2605284-115.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jofitz’s picture

Status: Needs work » Needs review
FileSize
1.38 KB
29.45 KB

Many of the test failures were due to a call to file_scan_directory() so I have replaced that with a (over-?)simplified version of the function. Let's see what this gives us...

Status: Needs review » Needs work

The last submitted patch, 119: 2605284-119.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jofitz’s picture

Status: Needs work » Needs review
FileSize
4.27 KB
29.76 KB
  1. Corrected missing rename of StubConnection.php to Connection.php. (N.B. not included in interdiff)
  2. Hard-coded DRUPAL_ROOT because it is undefined here.
  3. Removed redundant DatabaseDriverDiscovery parameter drupalFunctionPattern.
  4. Corrected test failures
  5. Corrected coding standards errors
mondrake’s picture

Thanks a lot! This runs fine with a contrib db driver too: https://travis-ci.org/mondrake/drudbal/builds/318605474?utm_source=githu...

We are deprecating 3 functions in install.inc now, and no longer passing the 'namespace' in the querystring. I think we need to update both the IS and the CR with this evolution.

  1. +++ b/core/includes/install.inc
    @@ -141,15 +142,13 @@ function drupal_install_profile_distribution_version() {
    + *
    + * @deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.x. Use
    + *   \Drupal\Core\Database\DatabaseDriverDiscovery component.
      */
     function drupal_detect_database_types() {
    -  $databases = drupal_get_database_types();
    -
    -  foreach ($databases as $driver => $installer) {
    -    $databases[$driver] = $installer->name();
    -  }
    -
    -  return $databases;
    +  $discovery = new DatabaseDriverDiscovery();
    +  return $discovery->detectDatabaseTypes();
     }
    

    AFAIK, we need a @see referencing the CR, a @trigger_error at the beginning of each method repeating the deprecation information, addition of silenced deprecation messages to the list in Drupal\Tests\Listeners\DeprecationListenerTrait::getSkippedDeprecations, and a follow-up to remove the deprecated usages (yes, a lot of stuff:()

  2. +++ b/core/lib/Drupal/Core/Database/DatabaseDriverDiscovery.php
    @@ -0,0 +1,137 @@
    +  public function __construct() {
    +    // Hard-coded DRUPAL_ROOT (because bootstrap.inc is not accessible).
    +    $this->drupalRoot = dirname(dirname(dirname(dirname(dirname(__DIR__)))));
    +    // See composer.json for discoverable namespaces.
    +    $this->discoveryLocations = [
    +      'Drupal\\Core\\Database\\Driver' => '/core/lib/Drupal/Core/Database/Driver',
    +      'Drupal\\Driver\\Database' => '/drivers/lib/Drupal/Driver/Database'
    +    ];
    +  }
    

    dirname(dirname(dirname(dirname(dirname(__DIR__))))) is ugly... can we just change the discoveryLocations to something like 'Drupal\\Core\\Database\\Driver' => '../../../../..core/lib/Drupal/Core/Database/Driver', and drop drupalRoot altogether?

  3. +++ b/core/modules/system/tests/src/Kernel/Scripts/DbCommandBaseTest.php
    @@ -1,10 +1,5 @@
    -/**
    - * @file
    - * Contains \Drupal\Tests\system\Kernel\Scripts\DbCommandBaseTest.
    - */
    -
    

    I think we should keep this since there are 2 classes in this file, and according to CS only single-class php files can miss the annotation

mondrake’s picture

Assigned: Unassigned » mondrake

Working on update of IS and CR.

mondrake’s picture

Issue summary: View changes
Issue tags: -Needs issue summary update
mondrake’s picture

Assigned: mondrake » Unassigned
Issue summary: View changes
Issue tags: -Needs change record updates +Needs tests

Updated IS and CR.

Just realized... do we need separate tests for the new DatabaseDriverDiscovery class?

daffie’s picture

I reviewed the patch. I am sorry for being a nitpick.

  1. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1472,4 +1474,62 @@ public function __sleep() {
    +   * @param string $root
    +   *   The root directory of the Drupal installation.
    ...
    +  public static function convertDbUrlToConnectionInfoHelper(UriInterface $uri, $root, array $connection_options) {
    

    Why do we need the second parameter? Its not used in the method. Remove?

    Update: I found that it being used by SQLite. Should we document that here?

  2. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1472,4 +1474,62 @@ public function __sleep() {
    +    $uri = $uri->withUserInfo($user, $password);
    

    Not sure if we need to do something if the value for $user is empty. Do we have testing for this?

  3. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1472,4 +1474,62 @@ public function __sleep() {
    +   * @return $this|\Psr\Http\Message\UriInterface
    

    This method will never return $this.

  4. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1472,4 +1474,62 @@ public function __sleep() {
    +    $user = isset($connection_options['username']) ? $connection_options['username'] : '';
    

    Nit. Can we change the variable name $user to $username, because that is the name we use in the Drupal DB layer.

  5. +++ b/core/modules/system/tests/src/Kernel/Scripts/DbCommandBaseTest.php
    @@ -58,18 +53,16 @@ public function testSpecifyDatabaseDoesNotExist() {
    +      '-db-url' => Database::getConnectionInfoAsUrl('default')
    ...
    +      '--database-url' => Database::getConnectionInfoAsUrl('default')
    
    @@ -91,9 +84,8 @@ public function testPrefix() {
    +      '-db-url' => Database::getConnectionInfoAsUrl('default'),
    

    The parameter value "default" can be removed. It is the default parameter value.

  6. +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
    @@ -436,4 +437,30 @@ public function getFullQualifiedTableName($table) {
    +  public static function convertDbUrlToConnectionInfoHelper(UriInterface $uri, $root, array $database) {
    

    The parameter name should be $connection_options and not $database.

  7. +++ b/core/lib/Drupal/Core/Database/DatabaseDriverDiscovery.php
    @@ -0,0 +1,137 @@
    + * Component used to perform database driver
    + * discovery.
    

    Nit. Can be put on one line.

  8. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -498,29 +494,64 @@ public static function convertDbUrlToConnectionInfo($url, $root) {
    +   *   A custom namespace to use.
    

    Nit. "THE" custom namespace to use.

  9. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -455,36 +452,35 @@ public static function ignoreTarget($key, $target) {
    +      throw new \RuntimeException("Can not convert $url to a database connection, class $driver_class does not exist");
    

    There is no documentation in the docblock for this exception.

  10. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -498,29 +494,64 @@ public static function convertDbUrlToConnectionInfo($url, $root) {
    +
    

    Nit. This empty line can be removed.

  11. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -498,29 +494,64 @@ public static function convertDbUrlToConnectionInfo($url, $root) {
    +      throw new \RuntimeException("Database connection $key not defined");
    

    Again. There is no documentation in the docblock for this exception.

  12. +++ b/core/lib/Drupal/Core/Database/DatabaseDriverDiscovery.php
    @@ -0,0 +1,137 @@
    +    // The internal database driver name is any valid PHP identifier.
    

    I am sorry, but I do not understand this line. What is a valid PHP identifier?

  13. +++ b/core/lib/Drupal/Core/Database/DatabaseDriverDiscovery.php
    @@ -0,0 +1,137 @@
    +          while (FALSE !== ($driverName = readdir($handle))) {
    

    Should this line not be written as while (($driverName = readdir($handle)) !== FALSE) {

  14. +++ b/core/lib/Drupal/Core/Database/DatabaseDriverDiscovery.php
    @@ -0,0 +1,137 @@
    +              if (isset($files[$driverName])) {
    +                throw new \Exception("Conflicting database driver name: " . $driverName);
    +              }
    

    Not sure about this. If somebody wants to make a better/faster/improved version of say for instance the Postgres driver, then that is not possible.

  15. +++ b/core/lib/Drupal/Core/Database/DatabaseDriverDiscovery.php
    @@ -0,0 +1,137 @@
    +          while (FALSE !== ($driverName = readdir($handle))) {
    +            // Skip this file if it matches the nomask or starts with a dot.
    +            if ($driverName[0] != '.') {
    +              if (isset($files[$driverName])) {
    +                throw new \Exception("Conflicting database driver name: " . $driverName);
    +              }
    +              $files[$driverName] = $namespace . '\\' . $driverName;
    

    Can we add some documentation about what the value can and/or should be. It is a bit vague to me.

  16. +++ b/core/lib/Drupal/Core/Database/DatabaseDriverDiscovery.php
    @@ -0,0 +1,137 @@
    +  public function discoverDatabaseDrivers() {
    

    Missing documentation about the possible exception that can be thrown. Also the return value is not documented. This is a complicated array. Lots of documentation for this please.

  17. +++ b/core/lib/Drupal/Core/Database/DatabaseDriverDiscovery.php
    @@ -0,0 +1,137 @@
    +    $drivers = [];
    

    Can we move this line to just before the line with: foreach ($files as $driver => $namespace) {.

  18. The database drivers are file scanned every time. Should we not cache this?
mondrake’s picture

Assigned: Unassigned » mondrake

I'll be working on #122, #125 and #126

mondrake’s picture

Status: Needs work » Needs review
FileSize
2.86 KB
32.92 KB

This is adding only the deprecation triggers per #122.1. Running a test to see if they pop up as expected, will silence in next patch.

mondrake’s picture

This patch is

  • silencing the deprecations
  • made several changes to the DatabaseDriverDiscovery class - include renaming the methods to something more understandable and specific to what they are doing (the function names in install.inc are misleading)
  • dropping the Database::getDatabaseConnectionClass in favour of a more generic Database::getDatabaseDriverNamespace that can be used also for other purposes (for example, in #2867788: Log::findCaller fails to report the correct caller function with non-core drivers.).

With regards to #126.14 - yes, you can write an alternative db driver for a db already covered by a core driver - but you have to specify its name differently otherwise you'll get namespace clashes.

This addresses #122 and #126.7/12/13/14/15/16/17/18.

Left out: #125 (tests) and #126.1-6/8-11

mondrake’s picture

the patch...

The last submitted patch, 128: 2605284-128.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mondrake’s picture

Assigned: mondrake » Unassigned
Issue tags: -Needs tests
FileSize
8.63 KB
40.56 KB

This adds tests for the DatabaseDriverDiscovery class. The 'Stub' database driver in the Database unit test directory is expanded wit a stub installer too, so we can test its discovery. Additional change to runtime code to match the testing.

Left out: #126.1-6/8-11

Status: Needs review » Needs work

The last submitted patch, 132: 2605284-132.patch, failed testing. View results

mondrake’s picture

#133 was a conflict in the list of silenced deprecations.

mondrake’s picture

Status: Needs work » Needs review
daffie’s picture

Status: Needs review » Needs work

@mondrake: The changes that you made in #127 - #135 look good to me. There are some points left to fix, so back to "Needs work".

mondrake’s picture

Status: Needs work » Needs review
FileSize
9.77 KB
41.44 KB

Addressed the remaining points from #126: 1-6 and 8-11.

Status: Needs review » Needs work

The last submitted patch, 138: 2605284-137.patch, failed testing. View results

mondrake’s picture

#139 is an apcu failure

mondrake’s picture

Status: Needs work » Needs review
mondrake’s picture

Very hard to get tests to pass these days...

Thinking about DatabaseDriverDiscoveryTest, this assumes that during a test run all of the core drivers (mysql, sqlite, pgsql) are installable. This is true for DrupalCI (and TravisCI), but on local runs this may lead to failure if the dbs are not installed. Let's see if we can sort out something that can work in these cases.

mondrake’s picture

The last submitted patch, 138: 2605284-137.patch, failed testing. View results

daffie’s picture

It is almost RTBC for me. Two minor points left for me. Great work @mondrake and @Jo_Fitzgerald.

  1. +++ b/core/tests/Drupal/Tests/Core/Database/ConnectionTest.php
    @@ -288,10 +288,10 @@ public function providerFilterComments() {
    diff --git a/core/tests/Drupal/Tests/Core/Database/Stub/Connection.php b/core/tests/Drupal/Tests/Core/Database/Stub/Connection.php
    
    diff --git a/core/tests/Drupal/Tests/Core/Database/Stub/Connection.php b/core/tests/Drupal/Tests/Core/Database/Stub/Connection.php
    new file mode 100644
    
    new file mode 100644
    index 0000000..06500f7
    
    index 0000000..06500f7
    --- /dev/null
    
    --- /dev/null
    +++ b/core/tests/Drupal/Tests/Core/Database/Stub/Connection.php
    
    +++ b/core/tests/Drupal/Tests/Core/Database/Stub/Install/Tasks.php
    @@ -0,0 +1,26 @@
    diff --git a/core/tests/Drupal/Tests/Core/Database/Stub/StubConnection.php b/core/tests/Drupal/Tests/Core/Database/Stub/StubConnection.php
    
    diff --git a/core/tests/Drupal/Tests/Core/Database/Stub/StubConnection.php b/core/tests/Drupal/Tests/Core/Database/Stub/StubConnection.php
    deleted file mode 100644
    
    deleted file mode 100644
    index 4692dff..0000000
    
    index 4692dff..0000000
    --- a/core/tests/Drupal/Tests/Core/Database/Stub/StubConnection.php
    
    --- a/core/tests/Drupal/Tests/Core/Database/Stub/StubConnection.php
    +++ /dev/null
    

    It is better to change this to a rename operation.

  2. +++ b/core/tests/Drupal/KernelTests/Core/Database/DatabaseDriverDiscoveryTest.php
    @@ -0,0 +1,109 @@
    +  public function testGetInstallableDriversInstallerNames() {
    ...
    +  public function testGetInstallableDriversInstallers() {
    

    Can we add an assertion that the "Stub" driver is not installable.

mondrake’s picture

@daffie thanks for review. #145 done, plus moved the deprecation silencers a little bit in the mid of the array to avoid re-roll juggling with this patch, external CI tools applying this patch, and other silencers being added to the end of the list by other issues. We can move it at the end of the list later, but while it waits...

mondrake’s picture

Status: Needs work » Needs review

Always, always forget to change status...

daffie’s picture

Status: Needs review » Reviewed & tested by the community

The patch looks now great to me.

There is a change record added for this issue.

We have no subsystem maintainer that can do a review. Setting the status to RTBC.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 146: 2605284-146.patch, failed testing. View results

daffie’s picture

Status: Needs work » Reviewed & tested by the community

Back to RTBC.

Mile23’s picture

Issue tags: +Needs followup
+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
@@ -97,6 +97,9 @@ public static function getSkippedDeprecations() {
+      'drupal_detect_database_types() is deprecated in Drupal 8.5.0, will be removed before Drupal 9.0.0. Use \Drupal\Core\Database\DatabaseDriverDiscovery::getInstallableDriversInstallerNames instead. See https://www.drupal.org/node/2896416.',
+      'drupal_get_database_types() is deprecated in Drupal 8.5.0, will be removed before Drupal 9.0.0. Use \Drupal\Core\Database\DatabaseDriverDiscovery::getInstallableDriversInstallers instead. See https://www.drupal.org/node/2896416.',
+      'db_installer_object() is deprecated in Drupal 8.5.0, will be removed before Drupal 9.0.0. Use \Drupal\Core\Database\DatabaseDriverDiscovery::getInstaller instead. See https://www.drupal.org/node/2896416.',

Are these usages in core? If they are, then they need a follow-up to remove them.

mondrake’s picture

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/includes/install.inc
@@ -157,37 +159,16 @@ function drupal_detect_database_types() {
-  if (is_dir(DRUPAL_ROOT . '/drivers/lib/Drupal/Driver/Database')) {
-    $files += file_scan_directory(DRUPAL_ROOT . '/drivers/lib/Drupal/Driver/Database/', $mask, ['recurse' => FALSE]);
-  }

+++ b/core/lib/Drupal/Core/Database/DatabaseDriverDiscovery.php
@@ -0,0 +1,160 @@
+      'Drupal\\Driver\\Database' => __DIR__ . '/../../../../../drivers/lib/Drupal/Driver/Database',

This is a breaking change. If the core directory is a symlink as some people do this will not work now. Whereas before with DRUPAL_ROOT it would. I guess DatabaseDriverDiscovery should be a service with drupal root injected.

+++ b/core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php
@@ -37,25 +37,25 @@ public function providerConvertDbUrlToConnectionInfo() {
-      [$root2, $url2, $database_array2],
+      [$root2, $url2, $database_array2]

Isn't this an incorrect change?

mondrake’s picture

Assigned: Unassigned » mondrake
Status: Needs work » Needs review
FileSize
1.18 KB
42.91 KB

Thanks @alexpott for your review. This addresses #153.

I did not know that we can use services during installation. I will experiment a bit.

Status: Needs review » Needs work

The last submitted patch, 154: 2605284-154.patch, failed testing. View results

daffie’s picture

@mondrake: Your solution is not what @alexpott is proposing:

I guess DatabaseDriverDiscovery should be a service with drupal root injected.
mondrake’s picture

@daffie yes, you are right, I am working on that and will post a patch when I will have sth ready. So far I found out that you need to register the service in the basic container in install.core.inc to have it available at installation time - it’s not sufficient to put it in the services YAML.

daffie’s picture

As an alternative we could also just do:

return (new DatabaseDriverDiscovery(DRUPAL_ROOT))->getInstallableDriversInstallerNames();

And not create a service. Maybe easier to do.

mondrake’s picture

Status: Needs work » Needs review
FileSize
43.34 KB
15.92 KB

@daffie AFAICS the usage of DRUPAL_ROOT is discouraged. This patch now introduces the service. It is required very early for instance in KernelTest, so I had to add it in a basic container in KernelTestBase::bootEnvironment. I do not know if that's the right way.

Interdiff against #146.

Status: Needs review » Needs work

The last submitted patch, 159: 2605284-159.patch, failed testing. View results

mondrake’s picture

Status: Needs work » Needs review
FileSize
44.25 KB
4.55 KB

Need to enable a basic container also in UpdatePathTestBase, then deprecation silencing does not work for Simpletest tests?

Status: Needs review » Needs work

The last submitted patch, 161: 2605284-161.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mondrake’s picture

Status: Needs work » Needs review
FileSize
4.59 KB
41.8 KB

Maybe the entire setting up of the basic container in UpdatePathTestBase could be moved from ::runDbTask to ::setUp, but I'd love feedback before doing so.

mondrake’s picture

Not all UpdatePath test have been converted to PHPUnit, so we need to include the new service also in the legacy UpdatePathTestBase.

The last submitted patch, 163: 2605284-163.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 164: 2605284-164.patch, failed testing. View results

mondrake’s picture

Assigned: mondrake » Unassigned
Status: Needs work » Needs review
FileSize
42.61 KB
722 bytes

Uh, yes.

Status: Needs review » Needs work

The last submitted patch, 167: 2605284-167.patch, failed testing. View results

mondrake’s picture

mondrake’s picture

#167 is green and ready for review. #168 is an apcu failure.

mondrake’s picture

Status: Needs work » Needs review
mondrake’s picture

daffie’s picture

Status: Needs review » Needs work

It all looks good to me.

Only the second point of @alaxpott in comment #153 is not been addressed.

For the rest is this patch RTBC for me.

Great work @mondrake!

mondrake’s picture

Status: Needs work » Needs review

@daffie it is addressed, the comma is added back. or i did not understand 🤨 thanks

daffie’s picture

Status: Needs review » Reviewed & tested by the community

ok, it has been addressed.

For me it is back to RTBC.

alexpott’s picture

  1. +++ b/core/core.services.yml
    @@ -354,6 +354,9 @@ services:
    +  database.driver.discovery:
    +    class: Drupal\Core\Database\DatabaseDriverDiscovery
    +    arguments: ['@app.root']
    

    Do we actually need this service ever apart from during the very early installer?

  2. +++ b/core/lib/Drupal/Core/Database/DatabaseDriverDiscovery.php
    @@ -0,0 +1,169 @@
    +  /**
    +   * Discovered database drivers.
    +   *
    +   * @var array
    +   */
    +  protected $discoveredDrivers;
    ...
    +    if (isset($this->discoveredDrivers)) {
    +      return $this->discoveredDrivers;
    +    }
    

    The original code had no statics for this. The new code before the service introduced a static and the new service is maintaining that by keeping it as a property on the class. Is this worth it? Does it actually save anything during installation? I'd be surprised.

mondrake’s picture

#176:

1. drupal_get_database_types is used in MigrateUpgradeForm of the migrate_drupal_ui module. Besides, not having it as a service during runtime will mean a BC break - today custom/contrib can include install.inc and call one of the deprecated functions to inspect the available database drivers.

2. Dunno... in install.core.inc we currently have

function install_database_errors($database, $settings_file) {
  $errors = [];

  // Check database type.
  $database_types = drupal_get_database_types();
  $driver = $database['driver'];
  if (!isset($database_types[$driver])) {
    $errors['driver'] = t("In your %settings_file file you have configured @drupal to use a %driver server, however your PHP installation currently does not support this database type.", ['%settings_file' => $settings_file, '@drupal' => drupal_install_profile_distribution_name(), '%driver' => $driver]);
  }
  else {
    // Run driver specific validation
    $errors += $database_types[$driver]->validateDatabaseSettings($database);
    if (!empty($errors)) {
      // No point to try further.
      return $errors;
    }
    // Run tasks associated with the database type. Any errors are caught in the
    // calling function.
    Database::addConnectionInfo('default', 'default', $database);

    $errors = db_installer_object($driver)->runTasks();
  }
  return $errors;
}

a call to drupal_get_database_types() followed by one to db_installer_object($driver) a few lines below. These will both (currently) discover the drivers with I/O then, if we do not cache. Not a big deal, but who knows how these functions/methods will be called - caching during the request lifetime seemed appropriate to me. But I have no strong argument, we can remove @alexpott if you believe so.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 167: 2605284-167.patch, failed testing. View results

daffie’s picture

Status: Needs work » Reviewed & tested by the community

Back to RTBC.

alexpott’s picture

@mondrake not having it as a service won't be a BC break as long as those functions still work. We could just do something like:

new DatabaseDriverDiscovery(\Drupal::root())

in there. Which is fine afaics. Let's take the opportunity not to pollute the container will services that will never ever be needed during the normal runtime of the site.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

Also let's not add cache layers here. Yes it might save I/O but adding it here seems unnecessary. install_database_errors() is only ever called once.

mondrake’s picture

@alexpott this is removing the caching.

I disagree on doing

new DatabaseDriverDiscovery(\Drupal::root())

this will only work if the 'app.root' service is available from the container. But the deprecated functions will be called during early install when the 'normal' container is not yet available. So in these functions we'll have to conditionally call the service (if the normal container is NOT available) or instantiate a new class (if the container IS available) which is ... DX confusing. Also, Database::convertDbUrlToConnectionInfo is invoking the service, and this method can be called at runtime - here again this would then have to be conditional, and in case of \Drupal::root not available, we'll need to pass in the app root in the way we're trying to avoid.

daffie’s picture

Status: Needs review » Reviewed & tested by the community

@alexpott wanted 2 things changed. The caching layers has been removed. For the not adding a new service: @mondrake has made a good argument, why it should be added. Changing the status of the issue back to RTBC, so that @alexpott can respond.

mondrake’s picture

Please also see #2933407-2: [PP-1] Remove usages of drupal_detect_database_types, drupal_get_database_types and db_installer_object where usage of the deprecated functions is removed from core.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

alexpott’s picture

Status: Reviewed & tested by the community » Needs review

Re #182 we can add the existing app.root service to the early installer really simply. It works I've tested this. We need to avoid polluting the run-time container with installer services. I think this issue sets the wrong precedent.

I'm still not really understanding why we are rewriting database discovery in this change. The issue is doing two things at the same time which is why progress is slow. It's (a) rewriting db discovery and (b) it is fixing db url conversion to support non core drivers. It we need to do (a) to do (b) then we should create a blocking issue where we can concentrate on the refactor of db discovery.

As I discussed the Uri pollution of the Connection class with @dawehner and I wondered if we could move all of the connection url stuff to a separate class for databases eg. ConnectionUrl where core provides an implementation and non-core drivers can implement there own versions.

mondrake’s picture

Assigned: Unassigned » mondrake
Status: Needs review » Needs work

On it, trying to simplify.

mondrake’s picture

I'm still not really understanding why we are rewriting database discovery in this change.

Actually, was just following the flow and never thought about that. It turns out we can do without touching db driver discovery at all.

... the Uri pollution of the Connection class ...

Removed usage of GuzzleHttp\Psr7\Uri from this patch.

The patch here does the minimum AFAICS - only Database, Connection and sqlite/Connection classes are involved + test changes. Just needed to add a couple methods getConnectionInfoFromUrl and getConnectionUrl to abstract Connection class and an override of those for 'sqlite' to cope with its specifics.

Did not try to use this in contrib yet, wanted to get feedback if this is going in the right direction first.

No interdiff, will not make sense as the patch is a totally different approach.

alexpott’s picture

  1. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1472,4 +1472,94 @@ public function __sleep() {
    +      'namespace' => "Drupal\\Core\\Database\\Driver\\{$url_components['scheme']}",
    

    This doesn't look right. Haven't we worked that out before. I think we might able to work this out from the concrete class.

  2. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -453,38 +448,28 @@ public static function ignoreTarget($key, $target) {
         $info = parse_url($url);
         if (!isset($info['scheme'], $info['host'], $info['path'])) {
           throw new \InvalidArgumentException('Minimum requirement: driver://host/database');
         }
    

    Let's do this in the base connection class. And just pass the url along here.

  3. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -495,32 +480,36 @@ public static function convertDbUrlToConnectionInfo($url, $root) {
    +  public static function getDatabaseDriverNamespace(array $connection_info) {
    

    It's fine to refactor the code into a method but I don't think we need to make it public here.

Status: Needs review » Needs work

The last submitted patch, 188: 2605284-188.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mondrake’s picture

Status: Needs work » Needs review
FileSize
19.92 KB
8.76 KB

Thanks for quick review @alexpott

Made changes according to #189. We could also refactor Connection::getDriverClass to use the newly introduced getNamespace method and avoid reflection.

Status: Needs review » Needs work

The last submitted patch, 191: 2605284-191.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mondrake’s picture

Readded KernelTestBase fix from #57 and removed a stray @throws.

mondrake’s picture

alexpott’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Core/Database/Connection.php
@@ -1291,6 +1291,14 @@ protected function generateTemporaryTableName() {
   /**
+   * Returns the database driver PHP namespace.
+   *
+   * @return string
+   *   The PHP namespace of the database driver.
+   */
+  abstract public static function getNamespace();

+++ b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php
@@ -226,6 +226,13 @@ public function driver() {
+  /**
+   * {@inheritdoc}
+   */
+  public static function getNamespace() {
+    return __NAMESPACE__;
+  }

All of the implementations are going to be same. Why not use reflection here and just have a protected static or even just inline in getConnectionInfoFromUrl()

mondrake’s picture

Status: Needs work » Needs review

a) Sure we can, but reflection is heavy. Are we sure we want it?
b) I would like to have a public API to retrieve the actual namespace of the db driver. We need it in #2867788: Log::findCaller fails to report the correct caller function with non-core drivers..

mondrake’s picture

mondrake’s picture

Assigned: mondrake » Unassigned
Kuldeep K’s picture

I have applied the above patch, well it's still showing me "InvalidArgumentException: Missing scheme in URL" when I am running functional test.

mondrake’s picture

#199 @Kuldeep K can you please share your steps to reproduce? Core tests pass and for me it also works with an experimental driver for Doctrine DBAL, once implemented Connection::getConnectionInfoFromUrl and Connection::getConnectionUrl methods.

Status: Needs review » Needs work

The last submitted patch, 197: 2605284-197.patch, failed testing. View results

mondrake’s picture

daffie’s picture

The patch looks good. I do have some remarks:

  1. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1472,4 +1483,103 @@ public function __sleep() {
    +    if ($connection_options['prefix']['default']) {
    

    Do we need to use the isset() function here?

  2. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1472,4 +1483,103 @@ public function __sleep() {
    +   * @throws \InvalidArgumentException
    +   *   Exception thrown when the provided URL does not meet the minimum
    +   *   requirements.
    ...
    +  public static function getConnectionInfoFromUrl($url, $root) {
    ...
    +  public static function getConnectionUrl(array $connection_options) {
    

    The method getConnectionInfoFromUrl() will throw an exception when there is something wrong with the parameter $url.
    Can we do the same for the method getConnectionUrl()?

  3. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1472,4 +1483,103 @@ public function __sleep() {
    +  public static function getConnectionInfoFromUrl($url, $root) {
    ...
    +  public static function getConnectionUrl(array $connection_options) {
    

    Can we give both methods the same kind of name. Change getConnectionUrl() to getConnectionUrlFromConnectionOptions(). Not sure about this. Just an idea.

  4. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -455,36 +450,23 @@ public static function ignoreTarget($key, $target) {
    +    if (preg_match('/^(.*):\/\//', $url, $matches) !== 1) {
    

    Can we add some documentation about why we use the preg_match() function here.

  5. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -495,32 +477,36 @@ public static function convertDbUrlToConnectionInfo($url, $root) {
    +    if (empty($db_info)) {
    

    Is it not better to test $db_info['default'] instead of just $db_info.

We do not have a subsystem maintainer any more after @crell left. So changing it to the framework maintainer.

mondrake’s picture

Assigned: Unassigned » mondrake
mondrake’s picture

Assigned: mondrake » Unassigned
Status: Needs work » Needs review
FileSize
19.49 KB
5.77 KB

Addressed @daffie's points in #203.

Status: Needs review » Needs work

The last submitted patch, 205: 2605284-205.patch, failed testing. View results

mondrake’s picture

Fix test failure in #205 + add tests for #203.2

daffie’s picture

Status: Needs review » Reviewed & tested by the community

@mondrake: Thank you for your great work on this patch!

All my points from comment #203 are fixed.
For me it is RTBC.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. This issue feels close - thanks for continuing to work on it
  2. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -266,6 +266,7 @@
    +    return NULL;
    

    out of scope.

  3. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -455,36 +451,25 @@ public static function ignoreTarget($key, $target) {
    +      throw new \InvalidArgumentException("Missing scheme in URL '$url'");
    

    Let's assert on the message.

  4. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -455,36 +451,25 @@ public static function ignoreTarget($key, $target) {
    +      // If the URL is not relative to a core driver, try with custom ones.
    +      $connection_class = "Drupal\\Driver\\Database\\{$driver}\\Connection";
    +      if (!class_exists($connection_class)) {
    +        throw new \InvalidArgumentException("Can not convert '$url' to a database connection, class '$connection_class' does not exist");
    +      }
    

    It would be great to have test coverage of this fallback and exception. Also the as the exception message is per url we should assert on that too.

  5. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -495,32 +480,36 @@ public static function convertDbUrlToConnectionInfo($url, $root) {
    +    if (isset($connection_info['namespace']) && $connection_info['namespace'] !== NULL) {
    +      return $connection_info['namespace'];
         }
    

    The check for !== NULL doesn't make sense. See https://3v4l.org/fsL3c

  6. +++ b/core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php
    @@ -144,10 +182,57 @@ public function providerGetConnectionInfoAsUrl() {
    +    // Remove the connection to not pollute subsequent datasets being tested.
    +    Database::removeConnection('default');
    

    Given that we're dealing with statics I've prefer to run the test in a separate process rather than clean up. Let's stick

     * @runTestsInSeparateProcesses
     * @preserveGlobalState disabled
    

    at the top of the test and note that we don't want the database static to affect other tests.

mondrake’s picture

Thanks for review @alexpott.

This should address #209.

mondrake’s picture

Issue summary: View changes
Issue tags: +Needs change record updates

Updated IS to remove some parts that no longer stand. The CR will also need updates, but let's do that when we are agreed on final status.

daffie’s picture

Status: Needs review » Needs work
Issue tags: -Needs framework manager review

@mondrake: The changes that you made look good. Only I am missing some things that @alexpott wants in comment #209:

  1. +++ b/core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php
    @@ -144,10 +186,58 @@ public function providerGetConnectionInfoAsUrl() {
    +  public function testGetInvalidArgumentGetConnectionInfoAsUrl(array $connection_options, $expected_exception_message) {
    +    Database::addConnectionInfo('default', 'default', $connection_options);
    +    $this->setExpectedException(\InvalidArgumentException::class);
    +    $url = Database::getConnectionInfoAsUrl();
    +  }
    

    There is no testing on the expected exception message. The parameter is added to the testing method, but it is not used!

  2. For the other new exception being thrown there is no testing and with testing on the expected message.

@alexpott reviewed this patch. His words were: "This issue feels close - thanks for continuing to work on it". My conclusion is that we can remove the "needs framework manager review" tag.

@mondrake: What updates do you think we need on the change record?

alexpott’s picture

  1. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -1290,6 +1290,17 @@ protected function generateTemporaryTableName() {
    +  /**
    +   * Returns the database driver PHP namespace.
    +   *
    +   * @return string
    +   *   The PHP namespace of the database driver.
    +   */
    +  public static function getNamespace() {
    +    preg_match('/(.*)\\\\Connection$/', static::class, $matches);
    +    return $matches[1];
    +  }
    
    @@ -1472,4 +1483,113 @@ public function __sleep() {
    +      'namespace' => static::getNamespace(),
    

    Let's not add this. Do we need to? It's only called once. Also I'm not a huge fan of using regular expressions here. We can do this in the getOptionsFromUrl(). Here's a way without regular expressions:
    substr(get_called_class(), 0, strrpos(get_called_class(), "\\"))
    But I think given performance is not an issue here - we're not on the critical path and this is not called millions of times why not use reflection? Ie.

    $reflector = new ReflectionClass(get_called_class()); 
    $reflector->getNamespaceName();
    
  2. +++ b/core/lib/Drupal/Core/Database/Database.php
    @@ -455,36 +450,25 @@ public static function ignoreTarget($key, $target) {
    +      // If the URL is not relative to a core driver, try with custom ones.
    +      $connection_class = "Drupal\\Driver\\Database\\{$driver}\\Connection";
    +      if (!class_exists($connection_class)) {
    

    As far as I can see - and I might be missing something - but we are missing test coverage for this custom driver actually working. Can we not add a fake one to the autoloader during a test (or something like that)?

  3. +++ b/core/modules/simpletest/simpletest.module
    @@ -316,9 +316,11 @@ function simpletest_phpunit_configuration_filepath() {
    +  if (Database::getConnectionInfo()) {
    +    // Setup an environment variable containing the database connection so that
    +    // functional tests can connect to the database.
    +    putenv('SIMPLETEST_DB=' . Database::getConnectionInfoAsUrl());
    +  }
     
    

    Why is this change necessary?

mondrake’s picture

Status: Needs work » Needs review
FileSize
22.49 KB
3.19 KB

Thanks for reviews @daffie and @alexpott.

#212
1. done
2. which other new exception?

What updates do you think we need on the change record?

It still mentions the changes to the installer/database driver discovery that we dropped in latest versions of the patch here + we need to update the names of the methods added.

#213
1. done
2. not done, above me ATM, will look into it
3. I am sure there was a test failure if not, just removing that change here to see what happens

mondrake’s picture

The last submitted patch, 214: 2605284-214.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 215: 2605284-216.patch, failed testing. View results

mondrake’s picture

Status: Needs work » Needs review
FileSize
1.59 KB
24.08 KB

#213.3 SimpletestPhpunitRunCommandTest::testSimpletestPhpUnitRunCommand fails because being a unit test, Database::getConnectionInfoAsUrl() throws an exception now since no default db connection is defined. Here, changing the test instead of changing runtime code, by adding a default database connection entry.

#213.2 still outstanding.

Status: Needs review » Needs work

The last submitted patch, 218: 2605284-218.patch, failed testing. View results

mondrake’s picture

daffie’s picture

Status: Needs review » Needs work

@mondrake: On comment #212.2. I was wrong the other exception is only being moved. The changes you are making look good to me.

Only comment #213.2 still outstanding.

mondrake’s picture

Status: Needs work » Needs review
FileSize
3.01 KB
26.5 KB

Re. #213.2

Here's what I came up with - trying to register an additional class loader, adding an additional namespace for a 'fake' db driver, and prepending the loader to others to avoid name collisions.

I does not work though, test will fail.

@alexpott maybe you can lend a hand?

mondrake’s picture

The last submitted patch, 222: 2605284-222.patch, failed testing. View results

daffie’s picture

Status: Needs review » Reviewed & tested by the community

All the changes look good to me and as far as I can see all the points of @alexpott have been addressed. Back to RTBC. Great work @mondrake!

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/lib/Drupal/Core/Database/Connection.php
@@ -1472,4 +1472,116 @@ public function __sleep() {
+   * Converts a URL to a database connection info array.
...
+  public static function getOptionsFromUrl($url, $root) {
...
+   * Gets database connection as a URL.
...
+  public static function getUrlFromOptions(array $connection_options) {

I feel like columbo. Sorry. One more thing. I just was wondering about naming. Looking at the existing Connection class options has multiple meanings. See \Drupal\Core\Database\Connection::defaultOptions() and \Drupal\Core\Database\Connection::getConnectionOptions.

So let's use the existing nomenclature. And also lets use the word create instead of get. Imo get implies it already exists whereas this methods are creating something from something. So createConnectionOptionsFromUrl and createUrlFromConnectionOptions.

We should also update the doc blocks respectively.

Creates a URL from an array of database connection options.
Creates an array of database connection options from a URL.
mondrake’s picture

#226:

I feel like columbo. Sorry.

No worries. Let's do things right here :)

mondrake’s picture

Status: Needs work » Needs review
mondrake’s picture

Updated draft CR.

daffie’s picture

Status: Needs review » Reviewed & tested by the community

I feel like columbo. Sorry.

We must keep Colombo happy or you will go to jail. ;-)
Back to RTBC.

alexpott’s picture

Adding review credit.

alexpott’s picture

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 61cbaba and pushed to 8.6.x. Thanks!

  • alexpott committed 61cbaba on 8.6.x
    Issue #2605284 by mondrake, david_garcia, GoZ, Jo Fitzgerald, alexpott,...
mondrake’s picture

For anyone interested, filed [site:install] Add a --db-url option in Drupal Console issue queue on GitHub.

Status: Fixed » Closed (fixed)

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