|
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 |