MediaWiki 1.43.1
DatabaseInstaller.php
Go to the documentation of this file.
1<?php
2
25namespace MediaWiki\Installer;
26
28use RuntimeException;
29use Wikimedia\AtEase\AtEase;
34
41abstract class DatabaseInstaller {
45 public const CONN_CREATE_DATABASE = 'create-database';
46
51 public const CONN_CREATE_SCHEMA = 'create-schema';
52
56 public const CONN_CREATE_TABLES = 'create-tables';
57
66 public const CONN_DONT_KNOW = 'dont-know';
67
73 public $parent;
74
78 public static $minimumVersion;
79
83 protected static $notMinimumVersionMessage;
84
89 public $db = null;
90
92 private $cachedConn;
94 private $cachedConnType;
95
101 protected $internalDefaults = [];
102
108 protected $globalNames = [];
109
117 public static function meetsMinimumRequirement( IDatabase $conn ) {
118 $serverVersion = $conn->getServerVersion();
119 if ( version_compare( $serverVersion, static::$minimumVersion ) < 0 ) {
120 return Status::newFatal(
121 static::$notMinimumVersionMessage, static::$minimumVersion, $serverVersion
122 );
123 }
124
125 return Status::newGood();
126 }
127
131 abstract public function getName();
132
136 abstract public function isCompiled();
137
143 public function checkPrerequisites() {
144 return Status::newGood();
145 }
146
158 abstract protected function openConnection( string $type );
159
166 abstract public function setupDatabase();
167
179 public function getConnection( $type = self::CONN_DONT_KNOW ) {
180 if ( $type === self::CONN_DONT_KNOW ) {
181 if ( $this->cachedConnType ) {
182 $type = $this->cachedConnType;
183 } else {
185 }
186 }
187 if ( $this->cachedConn ) {
188 if ( $this->cachedConnType === $type ) {
189 return new ConnectionStatus( $this->cachedConn );
190 } else {
191 return $this->changeConnType( $this->cachedConn, $this->cachedConnType, $type );
192 }
193 }
194 $status = $this->openConnection( $type );
195 if ( $status->isOK() ) {
196 $this->cachedConn = $status->getDB();
197 $this->cachedConnType = $type;
198 // Assign to $this->db for b/c
199 $this->db = $this->cachedConn;
200
201 if ( $type === self::CONN_CREATE_SCHEMA || $type === self::CONN_CREATE_TABLES ) {
202 $this->cachedConn->setSchemaVars( $this->getSchemaVars() );
203 }
204 }
205
206 return $status;
207 }
208
216 public function definitelyGetConnection( string $type ): Database {
217 $status = $this->getConnection( $type );
218 if ( !$status->isOK() ) {
219 throw new RuntimeException( __METHOD__ . ': unexpected DB connection error' );
220 }
221 return $status->getDB();
222 }
223
247 protected function changeConnType( Database $conn, &$storedType, $newType ) {
248 // Change type from database to schema, if requested
249 if ( $storedType === self::CONN_CREATE_DATABASE ) {
250 if ( $newType === self::CONN_CREATE_SCHEMA || $newType === self::CONN_CREATE_TABLES ) {
251 // TODO: catch exceptions from selectDomain and report as a Status
252 $conn->selectDomain( new DatabaseDomain(
253 $this->getVar( 'wgDBname' ),
254 $this->getVar( 'wgDBmwschema' ),
255 $this->getVar( 'wgDBprefix' ) ?? ''
256 ) );
257 $conn->setSchemaVars( $this->getSchemaVars() );
258 $storedType = self::CONN_CREATE_SCHEMA;
259 }
260 }
261 // Change type from schema to tables, if requested
262 if ( $newType === self::CONN_CREATE_TABLES && $storedType === self::CONN_CREATE_SCHEMA ) {
263 $status = $this->changeConnTypeFromSchemaToTables( $conn );
264 if ( $status->isOK() ) {
265 $storedType = self::CONN_CREATE_TABLES;
266 }
267 return $status;
268 }
269 return new ConnectionStatus( $conn );
270 }
271
279 protected function changeConnTypeFromSchemaToTables( Database $conn ) {
280 return new ConnectionStatus( $conn );
281 }
282
290 private function applySourceFile( $conn, $sqlFile ) {
291 $status = Status::newGood();
292 try {
293 $conn->doAtomicSection( __METHOD__,
294 static function ( $conn ) use ( $sqlFile ) {
295 $conn->sourceFile( $sqlFile );
296 },
297 IDatabase::ATOMIC_CANCELABLE
298 );
299 } catch ( DBQueryError $e ) {
300 $status->fatal( "config-install-tables-failed", $e->getMessage() );
301 }
302 return $status;
303 }
304
310 public function createTables() {
311 $status = $this->getConnection( self::CONN_CREATE_TABLES );
312 if ( !$status->isOK() ) {
313 return $status;
314 }
315 $conn = $status->getDB();
316 if ( $conn->tableExists( 'archive', __METHOD__ ) ) {
317 $status->warning( "config-install-tables-exist" );
318 return $status;
319 }
320 $status = $this->applySourceFile( $conn,
321 $this->getSqlFilePath( 'tables-generated.sql' ) );
322 if ( !$status->isOK() ) {
323 return $status;
324 }
325 $status->merge( $this->applySourceFile( $conn,
326 $this->getSqlFilePath( 'tables.sql' ) ) );
327 return $status;
328 }
329
335 public function insertUpdateKeys() {
336 $updater = DatabaseUpdater::newForDB(
337 $this->definitelyGetConnection( self::CONN_CREATE_TABLES ) );
338 $updater->insertInitialUpdateKeys();
339 return Status::newGood();
340 }
341
349 private function getSqlFilePath( string $filename ) {
350 global $IP;
351
352 $dbmsSpecificFilePath = "$IP/maintenance/" . $this->getName() . "/$filename";
353 if ( file_exists( $dbmsSpecificFilePath ) ) {
354 return $dbmsSpecificFilePath;
355 } else {
356 return "$IP/maintenance/$filename";
357 }
358 }
359
364 public function createExtensionTables() {
365 $status = $this->getConnection( self::CONN_CREATE_TABLES );
366 if ( !$status->isOK() ) {
367 return $status;
368 }
369
370 // Now run updates to create tables for old extensions
371 $updater = DatabaseUpdater::newForDB( $status->getDB() );
372 $updater->setAutoExtensionHookContainer( $this->parent->getAutoExtensionHookContainer() );
373 $updater->doUpdates( [ 'extensions' ] );
374
375 return $status;
376 }
377
383 abstract public function getLocalSettings();
384
390 public function getSchemaVars() {
391 return [];
392 }
393
397 public function enableLB() {
398 }
399
405 public function preInstall() {
406 }
407
411 public function preUpgrade() {
412 }
413
418 public function getGlobalNames() {
419 return $this->globalNames;
420 }
421
427 public function __construct( $parent ) {
428 $this->parent = $parent;
429 }
430
438 protected static function checkExtension( $name ) {
439 return extension_loaded( $name );
440 }
441
446 public function getReadableName() {
447 // Messages: config-type-mysql, config-type-postgres, config-type-sqlite
448 return wfMessage( 'config-type-' . $this->getName() )->text();
449 }
450
456 public function getGlobalDefaults() {
457 $defaults = [];
458 foreach ( $this->getGlobalNames() as $var ) {
459 if ( isset( $GLOBALS[$var] ) ) {
460 $defaults[$var] = $GLOBALS[$var];
461 }
462 }
463 return $defaults;
464 }
465
470 public function getInternalDefaults() {
471 return $this->internalDefaults;
472 }
473
480 public function getVar( $var, $default = null ) {
481 $defaults = $this->getGlobalDefaults();
482 $internal = $this->getInternalDefaults();
483 if ( isset( $defaults[$var] ) ) {
484 $default = $defaults[$var];
485 } elseif ( isset( $internal[$var] ) ) {
486 $default = $internal[$var];
487 }
488
489 return $this->parent->getVar( $var, $default );
490 }
491
497 public function setVar( $name, $value ) {
498 $this->parent->setVar( $name, $value );
499 }
500
501 abstract public function getConnectForm( WebInstaller $webInstaller ): DatabaseConnectForm;
502
503 abstract public function getSettingsForm( WebInstaller $webInstaller ): DatabaseSettingsForm;
504
515 public function needsUpgrade() {
516 $status = $this->getConnection( self::CONN_CREATE_SCHEMA );
517 if ( !$status->isOK() ) {
518 return false;
519 }
520 $db = $status->getDB();
521 return $db->tableExists( 'cur', __METHOD__ ) ||
522 $db->tableExists( 'revision', __METHOD__ );
523 }
524
530 public function populateInterwikiTable() {
531 $status = $this->getConnection( self::CONN_CREATE_TABLES );
532 if ( !$status->isOK() ) {
533 return $status;
534 }
535 $conn = $status->getDB();
536
537 $row = $conn->newSelectQueryBuilder()
538 ->select( '1' )
539 ->from( 'interwiki' )
540 ->caller( __METHOD__ )->fetchRow();
541 if ( $row ) {
542 $status->warning( 'config-install-interwiki-exists' );
543
544 return $status;
545 }
546 global $IP;
547 AtEase::suppressWarnings();
548 $rows = file( "$IP/maintenance/interwiki.list",
549 FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
550 AtEase::restoreWarnings();
551 if ( !$rows ) {
552 return Status::newFatal( 'config-install-interwiki-list' );
553 }
554 $insert = $conn->newInsertQueryBuilder()
555 ->insertInto( 'interwiki' );
556 foreach ( $rows as $row ) {
557 $row = preg_replace( '/^\s*([^#]*?)\s*(#.*)?$/', '\\1', $row ); // strip comments - whee
558 if ( $row == "" ) {
559 continue;
560 }
561 $row .= "|";
562 $insert->row(
563 array_combine(
564 [ 'iw_prefix', 'iw_url', 'iw_local', 'iw_api', 'iw_wikiid' ],
565 explode( '|', $row )
566 )
567 );
568 }
569 $insert->caller( __METHOD__ )->execute();
570
571 return Status::newGood();
572 }
573
580 protected function selectDatabase( Database $conn, string $database ) {
581 $schema = $conn->dbSchema();
582 $prefix = $conn->tablePrefix();
583
584 $conn->selectDomain( new DatabaseDomain(
585 $database,
586 // DatabaseDomain uses null for unspecified schemas
587 ( $schema !== '' ) ? $schema : null,
588 $prefix
589 ) );
590
591 return true;
592 }
593}
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
if(!defined( 'MEDIAWIKI')) if(ini_get('mbstring.func_overload')) if(!defined( 'MW_ENTRY_POINT')) global $IP
Environment checks.
Definition Setup.php:103
Base class for DBMS-specific installation helper classes.
selectDatabase(Database $conn, string $database)
getConnectForm(WebInstaller $webInstaller)
getVar( $var, $default=null)
Get a variable, taking local defaults into account.
populateInterwikiTable()
Common function for databases that don't understand the MySQLish syntax of interwiki....
openConnection(string $type)
Open a connection to the database using the administrative user/password currently defined in the ses...
const CONN_CREATE_SCHEMA
A connection to the new DB, for creating schemas and other similar objects in the new DB.
preUpgrade()
Allow DB installers a chance to make checks before upgrade.
insertUpdateKeys()
Insert update keys into table to prevent running unneeded updates.
needsUpgrade()
Determine whether an existing installation of MediaWiki is present in the configured administrative c...
const CONN_DONT_KNOW
Legacy default connection type.
array $internalDefaults
Internal variables for installation.
getSettingsForm(WebInstaller $webInstaller)
static string $notMinimumVersionMessage
Set by subclasses.
getGlobalNames()
Get an array of MW configuration globals that will be configured by this class.
static checkExtension( $name)
Convenience function.
array $globalNames
Array of MW configuration globals this class uses.
changeConnType(Database $conn, &$storedType, $newType)
Change the type of a connection.
__construct( $parent)
Construct and initialise parent.
getSchemaVars()
Override this to provide DBMS-specific schema variables, to be substituted into tables....
getLocalSettings()
Get the DBMS-specific options for LocalSettings.php generation.
getGlobalDefaults()
Get a name=>value map of MW configuration globals for the default values.
static meetsMinimumRequirement(IDatabase $conn)
Whether the provided version meets the necessary requirements for this type.
getConnection( $type=self::CONN_DONT_KNOW)
Connect to the database using the administrative user/password currently defined in the session.
static string $minimumVersion
Set by subclasses.
definitelyGetConnection(string $type)
Get a connection and unwrap it from its Status object, throwing an exception on failure.
getReadableName()
Get the internationalised name for this DBMS.
const CONN_CREATE_TABLES
A connection with a role suitable for creating tables.
createExtensionTables()
Create the tables for each extension the user enabled.
preInstall()
Allow DB installers a chance to make last-minute changes before installation occurs.
checkPrerequisites()
Checks for installation prerequisites other than those checked by isCompiled()
setupDatabase()
Create the database and return a Status object indicating success or failure.
Installer $parent
The Installer object.
getName()
Return the internal name, e.g.
getInternalDefaults()
Get a name=>value map of internal variables used during installation.
createTables()
Create database tables from scratch from the automatically generated file.
const CONN_CREATE_DATABASE
A connection for creating DBs, suitable for pre-installation.
changeConnTypeFromSchemaToTables(Database $conn)
Change the type of a connection from CONN_CREATE_SCHEMA to CONN_CREATE_TABLES.
setVar( $name, $value)
Convenience alias for $this->parent->setVar()
static newForDB(IMaintainableDatabase $db, $shared=false, ?Maintenance $maintenance=null)
Base installer class.
Definition Installer.php:92
Class for the core installer web interface.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
Class to handle database/schema/prefix specifications for IDatabase.
A single concrete connection to a relational database.
Definition Database.php:48
tablePrefix( $prefix=null)
Get/set the table prefix.
Definition Database.php:308
dbSchema( $schema=null)
Get/set the db schema.
Definition Database.php:323
selectDomain( $domain)
Set the current domain (database, schema, and table prefix)
setSchemaVars( $vars)
Set schema variables to be used when streaming commands from SQL files or stdin.
tableExists( $table, $fname=__METHOD__)
Query whether a given table exists.
Interface to a relational database.
Definition IDatabase.php:48
getServerVersion()
A string describing the current software version.