cms/drupal/includes/database/pgsql/install.inc
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * Install functions for PostgreSQL embedded database engine.
       
     6  */
       
     7 
       
     8 
       
     9 // PostgreSQL specific install functions
       
    10 
       
    11 class DatabaseTasks_pgsql extends DatabaseTasks {
       
    12   protected $pdoDriver = 'pgsql';
       
    13 
       
    14   public function __construct() {
       
    15     $this->tasks[] = array(
       
    16       'function' => 'checkEncoding',
       
    17       'arguments' => array(),
       
    18     );
       
    19     $this->tasks[] = array(
       
    20       'function' => 'checkPHPVersion',
       
    21       'arguments' => array(),
       
    22     );
       
    23     $this->tasks[] = array(
       
    24       'function' => 'checkBinaryOutput',
       
    25       'arguments' => array(),
       
    26     );
       
    27     $this->tasks[] = array(
       
    28       'function' => 'initializeDatabase',
       
    29       'arguments' => array(),
       
    30     );
       
    31   }
       
    32 
       
    33   public function name() {
       
    34     return st('PostgreSQL');
       
    35   }
       
    36 
       
    37   public function minimumVersion() {
       
    38     return '8.3';
       
    39   }
       
    40 
       
    41   /**
       
    42    * Check encoding is UTF8.
       
    43    */
       
    44   protected function checkEncoding() {
       
    45     try {
       
    46       if (db_query('SHOW server_encoding')->fetchField() == 'UTF8') {
       
    47         $this->pass(st('Database is encoded in UTF-8'));
       
    48       }
       
    49       else {
       
    50         $replacements = array(
       
    51           '%encoding' => 'UTF8',
       
    52           '%driver' => $this->name(),
       
    53           '!link' => '<a href="INSTALL.pgsql.txt">INSTALL.pgsql.txt</a>'
       
    54         );
       
    55         $text  = 'The %driver database must use %encoding encoding to work with Drupal.';
       
    56         $text .= 'Recreate the database with %encoding encoding. See !link for more details.';
       
    57         $this->fail(st($text, $replacements));
       
    58       }
       
    59     }
       
    60     catch (Exception $e) {
       
    61       $this->fail(st('Drupal could not determine the encoding of the database was set to UTF-8'));
       
    62     }
       
    63   }
       
    64 
       
    65   /**
       
    66    * Check PHP version.
       
    67    *
       
    68    * There are two bugs in PDO_pgsql affecting Drupal:
       
    69    *
       
    70    * - in versions < 5.2.7, PDO_pgsql refuses to insert an empty string into
       
    71    *   a NOT NULL BLOB column. See: http://bugs.php.net/bug.php?id=46249
       
    72    * - in versions < 5.2.11 and < 5.3.1 that prevents inserting integer values
       
    73    *   into numeric columns that exceed the PHP_INT_MAX value.
       
    74    *   See: http://bugs.php.net/bug.php?id=48924
       
    75    */
       
    76   function checkPHPVersion() {
       
    77     if (!version_compare(PHP_VERSION, '5.2.11', '>=') || (version_compare(PHP_VERSION, '5.3.0', '>=') && !version_compare(PHP_VERSION, '5.3.1', '>='))) {
       
    78       $this->fail(st('The version of PHP you are using has known issues with PostgreSQL. You need to upgrade PHP to 5.2.11, 5.3.1 or greater.'));
       
    79     };
       
    80   }
       
    81 
       
    82   /**
       
    83    * Check Binary Output.
       
    84    *
       
    85    * Unserializing does not work on Postgresql 9 when bytea_output is 'hex'.
       
    86    */
       
    87   function checkBinaryOutput() {
       
    88     // PostgreSQL < 9 doesn't support bytea_output, so verify we are running
       
    89     // at least PostgreSQL 9.
       
    90     $database_connection = Database::getConnection();
       
    91     if (version_compare($database_connection->version(), '9') >= 0) {
       
    92       if (!$this->checkBinaryOutputSuccess()) {
       
    93         // First try to alter the database. If it fails, raise an error telling
       
    94         // the user to do it themselves.
       
    95         $connection_options = $database_connection->getConnectionOptions();
       
    96         // It is safe to include the database name directly here, because this
       
    97         // code is only called when a connection to the database is already
       
    98         // established, thus the database name is guaranteed to be a correct
       
    99         // value.
       
   100         $query = "ALTER DATABASE \"" . $connection_options['database'] . "\" SET bytea_output = 'escape';";
       
   101         try {
       
   102           db_query($query);
       
   103         }
       
   104         catch (Exception $e) {
       
   105           // Ignore possible errors when the user doesn't have the necessary
       
   106           // privileges to ALTER the database.
       
   107         }
       
   108 
       
   109         // Close the database connection so that the configuration parameter
       
   110         // is applied to the current connection.
       
   111         db_close();
       
   112 
       
   113         // Recheck, if it fails, finally just rely on the end user to do the
       
   114         // right thing.
       
   115         if (!$this->checkBinaryOutputSuccess()) {
       
   116           $replacements = array(
       
   117             '%setting' => 'bytea_output',
       
   118             '%current_value' => 'hex',
       
   119             '%needed_value' => 'escape',
       
   120             '!query' => "<code>" . $query . "</code>",
       
   121           );
       
   122           $this->fail(st("The %setting setting is currently set to '%current_value', but needs to be '%needed_value'. Change this by running the following query: !query", $replacements));
       
   123         }
       
   124       }
       
   125     }
       
   126   }
       
   127 
       
   128   /**
       
   129    * Verify that a binary data roundtrip returns the original string.
       
   130    */
       
   131   protected function checkBinaryOutputSuccess() {
       
   132     $bytea_output = db_query("SELECT 'encoding'::bytea AS output")->fetchField();
       
   133     return ($bytea_output == 'encoding');
       
   134   }
       
   135 
       
   136   /**
       
   137    * Make PostgreSQL Drupal friendly.
       
   138    */
       
   139   function initializeDatabase() {
       
   140     // We create some functions using global names instead of prefixing them
       
   141     // like we do with table names. This is so that we don't double up if more
       
   142     // than one instance of Drupal is running on a single database. We therefore
       
   143     // avoid trying to create them again in that case.
       
   144 
       
   145     try {
       
   146       // Create functions.
       
   147       db_query('CREATE OR REPLACE FUNCTION "greatest"(numeric, numeric) RETURNS numeric AS
       
   148         \'SELECT CASE WHEN (($1 > $2) OR ($2 IS NULL)) THEN $1 ELSE $2 END;\'
       
   149         LANGUAGE \'sql\''
       
   150       );
       
   151       db_query('CREATE OR REPLACE FUNCTION "greatest"(numeric, numeric, numeric) RETURNS numeric AS
       
   152         \'SELECT greatest($1, greatest($2, $3));\'
       
   153         LANGUAGE \'sql\''
       
   154       );
       
   155       // Don't use {} around pg_proc table.
       
   156       if (!db_query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'rand'")->fetchField()) {
       
   157         db_query('CREATE OR REPLACE FUNCTION "rand"() RETURNS float AS
       
   158           \'SELECT random();\'
       
   159           LANGUAGE \'sql\''
       
   160         );
       
   161       }
       
   162 
       
   163       db_query('CREATE OR REPLACE FUNCTION "substring_index"(text, text, integer) RETURNS text AS
       
   164         \'SELECT array_to_string((string_to_array($1, $2)) [1:$3], $2);\'
       
   165         LANGUAGE \'sql\''
       
   166       );
       
   167 
       
   168       // Using || to concatenate in Drupal is not recommended because there are
       
   169       // database drivers for Drupal that do not support the syntax, however
       
   170       // they do support CONCAT(item1, item2) which we can replicate in
       
   171       // PostgreSQL. PostgreSQL requires the function to be defined for each
       
   172       // different argument variation the function can handle.
       
   173       db_query('CREATE OR REPLACE FUNCTION "concat"(anynonarray, anynonarray) RETURNS text AS
       
   174         \'SELECT CAST($1 AS text) || CAST($2 AS text);\'
       
   175         LANGUAGE \'sql\'
       
   176       ');
       
   177       db_query('CREATE OR REPLACE FUNCTION "concat"(text, anynonarray) RETURNS text AS
       
   178         \'SELECT $1 || CAST($2 AS text);\'
       
   179         LANGUAGE \'sql\'
       
   180       ');
       
   181       db_query('CREATE OR REPLACE FUNCTION "concat"(anynonarray, text) RETURNS text AS
       
   182         \'SELECT CAST($1 AS text) || $2;\'
       
   183         LANGUAGE \'sql\'
       
   184       ');
       
   185       db_query('CREATE OR REPLACE FUNCTION "concat"(text, text) RETURNS text AS
       
   186         \'SELECT $1 || $2;\'
       
   187         LANGUAGE \'sql\'
       
   188       ');
       
   189 
       
   190       $this->pass(st('PostgreSQL has initialized itself.'));
       
   191     }
       
   192     catch (Exception $e) {
       
   193       $this->fail(st('Drupal could not be correctly setup with the existing database. Revise any errors.'));
       
   194     }
       
   195   }
       
   196 }
       
   197