52 '_MysqlEngine' =>
'InnoDB',
53 '_MysqlCharset' =>
'binary',
54 '_InstallUser' =>
'root',
59 private const MIN_VERSIONS = [
71 'CREATE TEMPORARY TABLES',
96 $this->parent->getHelpBox(
'config-db-host-help' )
99 Html::openElement(
'fieldset' ) .
100 Html::element(
'legend', [],
wfMessage(
'config-db-wiki-settings' )->text() ) .
101 $this->
getTextBox(
'wgDBname',
'config-db-name', [
'dir' =>
'ltr' ],
102 $this->parent->getHelpBox(
'config-db-name-help' ) ) .
103 $this->
getTextBox(
'wgDBprefix',
'config-db-prefix', [
'dir' =>
'ltr' ],
104 $this->parent->getHelpBox(
'config-db-prefix-help' ) ) .
105 Html::closeElement(
'fieldset' ) .
111 $newValues = $this->
setVarsFromRequest( [
'wgDBserver',
'wgDBname',
'wgDBprefix',
'wgDBssl' ] );
114 $status = Status::newGood();
115 if ( !strlen( $newValues[
'wgDBserver'] ) ) {
116 $status->fatal(
'config-missing-db-host' );
118 if ( !strlen( $newValues[
'wgDBname'] ) ) {
119 $status->fatal(
'config-missing-db-name' );
120 } elseif ( !preg_match(
'/^[a-z0-9+_-]+$/i', $newValues[
'wgDBname'] ) ) {
121 $status->fatal(
'config-invalid-db-name', $newValues[
'wgDBname'] );
123 if ( !preg_match(
'/^[a-z0-9_-]*$/i', $newValues[
'wgDBprefix'] ) ) {
124 $status->fatal(
'config-invalid-db-prefix', $newValues[
'wgDBprefix'] );
126 if ( !$status->isOK() ) {
132 if ( !$status->isOK() ) {
138 if ( !$status->isOK() ) {
144 $conn = $status->value;
145 '@phan-var Database $conn';
148 return static::meetsMinimumRequirement( $conn );
152 $type = str_contains( $conn->
getSoftwareLink(),
'MariaDB' ) ?
'MariaDB' :
'MySQL';
153 self::$minimumVersion = self::MIN_VERSIONS[$type];
155 self::$notMinimumVersionMessage =
'config-' . strtolower( $type ) .
'-old';
156 return parent::meetsMinimumRequirement( $conn );
163 $status = Status::newGood();
167 'host' => $this->
getVar(
'wgDBserver' ),
168 'user' => $this->
getVar(
'_InstallUser' ),
169 'password' => $this->
getVar(
'_InstallPassword' ),
170 'ssl' => $this->
getVar(
'wgDBssl' ),
173 'tablePrefix' => $this->
getVar(
'wgDBprefix' ) ] );
174 $status->value =
$db;
176 $status->fatal(
'config-connection-error', $e->getMessage() );
186 if ( !$status->isOK() ) {
187 $this->parent->showStatusMessage( $status );
194 $conn = $status->value;
196 # Determine existing default character set
197 if ( $conn->tableExists(
"revision", __METHOD__ ) ) {
199 $res = $conn->query(
"SHOW TABLE STATUS LIKE '$revision'", __METHOD__ );
200 $row = $res->fetchObject();
202 $this->parent->showMessage(
'config-show-table-status' );
203 $existingSchema =
false;
204 $existingEngine =
false;
206 if ( preg_match(
'/^latin1/', $row->Collation ) ) {
207 $existingSchema =
'latin1';
208 } elseif ( preg_match(
'/^utf8/', $row->Collation ) ) {
209 $existingSchema =
'utf8';
210 } elseif ( preg_match(
'/^binary/', $row->Collation ) ) {
211 $existingSchema =
'binary';
213 $existingSchema =
false;
214 $this->parent->showMessage(
'config-unknown-collation' );
216 $existingEngine = $row->Engine ?? $row->Type;
219 $existingSchema =
false;
220 $existingEngine =
false;
223 if ( $existingSchema && $existingSchema != $this->
getVar(
'_MysqlCharset' ) ) {
224 $this->
setVar(
'_MysqlCharset', $existingSchema );
226 if ( $existingEngine && $existingEngine != $this->
getVar(
'_MysqlEngine' ) ) {
227 $this->
setVar(
'_MysqlEngine', $existingEngine );
230 # Normal user and password are selected after this step, so for now
231 # just copy these two
233 $wgDBpassword = $this->
getVar(
'_InstallPassword' );
242 return str_replace( [ $escapeChar,
'%',
'_' ],
243 [
"{$escapeChar}{$escapeChar}",
"{$escapeChar}%",
"{$escapeChar}_" ],
258 $conn = $status->value;
261 $res = $conn->query(
'SHOW ENGINES', __METHOD__ );
262 foreach ( $res as $row ) {
263 if ( $row->Support ==
'YES' || $row->Support ==
'DEFAULT' ) {
264 $engines[] = $row->Engine;
267 $engines = array_intersect( $this->supportedEngines, $engines );
278 return [
'binary',
'utf8' ];
288 if ( !$status->isOK() ) {
292 $conn = $status->value;
295 $currentName = $conn->selectField(
'',
'CURRENT_USER()',
'', __METHOD__ );
296 $parts = explode(
'@', $currentName );
297 if ( count( $parts ) != 2 ) {
300 $quotedUser = $conn->addQuotes( $parts[0] ) .
301 '@' . $conn->addQuotes( $parts[1] );
305 $res = $conn->select(
'INFORMATION_SCHEMA.USER_PRIVILEGES',
'*',
306 [
'GRANTEE' => $quotedUser ], __METHOD__ );
307 $insertMysql =
false;
308 $grantOptions = array_fill_keys( $this->webUserPrivs,
true );
309 foreach ( $res as $row ) {
310 if ( $row->PRIVILEGE_TYPE ==
'INSERT' ) {
313 if ( $row->IS_GRANTABLE ) {
314 unset( $grantOptions[$row->PRIVILEGE_TYPE] );
319 if ( !$insertMysql ) {
320 $row = $conn->selectRow(
'INFORMATION_SCHEMA.SCHEMA_PRIVILEGES',
'*',
322 'GRANTEE' => $quotedUser,
323 'TABLE_SCHEMA' =>
'mysql',
324 'PRIVILEGE_TYPE' =>
'INSERT',
331 if ( !$insertMysql ) {
336 $res = $conn->select(
'INFORMATION_SCHEMA.SCHEMA_PRIVILEGES',
'*',
338 'GRANTEE' => $quotedUser,
341 foreach ( $res as $row ) {
343 if ( preg_match( $regex, $this->
getVar(
'wgDBname' ) ) ) {
344 unset( $grantOptions[$row->PRIVILEGE_TYPE] );
347 if ( count( $grantOptions ) ) {
362 $r = preg_quote( $wildcard,
'/' );
375 $noCreateMsg =
false;
377 $noCreateMsg =
'config-db-web-no-create-privs';
384 if ( !in_array( $this->
getVar(
'_MysqlEngine' ), $engines ) ) {
385 $this->
setVar(
'_MysqlEngine', reset( $engines ) );
390 if ( !in_array( $this->
getVar(
'_MysqlCharset' ), $charsets ) ) {
391 $this->
setVar(
'_MysqlCharset', reset( $charsets ) );
403 if ( !$status->isOK() ) {
410 $this->
setVar(
'_CreateDBAccount',
false );
413 $create = $this->
getVar(
'_CreateDBAccount' );
419 MediaWikiServices::getInstance()->getDatabaseFactory()->create(
'mysql', [
420 'host' => $this->
getVar(
'wgDBserver' ),
421 'user' => $this->
getVar(
'wgDBuser' ),
422 'password' => $this->
getVar(
'wgDBpassword' ),
423 'ssl' => $this->
getVar(
'wgDBssl' ),
426 'tablePrefix' => $this->
getVar(
'wgDBprefix' )
429 return Status::newFatal(
'config-connection-error', $e->getMessage() );
436 if ( !in_array( $this->
getVar(
'_MysqlEngine' ), $engines ) ) {
437 $this->
setVar(
'_MysqlEngine', reset( $engines ) );
440 if ( !in_array( $this->
getVar(
'_MysqlCharset' ), $charsets ) ) {
441 $this->
setVar(
'_MysqlCharset', reset( $charsets ) );
444 return Status::newGood();
448 # Add our user callback to installSteps, right before the tables are created.
451 'callback' => [ $this,
'setupUser' ],
453 $this->parent->addInstallStep( $callback,
'tables' );
461 if ( !$status->isOK() ) {
465 $conn = $status->value;
466 $dbName = $this->
getVar(
'wgDBname' );
467 if ( !$this->databaseExists( $dbName ) ) {
469 "CREATE DATABASE " . $conn->addIdentifierQuotes( $dbName ) .
"CHARACTER SET utf8",
484 private function databaseExists( $dbName ) {
485 $encDatabase = $this->db->addQuotes( $dbName );
487 return $this->db->query(
488 "SELECT 1 FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = $encDatabase",
497 $dbUser = $this->
getVar(
'wgDBuser' );
498 if ( $dbUser == $this->
getVar(
'_InstallUser' ) ) {
499 return Status::newGood();
502 if ( !$status->isOK() ) {
507 $dbName = $this->
getVar(
'wgDBname' );
509 $server = $this->
getVar(
'wgDBserver' );
510 $password = $this->
getVar(
'wgDBpassword' );
511 $grantableNames = [];
513 if ( $this->
getVar(
'_CreateDBAccount' ) ) {
519 'password' => $password,
520 'ssl' => $this->
getVar(
'wgDBssl' ),
523 'tablePrefix' => $this->
getVar(
'wgDBprefix' )
525 $grantableNames[] = $this->buildFullUserName( $dbUser, $server );
526 $tryToCreate =
false;
531 $grantableNames[] = $this->buildFullUserName( $dbUser, $server );
532 $tryToCreate =
false;
535 if ( $tryToCreate ) {
539 'localhost.localdomain',
543 $createHostList = array_unique( $createHostList );
544 $escPass = $this->db->addQuotes( $password );
546 foreach ( $createHostList as $host ) {
547 $fullName = $this->buildFullUserName( $dbUser, $host );
548 if ( !$this->userDefinitelyExists( $host, $dbUser ) ) {
550 $this->db->begin( __METHOD__ );
551 $this->db->query(
"CREATE USER $fullName IDENTIFIED BY $escPass", __METHOD__ );
552 $this->db->commit( __METHOD__ );
553 $grantableNames[] = $fullName;
555 if ( $this->db->lastErrno() == 1396 ) {
557 $this->db->rollback( __METHOD__ );
558 $status->warning(
'config-install-user-alreadyexists', $dbUser );
559 $grantableNames[] = $fullName;
564 $this->db->rollback( __METHOD__ );
565 $status->warning(
'config-install-user-create-failed', $dbUser, $dqe->getMessage() );
569 $status->warning(
'config-install-user-alreadyexists', $dbUser );
570 $grantableNames[] = $fullName;
577 $dbAllTables = $this->db->addIdentifierQuotes( $dbName ) .
'.*';
578 foreach ( $grantableNames as $name ) {
580 $this->db->begin( __METHOD__ );
581 $this->db->query(
"GRANT ALL PRIVILEGES ON $dbAllTables TO $name", __METHOD__ );
582 $this->db->commit( __METHOD__ );
584 $this->db->rollback( __METHOD__ );
585 $status->fatal(
'config-install-user-grant-failed', $dbUser, $dqe->getMessage() );
598 private function buildFullUserName( $name, $host ) {
599 return $this->db->addQuotes( $name ) .
'@' . $this->db->addQuotes( $host );
609 private function userDefinitelyExists( $host, $user ) {
611 $res = $this->db->newSelectQueryBuilder()
612 ->select( [
'Host',
'User' ] )
613 ->from(
'mysql.user' )
614 ->where( [
'Host' => $host,
'User' => $user ] )
615 ->caller( __METHOD__ )->fetchRow();
631 if ( $this->
getVar(
'_MysqlEngine' ) !==
null ) {
632 $options[] =
"ENGINE=" . $this->
getVar(
'_MysqlEngine' );
634 if ( $this->
getVar(
'_MysqlCharset' ) !==
null ) {
635 $options[] =
'DEFAULT CHARSET=' . $this->
getVar(
'_MysqlCharset' );
638 return implode(
', ', $options );
649 'wgDBname' => $this->
getVar(
'wgDBname' ),
650 'wgDBuser' => $this->
getVar(
'wgDBuser' ),
651 'wgDBpassword' => $this->
getVar(
'wgDBpassword' ),
656 $prefix = LocalSettingsGenerator::escapePhpString( $this->
getVar(
'wgDBprefix' ) );
657 $useSsl = $this->
getVar(
'wgDBssl' ) ?
'true' :
'false';
658 $tblOpts = LocalSettingsGenerator::escapePhpString( $this->
getTableOptions() );
660 return "# MySQL specific settings
661\$wgDBprefix = \"{$prefix}\";
662\$wgDBssl = {$useSsl};
664# MySQL table options to use during installation or update
665\$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.
selectDatabase(Database $conn, string $database)
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.