48 '_MysqlEngine' =>
'InnoDB',
49 '_MysqlCharset' =>
'binary',
50 '_InstallUser' =>
'root',
55 private const MIN_VERSIONS = [
67 'CREATE TEMPORARY TABLES',
92 $this->parent->getHelpBox(
'config-db-host-help' )
94 Html::openElement(
'fieldset' ) .
95 Html::element(
'legend', [],
wfMessage(
'config-db-wiki-settings' )->text() ) .
96 $this->
getTextBox(
'wgDBname',
'config-db-name', [
'dir' =>
'ltr' ],
97 $this->parent->getHelpBox(
'config-db-name-help' ) ) .
98 $this->
getTextBox(
'wgDBprefix',
'config-db-prefix', [
'dir' =>
'ltr' ],
99 $this->parent->getHelpBox(
'config-db-prefix-help' ) ) .
100 Html::closeElement(
'fieldset' ) .
109 $status = Status::newGood();
110 if ( !strlen( $newValues[
'wgDBserver'] ) ) {
111 $status->fatal(
'config-missing-db-host' );
113 if ( !strlen( $newValues[
'wgDBname'] ) ) {
114 $status->fatal(
'config-missing-db-name' );
115 } elseif ( !preg_match(
'/^[a-z0-9+_-]+$/i', $newValues[
'wgDBname'] ) ) {
116 $status->fatal(
'config-invalid-db-name', $newValues[
'wgDBname'] );
118 if ( !preg_match(
'/^[a-z0-9_-]*$/i', $newValues[
'wgDBprefix'] ) ) {
119 $status->fatal(
'config-invalid-db-prefix', $newValues[
'wgDBprefix'] );
121 if ( !$status->isOK() ) {
127 if ( !$status->isOK() ) {
133 if ( !$status->isOK() ) {
139 $conn = $status->value;
140 '@phan-var Database $conn';
143 return static::meetsMinimumRequirement( $conn );
148 self::$minimumVersion = self::MIN_VERSIONS[
$type];
150 self::$notMinimumVersionMessage =
'config-' . strtolower(
$type ) .
'-old';
151 return parent::meetsMinimumRequirement( $conn );
158 $status = Status::newGood();
161 $db = Database::factory(
'mysql', [
162 'host' => $this->
getVar(
'wgDBserver' ),
163 'user' => $this->
getVar(
'_InstallUser' ),
164 'password' => $this->
getVar(
'_InstallPassword' ),
167 'tablePrefix' => $this->
getVar(
'wgDBprefix' ) ] );
168 $status->value =
$db;
170 $status->fatal(
'config-connection-error', $e->getMessage() );
180 if ( !$status->isOK() ) {
181 $this->parent->showStatusMessage( $status );
188 $conn = $status->value;
189 $conn->selectDB( $this->
getVar(
'wgDBname' ) );
191 # Determine existing default character set
192 if ( $conn->tableExists(
"revision", __METHOD__ ) ) {
194 $res = $conn->query(
"SHOW TABLE STATUS LIKE '$revision'", __METHOD__ );
195 $row =
$res->fetchObject();
197 $this->parent->showMessage(
'config-show-table-status' );
198 $existingSchema =
false;
199 $existingEngine =
false;
201 if ( preg_match(
'/^latin1/', $row->Collation ) ) {
202 $existingSchema =
'latin1';
203 } elseif ( preg_match(
'/^utf8/', $row->Collation ) ) {
204 $existingSchema =
'utf8';
205 } elseif ( preg_match(
'/^binary/', $row->Collation ) ) {
206 $existingSchema =
'binary';
208 $existingSchema =
false;
209 $this->parent->showMessage(
'config-unknown-collation' );
211 $existingEngine = $row->Engine ?? $row->Type;
214 $existingSchema =
false;
215 $existingEngine =
false;
218 if ( $existingSchema && $existingSchema != $this->
getVar(
'_MysqlCharset' ) ) {
219 $this->
setVar(
'_MysqlCharset', $existingSchema );
221 if ( $existingEngine && $existingEngine != $this->
getVar(
'_MysqlEngine' ) ) {
222 $this->
setVar(
'_MysqlEngine', $existingEngine );
225 # Normal user and password are selected after this step, so for now
226 # just copy these two
228 $wgDBpassword = $this->
getVar(
'_InstallPassword' );
237 return str_replace( [ $escapeChar,
'%',
'_' ],
238 [
"{$escapeChar}{$escapeChar}",
"{$escapeChar}%",
"{$escapeChar}_" ],
253 $conn = $status->value;
256 $res = $conn->query(
'SHOW ENGINES', __METHOD__ );
257 foreach (
$res as $row ) {
258 if ( $row->Support ==
'YES' || $row->Support ==
'DEFAULT' ) {
259 $engines[] = $row->Engine;
262 $engines = array_intersect( $this->supportedEngines, $engines );
273 return [
'binary',
'utf8' ];
283 if ( !$status->isOK() ) {
287 $conn = $status->value;
290 $currentName = $conn->selectField(
'',
'CURRENT_USER()',
'', __METHOD__ );
291 $parts = explode(
'@', $currentName );
292 if ( count( $parts ) != 2 ) {
295 $quotedUser = $conn->addQuotes( $parts[0] ) .
296 '@' . $conn->addQuotes( $parts[1] );
300 $res = $conn->select(
'INFORMATION_SCHEMA.USER_PRIVILEGES',
'*',
301 [
'GRANTEE' => $quotedUser ], __METHOD__ );
302 $insertMysql =
false;
303 $grantOptions = array_fill_keys( $this->webUserPrivs,
true );
304 foreach (
$res as $row ) {
305 if ( $row->PRIVILEGE_TYPE ==
'INSERT' ) {
308 if ( $row->IS_GRANTABLE ) {
309 unset( $grantOptions[$row->PRIVILEGE_TYPE] );
314 if ( !$insertMysql ) {
315 $row = $conn->selectRow(
'INFORMATION_SCHEMA.SCHEMA_PRIVILEGES',
'*',
317 'GRANTEE' => $quotedUser,
318 'TABLE_SCHEMA' =>
'mysql',
319 'PRIVILEGE_TYPE' =>
'INSERT',
326 if ( !$insertMysql ) {
331 $res = $conn->select(
'INFORMATION_SCHEMA.SCHEMA_PRIVILEGES',
'*',
333 'GRANTEE' => $quotedUser,
336 foreach (
$res as $row ) {
338 if ( preg_match( $regex, $this->
getVar(
'wgDBname' ) ) ) {
339 unset( $grantOptions[$row->PRIVILEGE_TYPE] );
342 if ( count( $grantOptions ) ) {
357 $r = preg_quote( $wildcard,
'/' );
370 $noCreateMsg =
false;
372 $noCreateMsg =
'config-db-web-no-create-privs';
379 if ( !in_array( $this->
getVar(
'_MysqlEngine' ), $engines ) ) {
380 $this->
setVar(
'_MysqlEngine', reset( $engines ) );
385 if ( !in_array( $this->
getVar(
'_MysqlCharset' ), $charsets ) ) {
386 $this->
setVar(
'_MysqlCharset', reset( $charsets ) );
398 if ( !$status->isOK() ) {
405 $this->
setVar(
'_CreateDBAccount',
false );
408 $create = $this->
getVar(
'_CreateDBAccount' );
414 MediaWikiServices::getInstance()->getDatabaseFactory()->create(
'mysql', [
415 'host' => $this->
getVar(
'wgDBserver' ),
416 'user' => $this->
getVar(
'wgDBuser' ),
417 'password' => $this->
getVar(
'wgDBpassword' ),
420 'tablePrefix' => $this->
getVar(
'wgDBprefix' )
423 return Status::newFatal(
'config-connection-error', $e->getMessage() );
430 if ( !in_array( $this->
getVar(
'_MysqlEngine' ), $engines ) ) {
431 $this->
setVar(
'_MysqlEngine', reset( $engines ) );
434 if ( !in_array( $this->
getVar(
'_MysqlCharset' ), $charsets ) ) {
435 $this->
setVar(
'_MysqlCharset', reset( $charsets ) );
438 return Status::newGood();
442 # Add our user callback to installSteps, right before the tables are created.
445 'callback' => [ $this,
'setupUser' ],
447 $this->parent->addInstallStep( $callback,
'tables' );
455 if ( !$status->isOK() ) {
459 $conn = $status->value;
460 $dbName = $this->
getVar(
'wgDBname' );
461 if ( !$this->databaseExists( $dbName ) ) {
463 "CREATE DATABASE " . $conn->addIdentifierQuotes( $dbName ) .
"CHARACTER SET utf8",
467 $conn->selectDB( $dbName );
478 private function databaseExists( $dbName ) {
479 $encDatabase = $this->db->addQuotes( $dbName );
481 return $this->db->query(
482 "SELECT 1 FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = $encDatabase",
491 $dbUser = $this->
getVar(
'wgDBuser' );
492 if ( $dbUser == $this->
getVar(
'_InstallUser' ) ) {
493 return Status::newGood();
496 if ( !$status->isOK() ) {
501 $dbName = $this->
getVar(
'wgDBname' );
502 $this->db->selectDB( $dbName );
503 $server = $this->
getVar(
'wgDBserver' );
504 $password = $this->
getVar(
'wgDBpassword' );
505 $grantableNames = [];
507 if ( $this->
getVar(
'_CreateDBAccount' ) ) {
510 Database::factory(
'mysql', [
513 'password' => $password,
516 'tablePrefix' => $this->
getVar(
'wgDBprefix' )
518 $grantableNames[] = $this->buildFullUserName( $dbUser, $server );
519 $tryToCreate =
false;
524 $grantableNames[] = $this->buildFullUserName( $dbUser, $server );
525 $tryToCreate =
false;
528 if ( $tryToCreate ) {
532 'localhost.localdomain',
536 $createHostList = array_unique( $createHostList );
537 $escPass = $this->db->addQuotes( $password );
539 foreach ( $createHostList as $host ) {
540 $fullName = $this->buildFullUserName( $dbUser, $host );
541 if ( !$this->userDefinitelyExists( $host, $dbUser ) ) {
543 $this->db->begin( __METHOD__ );
544 $this->db->query(
"CREATE USER $fullName IDENTIFIED BY $escPass", __METHOD__ );
545 $this->db->commit( __METHOD__ );
546 $grantableNames[] = $fullName;
548 if ( $this->db->lastErrno() == 1396 ) {
550 $this->db->rollback( __METHOD__ );
551 $status->warning(
'config-install-user-alreadyexists', $dbUser );
552 $grantableNames[] = $fullName;
557 $this->db->rollback( __METHOD__ );
558 $status->warning(
'config-install-user-create-failed', $dbUser, $dqe->getMessage() );
562 $status->warning(
'config-install-user-alreadyexists', $dbUser );
563 $grantableNames[] = $fullName;
570 $dbAllTables = $this->db->addIdentifierQuotes( $dbName ) .
'.*';
571 foreach ( $grantableNames as $name ) {
573 $this->db->begin( __METHOD__ );
574 $this->db->query(
"GRANT ALL PRIVILEGES ON $dbAllTables TO $name", __METHOD__ );
575 $this->db->commit( __METHOD__ );
577 $this->db->rollback( __METHOD__ );
578 $status->fatal(
'config-install-user-grant-failed', $dbUser, $dqe->getMessage() );
591 private function buildFullUserName( $name, $host ) {
592 return $this->db->addQuotes( $name ) .
'@' . $this->db->addQuotes( $host );
602 private function userDefinitelyExists( $host, $user ) {
604 $res = $this->db->selectRow(
'mysql.user', [
'Host',
'User' ],
605 [
'Host' => $host,
'User' => $user ], __METHOD__ );
621 if ( $this->
getVar(
'_MysqlEngine' ) !==
null ) {
622 $options[] =
"ENGINE=" . $this->
getVar(
'_MysqlEngine' );
624 if ( $this->
getVar(
'_MysqlCharset' ) !==
null ) {
625 $options[] =
'DEFAULT CHARSET=' . $this->
getVar(
'_MysqlCharset' );
628 return implode(
', ', $options );
639 'wgDBname' => $this->
getVar(
'wgDBname' ),
640 'wgDBuser' => $this->
getVar(
'wgDBuser' ),
641 'wgDBpassword' => $this->
getVar(
'wgDBpassword' ),
646 $prefix = LocalSettingsGenerator::escapePhpString( $this->
getVar(
'wgDBprefix' ) );
647 $tblOpts = LocalSettingsGenerator::escapePhpString( $this->
getTableOptions() );
649 return "# MySQL specific settings
650\$wgDBprefix = \"{$prefix}\";
652# MySQL table options to use during installation or update
653\$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.
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