From 94f41df4a3da8ed73b1da3f37b20d819ac193571 Mon Sep 17 00:00:00 2001 From: amontero Date: Thu, 13 Dec 2012 17:55:38 +0100 Subject: [PATCH] Create database at installation time. --- INSTALL.txt | 12 ++++----- includes/database/database.inc | 24 ++++++++++++++++++ includes/database/mysql/database.inc | 31 ++++++++++++++++++++++- includes/database/mysql/install.inc | 46 ++++++++++++++++++++++++++++++++++ includes/install.inc | 2 +- 5 files changed, 107 insertions(+), 8 deletions(-) diff --git a/INSTALL.txt b/INSTALL.txt index c3a26ad..c48362d 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -99,12 +99,12 @@ INSTALLATION 3. Create the Drupal database. - Because Drupal stores all site information in a database, you must create - this database in order to install Drupal, and grant Drupal certain database - privileges (such as the ability to create tables). For details, consult - INSTALL.mysql.txt, INSTALL.pgsql.txt, or INSTALL.sqlite.txt. You may also - need to consult your web hosting provider for instructions specific to your - web host. + Because Drupal stores all site information in a database, the Drupal + installer will attempt to create this database for you. If you create the + database manually, you must grant Drupal certain database privileges (such as + the ability to create tables). For details, consult INSTALL.mysql.txt, + INSTALL.pgsql.txt, or INSTALL.sqlite.txt. You may also need to consult your + web hosting provider for instructions specific to your web host. Take note of the username, password, database name, and hostname as you create the database. You will enter this information during the install. diff --git a/includes/database/database.inc b/includes/database/database.inc index 26ce6fc..f85288c 100644 --- a/includes/database/database.inc +++ b/includes/database/database.inc @@ -910,6 +910,20 @@ abstract class DatabaseConnection extends PDO { } /** + * 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. @@ -1257,6 +1271,16 @@ abstract class DatabaseConnection extends PDO { */ 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/includes/database/mysql/database.inc b/includes/database/mysql/database.inc index 00d81f4..2304d3a 100644 --- a/includes/database/mysql/database.inc +++ b/includes/database/mysql/database.inc @@ -13,6 +13,11 @@ class DatabaseConnection_mysql 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 @@ -36,7 +41,9 @@ class DatabaseConnection_mysql extends DatabaseConnection { // 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(), @@ -102,6 +109,28 @@ class DatabaseConnection_mysql extends DatabaseConnection { return 'mysql'; } + /** + * Overrides DatabaseConnection::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/includes/database/mysql/install.inc b/includes/database/mysql/install.inc index 75f2ae3..2550a1c 100644 --- a/includes/database/mysql/install.inc +++ b/includes/database/mysql/install.inc @@ -29,5 +29,51 @@ class DatabaseTasks_mysql extends DatabaseTasks { 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() == DatabaseConnection_mysql::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.', array('%error' => $e->getMessage()))); + return FALSE; + } + } + return TRUE; + } } diff --git a/includes/install.inc b/includes/install.inc index c4bcb88..d9d4009 100644 --- a/includes/install.inc +++ b/includes/install.inc @@ -482,7 +482,7 @@ abstract class DatabaseTasks { '#default_value' => empty($database['database']) ? '' : $database['database'], '#size' => 45, '#required' => TRUE, - '#description' => st('The name of the database your @drupal data will be stored in. It must exist on your server before @drupal can be installed.', array('@drupal' => drupal_install_profile_distribution_name())), + '#description' => st('The name of the database your @drupal data will be stored in.', array('@drupal' => drupal_install_profile_distribution_name())), ); $form['username'] = array( -- 1.7.9.5