Commit 1aec2983 authored by Dries's avatar Dries

- Patch #349508 by Josh Waihi: make sure that the database is installed using UTF-8 on PostgreSQL.

parent a7e7c0a9
......@@ -12,9 +12,9 @@ Note that the database must be created with UTF-8 (Unicode) encoding.
following command creates a new user named "username" and asks for a
password for that user:
createuser --pwprompt --encrypted --no-adduser --no-createdb username
createuser --pwprompt --encrypted --no-createrole --no-createdb username
If everything works correctly, you'll see a "CREATE USER" notice.
If there are no errors then the command was successful
2. CREATE THE DRUPAL DATABASE
......@@ -23,6 +23,6 @@ Note that the database must be created with UTF-8 (Unicode) encoding.
The following command creates a new database named "databasename", which is
owned by previously created "username":
createdb --encoding=UNICODE --owner=username databasename
createdb --encoding=UTF8 --owner=username databasename
If everything works correctly, you'll see a "CREATE DATABASE" notice.
If there are no errors then the command was successful
......@@ -2693,6 +2693,21 @@ function db_rewrite_sql($query, $primary_table = 'n', $primary_field = 'nid', $
return $query;
}
/**
* Ensures the environment for a Drupal database on a predefined connection.
*
* This will run tasks that check that Drupal can perform all of the functions
* on a database, that Drupal needs. Tasks include simple checks like CREATE
* TABLE to database specfic functions like stored procedures and client
* encoding.
*/
function db_run_tasks($driver) {
$task_class = 'DatabaseTasks_' . $driver;
$DatabaseTasks = new $task_class();
$DatabaseTasks->runTasks();
return true;
}
/**
* @} End of "ingroup database-legacy".
*/
......@@ -9,7 +9,7 @@
// MySQL specific install functions
class DatabaseInstaller_mysql extends DatabaseInstaller {
class DatabaseTasks_mysql extends DatabaseTasks {
protected $pdoDriver = 'mysql';
public function name() {
return 'MySQL';
......
......@@ -9,10 +9,41 @@
// PostgreSQL specific install functions
class DatabaseInstaller_pgsql extends DatabaseInstaller {
class DatabaseTasks_pgsql extends DatabaseTasks {
protected $pdoDriver = 'pgsql';
public function __construct() {
$this->tasks[] = array(
'function' => 'checkEncoding',
'arguments' => array(),
);
}
public function name() {
return 'PostgreSQL';
}
/**
* Check encoding is UTF8.
*/
protected function checkEncoding() {
try {
if (db_query('SHOW server_encoding')->fetchField() == 'UTF8') {
$this->pass(st('Database is encoded in UTF-8'));
}
else {
$replacements = array(
'%encoding' => 'UTF8',
'%driver' => $this->name(),
'!link' => '<a href="INSTALL.pgsql.txt">INSTALL.pgsql.txt</a>'
);
$text = 'The %driver database must use %encoding encoding to work with Drupal.';
$text .= 'Please recreate the database with %encoding encoding. See !link for more details.';
$this->fail(st($text, $replacements));
}
} catch (Exception $e) {
$this->fail(st('Drupal could not determine the encoding of the database was set to UTF-8'));
}
}
}
......@@ -6,7 +6,7 @@
* SQLite specific install functions
*/
class DatabaseInstaller_sqlite extends DatabaseInstaller {
class DatabaseTasks_sqlite extends DatabaseTasks {
protected $pdoDriver = 'sqlite';
public function name() {
return 'SQLite';
......
......@@ -222,7 +222,7 @@ function drupal_detect_database_types() {
}
foreach ($drivers as $driver => $file) {
$class = 'DatabaseInstaller_' . $driver;
$class = 'DatabaseTasks_' . $driver;
$installer = new $class();
if ($installer->installable()) {
$databases[$driver] = $installer->name();
......@@ -239,96 +239,167 @@ function drupal_detect_database_types() {
return $databases;
}
abstract class DatabaseInstaller {
protected $success = array();
protected $tests = array(
'testCreate' => array(
'query' => 'CREATE TABLE drupal_install_test (id int NULL)',
'success' => 'CREATE',
'message' => 'Failed to create a test table on your %name database server with the command %query. %name reports the following message: %error.<ul><li>Are you sure the configured username has the necessary %name permissions to create tables in the database?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.',
'fatal' => TRUE,
/**
* Database installer structure.
*
* Defines basic Drupal requirements for databases.
*/
abstract class DatabaseTasks {
/**
* Structure that describes each task to run.
*
* @var array
*
* Each value of the tasks array is an associative array defining the function
* to call (optional) and any arguments to be passed to the function.
*/
protected $tasks = array(
array(
'arguments' => array(
'CREATE TABLE drupal_install_test (id int NULL)',
'Drupal can use CREATE TABLE database commands.',
'Failed to <strong>CREATE</strong> a test table on your %name database server with the command %query. %name reports the following message: %error.<p>Are you sure the configured username has the necessary %name permissions to create tables in the database?</p>',
TRUE,
),
),
'testInsert' => array(
'query' => 'INSERT INTO drupal_install_test (id) VALUES (1)',
'success' => 'INSERT',
'message' => 'Failed to insert a value into a test table on your %name database server. We tried inserting a value with the command %query and %name reported the following error: %error.',
array(
'arguments' => array(
'INSERT INTO drupal_install_test (id) VALUES (1)',
'Drupal can use INSERT database commands.',
'Failed to <strong>INSERT</strong> a value into a test table on your %name database server. We tried inserting a value with the command %query and %name reported the following error: %error.',
),
),
'testUpdate' => array(
'query' => 'UPDATE drupal_install_test SET id = 2',
'success' => 'UPDATE',
'message' => 'Failed to update a value in a test table on your %name database server. We tried updating a value with the command %query and %name reported the following error: %error.',
array(
'arguments' => array(
'UPDATE drupal_install_test SET id = 2',
'Drupal can use UPDATE database commands.',
'Failed to <strong>UPDATE</strong> a value in a test table on your %name database server. We tried updating a value with the command %query and %name reported the following error: %error.',
),
),
'testDelete' => array(
'query' => 'DELETE FROM drupal_install_test',
'success' => 'DELETE',
'message' => 'Failed to delete a value from a test table on your %name database server. We tried deleting a value with the command %query and %name reported the following error: %error.',
array(
'arguments' => array(
'DELETE FROM drupal_install_test',
'Drupal can use DELETE database commands.',
'Failed to <strong>DELETE</strong> a value from a test table on your %name database server. We tried deleting a value with the command %query and %name reported the following error: %error.',
),
),
'testDrop' => array(
'query' => 'DROP TABLE drupal_install_test',
'success' => 'DELETE',
'message' => 'Failed to drop a test table from your %name database server. We tried dropping a table with the command %query and %name reported the following error %error.',
array(
'arguments' => array(
'DROP TABLE drupal_install_test',
'Drupal can use DROP TABLE database commands.',
'Failed to <strong>DROP</strong> a test table from your %name database server. We tried dropping a table with the command %query and %name reported the following error %error.',
),
),
);
public $error = FALSE;
/**
* Results from tasks.
*
* @var array
*/
protected $results = array();
/**
* Ensure the PDO driver is supported by the version of PHP in use.
*/
protected function hasPdoDriver() {
return in_array($this->pdoDriver, PDO::getAvailableDrivers());
}
/**
* Assert test as failed.
*/
protected function fail($message) {
$this->results[$message] = FALSE;
}
/**
* Assert test as a pass.
*/
protected function pass($message) {
$this->results[$message] = TRUE;
}
/**
* Check whether Drupal is installable on the database.
*/
public function installable() {
return $this->hasPdoDriver();
return $this->hasPdoDriver() && empty($this->error);
}
abstract public function name();
public function test() {
$return = $this->testConnect();
if ($return === FALSE) {
return FALSE;
/**
* Run database tasks and tests to see if Drupal can run on the database.
*/
public function runTasks() {
// We need to establish a connection before we can run tests.
if ($this->connect()) {
foreach ($this->tasks as $task) {
if (!isset($task['function'])) {
$task['function'] = 'runTestQuery';
}
if (method_exists($this, $task['function'])) {
// Returning false is fatal. No other tasks can run.
if (FALSE === call_user_func_array(array($this, $task['function']), $task['arguments'])) {
break;
}
}
else {
drupal_set_message(st('Failed to run all tasks against the database server. The task %task wasn\'t found.', array('%task' => $task['function'])), 'error');
}
}
}
foreach ($this->tests as $test) {
$return = $this->runTestQuery($test['query'], $test['success'], $test['message'], !empty($test['fatal']));
if ($return === FALSE) {
return FALSE;
// Check for failed results and compile message
$message = '';
foreach ($this->results as $result => $success) {
if (!$success) {
$message .= '<p class="error">' . $result . '</p>';
}
}
return $this->success;
if (!empty($message)) {
$message = '<p>In order for Drupal to work, and to continue with the installation process, you must resolve all issues reported below. For more help with configuring your database server, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what any of this means you should probably contact your hosting provider.</p>' . $message;
throw new DatabaseTaskException($message);
}
}
/**
* Check if we can connect to the database.
*
* @return
* FALSE on failure.
*/
protected function testConnect() {
protected function connect() {
try {
// This doesn't actually test the connection.
db_set_active();
// Now actually do a check.
Database::getConnection();
$this->success[] = 'CONNECT';
$this->pass('Drupal can CONNECT to the database ok.');
}
catch (Exception $e) {
drupal_set_message(st('Failed to connect to your %name database server. %name reports the following message: <strong>%error</strong>.<ul><li>Are you sure that you have typed the correct database hostname?</li><li>Are you sure that the database server is running?</li><li>Are you sure you have the correct database name?</li><li>Are you sure you have the correct username and password?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%error' => $e->getMessage(), '%name' => $this->name())), 'error');
$this->fail(st('Failed to connect to your %name database server. %name reports the following message: %error.<ul><li>Are you sure you have the correct username and password?</li><li>Are you sure that you have typed the correct database hostname?</li><li>Are you sure that the database server is running?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%error' => $e->getMessage(), '%name' => $this->name())));
return FALSE;
}
return TRUE;
}
protected function runTestQuery($query, $success, $message, $fatal = FALSE) {
/**
* Run SQL tests to ensure the database can execute commands with the current user.
*/
protected function runTestQuery($query, $pass, $fail, $fatal = FALSE) {
try {
db_query($query);
$this->success[] = $success;
$this->pass(st($pass));
}
catch (Exception $e) {
drupal_set_message(st($message, array('%query' => $query, '%error' => $e->getMessage(), '%name' => $this->name())), 'error');
$this->error = TRUE;
if ($fatal) {
return FALSE;
}
$this->fail(st($fail, array('%query' => $query, '%error' => $e->getMessage(), '%name' => $this->name())));
return !$fatal;
}
}
}
/**
* @class Exception class used to throw error if the DatabaseInstaller fails.
*/
class DatabaseTaskException extends Exception {
}
/**
* Replace values in settings.php with values in the submitted array.
......
......@@ -336,7 +336,6 @@ function install_settings_form(&$form_state, $profile, $install_locale, $setting
* Form API validate for install_settings form.
*/
function install_settings_form_validate($form, &$form_state) {
global $db_url;
_install_settings_form_validate($form_state['values'], $form_state['values']['settings_file'], $form_state, $form);
}
......@@ -364,17 +363,15 @@ function _install_settings_form_validate($database, $settings_file, &$form_state
if (isset($form)) {
form_set_value($form['_database'], $database, $form_state);
}
$class = "DatabaseInstaller_$driver";
$test = new $class;
// Run tasks associated with the database type. Any errors are caught in the
// calling function
$databases['default']['default'] = $database;
$return = $test->test();
if (!$return || $test->error) {
if (!empty($test->success)) {
form_set_error('db_type', st('In order for Drupal to work, and to continue with the installation process, you must resolve all permission issues reported above. We were able to verify that we have permission for the following commands: %commands. For more help with configuring your database server, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what any of this means you should probably contact your hosting provider.', array('%commands' => implode($test->success, ', '))));
}
else {
form_set_error('driver', '');
}
try {
db_run_tasks($database['driver']);
}
catch (DatabaseTaskException $e) {
form_set_error('db_type', $e->getMessage());
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment