Merge "rdbms: Move Database::factory() to DatabaseFactory service"

This commit is contained in:
jenkins-bot 2022-09-02 01:51:38 +00:00 committed by Gerrit Code Review
commit c48a61b247
18 changed files with 308 additions and 155 deletions

View file

@ -1804,6 +1804,7 @@ $wgAutoloadLocalClasses = [
'Wikimedia\\Rdbms\\DBUnexpectedError' => __DIR__ . '/includes/libs/rdbms/exception/DBUnexpectedError.php',
'Wikimedia\\Rdbms\\Database' => __DIR__ . '/includes/libs/rdbms/database/Database.php',
'Wikimedia\\Rdbms\\DatabaseDomain' => __DIR__ . '/includes/libs/rdbms/database/domain/DatabaseDomain.php',
'Wikimedia\\Rdbms\\DatabaseFactory' => __DIR__ . '/includes/libs/rdbms/database/DatabaseFactory.php',
'Wikimedia\\Rdbms\\DatabaseMysqlBase' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMysqlBase.php',
'Wikimedia\\Rdbms\\DatabaseMysqli' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMysqli.php',
'Wikimedia\\Rdbms\\DatabasePostgres' => __DIR__ . '/includes/libs/rdbms/database/DatabasePostgres.php',

View file

@ -189,6 +189,7 @@ use Wikimedia\NonSerializable\NonSerializableTrait;
use Wikimedia\ObjectFactory\ObjectFactory;
use Wikimedia\Parsoid\Config\DataAccess;
use Wikimedia\Parsoid\Config\SiteConfig;
use Wikimedia\Rdbms\DatabaseFactory;
use Wikimedia\Rdbms\ILoadBalancer;
use Wikimedia\Rdbms\LBFactory;
use Wikimedia\RequestTimeout\CriticalSectionProvider;
@ -918,6 +919,14 @@ class MediaWikiServices extends ServiceContainer {
return $this->getService( 'DatabaseBlockStore' );
}
/**
* @since 1.39
* @return DatabaseFactory
*/
public function getDatabaseFactory(): DatabaseFactory {
return $this->getService( 'DatabaseFactory' );
}
/**
* @since 1.33
* @return DateFormatterFactory

View file

@ -188,6 +188,7 @@ use Wikimedia\Parsoid\Config\Api\SiteConfig as ApiSiteConfig;
use Wikimedia\Parsoid\Config\DataAccess;
use Wikimedia\Parsoid\Config\SiteConfig;
use Wikimedia\Parsoid\Parsoid;
use Wikimedia\Rdbms\DatabaseFactory;
use Wikimedia\RequestTimeout\CriticalSectionProvider;
use Wikimedia\RequestTimeout\RequestTimeout;
use Wikimedia\Services\RecursiveServiceDependencyException;
@ -525,6 +526,10 @@ return [
);
},
'DatabaseFactory' => static function ( MediaWikiServices $services ): DatabaseFactory {
return new DatabaseFactory();
},
'DateFormatterFactory' => static function ( MediaWikiServices $services ): DateFormatterFactory {
return new DateFormatterFactory();
},
@ -591,7 +596,8 @@ return [
$srvCache,
$wanCache,
$services->getCriticalSectionProvider(),
$services->getStatsdDataFactory()
$services->getStatsdDataFactory(),
$services->getDatabaseFactory()
);
},

View file

@ -27,6 +27,7 @@ use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MainConfigNames;
use Wikimedia\Rdbms\ChronologyProtector;
use Wikimedia\Rdbms\DatabaseDomain;
use Wikimedia\Rdbms\DatabaseFactory;
use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\ILBFactory;
use Wikimedia\Rdbms\LBFactory;
@ -95,6 +96,10 @@ class MWLBFactory {
* @var StatsdDataFactoryInterface
*/
private $statsdDataFactory;
/**
* @var DatabaseFactory
*/
private $databaseFactory;
/**
* @param ServiceOptions $options
@ -104,6 +109,7 @@ class MWLBFactory {
* @param WANObjectCache $wanCache
* @param CriticalSectionProvider $csProvider
* @param StatsdDataFactoryInterface $statsdDataFactory
* @param DatabaseFactory $databaseFactory
*/
public function __construct(
ServiceOptions $options,
@ -112,7 +118,8 @@ class MWLBFactory {
BagOStuff $srvCache,
WANObjectCache $wanCache,
CriticalSectionProvider $csProvider,
StatsdDataFactoryInterface $statsdDataFactory
StatsdDataFactoryInterface $statsdDataFactory,
DatabaseFactory $databaseFactory
) {
$this->options = $options;
$this->readOnlyMode = $readOnlyMode;
@ -121,6 +128,7 @@ class MWLBFactory {
$this->wanCache = $wanCache;
$this->csProvider = $csProvider;
$this->statsdDataFactory = $statsdDataFactory;
$this->databaseFactory = $databaseFactory;
}
/**
@ -212,6 +220,7 @@ class MWLBFactory {
$lbConf['cpStash'] = $this->cpStash;
$lbConf['srvCache'] = $this->srvCache;
$lbConf['wanCache'] = $this->wanCache;
$lbConf['databaseFactory'] = $this->databaseFactory;
return $lbConf;
}

View file

@ -21,6 +21,7 @@
* @ingroup Installer
*/
use MediaWiki\MediaWikiServices;
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\DBConnectionError;
use Wikimedia\Rdbms\DBQueryError;
@ -397,7 +398,7 @@ class MysqlInstaller extends DatabaseInstaller {
if ( !$create ) {
// Test the web account
try {
Database::factory( 'mysql', [
MediaWikiServices::getInstance()->getDatabaseFactory()->create( 'mysql', [
'host' => $this->getVar( 'wgDBserver' ),
'user' => $this->getVar( 'wgDBuser' ),
'password' => $this->getVar( 'wgDBpassword' ),

View file

@ -21,6 +21,7 @@
* @ingroup Installer
*/
use MediaWiki\MediaWikiServices;
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\DatabasePostgres;
use Wikimedia\Rdbms\DBConnectionError;
@ -161,7 +162,7 @@ class PostgresInstaller extends DatabaseInstaller {
protected function openConnectionWithParams( $user, $password, $dbName, $schema ) {
$status = Status::newGood();
try {
$db = Database::factory( 'postgres', [
$db = MediaWikiServices::getInstance()->getDatabaseFactory()->create( 'postgres', [
'host' => $this->getVar( 'wgDBserver' ),
'port' => $this->getVar( 'wgDBport' ),
'user' => $user,

View file

@ -21,6 +21,7 @@
* @ingroup Installer
*/
use MediaWiki\MediaWikiServices;
use Wikimedia\AtEase\AtEase;
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\DatabaseSqlite;
@ -194,8 +195,9 @@ class SqliteInstaller extends DatabaseInstaller {
$dir = $this->getVar( 'wgSQLiteDataDir' );
$dbName = $this->getVar( 'wgDBname' );
try {
# @todo FIXME: Need more sensible constructor parameters, e.g. single associative array
$db = Database::factory( 'sqlite', [ 'dbname' => $dbName, 'dbDirectory' => $dir ] );
$db = MediaWikiServices::getInstance()->getDatabaseFactory()->create(
'sqlite', [ 'dbname' => $dbName, 'dbDirectory' => $dir ]
);
$status->value = $db;
} catch ( DBConnectionError $e ) {
$status->fatal( 'config-sqlite-connection-error', $e->getMessage() );

View file

@ -20,7 +20,6 @@
namespace Wikimedia\Rdbms;
use BagOStuff;
use HashBagOStuff;
use InvalidArgumentException;
use LogicException;
use Psr\Log\LoggerAwareInterface;
@ -354,7 +353,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
* The "master" and "replica" fields are used to flag the replication role of this
* database server and whether methods like getLag() should actually issue queries.
* - topologicalPrimaryConnRef: lazy-connecting IDatabase handle to the most authoritative
* primary database server for the cluster that this database belongs to. This hande is
* primary database server for the cluster that this database belongs to. This handle is
* used for replication status purposes. This is generally managed by LoadBalancer.
* - connLogger: Optional PSR-3 logger interface instance.
* - queryLogger: Optional PSR-3 logger interface instance.
@ -373,53 +372,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
* @param int $connect One of the class constants (NEW_CONNECTED, NEW_UNCONNECTED) [optional]
* @return Database|null If the database driver or extension cannot be found
* @throws InvalidArgumentException If the database driver or extension cannot be found
* @deprecated since 1.39, use DatabaseFactory::create instead
* @since 1.18
*/
final public static function factory( $type, $params = [], $connect = self::NEW_CONNECTED ) {
$class = self::getClass( $type, $params['driver'] ?? null );
if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) {
$params += [
// Default configuration
'host' => null,
'user' => null,
'password' => null,
'dbname' => null,
'schema' => null,
'tablePrefix' => '',
'flags' => 0,
'variables' => [],
'lbInfo' => [],
'cliMode' => ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ),
'agent' => '',
'serverName' => null,
'topologyRole' => null,
// Objects and callbacks
'topologicalPrimaryConnRef' => $params['topologicalPrimaryConnRef'] ?? null,
'srvCache' => $params['srvCache'] ?? new HashBagOStuff(),
'profiler' => $params['profiler'] ?? null,
'trxProfiler' => $params['trxProfiler'] ?? new TransactionProfiler(),
'connLogger' => $params['connLogger'] ?? new NullLogger(),
'queryLogger' => $params['queryLogger'] ?? new NullLogger(),
'replLogger' => $params['replLogger'] ?? new NullLogger(),
'errorLogger' => $params['errorLogger'] ?? static function ( Throwable $e ) {
trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
},
'deprecationLogger' => $params['deprecationLogger'] ?? static function ( $msg ) {
trigger_error( $msg, E_USER_DEPRECATED );
}
];
/** @var Database $conn */
$conn = new $class( $params );
if ( $connect === self::NEW_CONNECTED ) {
$conn->initConnection();
}
} else {
$conn = null;
}
return $conn;
return ( new DatabaseFactory() )->create( $type, $params, $connect );
}
/**
@ -427,75 +384,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
* @param string|null $driver Optional name of a specific DB client driver
* @return array Map of (Database::ATTR_* constant => value) for all such constants
* @throws DBUnexpectedError
* @deprecated since 1.39, use DatabaseFactory::attributesFromType instead
* @since 1.31
*/
final public static function attributesFromType( $dbType, $driver = null ) {
static $defaults = [
self::ATTR_DB_IS_FILE => false,
self::ATTR_DB_LEVEL_LOCKING => false,
self::ATTR_SCHEMAS_AS_TABLE_GROUPS => false
];
$class = self::getClass( $dbType, $driver );
if ( class_exists( $class ) ) {
return call_user_func( [ $class, 'getAttributes' ] ) + $defaults;
} else {
throw new DBUnexpectedError( null, "$dbType is not a supported database type." );
}
}
/**
* @param string $dbType A possible DB type (sqlite, mysql, postgres,...)
* @param string|null $driver Optional name of a specific DB client driver
* @return string Database subclass name to use
* @throws InvalidArgumentException
*/
private static function getClass( $dbType, $driver = null ) {
// For database types with built-in support, the below maps type to IDatabase
// implementations. For types with multiple driver implementations (PHP extensions),
// an array can be used, keyed by extension name. In case of an array, the
// optional 'driver' parameter can be used to force a specific driver. Otherwise,
// we auto-detect the first available driver. For types without built-in support,
// an class named "Database<Type>" us used, eg. DatabaseFoo for type 'foo'.
static $builtinTypes = [
'mysql' => [ 'mysqli' => DatabaseMysqli::class ],
'sqlite' => DatabaseSqlite::class,
'postgres' => DatabasePostgres::class,
];
$dbType = strtolower( $dbType );
if ( !isset( $builtinTypes[$dbType] ) ) {
// Not a built in type, assume standard naming scheme
return 'Database' . ucfirst( $dbType );
}
$class = false;
$possibleDrivers = $builtinTypes[$dbType];
if ( is_string( $possibleDrivers ) ) {
$class = $possibleDrivers;
} elseif ( (string)$driver !== '' ) {
if ( !isset( $possibleDrivers[$driver] ) ) {
throw new InvalidArgumentException( __METHOD__ .
" type '$dbType' does not support driver '{$driver}'" );
}
$class = $possibleDrivers[$driver];
} else {
foreach ( $possibleDrivers as $posDriver => $possibleClass ) {
if ( extension_loaded( $posDriver ) ) {
$class = $possibleClass;
break;
}
}
}
if ( $class === false ) {
throw new InvalidArgumentException( __METHOD__ .
" no viable database extension found for type '$dbType'" );
}
return $class;
return ( new DatabaseFactory() )->attributesFromType( $dbType, $driver );
}
/**
@ -503,7 +396,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
* @return array Map of (Database::ATTR_* constant => value)
* @since 1.31
*/
protected static function getAttributes() {
public static function getAttributes() {
return [];
}

View file

@ -0,0 +1,208 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Database
*/
namespace Wikimedia\Rdbms;
use HashBagOStuff;
use InvalidArgumentException;
use Psr\Log\NullLogger;
use Throwable;
/**
* Constructs Database objects
*
* @since 1.39
*/
class DatabaseFactory {
/**
* Construct a Database subclass instance given a database type and parameters
*
* This also connects to the database immediately upon object construction
*
* @param string $type A possible DB type (sqlite, mysql, postgres,...)
* @param array $params Parameter map with keys:
* - host : The hostname of the DB server
* - user : The name of the database user the client operates under
* - password : The password for the database user
* - dbname : The name of the database to use where queries do not specify one.
* The database must exist or an error might be thrown. Setting this to the empty string
* will avoid any such errors and make the handle have no implicit database scope. This is
* useful for queries like SHOW STATUS, CREATE DATABASE, or DROP DATABASE. Note that a
* "database" in Postgres is rougly equivalent to an entire MySQL server. This the domain
* in which user names and such are defined, e.g. users are database-specific in Postgres.
* - schema : The database schema to use (if supported). A "schema" in Postgres is roughly
* equivalent to a "database" in MySQL. Note that MySQL and SQLite do not use schemas.
* - tablePrefix : Optional table prefix that is implicitly added on to all table names
* recognized in queries. This can be used in place of schemas for handle site farms.
* - flags : Optional bit field of DBO_* constants that define connection, protocol,
* buffering, and transaction behavior. It is STRONGLY adviced to leave the DBO_DEFAULT
* flag in place UNLESS this this database simply acts as a key/value store.
* - driver: Optional name of a specific DB client driver. For MySQL, there is only the
* 'mysqli' driver; the old one 'mysql' has been removed.
* - variables: Optional map of session variables to set after connecting. This can be
* used to adjust lock timeouts or encoding modes and the like.
* - topologyRole: Optional IDatabase::ROLE_* constant for the server.
* - lbInfo: Optional map of field/values for the managing load balancer instance.
* The "master" and "replica" fields are used to flag the replication role of this
* database server and whether methods like getLag() should actually issue queries.
* - topologicalPrimaryConnRef: lazy-connecting IDatabase handle to the most authoritative
* primary database server for the cluster that this database belongs to. This handle is
* used for replication status purposes. This is generally managed by LoadBalancer.
* - connLogger: Optional PSR-3 logger interface instance.
* - queryLogger: Optional PSR-3 logger interface instance.
* - profiler : Optional callback that takes a section name argument and returns
* a ScopedCallback instance that ends the profile section in its destructor.
* These will be called in query(), using a simplified version of the SQL that
* also includes the agent as a SQL comment.
* - trxProfiler: Optional TransactionProfiler instance.
* - errorLogger: Optional callback that takes an Exception and logs it.
* - deprecationLogger: Optional callback that takes a string and logs it.
* - cliMode: Whether to consider the execution context that of a CLI script.
* - agent: Optional name used to identify the end-user in query profiling/logging.
* - serverName: Optional human-readable server name
* - srvCache: Optional BagOStuff instance to an APC-style cache.
* - nonNativeInsertSelectBatchSize: Optional batch size for non-native INSERT SELECT.
* @param int $connect One of the class constants (NEW_CONNECTED, NEW_UNCONNECTED) [optional]
* @return Database|null If the database driver or extension cannot be found
* @throws InvalidArgumentException If the database driver or extension cannot be found
*/
public function create( $type, $params = [], $connect = Database::NEW_CONNECTED ) {
$class = $this->getClass( $type, $params['driver'] ?? null );
if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) {
$params += [
// Default configuration
'host' => null,
'user' => null,
'password' => null,
'dbname' => null,
'schema' => null,
'tablePrefix' => '',
'flags' => 0,
'variables' => [],
'lbInfo' => [],
'cliMode' => ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ),
'agent' => '',
'serverName' => null,
'topologyRole' => null,
// Objects and callbacks
'topologicalPrimaryConnRef' => $params['topologicalPrimaryConnRef'] ?? null,
'srvCache' => $params['srvCache'] ?? new HashBagOStuff(),
'profiler' => $params['profiler'] ?? null,
'trxProfiler' => $params['trxProfiler'] ?? new TransactionProfiler(),
'connLogger' => $params['connLogger'] ?? new NullLogger(),
'queryLogger' => $params['queryLogger'] ?? new NullLogger(),
'replLogger' => $params['replLogger'] ?? new NullLogger(),
'errorLogger' => $params['errorLogger'] ?? static function ( Throwable $e ) {
trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
},
'deprecationLogger' => $params['deprecationLogger'] ?? static function ( $msg ) {
trigger_error( $msg, E_USER_DEPRECATED );
}
];
/** @var Database $conn */
$conn = new $class( $params );
if ( $connect === Database::NEW_CONNECTED ) {
$conn->initConnection();
}
} else {
$conn = null;
}
return $conn;
}
/**
* @param string $dbType A possible DB type (sqlite, mysql, postgres,...)
* @param string|null $driver Optional name of a specific DB client driver
* @return array Map of (Database::ATTR_* constant => value) for all such constants
* @throws DBUnexpectedError
*/
public function attributesFromType( $dbType, $driver = null ) {
static $defaults = [
Database::ATTR_DB_IS_FILE => false,
Database::ATTR_DB_LEVEL_LOCKING => false,
Database::ATTR_SCHEMAS_AS_TABLE_GROUPS => false
];
$class = $this->getClass( $dbType, $driver );
if ( class_exists( $class ) ) {
return call_user_func( [ $class, 'getAttributes' ] ) + $defaults;
} else {
throw new DBUnexpectedError( null, "$dbType is not a supported database type." );
}
}
/**
* @param string $dbType A possible DB type (sqlite, mysql, postgres,...)
* @param string|null $driver Optional name of a specific DB client driver
* @return string Database subclass name to use
* @throws InvalidArgumentException
*/
protected function getClass( $dbType, $driver = null ) {
// For database types with built-in support, the below maps type to IDatabase
// implementations. For types with multiple driver implementations (PHP extensions),
// an array can be used, keyed by extension name. In case of an array, the
// optional 'driver' parameter can be used to force a specific driver. Otherwise,
// we auto-detect the first available driver. For types without built-in support,
// an class named "Database<Type>" us used, eg. DatabaseFoo for type 'foo'.
static $builtinTypes = [
'mysql' => [ 'mysqli' => DatabaseMysqli::class ],
'sqlite' => DatabaseSqlite::class,
'postgres' => DatabasePostgres::class,
];
$dbType = strtolower( $dbType );
if ( !isset( $builtinTypes[$dbType] ) ) {
// Not a built in type, assume standard naming scheme
return 'Database' . ucfirst( $dbType );
}
$class = false;
$possibleDrivers = $builtinTypes[$dbType];
if ( is_string( $possibleDrivers ) ) {
$class = $possibleDrivers;
} elseif ( (string)$driver !== '' ) {
if ( !isset( $possibleDrivers[$driver] ) ) {
throw new InvalidArgumentException( __METHOD__ .
" type '$dbType' does not support driver '{$driver}'" );
}
$class = $possibleDrivers[$driver];
} else {
foreach ( $possibleDrivers as $posDriver => $possibleClass ) {
if ( extension_loaded( $posDriver ) ) {
$class = $possibleClass;
break;
}
}
}
if ( $class === false ) {
throw new InvalidArgumentException( __METHOD__ .
" no viable database extension found for type '$dbType'" );
}
return $class;
}
}

View file

@ -1182,7 +1182,7 @@ SQL;
return $row ? ( strtolower( $row->default_transaction_read_only ) === 'on' ) : false;
}
protected static function getAttributes() {
public static function getAttributes() {
return [ self::ATTR_SCHEMAS_AS_TABLE_GROUPS => true ];
}
}

View file

@ -104,7 +104,7 @@ class DatabaseSqlite extends Database {
);
}
protected static function getAttributes() {
public static function getAttributes() {
return [
self::ATTR_DB_IS_FILE => true,
self::ATTR_DB_LEVEL_LOCKING => true

View file

@ -47,6 +47,7 @@ interface ILBFactory {
* - cpStash: BagOStuff instance for ChronologyProtector store [optional]
* See [ChronologyProtector requirements](@ref ChronologyProtector-storage-requirements).
* - wanCache: WANObjectCache instance [optional]
* - databaseFactory: DatabaseFactory instance [optional]
* - cliMode: Whether the execution context is a CLI script. [optional]
* - maxLag: Try to avoid DB replicas with lag above this many seconds [optional]
* - profiler: Callback that takes a section name argument and returns

View file

@ -71,6 +71,8 @@ abstract class LBFactory implements ILBFactory {
protected $srvCache;
/** @var WANObjectCache */
protected $wanCache;
/** @var DatabaseFactory */
protected $databaseFactory;
/** @var DatabaseDomain Local domain */
protected $localDomain;
@ -169,6 +171,8 @@ abstract class LBFactory implements ILBFactory {
$this->srvCache = $conf['srvCache'] ?? new EmptyBagOStuff();
$this->wanCache = $conf['wanCache'] ?? WANObjectCache::newEmpty();
$this->databaseFactory = $conf['databaseFactory'] ?? new DatabaseFactory();
foreach ( self::$loggerFields as $key ) {
$this->$key = $conf[ $key ] ?? new NullLogger();
}
@ -737,6 +741,7 @@ abstract class LBFactory implements ILBFactory {
'readOnlyReason' => $this->readOnlyReason,
'srvCache' => $this->srvCache,
'wanCache' => $this->wanCache,
'databaseFactory' => $this->databaseFactory,
'profiler' => $this->profiler,
'trxProfiler' => $this->trxProfiler,
'queryLogger' => $this->queryLogger,

View file

@ -46,6 +46,7 @@ interface ILoadBalancerForOwner extends ILoadBalancer {
* - maxLag: Try to avoid DB replicas with lag above this many seconds [optional]
* - srvCache : BagOStuff object for server cache [optional]
* - wanCache : WANObjectCache object [optional]
* - databaseFactory: DatabaseFactory object [optional]
* - chronologyCallback: Callback to run before the first connection attempt [optional]
* - defaultGroup: Default query group; the generic group if not specified [optional]
* - hostname : The name of the current server [optional]

View file

@ -50,6 +50,8 @@ class LoadBalancer implements ILoadBalancerForOwner {
private $srvCache;
/** @var WANObjectCache */
private $wanCache;
/** @var DatabaseFactory */
private $databaseFactory;
/**
* @var callable|null An optional callback that returns a ScopedCallback instance,
* meant to profile the actual query execution in {@see Database::doQuery}
@ -234,6 +236,7 @@ class LoadBalancer implements ILoadBalancerForOwner {
$this->srvCache = $params['srvCache'] ?? new EmptyBagOStuff();
$this->wanCache = $params['wanCache'] ?? WANObjectCache::newEmpty();
$this->databaseFactory = $params['databaseFactory'] ?? new DatabaseFactory();
$this->errorLogger = $params['errorLogger'] ?? static function ( Throwable $e ) {
trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
};
@ -1288,7 +1291,7 @@ class LoadBalancer implements ILoadBalancerForOwner {
}
public function getServerAttributes( $i ) {
return Database::attributesFromType(
return $this->databaseFactory->attributesFromType(
$this->getServerType( $i ),
$this->servers[$i]['driver'] ?? null
);
@ -1323,7 +1326,7 @@ class LoadBalancer implements ILoadBalancerForOwner {
$server = $this->getServerInfoStrict( $i );
$conn = Database::factory(
$conn = $this->databaseFactory->create(
$server['type'],
array_merge( $server, [
// Basic replication role information

View file

@ -0,0 +1,42 @@
<?php
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\DatabaseFactory;
use Wikimedia\Rdbms\DatabaseMysqli;
use Wikimedia\Rdbms\DatabasePostgres;
use Wikimedia\Rdbms\DatabaseSqlite;
/**
* @covers Wikimedia\Rdbms\DatabaseFactory
*/
class DatabaseFactoryTest extends PHPUnit\Framework\TestCase {
use MediaWikiCoversValidator;
public function testFactory() {
$factory = new DatabaseFactory();
$m = Database::NEW_UNCONNECTED; // no-connect mode
$p = [
'host' => 'localhost',
'serverName' => 'localdb',
'user' => 'me',
'password' => 'myself',
'dbname' => 'i'
];
$this->assertInstanceOf( DatabaseMysqli::class, $factory->create( 'mysqli', $p, $m ) );
$this->assertInstanceOf( DatabaseMysqli::class, $factory->create( 'MySqli', $p, $m ) );
$this->assertInstanceOf( DatabaseMysqli::class, $factory->create( 'MySQLi', $p, $m ) );
$this->assertInstanceOf( DatabasePostgres::class, $factory->create( 'postgres', $p, $m ) );
$this->assertInstanceOf( DatabasePostgres::class, $factory->create( 'Postgres', $p, $m ) );
$x = $p + [ 'dbFilePath' => 'some/file.sqlite' ];
$this->assertInstanceOf( DatabaseSqlite::class, $factory->create( 'sqlite', $x, $m ) );
$x = $p + [ 'dbDirectory' => 'some/file' ];
$this->assertInstanceOf( DatabaseSqlite::class, $factory->create( 'sqlite', $x, $m ) );
$conn = $factory->create( 'sqlite', $p, $m );
$this->assertEquals( 'localhost', $conn->getServer() );
$this->assertEquals( 'localdb', $conn->getServerName() );
}
}

View file

@ -20,6 +20,7 @@
use MediaWiki\Config\ServiceOptions;
use Wikimedia\Rdbms\DatabaseDomain;
use Wikimedia\Rdbms\DatabaseFactory;
use Wikimedia\Rdbms\LBFactorySimple;
use Wikimedia\RequestTimeout\CriticalSectionProvider;
use Wikimedia\RequestTimeout\RequestTimeout;
@ -39,7 +40,8 @@ class MWLBFactoryTest extends MediaWikiUnitTestCase {
new EmptyBagOStuff(),
new WANObjectCache( [ 'cache' => new EmptyBagOStuff() ] ),
new CriticalSectionProvider( RequestTimeout::singleton(), 1, null, null ),
new NullStatsdDataFactory()
new NullStatsdDataFactory(),
new DatabaseFactory()
);
}

View file

@ -5,8 +5,6 @@ use MediaWiki\Tests\Unit\Libs\Rdbms\SQLPlatformTestHelper;
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\DatabaseDomain;
use Wikimedia\Rdbms\DatabaseMysqli;
use Wikimedia\Rdbms\DatabasePostgres;
use Wikimedia\Rdbms\DatabaseSqlite;
use Wikimedia\Rdbms\DBReadOnlyRoleError;
use Wikimedia\Rdbms\DBTransactionStateError;
use Wikimedia\Rdbms\DBUnexpectedError;
@ -30,35 +28,6 @@ class DatabaseTest extends PHPUnit\Framework\TestCase {
$this->db = new DatabaseTestHelper( __CLASS__ . '::' . $this->getName() );
}
/**
* @covers Wikimedia\Rdbms\Database::factory
*/
public function testFactory() {
$m = Database::NEW_UNCONNECTED; // no-connect mode
$p = [
'host' => 'localhost',
'serverName' => 'localdb',
'user' => 'me',
'password' => 'myself',
'dbname' => 'i'
];
$this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'mysqli', $p, $m ) );
$this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySqli', $p, $m ) );
$this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySQLi', $p, $m ) );
$this->assertInstanceOf( DatabasePostgres::class, Database::factory( 'postgres', $p, $m ) );
$this->assertInstanceOf( DatabasePostgres::class, Database::factory( 'Postgres', $p, $m ) );
$x = $p + [ 'dbFilePath' => 'some/file.sqlite' ];
$this->assertInstanceOf( DatabaseSqlite::class, Database::factory( 'sqlite', $x, $m ) );
$x = $p + [ 'dbDirectory' => 'some/file' ];
$this->assertInstanceOf( DatabaseSqlite::class, Database::factory( 'sqlite', $x, $m ) );
$conn = Database::factory( 'sqlite', $p, $m );
$this->assertEquals( 'localhost', $conn->getServer() );
$this->assertEquals( 'localdb', $conn->getServerName() );
}
public static function provideAddQuotes() {
return [
[ null, 'NULL' ],