This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * PostgreSQL-specific installer. |
||
4 | * |
||
5 | * This program is free software; you can redistribute it and/or modify |
||
6 | * it under the terms of the GNU General Public License as published by |
||
7 | * the Free Software Foundation; either version 2 of the License, or |
||
8 | * (at your option) any later version. |
||
9 | * |
||
10 | * This program is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | * GNU General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU General Public License along |
||
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
18 | * http://www.gnu.org/copyleft/gpl.html |
||
19 | * |
||
20 | * @file |
||
21 | * @ingroup Deployment |
||
22 | */ |
||
23 | |||
24 | /** |
||
25 | * Class for setting up the MediaWiki database using Postgres. |
||
26 | * |
||
27 | * @ingroup Deployment |
||
28 | * @since 1.17 |
||
29 | */ |
||
30 | class PostgresInstaller extends DatabaseInstaller { |
||
31 | |||
32 | protected $globalNames = [ |
||
33 | 'wgDBserver', |
||
34 | 'wgDBport', |
||
35 | 'wgDBname', |
||
36 | 'wgDBuser', |
||
37 | 'wgDBpassword', |
||
38 | 'wgDBmwschema', |
||
39 | ]; |
||
40 | |||
41 | protected $internalDefaults = [ |
||
42 | '_InstallUser' => 'postgres', |
||
43 | ]; |
||
44 | |||
45 | public $minimumVersion = '8.3'; |
||
46 | public $maxRoleSearchDepth = 5; |
||
47 | |||
48 | protected $pgConns = []; |
||
49 | |||
50 | function getName() { |
||
51 | return 'postgres'; |
||
52 | } |
||
53 | |||
54 | public function isCompiled() { |
||
55 | return self::checkExtension( 'pgsql' ); |
||
56 | } |
||
57 | |||
58 | function getConnectForm() { |
||
59 | return $this->getTextBox( |
||
60 | 'wgDBserver', |
||
61 | 'config-db-host', |
||
62 | [], |
||
63 | $this->parent->getHelpBox( 'config-db-host-help' ) |
||
64 | ) . |
||
65 | $this->getTextBox( 'wgDBport', 'config-db-port' ) . |
||
66 | Html::openElement( 'fieldset' ) . |
||
67 | Html::element( 'legend', [], wfMessage( 'config-db-wiki-settings' )->text() ) . |
||
68 | $this->getTextBox( |
||
69 | 'wgDBname', |
||
70 | 'config-db-name', |
||
71 | [], |
||
72 | $this->parent->getHelpBox( 'config-db-name-help' ) |
||
73 | ) . |
||
74 | $this->getTextBox( |
||
75 | 'wgDBmwschema', |
||
76 | 'config-db-schema', |
||
77 | [], |
||
78 | $this->parent->getHelpBox( 'config-db-schema-help' ) |
||
79 | ) . |
||
80 | Html::closeElement( 'fieldset' ) . |
||
81 | $this->getInstallUserBox(); |
||
82 | } |
||
83 | |||
84 | function submitConnectForm() { |
||
85 | // Get variables from the request |
||
86 | $newValues = $this->setVarsFromRequest( [ |
||
87 | 'wgDBserver', |
||
88 | 'wgDBport', |
||
89 | 'wgDBname', |
||
90 | 'wgDBmwschema' |
||
91 | ] ); |
||
92 | |||
93 | // Validate them |
||
94 | $status = Status::newGood(); |
||
95 | View Code Duplication | if ( !strlen( $newValues['wgDBname'] ) ) { |
|
96 | $status->fatal( 'config-missing-db-name' ); |
||
97 | } elseif ( !preg_match( '/^[a-zA-Z0-9_]+$/', $newValues['wgDBname'] ) ) { |
||
98 | $status->fatal( 'config-invalid-db-name', $newValues['wgDBname'] ); |
||
99 | } |
||
100 | if ( !preg_match( '/^[a-zA-Z0-9_]*$/', $newValues['wgDBmwschema'] ) ) { |
||
101 | $status->fatal( 'config-invalid-schema', $newValues['wgDBmwschema'] ); |
||
102 | } |
||
103 | |||
104 | // Submit user box |
||
105 | if ( $status->isOK() ) { |
||
106 | $status->merge( $this->submitInstallUserBox() ); |
||
107 | } |
||
108 | if ( !$status->isOK() ) { |
||
109 | return $status; |
||
110 | } |
||
111 | |||
112 | $status = $this->getPgConnection( 'create-db' ); |
||
113 | if ( !$status->isOK() ) { |
||
114 | return $status; |
||
115 | } |
||
116 | /** |
||
117 | * @var $conn Database |
||
118 | */ |
||
119 | $conn = $status->value; |
||
120 | |||
121 | // Check version |
||
122 | $version = $conn->getServerVersion(); |
||
123 | if ( version_compare( $version, $this->minimumVersion ) < 0 ) { |
||
124 | return Status::newFatal( 'config-postgres-old', $this->minimumVersion, $version ); |
||
125 | } |
||
126 | |||
127 | $this->setVar( 'wgDBuser', $this->getVar( '_InstallUser' ) ); |
||
128 | $this->setVar( 'wgDBpassword', $this->getVar( '_InstallPassword' ) ); |
||
129 | |||
130 | return Status::newGood(); |
||
131 | } |
||
132 | |||
133 | public function getConnection() { |
||
134 | $status = $this->getPgConnection( 'create-tables' ); |
||
135 | if ( $status->isOK() ) { |
||
136 | $this->db = $status->value; |
||
137 | } |
||
138 | |||
139 | return $status; |
||
140 | } |
||
141 | |||
142 | public function openConnection() { |
||
143 | return $this->openPgConnection( 'create-tables' ); |
||
144 | } |
||
145 | |||
146 | /** |
||
147 | * Open a PG connection with given parameters |
||
148 | * @param string $user User name |
||
149 | * @param string $password Password |
||
150 | * @param string $dbName Database name |
||
151 | * @param string $schema Database schema |
||
152 | * @return Status |
||
153 | */ |
||
154 | protected function openConnectionWithParams( $user, $password, $dbName, $schema ) { |
||
155 | $status = Status::newGood(); |
||
156 | try { |
||
157 | $db = Database::factory( 'postgres', [ |
||
158 | 'host' => $this->getVar( 'wgDBserver' ), |
||
159 | 'user' => $user, |
||
160 | 'password' => $password, |
||
161 | 'dbname' => $dbName, |
||
162 | 'schema' => $schema ] ); |
||
163 | $status->value = $db; |
||
164 | } catch ( DBConnectionError $e ) { |
||
165 | $status->fatal( 'config-connection-error', $e->getMessage() ); |
||
166 | } |
||
167 | |||
168 | return $status; |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * Get a special type of connection |
||
173 | * @param string $type See openPgConnection() for details. |
||
174 | * @return Status |
||
175 | */ |
||
176 | protected function getPgConnection( $type ) { |
||
177 | if ( isset( $this->pgConns[$type] ) ) { |
||
178 | return Status::newGood( $this->pgConns[$type] ); |
||
179 | } |
||
180 | $status = $this->openPgConnection( $type ); |
||
181 | |||
182 | if ( $status->isOK() ) { |
||
183 | /** |
||
184 | * @var $conn Database |
||
185 | */ |
||
186 | $conn = $status->value; |
||
187 | $conn->clearFlag( DBO_TRX ); |
||
188 | $conn->commit( __METHOD__ ); |
||
189 | $this->pgConns[$type] = $conn; |
||
190 | } |
||
191 | |||
192 | return $status; |
||
193 | } |
||
194 | |||
195 | /** |
||
196 | * Get a connection of a specific PostgreSQL-specific type. Connections |
||
197 | * of a given type are cached. |
||
198 | * |
||
199 | * PostgreSQL lacks cross-database operations, so after the new database is |
||
200 | * created, you need to make a separate connection to connect to that |
||
201 | * database and add tables to it. |
||
202 | * |
||
203 | * New tables are owned by the user that creates them, and MediaWiki's |
||
204 | * PostgreSQL support has always assumed that the table owner will be |
||
205 | * $wgDBuser. So before we create new tables, we either need to either |
||
206 | * connect as the other user or to execute a SET ROLE command. Using a |
||
207 | * separate connection for this allows us to avoid accidental cross-module |
||
208 | * dependencies. |
||
209 | * |
||
210 | * @param string $type The type of connection to get: |
||
211 | * - create-db: A connection for creating DBs, suitable for pre- |
||
212 | * installation. |
||
213 | * - create-schema: A connection to the new DB, for creating schemas and |
||
214 | * other similar objects in the new DB. |
||
215 | * - create-tables: A connection with a role suitable for creating tables. |
||
216 | * |
||
217 | * @throws MWException |
||
218 | * @return Status On success, a connection object will be in the value member. |
||
219 | */ |
||
220 | protected function openPgConnection( $type ) { |
||
221 | switch ( $type ) { |
||
222 | case 'create-db': |
||
223 | return $this->openConnectionToAnyDB( |
||
224 | $this->getVar( '_InstallUser' ), |
||
225 | $this->getVar( '_InstallPassword' ) ); |
||
226 | case 'create-schema': |
||
227 | return $this->openConnectionWithParams( |
||
228 | $this->getVar( '_InstallUser' ), |
||
229 | $this->getVar( '_InstallPassword' ), |
||
230 | $this->getVar( 'wgDBname' ), |
||
231 | $this->getVar( 'wgDBmwschema' ) ); |
||
232 | case 'create-tables': |
||
233 | $status = $this->openPgConnection( 'create-schema' ); |
||
234 | if ( $status->isOK() ) { |
||
235 | /** |
||
236 | * @var $conn Database |
||
237 | */ |
||
238 | $conn = $status->value; |
||
239 | $safeRole = $conn->addIdentifierQuotes( $this->getVar( 'wgDBuser' ) ); |
||
240 | $conn->query( "SET ROLE $safeRole" ); |
||
241 | } |
||
242 | |||
243 | return $status; |
||
244 | default: |
||
245 | throw new MWException( "Invalid special connection type: \"$type\"" ); |
||
246 | } |
||
247 | } |
||
248 | |||
249 | public function openConnectionToAnyDB( $user, $password ) { |
||
250 | $dbs = [ |
||
251 | 'template1', |
||
252 | 'postgres', |
||
253 | ]; |
||
254 | if ( !in_array( $this->getVar( 'wgDBname' ), $dbs ) ) { |
||
255 | array_unshift( $dbs, $this->getVar( 'wgDBname' ) ); |
||
256 | } |
||
257 | $conn = false; |
||
258 | $status = Status::newGood(); |
||
259 | foreach ( $dbs as $db ) { |
||
260 | try { |
||
261 | $p = [ |
||
262 | 'host' => $this->getVar( 'wgDBserver' ), |
||
263 | 'user' => $user, |
||
264 | 'password' => $password, |
||
265 | 'dbname' => $db |
||
266 | ]; |
||
267 | $conn = Database::factory( 'postgres', $p ); |
||
268 | } catch ( DBConnectionError $error ) { |
||
269 | $conn = false; |
||
270 | $status->fatal( 'config-pg-test-error', $db, |
||
271 | $error->getMessage() ); |
||
272 | } |
||
273 | if ( $conn !== false ) { |
||
274 | break; |
||
275 | } |
||
276 | } |
||
277 | if ( $conn !== false ) { |
||
278 | return Status::newGood( $conn ); |
||
279 | } else { |
||
280 | return $status; |
||
281 | } |
||
282 | } |
||
283 | |||
284 | protected function getInstallUserPermissions() { |
||
285 | $status = $this->getPgConnection( 'create-db' ); |
||
286 | if ( !$status->isOK() ) { |
||
287 | return false; |
||
288 | } |
||
289 | /** |
||
290 | * @var $conn Database |
||
291 | */ |
||
292 | $conn = $status->value; |
||
293 | $superuser = $this->getVar( '_InstallUser' ); |
||
294 | |||
295 | $row = $conn->selectRow( '"pg_catalog"."pg_roles"', '*', |
||
296 | [ 'rolname' => $superuser ], __METHOD__ ); |
||
297 | |||
298 | return $row; |
||
299 | } |
||
300 | |||
301 | protected function canCreateAccounts() { |
||
302 | $perms = $this->getInstallUserPermissions(); |
||
303 | if ( !$perms ) { |
||
304 | return false; |
||
305 | } |
||
306 | |||
307 | return $perms->rolsuper === 't' || $perms->rolcreaterole === 't'; |
||
308 | } |
||
309 | |||
310 | protected function isSuperUser() { |
||
311 | $perms = $this->getInstallUserPermissions(); |
||
312 | if ( !$perms ) { |
||
313 | return false; |
||
314 | } |
||
315 | |||
316 | return $perms->rolsuper === 't'; |
||
317 | } |
||
318 | |||
319 | public function getSettingsForm() { |
||
320 | if ( $this->canCreateAccounts() ) { |
||
321 | $noCreateMsg = false; |
||
322 | } else { |
||
323 | $noCreateMsg = 'config-db-web-no-create-privs'; |
||
324 | } |
||
325 | $s = $this->getWebUserBox( $noCreateMsg ); |
||
326 | |||
327 | return $s; |
||
328 | } |
||
329 | |||
330 | public function submitSettingsForm() { |
||
331 | $status = $this->submitWebUserBox(); |
||
332 | if ( !$status->isOK() ) { |
||
333 | return $status; |
||
334 | } |
||
335 | |||
336 | $same = $this->getVar( 'wgDBuser' ) === $this->getVar( '_InstallUser' ); |
||
337 | |||
338 | if ( $same ) { |
||
339 | $exists = true; |
||
340 | } else { |
||
341 | // Check if the web user exists |
||
342 | // Connect to the database with the install user |
||
343 | $status = $this->getPgConnection( 'create-db' ); |
||
344 | if ( !$status->isOK() ) { |
||
345 | return $status; |
||
346 | } |
||
347 | $exists = $status->value->roleExists( $this->getVar( 'wgDBuser' ) ); |
||
348 | } |
||
349 | |||
350 | // Validate the create checkbox |
||
351 | if ( $this->canCreateAccounts() && !$same && !$exists ) { |
||
352 | $create = $this->getVar( '_CreateDBAccount' ); |
||
353 | } else { |
||
354 | $this->setVar( '_CreateDBAccount', false ); |
||
355 | $create = false; |
||
356 | } |
||
357 | |||
358 | if ( !$create && !$exists ) { |
||
359 | if ( $this->canCreateAccounts() ) { |
||
360 | $msg = 'config-install-user-missing-create'; |
||
361 | } else { |
||
362 | $msg = 'config-install-user-missing'; |
||
363 | } |
||
364 | |||
365 | return Status::newFatal( $msg, $this->getVar( 'wgDBuser' ) ); |
||
366 | } |
||
367 | |||
368 | if ( !$exists ) { |
||
369 | // No more checks to do |
||
370 | return Status::newGood(); |
||
371 | } |
||
372 | |||
373 | // Existing web account. Test the connection. |
||
374 | $status = $this->openConnectionToAnyDB( |
||
375 | $this->getVar( 'wgDBuser' ), |
||
376 | $this->getVar( 'wgDBpassword' ) ); |
||
377 | if ( !$status->isOK() ) { |
||
378 | return $status; |
||
379 | } |
||
380 | |||
381 | // The web user is conventionally the table owner in PostgreSQL |
||
382 | // installations. Make sure the install user is able to create |
||
383 | // objects on behalf of the web user. |
||
384 | if ( $same || $this->canCreateObjectsForWebUser() ) { |
||
385 | return Status::newGood(); |
||
386 | } else { |
||
387 | return Status::newFatal( 'config-pg-not-in-role' ); |
||
388 | } |
||
389 | } |
||
390 | |||
391 | /** |
||
392 | * Returns true if the install user is able to create objects owned |
||
393 | * by the web user, false otherwise. |
||
394 | * @return bool |
||
395 | */ |
||
396 | protected function canCreateObjectsForWebUser() { |
||
397 | if ( $this->isSuperUser() ) { |
||
398 | return true; |
||
399 | } |
||
400 | |||
401 | $status = $this->getPgConnection( 'create-db' ); |
||
402 | if ( !$status->isOK() ) { |
||
403 | return false; |
||
404 | } |
||
405 | $conn = $status->value; |
||
406 | $installerId = $conn->selectField( '"pg_catalog"."pg_roles"', 'oid', |
||
407 | [ 'rolname' => $this->getVar( '_InstallUser' ) ], __METHOD__ ); |
||
408 | $webId = $conn->selectField( '"pg_catalog"."pg_roles"', 'oid', |
||
409 | [ 'rolname' => $this->getVar( 'wgDBuser' ) ], __METHOD__ ); |
||
410 | |||
411 | return $this->isRoleMember( $conn, $installerId, $webId, $this->maxRoleSearchDepth ); |
||
412 | } |
||
413 | |||
414 | /** |
||
415 | * Recursive helper for canCreateObjectsForWebUser(). |
||
416 | * @param Database $conn |
||
417 | * @param int $targetMember Role ID of the member to look for |
||
418 | * @param int $group Role ID of the group to look for |
||
419 | * @param int $maxDepth Maximum recursive search depth |
||
420 | * @return bool |
||
421 | */ |
||
422 | protected function isRoleMember( $conn, $targetMember, $group, $maxDepth ) { |
||
423 | if ( $targetMember === $group ) { |
||
424 | // A role is always a member of itself |
||
425 | return true; |
||
426 | } |
||
427 | // Get all members of the given group |
||
428 | $res = $conn->select( '"pg_catalog"."pg_auth_members"', [ 'member' ], |
||
429 | [ 'roleid' => $group ], __METHOD__ ); |
||
430 | foreach ( $res as $row ) { |
||
0 ignored issues
–
show
|
|||
431 | if ( $row->member == $targetMember ) { |
||
432 | // Found target member |
||
433 | return true; |
||
434 | } |
||
435 | // Recursively search each member of the group to see if the target |
||
436 | // is a member of it, up to the given maximum depth. |
||
437 | if ( $maxDepth > 0 ) { |
||
438 | if ( $this->isRoleMember( $conn, $targetMember, $row->member, $maxDepth - 1 ) ) { |
||
439 | // Found member of member |
||
440 | return true; |
||
441 | } |
||
442 | } |
||
443 | } |
||
444 | |||
445 | return false; |
||
446 | } |
||
447 | |||
448 | public function preInstall() { |
||
449 | $createDbAccount = [ |
||
450 | 'name' => 'user', |
||
451 | 'callback' => [ $this, 'setupUser' ], |
||
452 | ]; |
||
453 | $commitCB = [ |
||
454 | 'name' => 'pg-commit', |
||
455 | 'callback' => [ $this, 'commitChanges' ], |
||
456 | ]; |
||
457 | $plpgCB = [ |
||
458 | 'name' => 'pg-plpgsql', |
||
459 | 'callback' => [ $this, 'setupPLpgSQL' ], |
||
460 | ]; |
||
461 | $schemaCB = [ |
||
462 | 'name' => 'schema', |
||
463 | 'callback' => [ $this, 'setupSchema' ] |
||
464 | ]; |
||
465 | |||
466 | if ( $this->getVar( '_CreateDBAccount' ) ) { |
||
467 | $this->parent->addInstallStep( $createDbAccount, 'database' ); |
||
468 | } |
||
469 | $this->parent->addInstallStep( $commitCB, 'interwiki' ); |
||
470 | $this->parent->addInstallStep( $plpgCB, 'database' ); |
||
471 | $this->parent->addInstallStep( $schemaCB, 'database' ); |
||
472 | } |
||
473 | |||
474 | function setupDatabase() { |
||
475 | $status = $this->getPgConnection( 'create-db' ); |
||
476 | if ( !$status->isOK() ) { |
||
477 | return $status; |
||
478 | } |
||
479 | $conn = $status->value; |
||
480 | |||
481 | $dbName = $this->getVar( 'wgDBname' ); |
||
482 | |||
483 | $exists = $conn->selectField( '"pg_catalog"."pg_database"', '1', |
||
484 | [ 'datname' => $dbName ], __METHOD__ ); |
||
485 | if ( !$exists ) { |
||
486 | $safedb = $conn->addIdentifierQuotes( $dbName ); |
||
487 | $conn->query( "CREATE DATABASE $safedb", __METHOD__ ); |
||
488 | } |
||
489 | |||
490 | return Status::newGood(); |
||
491 | } |
||
492 | |||
493 | function setupSchema() { |
||
494 | // Get a connection to the target database |
||
495 | $status = $this->getPgConnection( 'create-schema' ); |
||
496 | if ( !$status->isOK() ) { |
||
497 | return $status; |
||
498 | } |
||
499 | $conn = $status->value; |
||
500 | |||
501 | // Create the schema if necessary |
||
502 | $schema = $this->getVar( 'wgDBmwschema' ); |
||
503 | $safeschema = $conn->addIdentifierQuotes( $schema ); |
||
504 | $safeuser = $conn->addIdentifierQuotes( $this->getVar( 'wgDBuser' ) ); |
||
505 | if ( !$conn->schemaExists( $schema ) ) { |
||
506 | try { |
||
507 | $conn->query( "CREATE SCHEMA $safeschema AUTHORIZATION $safeuser" ); |
||
508 | } catch ( DBQueryError $e ) { |
||
509 | return Status::newFatal( 'config-install-pg-schema-failed', |
||
510 | $this->getVar( '_InstallUser' ), $schema ); |
||
511 | } |
||
512 | } |
||
513 | |||
514 | // Select the new schema in the current connection |
||
515 | $conn->determineCoreSchema( $schema ); |
||
516 | |||
517 | return Status::newGood(); |
||
518 | } |
||
519 | |||
520 | function commitChanges() { |
||
521 | $this->db->commit( __METHOD__ ); |
||
522 | |||
523 | return Status::newGood(); |
||
524 | } |
||
525 | |||
526 | function setupUser() { |
||
527 | if ( !$this->getVar( '_CreateDBAccount' ) ) { |
||
528 | return Status::newGood(); |
||
529 | } |
||
530 | |||
531 | $status = $this->getPgConnection( 'create-db' ); |
||
532 | if ( !$status->isOK() ) { |
||
533 | return $status; |
||
534 | } |
||
535 | $conn = $status->value; |
||
536 | |||
537 | $safeuser = $conn->addIdentifierQuotes( $this->getVar( 'wgDBuser' ) ); |
||
538 | $safepass = $conn->addQuotes( $this->getVar( 'wgDBpassword' ) ); |
||
539 | |||
540 | // Check if the user already exists |
||
541 | $userExists = $conn->roleExists( $this->getVar( 'wgDBuser' ) ); |
||
542 | if ( !$userExists ) { |
||
543 | // Create the user |
||
544 | try { |
||
545 | $sql = "CREATE ROLE $safeuser NOCREATEDB LOGIN PASSWORD $safepass"; |
||
546 | |||
547 | // If the install user is not a superuser, we need to make the install |
||
548 | // user a member of the new user's group, so that the install user will |
||
549 | // be able to create a schema and other objects on behalf of the new user. |
||
550 | if ( !$this->isSuperUser() ) { |
||
551 | $sql .= ' ROLE' . $conn->addIdentifierQuotes( $this->getVar( '_InstallUser' ) ); |
||
552 | } |
||
553 | |||
554 | $conn->query( $sql, __METHOD__ ); |
||
555 | } catch ( DBQueryError $e ) { |
||
556 | return Status::newFatal( 'config-install-user-create-failed', |
||
557 | $this->getVar( 'wgDBuser' ), $e->getMessage() ); |
||
558 | } |
||
559 | } |
||
560 | |||
561 | return Status::newGood(); |
||
562 | } |
||
563 | |||
564 | function getLocalSettings() { |
||
565 | $port = $this->getVar( 'wgDBport' ); |
||
566 | $schema = $this->getVar( 'wgDBmwschema' ); |
||
567 | |||
568 | return "# Postgres specific settings |
||
569 | \$wgDBport = \"{$port}\"; |
||
570 | \$wgDBmwschema = \"{$schema}\";"; |
||
571 | } |
||
572 | |||
573 | public function preUpgrade() { |
||
574 | global $wgDBuser, $wgDBpassword; |
||
575 | |||
576 | # Normal user and password are selected after this step, so for now |
||
577 | # just copy these two |
||
578 | $wgDBuser = $this->getVar( '_InstallUser' ); |
||
579 | $wgDBpassword = $this->getVar( '_InstallPassword' ); |
||
580 | } |
||
581 | |||
582 | public function createTables() { |
||
583 | $schema = $this->getVar( 'wgDBmwschema' ); |
||
584 | |||
585 | $status = $this->getConnection(); |
||
586 | if ( !$status->isOK() ) { |
||
587 | return $status; |
||
588 | } |
||
589 | |||
590 | /** @var $conn DatabasePostgres */ |
||
591 | $conn = $status->value; |
||
592 | |||
593 | if ( $conn->tableExists( 'archive' ) ) { |
||
594 | $status->warning( 'config-install-tables-exist' ); |
||
595 | $this->enableLB(); |
||
596 | |||
597 | return $status; |
||
598 | } |
||
599 | |||
600 | $conn->begin( __METHOD__ ); |
||
601 | |||
602 | if ( !$conn->schemaExists( $schema ) ) { |
||
603 | $status->fatal( 'config-install-pg-schema-not-exist' ); |
||
604 | |||
605 | return $status; |
||
606 | } |
||
607 | $error = $conn->sourceFile( $this->getSchemaPath( $conn ) ); |
||
608 | if ( $error !== true ) { |
||
609 | $conn->reportQueryError( $error, 0, '', __METHOD__ ); |
||
0 ignored issues
–
show
It seems like
$error defined by $conn->sourceFile($this->getSchemaPath($conn)) on line 607 can also be of type boolean ; however, DatabasePostgres::reportQueryError() does only seem to accept string , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
610 | $conn->rollback( __METHOD__ ); |
||
611 | $status->fatal( 'config-install-tables-failed', $error ); |
||
612 | } else { |
||
613 | $conn->commit( __METHOD__ ); |
||
614 | } |
||
615 | // Resume normal operations |
||
616 | if ( $status->isOK() ) { |
||
617 | $this->enableLB(); |
||
618 | } |
||
619 | |||
620 | return $status; |
||
621 | } |
||
622 | |||
623 | public function getGlobalDefaults() { |
||
624 | // The default $wgDBmwschema is null, which breaks Postgres and other DBMSes that require |
||
625 | // the use of a schema, so we need to set it here |
||
626 | return array_merge( parent::getGlobalDefaults(), [ |
||
627 | 'wgDBmwschema' => 'mediawiki', |
||
628 | ] ); |
||
629 | } |
||
630 | |||
631 | public function setupPLpgSQL() { |
||
632 | // Connect as the install user, since it owns the database and so is |
||
633 | // the user that needs to run "CREATE LANGAUGE" |
||
634 | $status = $this->getPgConnection( 'create-schema' ); |
||
635 | if ( !$status->isOK() ) { |
||
636 | return $status; |
||
637 | } |
||
638 | /** |
||
639 | * @var $conn Database |
||
640 | */ |
||
641 | $conn = $status->value; |
||
642 | |||
643 | $exists = $conn->selectField( '"pg_catalog"."pg_language"', 1, |
||
644 | [ 'lanname' => 'plpgsql' ], __METHOD__ ); |
||
645 | if ( $exists ) { |
||
646 | // Already exists, nothing to do |
||
647 | return Status::newGood(); |
||
648 | } |
||
649 | |||
650 | // plpgsql is not installed, but if we have a pg_pltemplate table, we |
||
651 | // should be able to create it |
||
652 | $exists = $conn->selectField( |
||
653 | [ '"pg_catalog"."pg_class"', '"pg_catalog"."pg_namespace"' ], |
||
654 | 1, |
||
655 | [ |
||
656 | 'pg_namespace.oid=relnamespace', |
||
657 | 'nspname' => 'pg_catalog', |
||
658 | 'relname' => 'pg_pltemplate', |
||
659 | ], |
||
660 | __METHOD__ ); |
||
661 | if ( $exists ) { |
||
662 | try { |
||
663 | $conn->query( 'CREATE LANGUAGE plpgsql' ); |
||
664 | } catch ( DBQueryError $e ) { |
||
665 | return Status::newFatal( 'config-pg-no-plpgsql', $this->getVar( 'wgDBname' ) ); |
||
666 | } |
||
667 | } else { |
||
668 | return Status::newFatal( 'config-pg-no-plpgsql', $this->getVar( 'wgDBname' ) ); |
||
669 | } |
||
670 | |||
671 | return Status::newGood(); |
||
672 | } |
||
673 | } |
||
674 |
There are different options of fixing this problem.
If you want to be on the safe side, you can add an additional type-check:
If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:
Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.