wikimedia /
mediawiki
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. Loading history...
|
|||
| 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.