diff --git a/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php
index 462699b..104fecf 100644
--- a/core/lib/Drupal/Core/Database/Connection.php
+++ b/core/lib/Drupal/Core/Database/Connection.php
@@ -741,6 +741,20 @@ public function schema() {
}
/**
+ * Escapes a database name string.
+ *
+ * Force all database names to be strictly alphanumeric-plus-underscore.
+ * For some database drivers, it may also wrap the database name in
+ * database-specific escape characters.
+ *
+ * @return string
+ * The sanitized database name string.
+ */
+ public function escapeDatabase($database) {
+ return preg_replace('/[^A-Za-z0-9_.]+/', '', $database);
+ }
+
+ /**
* Escapes a table name string.
*
* Force all table names to be strictly alphanumeric-plus-underscore.
@@ -1088,6 +1102,16 @@ public function supportsTransactionalDDL() {
*/
abstract public function databaseType();
+ /**
+ * Creates a database.
+ *
+ * In order to use this method, you must be connected without a database
+ * specified.
+ *
+ * @param string $database
+ * The name of the database to create.
+ */
+ abstract public function createDatabase($database);
/**
* Gets any special processing requirements for the condition operator.
diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php
index ae24176..128780f 100644
--- a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php
+++ b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php
@@ -10,6 +10,7 @@
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\Core\Database\Database;
+use Drupal\Core\Database\DatabaseNotFoundException;
use Drupal\Core\Database\TransactionCommitFailedException;
use Drupal\Core\Database\DatabaseException;
use Drupal\Core\Database\Connection as DatabaseConnection;
@@ -24,6 +25,11 @@
class Connection extends DatabaseConnection {
/**
+ * Error code for "Unknown database" error.
+ */
+ const DATABASE_NOT_FOUND = 1049;
+
+ /**
* Flag to indicate if the cleanup function in __destruct() should run.
*
* @var boolean
@@ -47,7 +53,9 @@ public function __construct(array $connection_options = array()) {
// Default to TCP connection on port 3306.
$dsn = 'mysql:host=' . $connection_options['host'] . ';port=' . (empty($connection_options['port']) ? 3306 : $connection_options['port']);
}
- $dsn .= ';dbname=' . $connection_options['database'];
+ if (!empty($connection_options['database'])) {
+ $dsn .= ';dbname=' . $connection_options['database'];
+ }
// Allow PDO options to be overridden.
$connection_options += array(
'pdo' => array(),
@@ -113,6 +121,28 @@ public function databaseType() {
return 'mysql';
}
+ /**
+ * Overrides \Drupal\Core\Database\Connection::createDatabase().
+ *
+ * @param string $database
+ * The name of the database to create.
+ *
+ * @throws DatabaseNotFoundException
+ */
+ public function createDatabase($database) {
+ // Escape the database name.
+ $database = Database::getConnection()->escapeDatabase($database);
+
+ try {
+ // Create the database and set it as active.
+ $this->exec("CREATE DATABASE $database");
+ $this->exec("USE $database");
+ }
+ catch (\Exception $e) {
+ throw new DatabaseNotFoundException($e->getMessage());
+ }
+ }
+
public function mapConditionOperator($operator) {
// We don't want to override any of the defaults.
return NULL;
diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Install/Tasks.php b/core/lib/Drupal/Core/Database/Driver/mysql/Install/Tasks.php
index 4835103..0f09aae 100644
--- a/core/lib/Drupal/Core/Database/Driver/mysql/Install/Tasks.php
+++ b/core/lib/Drupal/Core/Database/Driver/mysql/Install/Tasks.php
@@ -8,6 +8,9 @@
namespace Drupal\Core\Database\Driver\mysql\Install;
use Drupal\Core\Database\Install\Tasks as InstallTasks;
+use Drupal\Core\Database\Database;
+use Drupal\Core\Database\Driver\mysql\Connection;
+use Drupal\Core\Database\DatabaseNotFoundException;
/**
* Specifies installation tasks for MySQL and equivalent databases.
@@ -33,4 +36,50 @@ public function name() {
public function minimumVersion() {
return '5.0.15';
}
+
+ /**
+ * Check database connection and attempt to create database if the database is
+ * missing.
+ */
+ protected function connect() {
+ try {
+ // This doesn't actually test the connection.
+ db_set_active();
+ // Now actually do a check.
+ Database::getConnection();
+ $this->pass('Drupal can CONNECT to the database ok.');
+ }
+ catch (\Exception $e) {
+ // Attempt to create the database if it is not found.
+ if ($e->getCode() == Connection::DATABASE_NOT_FOUND) {
+ // Remove the database string from connection info.
+ $connection_info = Database::getConnectionInfo();
+ $database = $connection_info['default']['database'];
+ unset($connection_info['default']['database']);
+
+ // In order to change the Database::$databaseInfo array, need to remove
+ // the active connection, then re-add it with the new info.
+ Database::removeConnection('default');
+ Database::addConnectionInfo('default', 'default', $connection_info['default']);
+
+ try {
+ // Now, attempt the connection again; if it's successful, attempt to
+ // create the database.
+ Database::getConnection()->createDatabase($database);
+ }
+ catch (DatabaseNotFoundException $e) {
+ // Still no dice; probably a permission issue. Raise the error to the
+ // installer.
+ $this->fail(st('Database %database not found. The server reports the following message when attempting to create the database: %error.', array('%database' => $database, '%error' => $e->getMessage())));
+ }
+ }
+ else {
+ // Database connection failed for some other reason than the database
+ // not existing.
+ $this->fail(st('Failed to connect to your database server. The server reports the following message: %error.
- Is the database server running?
- Does the database exist or does the database user have sufficient privileges to create the database?
- Have you entered the correct database name?
- Have you entered the correct username and password?
- Have you entered the correct database hostname?
', array('%error' => $e->getMessage())));
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
}
diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php
index 91d95ff..c278004 100644
--- a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php
+++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php
@@ -9,8 +9,10 @@
use Drupal\Core\Database\Database;
use Drupal\Core\Database\Connection as DatabaseConnection;
+use Drupal\Core\Database\DatabaseNotFoundException;
use Drupal\Core\Database\StatementInterface;
+use Locale;
use PDO;
use PDOException;
@@ -26,6 +28,11 @@ class Connection extends DatabaseConnection {
*/
const POSTGRESQL_NEXTID_LOCK = 1000;
+ /**
+ * Error code for "Unknown database" error.
+ */
+ const DATABASE_NOT_FOUND = 7;
+
public function __construct(array $connection_options = array()) {
// This driver defaults to transaction support, except if explicitly passed FALSE.
$this->transactionSupport = !isset($connection_options['transactions']) || ($connection_options['transactions'] !== FALSE);
@@ -55,6 +62,7 @@ public function __construct(array $connection_options = array()) {
$this->connectionOptions = $connection_options;
+ $connection_options['database'] = (!empty($connection_options['database']) ? $connection_options['database'] : 'template1');
$dsn = 'pgsql:host=' . $connection_options['host'] . ' dbname=' . $connection_options['database'] . ' port=' . $connection_options['port'];
// Allow PDO options to be overridden.
@@ -167,6 +175,36 @@ public function databaseType() {
return 'pgsql';
}
+ /**
+ * Overrides \Drupal\Core\Database\Connection::createDatabase().
+ *
+ * @param string $database
+ * The name of the database to create.
+ *
+ * @throws DatabaseNotFoundException
+ */
+ public function createDatabase($database) {
+ // Escape the database name.
+ $database = Database::getConnection()->escapeDatabase($database);
+
+ // If the PECL intl extension is installed, use it to determine the proper
+ // locale. Otherwise, fall back to en_US.
+ if (class_exists('Locale')) {
+ $locale = Locale::getDefault();
+ }
+ else {
+ $locale = 'en_US';
+ }
+
+ try {
+ // Create the database and set it as active.
+ $this->exec("CREATE DATABASE $database WITH TEMPLATE template0 ENCODING='utf8' LC_CTYPE='$locale.utf8' LC_COLLATE='$locale.utf8'");
+ }
+ catch (\Exception $e) {
+ throw new DatabaseNotFoundException($e->getMessage());
+ }
+ }
+
public function mapConditionOperator($operator) {
static $specials = array(
// In PostgreSQL, 'LIKE' is case-sensitive. For case-insensitive LIKE
diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Install/Tasks.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Install/Tasks.php
index 6dc40a2..78b7d06 100644
--- a/core/lib/Drupal/Core/Database/Driver/pgsql/Install/Tasks.php
+++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Install/Tasks.php
@@ -9,6 +9,8 @@
use Drupal\Core\Database\Database;
use Drupal\Core\Database\Install\Tasks as InstallTasks;
+use Drupal\Core\Database\Driver\pgsql\Connection;
+use Drupal\Core\Database\DatabaseNotFoundException;
use Exception;
@@ -42,6 +44,62 @@ public function minimumVersion() {
}
/**
+ * Check database connection and attempt to create database if the database is
+ * missing.
+ */
+ protected function connect() {
+ try {
+ // This doesn't actually test the connection.
+ db_set_active();
+ // Now actually do a check.
+ Database::getConnection();
+ $this->pass('Drupal can CONNECT to the database ok.');
+ }
+ catch (Exception $e) {
+ // Attempt to create the database if it is not found.
+ if ($e->getCode() == Connection::DATABASE_NOT_FOUND) {
+ // Remove the database string from connection info.
+ $connection_info = Database::getConnectionInfo();
+ $database = $connection_info['default']['database'];
+ unset($connection_info['default']['database']);
+
+ // In order to change the Database::$databaseInfo array, need to remove
+ // the active connection, then re-add it with the new info.
+ Database::removeConnection('default');
+ Database::addConnectionInfo('default', 'default', $connection_info['default']);
+
+ try {
+ // Now, attempt the connection again; if it's successful, attempt to
+ // create the database.
+ Database::getConnection()->createDatabase($database);
+ Database::closeConnection();
+
+ // Now, restore the database config.
+ Database::removeConnection('default');
+ $connection_info['default']['database'] = $database;
+ Database::addConnectionInfo('default', 'default', $connection_info['default']);
+
+ // Check the database connection.
+ Database::getConnection();
+ $this->pass('Drupal can CONNECT to the database ok.');
+ }
+ catch (DatabaseNotFoundException $e) {
+ // Still no dice; probably a permission issue. Raise the error to the
+ // installer.
+ $this->fail(st('Database %database not found. The server reports the following message when attempting to create the database: %error.', array('%database' => $database, '%error' => $e->getMessage())));
+ }
+ }
+ else {
+ // Database connection failed for some other reason than the database
+ // not existing.
+ $this->fail(st('Failed to connect to your database server. The server reports the following message: %error.- Is the database server running?
- Does the database exist, and have you entered the correct database name?
- Have you entered the correct username and password?
- Have you entered the correct database hostname?
', array('%error' => $e->getMessage())));
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+
+ /**
* Check encoding is UTF8.
*/
protected function checkEncoding() {
diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
index 02a55a4..abda556 100644
--- a/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
@@ -8,6 +8,7 @@
namespace Drupal\Core\Database\Driver\sqlite;
use Drupal\Core\Database\Database;
+use Drupal\Core\Database\DatabaseNotFoundException;
use Drupal\Core\Database\TransactionNoActiveException;
use Drupal\Core\Database\TransactionNameNonUniqueException;
use Drupal\Core\Database\TransactionCommitFailedException;
@@ -16,6 +17,7 @@
use PDO;
use Exception;
+use SplFileInfo;
/**
* Specific SQLite implementation of DatabaseConnection.
@@ -33,6 +35,11 @@ class Connection extends DatabaseConnection {
protected $savepointSupport = FALSE;
/**
+ * Error code for "Unable to open database file" error.
+ */
+ const DATABASE_NOT_FOUND = 14;
+
+ /**
* Whether or not the active transaction (if any) will be rolled back.
*
* @var boolean
@@ -267,6 +274,22 @@ public function databaseType() {
return 'sqlite';
}
+ /**
+ * Overrides \Drupal\Core\Database\Connection::createDatabase().
+ *
+ * @param string $database
+ * The name of the database to create.
+ *
+ * @throws DatabaseNotFoundException
+ */
+ public function createDatabase($database) {
+ // Verify the database is writable.
+ $db_directory = new SplFileInfo(dirname($database));
+ if (!$db_directory->isDir() && !drupal_mkdir($db_directory->getPathName(), 0755, TRUE)) {
+ throw new DatabaseNotFoundException('Unable to create database directory ' . $db_directory->getPathName());
+ }
+ }
+
public function mapConditionOperator($operator) {
// We don't want to override any of the defaults.
static $specials = array(
diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php
index 4f0e16b..5bf2642 100644
--- a/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php
@@ -7,9 +7,12 @@
namespace Drupal\Core\Database\Driver\sqlite\Install;
+use Drupal\Core\Database\Database;
+use Drupal\Core\Database\Driver\sqlite\Connection;
+use Drupal\Core\Database\DatabaseNotFoundException;
use Drupal\Core\Database\Install\Tasks as InstallTasks;
-use SplFileInfo;
+use Exception;
class Tasks extends InstallTasks {
protected $pdoDriver = 'sqlite';
@@ -41,17 +44,61 @@ public function getFormOptions($database) {
return $form;
}
- public function validateDatabaseSettings($database) {
- // Perform standard validation.
- $errors = parent::validateDatabaseSettings($database);
-
- // Verify the database is writable.
- $db_directory = new SplFileInfo(dirname($database['database']));
- if (!$db_directory->isWritable()) {
- $errors[$database['driver'] . '][database'] = st('The directory you specified is not writable by the web server.');
+ /**
+ * Check database connection and attempt to create database if the database is
+ * missing.
+ */
+ protected function connect() {
+ try {
+ // This doesn't actually test the connection.
+ db_set_active();
+ // Now actually do a check.
+ Database::getConnection();
+ $this->pass('Drupal can CONNECT to the database ok.');
}
+ catch (Exception $e) {
+ // Attempt to create the database if it is not found.
+ if ($e->getCode() == Connection::DATABASE_NOT_FOUND) {
+ // Remove the database string from connection info.
+ $connection_info = Database::getConnectionInfo();
+ $database = $connection_info['default']['database'];
+
+ // We cannot use file_directory_temp() here because we haven't yet
+ // successfully connected to the database.
+ $connection_info['default']['database'] = drupal_tempnam(sys_get_temp_dir(), 'sqlite');
- return $errors;
+ // In order to change the Database::$databaseInfo array, need to remove
+ // the active connection, then re-add it with the new info.
+ Database::removeConnection('default');
+ Database::addConnectionInfo('default', 'default', $connection_info['default']);
+
+ try {
+ Database::getConnection()->createDatabase($database);
+ Database::closeConnection();
+
+ // Now, restore the database config.
+ Database::removeConnection('default');
+ $connection_info['default']['database'] = $database;
+ Database::addConnectionInfo('default', 'default', $connection_info['default']);
+
+ // Check the database connection.
+ Database::getConnection();
+ $this->pass('Drupal can CONNECT to the database ok.');
+ }
+ catch (DatabaseNotFoundException $e) {
+ // Still no dice; probably a permission issue. Raise the error to the
+ // installer.
+ $this->fail(st('Database %database not found. The server reports the following message when attempting to create the database: %error.', array('%database' => $database, '%error' => $e->getMessage())));
+ }
+ }
+ else {
+ // Database connection failed for some other reason than the database
+ // not existing.
+ $this->fail(st('Failed to connect to your database server. The server reports the following message: %error.- Is the database server running?
- Does the database exist, and have you entered the correct database name?
- Have you entered the correct username and password?
- Have you entered the correct database hostname?
', array('%error' => $e->getMessage())));
+ return FALSE;
+ }
+ }
+ return TRUE;
}
}