4 * DBMS-specific installation helper.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * http://www.gnu.org/copyleft/gpl.html
25 namespace MediaWiki\Installer
;
27 use MediaWiki\Installer\Task\ITaskContext
;
28 use MediaWiki\Status\Status
;
30 use Wikimedia\Rdbms\DatabaseDomain
;
31 use Wikimedia\Rdbms\IDatabase
;
32 use Wikimedia\Rdbms\IMaintainableDatabase
;
35 * Base class for DBMS-specific installation helper classes.
40 abstract class DatabaseInstaller
implements ITaskContext
{
43 * The Installer object.
50 * @var string Set by subclasses
52 public static $minimumVersion;
55 * @var string Set by subclasses
57 protected static $notMinimumVersionMessage;
60 * @deprecated since 1.43 -- use definitelyGetConnection()
61 * @var IMaintainableDatabase
65 /** @var IMaintainableDatabase|null */
67 /** @var string|null */
68 private $cachedConnType;
71 * Internal variables for installation.
75 protected $internalDefaults = [];
78 * Array of MW configuration globals this class uses.
82 protected $globalNames = [];
85 private $provisions = [];
88 * Whether the provided version meets the necessary requirements for this type
90 * @param IDatabase $conn
94 public static function meetsMinimumRequirement( IDatabase
$conn ) {
95 $serverVersion = $conn->getServerVersion();
96 if ( version_compare( $serverVersion, static::$minimumVersion ) < 0 ) {
97 return Status
::newFatal(
98 static::$notMinimumVersionMessage, static::$minimumVersion, $serverVersion
102 return Status
::newGood();
106 * Return the internal name, e.g. 'mysql', or 'sqlite'.
108 abstract public function getName();
111 * @return bool Returns true if the client library is compiled in.
113 abstract public function isCompiled();
116 * Checks for installation prerequisites other than those checked by isCompiled()
120 public function checkPrerequisites() {
121 return Status
::newGood();
125 * Open a connection to the database using the administrative user/password
126 * currently defined in the session, without any caching. Returns a status
127 * object. On success, the status object will contain a Database object in
130 * The database should not be implicitly created.
132 * @param string $type One of the self::CONN_* constants, except CONN_DONT_KNOW
133 * @return ConnectionStatus
135 abstract protected function openConnection( string $type );
138 * Connect to the database using the administrative user/password currently
139 * defined in the session. Returns a status object. On success, the status
140 * object will contain a Database object in its value member.
142 * This will return a cached connection if one is available.
144 * @param string $type One of the self::CONN_* constants. Using CONN_DONT_KNOW
145 * is deprecated and will cause an exception to be thrown in a future release.
146 * @return ConnectionStatus
148 public function getConnection( $type = self
::CONN_DONT_KNOW
): ConnectionStatus
{
149 if ( $type === self
::CONN_DONT_KNOW
) {
150 if ( $this->cachedConnType
) {
151 $type = $this->cachedConnType
;
153 $type = self
::CONN_CREATE_DATABASE
;
156 if ( $this->cachedConn
) {
157 if ( $this->cachedConnType
=== $type ) {
158 return new ConnectionStatus( $this->cachedConn
);
160 return $this->changeConnType( $this->cachedConn
, $this->cachedConnType
, $type );
163 $status = $this->openConnection( $type );
164 if ( $status->isOK() ) {
165 $this->cachedConn
= $status->getDB();
166 $this->cachedConnType
= $type;
167 // Assign to $this->db for b/c
168 $this->db
= $this->cachedConn
;
170 if ( $type === self
::CONN_CREATE_SCHEMA ||
$type === self
::CONN_CREATE_TABLES
) {
171 $this->cachedConn
->setSchemaVars( $this->getSchemaVars() );
179 * Get a connection and unwrap it from its Status object, throwing an
180 * exception on failure.
182 * @param string $type
183 * @return IMaintainableDatabase
185 public function definitelyGetConnection( string $type ): IMaintainableDatabase
{
186 $status = $this->getConnection( $type );
187 if ( !$status->isOK() ) {
188 throw new RuntimeException( __METHOD__
. ': unexpected DB connection error' );
190 return $status->getDB();
194 * Change the type of a connection.
196 * CONN_CREATE_DATABASE means the domain is indeterminate and irrelevant,
197 * so converting from this type can be done by selecting the domain, and
198 * converting to it is a no-op.
200 * CONN_CREATE_SCHEMA means the domain is correct but tables created by
201 * PostgreSQL will have the incorrect role. So to convert from this to
202 * CONN_CREATE_TABLES, we set the role.
204 * CONN_CREATE_TABLES means a fully-configured connection, suitable for
205 * most tasks, so converting from it is a no-op.
207 * @param IMaintainableDatabase $conn
208 * @param string &$storedType One of the self::CONN_* constants. An in/out
209 * parameter, set to the new type on success. It is set to the "real" new
210 * type, reflecting the highest configuration level reached, to avoid
211 * unnecessary selectDomain() calls when we need to temporarily give an
212 * unconfigured connection.
213 * @param string $newType One of the self::CONN_* constants
214 * @return ConnectionStatus
216 protected function changeConnType( IMaintainableDatabase
$conn, &$storedType, $newType ) {
217 // Change type from database to schema, if requested
218 if ( $storedType === self
::CONN_CREATE_DATABASE
) {
219 if ( $newType === self
::CONN_CREATE_SCHEMA ||
$newType === self
::CONN_CREATE_TABLES
) {
220 // TODO: catch exceptions from selectDomain and report as a Status
221 $conn->selectDomain( new DatabaseDomain(
222 $this->getVar( 'wgDBname' ),
223 $this->getVar( 'wgDBmwschema' ),
224 $this->getVar( 'wgDBprefix' ) ??
''
226 $conn->setSchemaVars( $this->getSchemaVars() );
227 $storedType = self
::CONN_CREATE_SCHEMA
;
230 // Change type from schema to tables, if requested
231 if ( $newType === self
::CONN_CREATE_TABLES
&& $storedType === self
::CONN_CREATE_SCHEMA
) {
232 $status = $this->changeConnTypeFromSchemaToTables( $conn );
233 if ( $status->isOK() ) {
234 $storedType = self
::CONN_CREATE_TABLES
;
238 return new ConnectionStatus( $conn );
242 * Change the type of a connection from CONN_CREATE_SCHEMA to CONN_CREATE_TABLES.
243 * Postgres overrides this.
245 * @param IMaintainableDatabase $conn
246 * @return ConnectionStatus
248 protected function changeConnTypeFromSchemaToTables( IMaintainableDatabase
$conn ) {
249 return new ConnectionStatus( $conn );
252 public function getDbType(): string {
253 return $this->getName();
256 public function getConfigVar( string $name ) {
257 return $this->getVar( "wg$name" );
260 public function getOption( string $name ) {
261 return $this->getVar( "_$name" );
264 public function provide( string $name, $value ) {
265 $this->provisions
[$name] = $value;
268 public function getProvision( string $name ) {
269 if ( isset( $this->provisions
[$name] ) ) {
270 return $this->provisions
[$name];
272 throw new \
RuntimeException( "Can't find provided data \"$name\"" );
277 * Get the DBMS-specific options for LocalSettings.php generation.
281 abstract public function getLocalSettings();
284 * Override this to provide DBMS-specific schema variables, to be
285 * substituted into the schema files.
288 public function getSchemaVars() {
293 * Allow DB installers a chance to make checks before upgrade.
295 public function preUpgrade() {
299 * Get an array of MW configuration globals that will be configured by this class.
302 public function getGlobalNames() {
303 return $this->globalNames
;
307 * Construct and initialise parent.
308 * This is typically only called from Installer::getDBInstaller()
309 * @param Installer $parent
311 public function __construct( $parent ) {
312 $this->parent
= $parent;
316 * Convenience function.
317 * Check if a named extension is present.
319 * @param string $name
322 protected static function checkExtension( $name ) {
323 return extension_loaded( $name );
327 * Get the internationalised name for this DBMS.
330 public function getReadableName() {
331 // Messages: config-type-mysql, config-type-postgres, config-type-sqlite
332 return wfMessage( 'config-type-' . $this->getName() )->text();
336 * Get a name=>value map of MW configuration globals for the default values.
340 public function getGlobalDefaults() {
342 foreach ( $this->getGlobalNames() as $var ) {
343 if ( isset( $GLOBALS[$var] ) ) {
344 $defaults[$var] = $GLOBALS[$var];
351 * Get a name=>value map of internal variables used during installation.
354 public function getInternalDefaults() {
355 return $this->internalDefaults
;
359 * Get a variable, taking local defaults into account.
361 * @param mixed|null $default
364 public function getVar( $var, $default = null ) {
365 $defaults = $this->getGlobalDefaults();
366 $internal = $this->getInternalDefaults();
367 if ( isset( $defaults[$var] ) ) {
368 $default = $defaults[$var];
369 } elseif ( isset( $internal[$var] ) ) {
370 $default = $internal[$var];
373 return $this->parent
->getVar( $var, $default );
377 * Convenience alias for $this->parent->setVar()
378 * @param string $name
379 * @param mixed $value
381 public function setVar( $name, $value ) {
382 $this->parent
->setVar( $name, $value );
385 abstract public function getConnectForm( WebInstaller
$webInstaller ): DatabaseConnectForm
;
387 abstract public function getSettingsForm( WebInstaller
$webInstaller ): DatabaseSettingsForm
;
390 * Determine whether an existing installation of MediaWiki is present in
391 * the configured administrative connection. Returns true if there is
392 * such a wiki, false if the database doesn't exist.
394 * Traditionally, this is done by testing for the existence of either
395 * the revision table or the cur table.
399 public function needsUpgrade() {
400 $status = $this->getConnection( self
::CONN_CREATE_SCHEMA
);
401 if ( !$status->isOK() ) {
404 $db = $status->getDB();
405 return $db->tableExists( 'cur', __METHOD__
) ||
406 $db->tableExists( 'revision', __METHOD__
);