}
/**
- * @param string|Blob $s
- * @return string
+ * @param string|int|null|bool|Blob $s
+ * @return string|int
*/
public function addQuotes( $s ) {
if ( $s instanceof MssqlBlob ) {
/** @var array Map of (name => 1) for locks obtained via lock() */
private $mNamedLocksHeld = [];
/** @var array Map of (table name => 1) for TEMPORARY tables */
- private $mSessionTempTables = [];
+ protected $mSessionTempTables = [];
/** @var IDatabase|null Lazy handle to the master DB this server replicates from */
private $lazyMasterHandle;
*/
protected function registerTempTableOperation( $sql ) {
if ( preg_match(
- '/^(CREATE|DROP)\s+TEMPORARY\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"\']?(\w+)[`"\']?/i',
+ '/^CREATE\s+TEMPORARY\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"\']?(\w+)[`"\']?/i',
$sql,
$matches
) ) {
- list( , $verb, $table ) = $matches;
- if ( $verb === 'CREATE' ) {
- $this->mSessionTempTables[$table] = 1;
- } else {
- unset( $this->mSessionTempTables[$table] );
- }
+ $this->mSessionTempTables[$matches[1]] = 1;
+
+ return true;
+ } elseif ( preg_match(
+ '/^DROP\s+(?:TEMPORARY\s+)?TABLE\s+(?:IF\s+EXISTS\s+)?[`"\']?(\w+)[`"\']?/i',
+ $sql,
+ $matches
+ ) ) {
+ unset( $this->mSessionTempTables[$matches[1]] );
return true;
} elseif ( preg_match(
}
public function tableExists( $table, $fname = __METHOD__ ) {
+ $tableRaw = $this->tableName( $table, 'raw' );
+ if ( isset( $this->mSessionTempTables[$tableRaw] ) ) {
+ return true; // already known to exist
+ }
+
$table = $this->tableName( $table );
$old = $this->ignoreErrors( true );
$res = $this->query( "SELECT 1 FROM $table LIMIT 1", $fname );
}
if ( $s === null ) {
return 'NULL';
+ } elseif ( is_bool( $s ) ) {
+ return (int)$s;
} else {
# This will also quote numeric values. This should be harmless,
# and protects against weird problems that occur when they really
}
function tableExists( $table, $fname = __METHOD__ ) {
+ $table = $this->tableName( $table, 'raw' );
+ if ( isset( $this->mSessionTempTables[$table] ) ) {
+ return true; // already known to exist and won't show in SHOW TABLES anyway
+ }
+
$encLike = $this->buildLike( $table );
return $this->query( "SHOW TABLES $encLike", $fname )->numRows() > 0;
* @throws DBConnectionError
*/
protected function mysqlConnect( $realServer ) {
- global $wgDBmysql5;
-
# Avoid suppressed fatal error, which is very hard to track down
if ( !function_exists( 'mysqli_init' ) ) {
throw new DBConnectionError( $this, "MySQLi functions missing,"
$realServer = 'p:' . $realServer;
}
- if ( $wgDBmysql5 ) {
+ if ( $this->utf8Mode ) {
// Tell the server we're communicating with it in UTF-8.
// This may engage various charset conversions.
$mysqli->options( MYSQLI_SET_CHARSET_NAME, 'utf8' );
}
/**
- * @param null|bool|Blob $s
- * @return int|string
+ * @param string|int|null|bool|Blob $s
+ * @return string|int
*/
function addQuotes( $s ) {
if ( is_null( $s ) ) {
}
/**
- * @param Blob|string $s
- * @return string
+ * @param string|int|null|bool|Blob $s
+ * @return string|int
*/
function addQuotes( $s ) {
if ( $s instanceof Blob ) {
/**
* Adds quotes and backslashes.
*
- * @param string|Blob $s
- * @return string
+ * @param string|int|null|bool|Blob $s
+ * @return string|int
*/
public function addQuotes( $s );
* This makes sense when lag being waiting on is caused by the code that does this check.
* In that case, setting "ifWritesSince" can avoid the overhead of waiting for clusters
* that were not changed since the last wait check. To forcefully wait on a specific cluster
- * for a given wiki, use the 'wiki' parameter. To forcefully wait on an "external" cluster,
+ * for a given domain, use the 'domain' parameter. To forcefully wait on an "external" cluster,
* use the "cluster" parameter.
*
* Never call this function after a large DB write that is *still* in a transaction.
* It only makes sense to call this after the possible lag inducing changes were committed.
*
* @param array $opts Optional fields that include:
- * - wiki : wait on the load balancer DBs that handles the given wiki
+ * - domain : wait on the load balancer DBs that handles the given domain ID
* - cluster : wait on the given external load balancer DBs
* - timeout : Max wait time. Default: ~60 seconds
* - ifWritesSince: Only wait if writes were done since this UNIX timestamp
*/
public function waitForReplication( array $opts = [] ) {
$opts += [
- 'wiki' => false,
+ 'domain' => false,
'cluster' => false,
'timeout' => 60,
'ifWritesSince' => null
];
+ if ( $opts['domain'] === false && isset( $opts['wiki'] ) ) {
+ $opts['domain'] = $opts['wiki']; // b/c
+ }
+
// Figure out which clusters need to be checked
/** @var ILoadBalancer[] $lbs */
$lbs = [];
if ( $opts['cluster'] !== false ) {
$lbs[] = $this->getExternalLB( $opts['cluster'] );
- } elseif ( $opts['wiki'] !== false ) {
- $lbs[] = $this->getMainLB( $opts['wiki'] );
+ } elseif ( $opts['domain'] !== false ) {
+ $lbs[] = $this->getMainLB( $opts['domain'] );
} else {
$this->forEachLB( function ( ILoadBalancer $lb ) use ( &$lbs ) {
$lbs[] = $lb;
$this->mReadIndex = -1;
$this->mConns = [
- 'local' => [],
+ 'local' => [],
'foreignUsed' => [],
- 'foreignFree' => [] ];
+ 'foreignFree' => []
+ ];
$this->mLoads = [];
$this->mWaitForPos = false;
$this->mErrorConnection = false;
return;
}
- $dbName = $conn->getDBname();
- $prefix = $conn->tablePrefix();
- if ( strval( $prefix ) !== '' ) {
- $domain = "$dbName-$prefix";
- } else {
- $domain = $dbName;
- }
+ $domain = $conn->getDomainID();
if ( $this->mConns['foreignUsed'][$serverIndex][$domain] !== $conn ) {
- throw new InvalidArgumentException( __METHOD__ . ": connection not found, has " .
- "the connection been freed already?" );
+ throw new InvalidArgumentException( __METHOD__ .
+ ": connection not found, has the connection been freed already?" );
}
$conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
if ( $refCount <= 0 ) {
$this->mConns['foreignFree'][$serverIndex][$domain] = $conn;
unset( $this->mConns['foreignUsed'][$serverIndex][$domain] );
+ if ( !$this->mConns['foreignUsed'][$serverIndex] ) {
+ unset( $this->mConns[ 'foreignUsed' ][$serverIndex] ); // clean up
+ }
$this->connLogger->debug( __METHOD__ . ": freed connection $serverIndex/$domain" );
} else {
$this->connLogger->debug( __METHOD__ .
// Reuse a connection from another domain
$conn = reset( $this->mConns['foreignFree'][$i] );
$oldDomain = key( $this->mConns['foreignFree'][$i] );
-
// The empty string as a DB name means "don't care".
// DatabaseMysqlBase::open() already handle this on connection.
- if ( $dbName !== '' && !$conn->selectDB( $dbName ) ) {
- $this->mLastError = "Error selecting database $dbName on server " .
+ if ( strlen( $dbName ) && !$conn->selectDB( $dbName ) ) {
+ $this->mLastError = "Error selecting database '$dbName' on server " .
$conn->getServer() . " from client host {$this->host}";
$this->mErrorConnection = $conn;
$conn = false;
* @access private
*
* @param array $server
- * @param bool $dbNameOverride
+ * @param string|bool $dbNameOverride Use "" to not select any database
* @return IDatabase
* @throws DBAccessError
* @throws InvalidArgumentException
}
public function setDomainPrefix( $prefix ) {
+ if ( $this->mConns['foreignUsed'] ) {
+ // Do not switch connections to explicit foreign domains unless marked as free
+ $domains = [];
+ foreach ( $this->mConns['foreignUsed'] as $i => $connsByDomain ) {
+ $domains = array_merge( $domains, array_keys( $connsByDomain ) );
+ }
+ $domains = implode( ', ', $domains );
+ throw new DBUnexpectedError( null,
+ "Foreign domain connections are still in use ($domains)." );
+ }
+
$this->localDomain = new DatabaseDomain(
$this->localDomain->getDatabase(),
null,
direction: ltr;
}
-/* tooltip styles */
-.mw-help-field-hint {
- display: none;
- margin-left: 2px;
- margin-bottom: -8px;
- padding: 0 0 0 15px;
- background-image: url( images/help-question.gif );
- background-position: left center;
- background-repeat: no-repeat;
- cursor: pointer;
- font-size: .8em;
- text-decoration: underline;
- color: #0645ad;
-}
-
-.mw-help-field-hint:hover {
- background-image: url( images/help-question-hover.gif );
-}
-
-.mw-help-field-data {
- display: block;
- background-color: #d6f3ff;
- padding: 5px 8px 4px 8px;
- border: 1px solid #5dc9f4;
- margin-left: 20px;
-}
-
#mw-clearyourcache,
#mw-sitecsspreview,
#mw-sitejspreview,
this[ this.needToken() ? 'postWithEditToken' : 'post' ]( data, {
// Use FormData (if we got here, we know that it's available)
contentType: 'multipart/form-data',
+ // No timeout (default from mw.Api is 30 seconds)
+ timeout: 0,
// Provide upload progress notifications
xhr: function () {
var xhr = $.ajaxSettings.xhr();
],
];
}
+
+ public function testSessionTempTables() {
+ $temp1 = $this->database->tableName( 'tmp_table_1' );
+ $temp2 = $this->database->tableName( 'tmp_table_2' );
+ $temp3 = $this->database->tableName( 'tmp_table_3' );
+
+ $this->database->query( "CREATE TEMPORARY TABLE $temp1 LIKE orig_tbl", __METHOD__ );
+ $this->database->query( "CREATE TEMPORARY TABLE $temp2 LIKE orig_tbl", __METHOD__ );
+ $this->database->query( "CREATE TEMPORARY TABLE $temp3 LIKE orig_tbl", __METHOD__ );
+
+ $this->assertTrue( $this->database->tableExists( "tmp_table_1", __METHOD__ ) );
+ $this->assertTrue( $this->database->tableExists( "tmp_table_2", __METHOD__ ) );
+ $this->assertTrue( $this->database->tableExists( "tmp_table_3", __METHOD__ ) );
+
+ $this->database->dropTable( 'tmp_table_1', __METHOD__ );
+ $this->database->dropTable( 'tmp_table_2', __METHOD__ );
+ $this->database->dropTable( 'tmp_table_3', __METHOD__ );
+
+ $this->assertFalse( $this->database->tableExists( "tmp_table_1", __METHOD__ ) );
+ $this->assertFalse( $this->database->tableExists( "tmp_table_2", __METHOD__ ) );
+ $this->assertFalse( $this->database->tableExists( "tmp_table_3", __METHOD__ ) );
+
+ $this->database->query( "CREATE TEMPORARY TABLE tmp_table_1 LIKE orig_tbl", __METHOD__ );
+ $this->database->query( "CREATE TEMPORARY TABLE 'tmp_table_2' LIKE orig_tbl", __METHOD__ );
+ $this->database->query( "CREATE TEMPORARY TABLE `tmp_table_3` LIKE orig_tbl", __METHOD__ );
+
+ $this->assertTrue( $this->database->tableExists( "tmp_table_1", __METHOD__ ) );
+ $this->assertTrue( $this->database->tableExists( "tmp_table_2", __METHOD__ ) );
+ $this->assertTrue( $this->database->tableExists( "tmp_table_3", __METHOD__ ) );
+
+ $this->database->query( "DROP TEMPORARY TABLE tmp_table_1 LIKE orig_tbl", __METHOD__ );
+ $this->database->query( "DROP TEMPORARY TABLE 'tmp_table_2' LIKE orig_tbl", __METHOD__ );
+ $this->database->query( "DROP TABLE `tmp_table_3` LIKE orig_tbl", __METHOD__ );
+
+ $this->assertFalse( $this->database->tableExists( "tmp_table_1", __METHOD__ ) );
+ $this->assertFalse( $this->database->tableExists( "tmp_table_2", __METHOD__ ) );
+ $this->assertFalse( $this->database->tableExists( "tmp_table_3", __METHOD__ ) );
+ }
}
}
public function tableExists( $table, $fname = __METHOD__ ) {
+ $tableRaw = $this->tableName( $table, 'raw' );
+ if ( isset( $this->mSessionTempTables[$tableRaw] ) ) {
+ return true; // already known to exist
+ }
+
$this->checkFunctionName( $fname );
return in_array( $table, (array)$this->tablesExists );
/** @var DatabaseBase $db */
$db = $lb->getConnection( DB_MASTER, [], '' );
+ $lb->reuseConnection( $db ); // don't care
$this->assertEquals(
'',
$lb = $factory->getMainLB();
/** @var DatabaseBase $db */
$db = $lb->getConnection( DB_MASTER, [], '' );
+ $lb->reuseConnection( $db ); // don't care
$this->assertEquals(
'',