Follow-up to #203955: Create database at installation time

Expected Behavior

Illegal characters in database names (those not passing this regex: /[^A-Za-z0-9_.]+/) are handled appropriately for automatic database creation stage of the installer. "Appropriately" could mean different things—validation or normalization of the typed input.

Actual Behavior

If illegal characters are used in the database name, the user sees the "Drupal is already installed message", which is not true. Drupal is not installed and things are quite broken. Actually - the will have to recopy the settings files and start over. But Drupal doesn't explain what is happening.
Error now appears as (to screen or in logs depending on settings):

Uncaught exception 'Symfony\Component\DependencyInjection\Exception\InvalidArgumentException' with message 'The service definition "renderer" does not exist.' in core/vendor/symfony/dependency-injection/ContainerBuilder.php:868 
Stack trace: #0 core/vendor/symfony/dependency-injection/ContainerBuilder.php(478): Symfony\Component\DependencyInjection\ContainerBuilder->getDefinition('renderer') 
#1 core/lib/Drupal.php(157): Symfony\Component\DependencyInjection\ContainerBuilder->get('renderer') 
#2 core/includes/install.core.inc(1152): Drupal::service('renderer') 
#3 core/includes/install.core.inc(1090): install_database_errors(Array, './sites/default...') 
#4 core/includes/install.core.inc(369): install_verify_database_settings('sites/default') 
#5 core/includes/install.core.inc(113): insta in core/vendor/symfony/dependency-injection/ContainerBuilder.php on line 868

Steps to Reproduce

As described in the original report #203955-68: Create database at installation time:

  1. Try to install Drupal with a database by the name of test-database
  2. A database called testdatabase will be created
  3. 'database' => 'test-database' will be inserted into settings.php
  4. Installation will fail due to Drupal not being able to connect to the database

Because database creation occurs BEFORE install_settings_form_validate we can't just inform the user that the name is invalid, rather we have to ensure that the same escaped name is placed into settings.php. As a caveat, perhaps this should only be escaped if Drupal creates the database.

We also need to ensure that the SQLite implementation is not escaped as an escaped filepath (stripped of /) causes install errors for obvious reasons. So we cannot simply run some REGEX over $database['database'] before being written to settings.php.

Rather I have a feeling we may have to call escapeDatabase so that each of the database drivers may handle the escape in the same way they do when createDatabase is called.

Comments

cilefen’s picture

Some patches were offered after comment #68 in the parent issue: #203955-68: Create database at installation time

cilefen’s picture

Priority: Normal » Major
Issue summary: View changes

I am bumping this to major because it should be. Anyone in this situation will have a broken install and won't know what to do to fix it.

cilefen’s picture

Issue summary: View changes
cilefen’s picture

Title: Hyphens in database names break database auto-creation on install » Non-alphanumeric characters in database names break database auto-creation on install
Issue summary: View changes

Commas also cause this.

cilefen’s picture

Title: Non-alphanumeric characters in database names break database auto-creation on install » Illegal characters in database names break database auto-creation on install
Issue summary: View changes
Status: Active » Needs review
StatusFileSize
new6.93 KB
cilefen’s picture

I feel it is wrong to escape the typed database name without telling the user. So #5 throws a new exception that is caught in the database install task and is sent up to the install form.

larowlan’s picture

+++ b/core/lib/Drupal/Core/Database/Driver/mysql/Install/Tasks.php
@@ -83,10 +83,19 @@ protected function connect() {
+          switch (get_class($e)) {
+            case 'Drupal\Core\Database\DatabaseInvalidNameException':
+              $this->fail(t('The database name contains illegal characters'));
+              break;
+
+            case 'Drupal\Core\Database\DatabaseNotFoundException':
+              $this->fail(t('Database %database not found. The server reports the following message when attempting to create the database: %error.', array('%database' => $database, '%error' => $e->getMessage())));

This feels wrong. Why not set the message on the Exception when it is created and then use $e->getMessage()?

cilefen’s picture

StatusFileSize
new5.16 KB
new7.31 KB
cilefen’s picture

I feel that we should add an isValidDatabaseName() method to the Connection class to improve readability.

daffie’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

The patch looks good to me. I do have some questions:

  1. I am missing tests to make sure that this patch fixes the problem and to make sure that the problem does not return.
  2. In the patch there is only a change for MySQL and PostgreSQL. Does SQLite not have the problem?
justachris’s picture

Issue summary: View changes

Updated Actual Behavior based on most recent HEAD

jeroent’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
StatusFileSize
new8.42 KB

Wrote my first test for Drupal. Patch attached.

jeroent’s picture

StatusFileSize
new9.12 KB

Wrong patch. This is the right one...

stefan.r’s picture

  1. +++ b/core/lib/Drupal/Core/Database/Install/Tasks.php
    @@ -40,7 +40,7 @@
    +        'Failed to <strong>CREATE</strong> a test table on your database server with the command %query. The server reports the following message: %error.<p>Are you sure the configured username has the necessary permissions to create tables in the database and that the database name contains no illegal characters?</p>',
    

    I don't see how creating a table has anything to do with having an invalid database name, shouldn't this have its own test and its own error message?

  2. +++ b/core/tests/Drupal/Tests/Core/Database/ConnectionTest.php
    @@ -311,4 +312,41 @@ public function testFilterComments($expected, $comment) {
    +    } catch (DatabaseException $e) {
    

    missing newline after the bracket

cilefen’s picture

We need to think about #10-2, sqlite.

cilefen’s picture

#15-1 That's not new to this patch.

The last submitted patch, 13: illegal_characters_in-2525906-13.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 14: illegal_characters_in-2525906-14.patch, failed testing.

stefan.r’s picture

@cilefen yes it's arguable whether it's in scope here, but what is appended to the error message is new. So if possible it would still be good if we could fix this here instead of making that error message even longer and more confusing...

cilefen’s picture

I have not thought about this issue in a while. From what I understand from looking at the surrounding code is that a table create is the ultimate test of whether the database is *really* ready. This patch could maybe do a better job of handling the exceptions as they bubble up.

jeroent’s picture

Status: Needs work » Needs review
StatusFileSize
new9.12 KB
new468 bytes

15.2: Fixed.

Patch attached.

Status: Needs review » Needs work

The last submitted patch, 22: illegal_characters_in-2525906-22.patch, failed testing.

JulienD’s picture

Status: Needs work » Closed (duplicate)
Related issues: -#2229793: Installation - Database name gets escaped for creation, but not for settings

Closing the issue because an older one is already existing #2443839

I have summarized patches and conversations over there