From: Aaron Schulz Date: Thu, 15 Sep 2016 17:07:47 +0000 (-0700) Subject: Dependency inject $wgSharedTables into DatabaseBase X-Git-Tag: 1.31.0-rc.0~5549^2 X-Git-Url: https://git.cyclocoop.org/%28%28?a=commitdiff_plain;h=13384d9988dd2a484d4650a4939f2817951735a8;p=lhc%2Fweb%2Fwiklou.git Dependency inject $wgSharedTables into DatabaseBase Change-Id: I3e659b56c587d2059fc3d3f922bc38dec7685df4 --- diff --git a/includes/Setup.php b/includes/Setup.php index 2f462b8b5d..ddf5b893c4 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -504,6 +504,19 @@ if ( !class_exists( 'AutoLoader' ) ) { // Reset the global service locator, so any services that have already been created will be // re-created while taking into account any custom settings and extensions. MediaWikiServices::resetGlobalInstance( new GlobalVarConfig(), 'quick' ); +// Apply $wgSharedDB table aliases for the local LB (all non-foreign DB connections) +if ( $wgSharedDB && $wgSharedTables ) { + MediaWikiServices::getInstance()->getDBLoadBalancer()->setTableAliases( + array_fill_keys( + $wgSharedTables, + [ + 'dbname' => $wgSharedDB, + 'schema' => $wgSharedSchema, + 'prefix' => $wgSharedPrefix + ] + ) + ); +} // Define a constant that indicates that the bootstrapping of the service locator // is complete. diff --git a/includes/db/Database.php b/includes/db/Database.php index 917bc910dc..5182fced52 100644 --- a/includes/db/Database.php +++ b/includes/db/Database.php @@ -60,6 +60,8 @@ abstract class DatabaseBase implements IDatabase, LoggerAwareInterface { protected $mPassword; /** @var string */ protected $mDBname; + /** @var array[] $aliases Map of (table => (dbname, schema, prefix) map) */ + protected $tableAliases = []; /** @var bool */ protected $cliMode; @@ -94,8 +96,6 @@ abstract class DatabaseBase implements IDatabase, LoggerAwareInterface { protected $mSchema; /** @var integer */ protected $mFlags; - /** @var bool */ - protected $mForeign; /** @var array */ protected $mLBInfo = []; /** @var bool|null */ @@ -263,8 +263,6 @@ abstract class DatabaseBase implements IDatabase, LoggerAwareInterface { $this->mSessionVars = $params['variables']; - $this->mForeign = $params['foreign']; - $this->srvCache = isset( $params['srvCache'] ) ? $params['srvCache'] : new EmptyBagOStuff(); @@ -1862,7 +1860,6 @@ abstract class DatabaseBase implements IDatabase, LoggerAwareInterface { * @return string Full database name */ public function tableName( $name, $format = 'quoted' ) { - global $wgSharedDB, $wgSharedPrefix, $wgSharedTables, $wgSharedSchema; # Skip the entire process when we have a string quoted on both ends. # Note that we check the end so that we will still quote any use of # use of `database`.table. But won't break things if someone wants @@ -1899,14 +1896,14 @@ abstract class DatabaseBase implements IDatabase, LoggerAwareInterface { $schema = null; } else { list( $table ) = $dbDetails; - if ( $wgSharedDB !== null # We have a shared database - && $this->mForeign == false # We're not working on a foreign database - && !$this->isQuotedIdentifier( $table ) # Prevent shared tables listing '`table`' - && in_array( $table, $wgSharedTables ) # A shared table is selected - ) { - $database = $wgSharedDB; - $schema = $wgSharedSchema === null ? $this->mSchema : $wgSharedSchema; - $prefix = $wgSharedPrefix === null ? $this->mTablePrefix : $wgSharedPrefix; + if ( isset( $this->tableAliases[$table] ) ) { + $database = $this->tableAliases[$table]['dbname']; + $schema = is_string( $this->tableAliases[$table]['schema'] ) + ? $this->tableAliases[$table]['schema'] + : $this->mSchema; + $prefix = is_string( $this->tableAliases[$table]['prefix'] ) + ? $this->tableAliases[$table]['prefix'] + : $this->mTablePrefix; } else { $database = null; $schema = $this->mSchema; # Default schema @@ -1917,7 +1914,9 @@ abstract class DatabaseBase implements IDatabase, LoggerAwareInterface { # Quote $table and apply the prefix if not quoted. # $tableName might be empty if this is called from Database::replaceVars() $tableName = "{$prefix}{$table}"; - if ( $format == 'quoted' && !$this->isQuotedIdentifier( $tableName ) && $tableName !== '' ) { + if ( $format == 'quoted' + && !$this->isQuotedIdentifier( $tableName ) && $tableName !== '' + ) { $tableName = $this->addIdentifierQuotes( $tableName ); } @@ -3686,6 +3685,10 @@ abstract class DatabaseBase implements IDatabase, LoggerAwareInterface { return is_string( $reason ) ? $reason : false; } + public function setTableAliases( array $aliases ) { + $this->tableAliases = $aliases; + } + /** * @since 1.19 * @return string diff --git a/includes/db/DatabaseSqlite.php b/includes/db/DatabaseSqlite.php index 3c0c2797b3..0cbb49633a 100644 --- a/includes/db/DatabaseSqlite.php +++ b/includes/db/DatabaseSqlite.php @@ -59,7 +59,7 @@ class DatabaseSqlite extends Database { * @param array $p */ function __construct( array $p ) { - global $wgSharedDB, $wgSQLiteDataDir; + global $wgSQLiteDataDir; $this->dbDir = isset( $p['dbDirectory'] ) ? $p['dbDirectory'] : $wgSQLiteDataDir; @@ -76,8 +76,13 @@ class DatabaseSqlite extends Database { // Super doesn't open when $user is false, but we can work with $dbName if ( $p['dbname'] && !$this->isOpen() ) { if ( $this->open( $p['host'], $p['user'], $p['password'], $p['dbname'] ) ) { - if ( $wgSharedDB ) { - $this->attachDatabase( $wgSharedDB ); + $done = []; + foreach ( $this->tableAliases as $params ) { + if ( isset( $done[$params['dbname']] ) ) { + continue; + } + $this->attachDatabase( $params['dbname'] ); + $done[$params['dbname']] = 1; } } } diff --git a/includes/libs/rdbms/database/DBConnRef.php b/includes/libs/rdbms/database/DBConnRef.php index 9035bbd5c9..3a18fc0b78 100644 --- a/includes/libs/rdbms/database/DBConnRef.php +++ b/includes/libs/rdbms/database/DBConnRef.php @@ -574,6 +574,10 @@ class DBConnRef implements IDatabase { return $this->__call( __FUNCTION__, func_get_args() ); } + public function setTableAliases( array $aliases ) { + return $this->__call( __FUNCTION__, func_get_args() ); + } + /** * Clean up the connection when out of scope */ diff --git a/includes/libs/rdbms/database/IDatabase.php b/includes/libs/rdbms/database/IDatabase.php index 671ce993c9..026cbc566c 100644 --- a/includes/libs/rdbms/database/IDatabase.php +++ b/includes/libs/rdbms/database/IDatabase.php @@ -1719,4 +1719,19 @@ interface IDatabase { * @since 1.27 */ public function isReadOnly(); + + /** + * Make certain table names use their own database, schema, and table prefix + * when passed into SQL queries pre-escaped and without a qualified database name + * + * For example, "user" can be converted to "myschema.mydbname.user" for convenience. + * Appearances like `user`, somedb.user, somedb.someschema.user will used literally. + * + * Calling this twice will completely clear any old table aliases. Also, note that + * callers are responsible for making sure the schemas and databases actually exist. + * + * @param array[] $aliases Map of (table => (dbname, schema, prefix) map) + * @since 1.28 + */ + public function setTableAliases( array $aliases ); } diff --git a/includes/libs/rdbms/loadbalancer/LoadBalancer.php b/includes/libs/rdbms/loadbalancer/LoadBalancer.php index 824279dc8b..cea7523ba5 100644 --- a/includes/libs/rdbms/loadbalancer/LoadBalancer.php +++ b/includes/libs/rdbms/loadbalancer/LoadBalancer.php @@ -42,6 +42,8 @@ class LoadBalancer implements ILoadBalancer { private $mWaitTimeout; /** @var string The LoadMonitor subclass name */ private $mLoadMonitorClass; + /** @var array[] $aliases Map of (table => (dbname, schema, prefix) map) */ + private $tableAliases = []; /** @var LoadMonitor */ private $mLoadMonitor; @@ -822,6 +824,7 @@ class LoadBalancer implements ILoadBalancer { $db->setLazyMasterHandle( $this->getLazyConnectionRef( DB_MASTER, [], $db->getWikiID() ) ); + $db->setTableAliases( $this->tableAliases ); if ( $server['serverIndex'] === $this->getWriterIndex() ) { if ( $this->trxRoundId !== false ) { @@ -1570,6 +1573,23 @@ class LoadBalancer implements ILoadBalancer { ); } + /** + * Make certain table names use their own database, schema, and table prefix + * when passed into SQL queries pre-escaped and without a qualified database name + * + * For example, "user" can be converted to "myschema.mydbname.user" for convenience. + * Appearances like `user`, somedb.user, somedb.someschema.user will used literally. + * + * Calling this twice will completely clear any old table aliases. Also, note that + * callers are responsible for making sure the schemas and databases actually exist. + * + * @param array[] $aliases Map of (table => (dbname, schema, prefix) map) + * @since 1.28 + */ + public function setTableAliases( array $aliases ) { + $this->tableAliases = $aliases; + } + /** * Set a new table prefix for the existing local domain ID for testing * diff --git a/tests/phpunit/includes/db/DatabaseTest.php b/tests/phpunit/includes/db/DatabaseTest.php index 0f9a401cd4..e884640fbc 100644 --- a/tests/phpunit/includes/db/DatabaseTest.php +++ b/tests/phpunit/includes/db/DatabaseTest.php @@ -68,21 +68,26 @@ class DatabaseTest extends MediaWikiTestCase { } private function getSharedTableName( $table, $database, $prefix, $format = 'quoted' ) { - global $wgSharedDB, $wgSharedTables, $wgSharedPrefix; + global $wgSharedDB, $wgSharedTables, $wgSharedPrefix, $wgSharedSchema; - $oldName = $wgSharedDB; - $oldTables = $wgSharedTables; - $oldPrefix = $wgSharedPrefix; - - $wgSharedDB = $database; - $wgSharedTables = [ $table ]; - $wgSharedPrefix = $prefix; + $this->db->setTableAliases( [ + $table => [ + 'dbname' => $database, + 'schema' => null, + 'prefix' => $prefix + ] + ] ); $ret = $this->db->tableName( $table, $format ); - $wgSharedDB = $oldName; - $wgSharedTables = $oldTables; - $wgSharedPrefix = $oldPrefix; + $this->db->setTableAliases( array_fill_keys( + $wgSharedDB ? $wgSharedTables : [], + [ + 'dbname' => $wgSharedDB, + 'schema' => $wgSharedSchema, + 'prefix' => $wgSharedPrefix + ] + ) ); return $ret; }