Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 11 |
CRAP | |
0.00% |
0 / 148 |
| Tasks | |
0.00% |
0 / 1 |
|
0.00% |
0 / 11 |
756 | |
0.00% |
0 / 148 |
| __construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 17 |
|||
| name | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| minimumVersion | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| connect | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 32 |
|||
| checkEncoding | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 15 |
|||
| checkBinaryOutput | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 23 |
|||
| checkBinaryOutputSuccess | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
| checkStandardConformingStrings | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 21 |
|||
| checkStandardConformingStringsSuccess | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
| initializeDatabase | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 24 |
|||
| getFormOptions | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 6 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\Core\Database\Driver\pgsql\Install\Tasks. | |
| */ | |
| namespace Drupal\Core\Database\Driver\pgsql\Install; | |
| 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; | |
| /** | |
| * Specifies installation tasks for PostgreSQL databases. | |
| */ | |
| class Tasks extends InstallTasks { | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| protected $pdoDriver = 'pgsql'; | |
| /** | |
| * Constructs a \Drupal\Core\Database\Driver\pgsql\Install\Tasks object. | |
| */ | |
| public function __construct() { | |
| $this->tasks[] = array( | |
| 'function' => 'checkEncoding', | |
| 'arguments' => array(), | |
| ); | |
| $this->tasks[] = array( | |
| 'function' => 'checkBinaryOutput', | |
| 'arguments' => array(), | |
| ); | |
| $this->tasks[] = array( | |
| 'function' => 'checkStandardConformingStrings', | |
| 'arguments' => array(), | |
| ); | |
| $this->tasks[] = array( | |
| 'function' => 'initializeDatabase', | |
| 'arguments' => array(), | |
| ); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function name() { | |
| return t('PostgreSQL'); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function minimumVersion() { | |
| return '9.1.2'; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| 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(t('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(t('Failed to connect to your database server. The server reports the following message: %error.<ul><li>Is the database server running?</li><li>Does the database exist, and have you entered the correct database name?</li><li>Have you entered the correct username and password?</li><li>Have you entered the correct database hostname?</li></ul>', array('%error' => $e->getMessage()))); | |
| return FALSE; | |
| } | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| * Check encoding is UTF8. | |
| */ | |
| protected function checkEncoding() { | |
| try { | |
| if (db_query('SHOW server_encoding')->fetchField() == 'UTF8') { | |
| $this->pass(t('Database is encoded in UTF-8')); | |
| } | |
| else { | |
| $this->fail(t('The %driver database must use %encoding encoding to work with Drupal. Recreate the database with %encoding encoding. See <a href="INSTALL.pgsql.txt">INSTALL.pgsql.txt</a> for more details.', array( | |
| '%encoding' => 'UTF8', | |
| '%driver' => $this->name(), | |
| ))); | |
| } | |
| } | |
| catch (\Exception $e) { | |
| $this->fail(t('Drupal could not determine the encoding of the database was set to UTF-8')); | |
| } | |
| } | |
| /** | |
| * Check Binary Output. | |
| * | |
| * Unserializing does not work on Postgresql 9 when bytea_output is 'hex'. | |
| */ | |
| function checkBinaryOutput() { | |
| // PostgreSQL < 9 doesn't support bytea_output, so verify we are running | |
| // at least PostgreSQL 9. | |
| $database_connection = Database::getConnection(); | |
| if (version_compare($database_connection->version(), '9') >= 0) { | |
| if (!$this->checkBinaryOutputSuccess()) { | |
| // First try to alter the database. If it fails, raise an error telling | |
| // the user to do it themselves. | |
| $connection_options = $database_connection->getConnectionOptions(); | |
| // It is safe to include the database name directly here, because this | |
| // code is only called when a connection to the database is already | |
| // established, thus the database name is guaranteed to be a correct | |
| // value. | |
| $query = "ALTER DATABASE \"" . $connection_options['database'] . "\" SET bytea_output = 'escape';"; | |
| try { | |
| db_query($query); | |
| } | |
| catch (\Exception $e) { | |
| // Ignore possible errors when the user doesn't have the necessary | |
| // privileges to ALTER the database. | |
| } | |
| // Close the database connection so that the configuration parameter | |
| // is applied to the current connection. | |
| db_close(); | |
| // Recheck, if it fails, finally just rely on the end user to do the | |
| // right thing. | |
| if (!$this->checkBinaryOutputSuccess()) { | |
| $replacements = array( | |
| '%setting' => 'bytea_output', | |
| '%current_value' => 'hex', | |
| '%needed_value' => 'escape', | |
| '@query' => $query, | |
| ); | |
| $this->fail(t("The %setting setting is currently set to '%current_value', but needs to be '%needed_value'. Change this by running the following query: <code>@query</code>", $replacements)); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Verify that a binary data roundtrip returns the original string. | |
| */ | |
| protected function checkBinaryOutputSuccess() { | |
| $bytea_output = db_query("SHOW bytea_output")->fetchField(); | |
| return ($bytea_output == 'escape'); | |
| } | |
| /** | |
| * Ensures standard_conforming_strings setting is 'on'. | |
| * | |
| * When standard_conforming_strings setting is 'on' string literals ('...') | |
| * treat backslashes literally, as specified in the SQL standard. This allows | |
| * Drupal to convert between bytea, text and varchar columns. | |
| */ | |
| public function checkStandardConformingStrings() { | |
| $database_connection = Database::getConnection(); | |
| if (!$this->checkStandardConformingStringsSuccess()) { | |
| // First try to alter the database. If it fails, raise an error telling | |
| // the user to do it themselves. | |
| $connection_options = $database_connection->getConnectionOptions(); | |
| // It is safe to include the database name directly here, because this | |
| // code is only called when a connection to the database is already | |
| // established, thus the database name is guaranteed to be a correct | |
| // value. | |
| $query = "ALTER DATABASE \"" . $connection_options['database'] . "\" SET standard_conforming_strings = 'on';"; | |
| try { | |
| $database_connection->query($query); | |
| } | |
| catch (\Exception $e) { | |
| // Ignore possible errors when the user doesn't have the necessary | |
| // privileges to ALTER the database. | |
| } | |
| // Close the database connection so that the configuration parameter | |
| // is applied to the current connection. | |
| Database::closeConnection(); | |
| // Recheck, if it fails, finally just rely on the end user to do the | |
| // right thing. | |
| if (!$this->checkStandardConformingStringsSuccess()) { | |
| $replacements = array( | |
| '%setting' => 'standard_conforming_strings', | |
| '%current_value' => 'off', | |
| '%needed_value' => 'on', | |
| '@query' => $query, | |
| ); | |
| $this->fail(t("The %setting setting is currently set to '%current_value', but needs to be '%needed_value'. Change this by running the following query: <code>@query</code>", $replacements)); | |
| } | |
| } | |
| } | |
| /** | |
| * Verifies the standard_conforming_strings setting. | |
| */ | |
| protected function checkStandardConformingStringsSuccess() { | |
| $standard_conforming_strings = Database::getConnection()->query("SHOW standard_conforming_strings")->fetchField(); | |
| return ($standard_conforming_strings == 'on'); | |
| } | |
| /** | |
| * Make PostgreSQL Drupal friendly. | |
| */ | |
| function initializeDatabase() { | |
| // We create some functions using global names instead of prefixing them | |
| // like we do with table names. This is so that we don't double up if more | |
| // than one instance of Drupal is running on a single database. We therefore | |
| // avoid trying to create them again in that case. | |
| // At the same time checking for the existence of the function fixes | |
| // concurrency issues, when both try to update at the same time. | |
| try { | |
| $connection = Database::getConnection(); | |
| // Don't use {} around pg_proc table. | |
| if (!$connection->query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'rand'")->fetchField()) { | |
| $connection->query('CREATE OR REPLACE FUNCTION "rand"() RETURNS float AS | |
| \'SELECT random();\' | |
| LANGUAGE \'sql\'', | |
| [], | |
| [ 'allow_delimiter_in_query' => TRUE ] | |
| ); | |
| } | |
| if (!$connection->query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'substring_index'")->fetchField()) { | |
| $connection->query('CREATE OR REPLACE FUNCTION "substring_index"(text, text, integer) RETURNS text AS | |
| \'SELECT array_to_string((string_to_array($1, $2)) [1:$3], $2);\' | |
| LANGUAGE \'sql\'', | |
| [], | |
| [ 'allow_delimiter_in_query' => TRUE ] | |
| ); | |
| } | |
| $this->pass(t('PostgreSQL has initialized itself.')); | |
| } | |
| catch (\Exception $e) { | |
| $this->fail(t('Drupal could not be correctly setup with the existing database due to the following error: @error.', ['@error' => $e->getMessage()])); | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getFormOptions(array $database) { | |
| $form = parent::getFormOptions($database); | |
| if (empty($form['advanced_options']['port']['#default_value'])) { | |
| $form['advanced_options']['port']['#default_value'] = '5432'; | |
| } | |
| return $form; | |
| } | |
| } |