49 '_MysqlEngine' =>
'InnoDB',
50 '_MysqlCharset' =>
'binary',
51 '_InstallUser' =>
'root',
56 private const MIN_VERSIONS = [
68 'CREATE TEMPORARY TABLES',
93 $this->parent->getHelpBox(
'config-db-host-help' )
96 Html::openElement(
'fieldset' ) .
97 Html::element(
'legend', [],
wfMessage(
'config-db-wiki-settings' )->text() ) .
98 $this->
getTextBox(
'wgDBname',
'config-db-name', [
'dir' =>
'ltr' ],
99 $this->parent->getHelpBox(
'config-db-name-help' ) ) .
100 $this->
getTextBox(
'wgDBprefix',
'config-db-prefix', [
'dir' =>
'ltr' ],
101 $this->parent->getHelpBox(
'config-db-prefix-help' ) ) .
102 Html::closeElement(
'fieldset' ) .
108 $newValues = $this->
setVarsFromRequest( [
'wgDBserver',
'wgDBname',
'wgDBprefix',
'wgDBssl' ] );
111 $status = Status::newGood();
112 if ( !strlen( $newValues[
'wgDBserver'] ) ) {
113 $status->fatal(
'config-missing-db-host' );
115 if ( !strlen( $newValues[
'wgDBname'] ) ) {
116 $status->fatal(
'config-missing-db-name' );
117 } elseif ( !preg_match(
'/^[a-z0-9+_-]+$/i', $newValues[
'wgDBname'] ) ) {
118 $status->fatal(
'config-invalid-db-name', $newValues[
'wgDBname'] );
120 if ( !preg_match(
'/^[a-z0-9_-]*$/i', $newValues[
'wgDBprefix'] ) ) {
121 $status->fatal(
'config-invalid-db-prefix', $newValues[
'wgDBprefix'] );
123 if ( !$status->isOK() ) {
129 if ( !$status->isOK() ) {
135 if ( !$status->isOK() ) {
141 $conn = $status->value;
142 '@phan-var Database $conn';
145 return static::meetsMinimumRequirement( $conn );
150 self::$minimumVersion = self::MIN_VERSIONS[
$type];
152 self::$notMinimumVersionMessage =
'config-' . strtolower(
$type ) .
'-old';
153 return parent::meetsMinimumRequirement( $conn );
160 $status = Status::newGood();
163 $db = Database::factory(
'mysql', [
164 'host' => $this->
getVar(
'wgDBserver' ),
165 'user' => $this->
getVar(
'_InstallUser' ),
166 'password' => $this->
getVar(
'_InstallPassword' ),
167 'ssl' => $this->
getVar(
'wgDBssl' ),
170 'tablePrefix' => $this->
getVar(
'wgDBprefix' ) ] );
171 $status->value =
$db;
173 $status->fatal(
'config-connection-error', $e->getMessage() );
183 if ( !$status->isOK() ) {
184 $this->parent->showStatusMessage( $status );
191 $conn = $status->value;
192 $conn->selectDB( $this->
getVar(
'wgDBname' ) );
194 # Determine existing default character set
195 if ( $conn->tableExists(
"revision", __METHOD__ ) ) {
197 $res = $conn->query(
"SHOW TABLE STATUS LIKE '$revision'", __METHOD__ );
198 $row =
$res->fetchObject();
200 $this->parent->showMessage(
'config-show-table-status' );
201 $existingSchema =
false;
202 $existingEngine =
false;
204 if ( preg_match(
'/^latin1/', $row->Collation ) ) {
205 $existingSchema =
'latin1';
206 } elseif ( preg_match(
'/^utf8/', $row->Collation ) ) {
207 $existingSchema =
'utf8';
208 } elseif ( preg_match(
'/^binary/', $row->Collation ) ) {
209 $existingSchema =
'binary';
211 $existingSchema =
false;
212 $this->parent->showMessage(
'config-unknown-collation' );
214 $existingEngine = $row->Engine ?? $row->Type;
217 $existingSchema =
false;
218 $existingEngine =
false;
221 if ( $existingSchema && $existingSchema != $this->
getVar(
'_MysqlCharset' ) ) {
222 $this->
setVar(
'_MysqlCharset', $existingSchema );
224 if ( $existingEngine && $existingEngine != $this->
getVar(
'_MysqlEngine' ) ) {
225 $this->
setVar(
'_MysqlEngine', $existingEngine );
228 # Normal user and password are selected after this step, so for now
229 # just copy these two
231 $wgDBpassword = $this->
getVar(
'_InstallPassword' );
240 return str_replace( [ $escapeChar,
'%',
'_' ],
241 [
"{$escapeChar}{$escapeChar}",
"{$escapeChar}%",
"{$escapeChar}_" ],
256 $conn = $status->value;
259 $res = $conn->query(
'SHOW ENGINES', __METHOD__ );
260 foreach (
$res as $row ) {
261 if ( $row->Support ==
'YES' || $row->Support ==
'DEFAULT' ) {
262 $engines[] = $row->Engine;
265 $engines = array_intersect( $this->supportedEngines, $engines );
276 return [
'binary',
'utf8' ];
286 if ( !$status->isOK() ) {
290 $conn = $status->value;
293 $currentName = $conn->selectField(
'',
'CURRENT_USER()',
'', __METHOD__ );
294 $parts = explode(
'@', $currentName );
295 if ( count( $parts ) != 2 ) {
298 $quotedUser = $conn->addQuotes( $parts[0] ) .
299 '@' . $conn->addQuotes( $parts[1] );
303 $res = $conn->select(
'INFORMATION_SCHEMA.USER_PRIVILEGES',
'*',
304 [
'GRANTEE' => $quotedUser ], __METHOD__ );
305 $insertMysql =
false;
306 $grantOptions = array_fill_keys( $this->webUserPrivs,
true );
307 foreach (
$res as $row ) {
308 if ( $row->PRIVILEGE_TYPE ==
'INSERT' ) {
311 if ( $row->IS_GRANTABLE ) {
312 unset( $grantOptions[$row->PRIVILEGE_TYPE] );
317 if ( !$insertMysql ) {
318 $row = $conn->selectRow(
'INFORMATION_SCHEMA.SCHEMA_PRIVILEGES',
'*',
320 'GRANTEE' => $quotedUser,
321 'TABLE_SCHEMA' =>
'mysql',
322 'PRIVILEGE_TYPE' =>
'INSERT',
329 if ( !$insertMysql ) {
334 $res = $conn->select(
'INFORMATION_SCHEMA.SCHEMA_PRIVILEGES',
'*',
336 'GRANTEE' => $quotedUser,
339 foreach (
$res as $row ) {
341 if ( preg_match( $regex, $this->
getVar(
'wgDBname' ) ) ) {
342 unset( $grantOptions[$row->PRIVILEGE_TYPE] );
345 if ( count( $grantOptions ) ) {
360 $r = preg_quote( $wildcard,
'/' );
373 $noCreateMsg =
false;
375 $noCreateMsg =
'config-db-web-no-create-privs';
382 if ( !in_array( $this->
getVar(
'_MysqlEngine' ), $engines ) ) {
383 $this->
setVar(
'_MysqlEngine', reset( $engines ) );
388 if ( !in_array( $this->
getVar(
'_MysqlCharset' ), $charsets ) ) {
389 $this->
setVar(
'_MysqlCharset', reset( $charsets ) );
401 if ( !$status->isOK() ) {
408 $this->
setVar(
'_CreateDBAccount',
false );
411 $create = $this->
getVar(
'_CreateDBAccount' );
417 MediaWikiServices::getInstance()->getDatabaseFactory()->create(
'mysql', [
418 'host' => $this->
getVar(
'wgDBserver' ),
419 'user' => $this->
getVar(
'wgDBuser' ),
420 'password' => $this->
getVar(
'wgDBpassword' ),
421 'ssl' => $this->
getVar(
'wgDBssl' ),
424 'tablePrefix' => $this->
getVar(
'wgDBprefix' )
427 return Status::newFatal(
'config-connection-error', $e->getMessage() );
434 if ( !in_array( $this->
getVar(
'_MysqlEngine' ), $engines ) ) {
435 $this->
setVar(
'_MysqlEngine', reset( $engines ) );
438 if ( !in_array( $this->
getVar(
'_MysqlCharset' ), $charsets ) ) {
439 $this->
setVar(
'_MysqlCharset', reset( $charsets ) );
442 return Status::newGood();
446 # Add our user callback to installSteps, right before the tables are created.
449 'callback' => [ $this,
'setupUser' ],
451 $this->parent->addInstallStep( $callback,
'tables' );
459 if ( !$status->isOK() ) {
463 $conn = $status->value;
464 $dbName = $this->
getVar(
'wgDBname' );
465 if ( !$this->databaseExists( $dbName ) ) {
467 "CREATE DATABASE " . $conn->addIdentifierQuotes( $dbName ) .
"CHARACTER SET utf8",
471 $conn->selectDB( $dbName );
482 private function databaseExists( $dbName ) {
483 $encDatabase = $this->db->addQuotes( $dbName );
485 return $this->db->query(
486 "SELECT 1 FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = $encDatabase",
495 $dbUser = $this->
getVar(
'wgDBuser' );
496 if ( $dbUser == $this->
getVar(
'_InstallUser' ) ) {
497 return Status::newGood();
500 if ( !$status->isOK() ) {
505 $dbName = $this->
getVar(
'wgDBname' );
506 $this->db->selectDB( $dbName );
507 $server = $this->
getVar(
'wgDBserver' );
508 $password = $this->
getVar(
'wgDBpassword' );
509 $grantableNames = [];
511 if ( $this->
getVar(
'_CreateDBAccount' ) ) {
514 Database::factory(
'mysql', [
517 'password' => $password,
518 'ssl' => $this->
getVar(
'wgDBssl' ),
521 'tablePrefix' => $this->
getVar(
'wgDBprefix' )
523 $grantableNames[] = $this->buildFullUserName( $dbUser, $server );
524 $tryToCreate =
false;
529 $grantableNames[] = $this->buildFullUserName( $dbUser, $server );
530 $tryToCreate =
false;
533 if ( $tryToCreate ) {
537 'localhost.localdomain',
541 $createHostList = array_unique( $createHostList );
542 $escPass = $this->db->addQuotes( $password );
544 foreach ( $createHostList as $host ) {
545 $fullName = $this->buildFullUserName( $dbUser, $host );
546 if ( !$this->userDefinitelyExists( $host, $dbUser ) ) {
548 $this->db->begin( __METHOD__ );
549 $this->db->query(
"CREATE USER $fullName IDENTIFIED BY $escPass", __METHOD__ );
550 $this->db->commit( __METHOD__ );
551 $grantableNames[] = $fullName;
553 if ( $this->db->lastErrno() == 1396 ) {
555 $this->db->rollback( __METHOD__ );
556 $status->warning(
'config-install-user-alreadyexists', $dbUser );
557 $grantableNames[] = $fullName;
562 $this->db->rollback( __METHOD__ );
563 $status->warning(
'config-install-user-create-failed', $dbUser, $dqe->getMessage() );
567 $status->warning(
'config-install-user-alreadyexists', $dbUser );
568 $grantableNames[] = $fullName;
575 $dbAllTables = $this->db->addIdentifierQuotes( $dbName ) .
'.*';
576 foreach ( $grantableNames as $name ) {
578 $this->db->begin( __METHOD__ );
579 $this->db->query(
"GRANT ALL PRIVILEGES ON $dbAllTables TO $name", __METHOD__ );
580 $this->db->commit( __METHOD__ );
582 $this->db->rollback( __METHOD__ );
583 $status->fatal(
'config-install-user-grant-failed', $dbUser, $dqe->getMessage() );
596 private function buildFullUserName( $name, $host ) {
597 return $this->db->addQuotes( $name ) .
'@' . $this->db->addQuotes( $host );
607 private function userDefinitelyExists( $host, $user ) {
609 $res = $this->db->selectRow(
'mysql.user', [
'Host',
'User' ],
610 [
'Host' => $host,
'User' => $user ], __METHOD__ );
626 if ( $this->
getVar(
'_MysqlEngine' ) !==
null ) {
627 $options[] =
"ENGINE=" . $this->
getVar(
'_MysqlEngine' );
629 if ( $this->
getVar(
'_MysqlCharset' ) !==
null ) {
630 $options[] =
'DEFAULT CHARSET=' . $this->
getVar(
'_MysqlCharset' );
633 return implode(
', ', $options );
644 'wgDBname' => $this->
getVar(
'wgDBname' ),
645 'wgDBuser' => $this->
getVar(
'wgDBuser' ),
646 'wgDBpassword' => $this->
getVar(
'wgDBpassword' ),
651 $prefix = LocalSettingsGenerator::escapePhpString( $this->
getVar(
'wgDBprefix' ) );
652 $useSsl = $this->
getVar(
'wgDBssl' ) ?
'true' :
'false';
653 $tblOpts = LocalSettingsGenerator::escapePhpString( $this->
getTableOptions() );
655 return "# MySQL specific settings
656\$wgDBprefix = \"{$prefix}\";
657\$wgDBssl = {$useSsl};
659# MySQL table options to use during installation or update
660\$wgDBTableOptions = \"{$tblOpts}\";";
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Base class for DBMS-specific installation helper classes.
getWebUserBox( $noCreateMsg=false)
Get a standard web-user fieldset.
submitWebUserBox()
Submit the form from getWebUserBox().
static checkExtension( $name)
Convenience function.
Database $db
The database connection.
setVarsFromRequest( $varNames)
Convenience function to set variables based on form data.
getCheckBox( $var, $label, $attribs=[], $helpData="")
Get a labelled checkbox to configure a local boolean variable.
getConnection()
Connect to the database using the administrative user/password currently defined in the session.
getVar( $var, $default=null)
Get a variable, taking local defaults into account.
getTextBox( $var, $label, $attribs=[], $helpData="")
Get a labelled text box to configure a local variable.
setVar( $name, $value)
Convenience alias for $this->parent->setVar()
submitInstallUserBox()
Submit a standard install user fieldset.
getInstallUserBox()
Get a standard install-user fieldset.
setupSchemaVars()
Set appropriate schema variables in the current database connection.
Class for setting up the MediaWiki database using MySQL.
likeToRegex( $wildcard)
Convert a wildcard (as used in LIKE) to a regex Slashes are escaped, slash terminators included.
escapeLikeInternal( $s, $escapeChar='`')
static meetsMinimumRequirement(IDatabase $conn)
Whether the provided version meets the necessary requirements for this type.
getSchemaVars()
Get variables to substitute into tables.sql and the SQL patch files.
preInstall()
Allow DB installers a chance to make last-minute changes before installation occurs.
getEngines()
Get a list of storage engines that are available and supported.
canCreateAccounts()
Return true if the install user can create accounts.
preUpgrade()
Allow DB installers a chance to make checks before upgrade.
getTableOptions()
Return any table options to be applied to all tables that don't override them.
getCharsets()
Get a list of character sets that are available and supported.
static $notMinimumVersionMessage
submitConnectForm()
Set variables based on the request array, assuming it was submitted via the form returned by getConne...
getLocalSettings()
Get the DBMS-specific options for LocalSettings.php generation.
$wgDBuser
Config variable stub for the DBuser setting, for use by phpdoc and IDEs.
$wgDBpassword
Config variable stub for the DBpassword setting, for use by phpdoc and IDEs.
foreach( $mmfl['setupFiles'] as $fileName) if($queue) if(empty( $mmfl['quiet'])) $s