wiki.techinc.nl/tests/phpunit/integration/includes/db/DatabaseMysqlTest.php
Dreamy Jazz 2e9414bd80 Fix inconsistently failing testQueryTimeout test
Why:
* DatabaseMysqlTest::testQueryTimeout when the query does not
  throw an exception, will instead expect that the result has one
  row with a single item of '1'.
* However, this check is done against the integer which does not
  match when ::assertSame is used.
* Converting both the expected and actual values to a string
  should avoid this.

What:
* Do this fix.

Bug: T365596
Change-Id: I21ce8b6014ee66795c2555f8a0eae2e4657f844e
2024-08-09 18:27:48 +00:00

628 lines
20 KiB
PHP

<?php
use MediaWiki\MediaWikiServices;
use Wikimedia\Rdbms\ChangedTablesTracker;
use Wikimedia\Rdbms\DatabaseMySQL;
use Wikimedia\Rdbms\DBQueryDisconnectedError;
use Wikimedia\Rdbms\DBQueryError;
use Wikimedia\Rdbms\DBQueryTimeoutError;
use Wikimedia\Rdbms\DBSessionStateError;
use Wikimedia\Rdbms\DBTransactionStateError;
use Wikimedia\Rdbms\DBUnexpectedError;
use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\Platform\ISQLPlatform;
use Wikimedia\Rdbms\Query;
use Wikimedia\Rdbms\TransactionManager;
/**
* @covers \Wikimedia\Rdbms\Database
* @covers \Wikimedia\Rdbms\DatabaseMySQL
* @covers \Wikimedia\Rdbms\Platform\MySQLPlatform
* @group mysql
* @group Database
* @group medium
* @requires extension mysqli
*/
class DatabaseMysqlTest extends \MediaWikiIntegrationTestCase {
/** @var DatabaseMySQL */
protected $conn;
protected function setUp(): void {
parent::setUp();
$lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
if ( $lb->getServerType( 0 ) !== 'mysql' ) {
$this->markTestSkipped( 'No MySQL $wgLBFactoryConf config detected' );
}
$this->conn = $this->newConnection();
// FIXME: Tables used by this test aren't parsed correctly, see T344510.
ChangedTablesTracker::stopTracking();
}
protected function tearDown(): void {
if ( $this->conn ) {
$this->conn->close( __METHOD__ );
ChangedTablesTracker::startTracking();
}
parent::tearDown();
}
public function testQueryTimeout() {
try {
$res = $this->conn->query(
'SET STATEMENT max_statement_time=0.001 FOR SELECT sleep(1) FROM dual',
__METHOD__
);
// if the query did not time out, there should be a single row where sleep() returned 1
$this->assertSame( 1, $res->numRows() );
$row = $res->fetchRow();
$this->assertSame( '1', (string)reset( $row ) );
} catch ( DBQueryTimeoutError $e ) {
$this->assertInstanceOf( DBQueryTimeoutError::class, $e );
}
$row = $this->conn->query( 'SELECT "x" AS v', __METHOD__ )->fetchObject();
$this->assertSame( 'x', $row->v, "Still connected/usable" );
}
public function testConnectionKill() {
try {
$this->conn->query( 'KILL (SELECT connection_id())', __METHOD__ );
$this->fail( "No DBQueryDisconnectedError caught" );
} catch ( DBQueryDisconnectedError $e ) {
$this->assertInstanceOf( DBQueryDisconnectedError::class, $e );
}
$row = $this->conn->query( 'SELECT "x" AS v', __METHOD__ )->fetchObject();
$this->assertSame( 'x', $row->v, "Recovered" );
}
public function testConnectionLossQuery() {
$row = $this->conn->query( 'SELECT connection_id() AS id', __METHOD__ )->fetchObject();
$encId = intval( $row->id );
$adminConn = $this->newConnection();
$adminConn->query( "KILL $encId", __METHOD__ );
$row = $this->conn->query( 'SELECT "x" AS v', __METHOD__ )->fetchObject();
$this->assertSame( 'x', $row->v, "Recovered" );
$this->conn->startAtomic( __METHOD__ );
$this->assertSame( 1, $this->conn->trxLevel(), "Transaction exists" );
$row = $this->conn->query( 'SELECT connection_id() AS id', __METHOD__ )->fetchObject();
$encId = intval( $row->id );
$adminConn->query( "KILL $encId", __METHOD__ );
try {
$this->conn->query( 'SELECT 1', __METHOD__ );
$this->fail( "No DBQueryDisconnectedError caught" );
} catch ( DBQueryDisconnectedError $e ) {
$this->assertTrue( $this->conn->isOpen(), "Reconnected" );
try {
$this->conn->endAtomic( __METHOD__ );
$this->fail( "No DBUnexpectedError caught" );
} catch ( DBUnexpectedError $e ) {
$this->assertInstanceOf( DBUnexpectedError::class, $e );
}
$this->assertSame( TransactionManager::STATUS_TRX_ERROR, $this->conn->trxStatus() );
try {
$this->conn->query( 'SELECT "x" AS v', __METHOD__ )->fetchObject();
$this->fail( "No DBTransactionStateError caught" );
} catch ( DBTransactionStateError $e ) {
$this->assertInstanceOf( DBTransactionStateError::class, $e );
}
$this->assertSame( 0, $this->conn->trxLevel(), "Transaction lost" );
$this->conn->rollback( __METHOD__ );
$this->assertSame( 0, $this->conn->trxLevel(), "No transaction" );
$row = $this->conn->query( 'SELECT "x" AS v', __METHOD__ )->fetchObject();
$this->assertSame( 'x', $row->v, "Recovered" );
}
$this->conn->lock( 'session_lock_' . mt_rand(), __METHOD__, 0 );
$row = $this->conn->query( 'SELECT connection_id() AS id', __METHOD__ )->fetchObject();
$encId = intval( $row->id );
$adminConn->query( "KILL $encId", __METHOD__ );
try {
$this->conn->query( 'SELECT 1', __METHOD__ );
$this->fail( "No DBQueryDisconnectedError caught" );
} catch ( DBQueryDisconnectedError $e ) {
$this->assertInstanceOf( DBQueryDisconnectedError::class, $e );
}
$this->assertTrue( $this->conn->isOpen(), "Reconnected" );
try {
$this->conn->query( 'SELECT "x" AS v', __METHOD__ )->fetchObject();
$this->fail( "No DBSessionStateError caught" );
} catch ( DBSessionStateError $e ) {
$this->assertInstanceOf( DBSessionStateError::class, $e );
}
$this->assertTrue( $this->conn->isOpen(), "Connection remains" );
$this->conn->rollback( __METHOD__ );
$this->conn->flushSession( __METHOD__ );
$row = $this->conn->query( 'SELECT "x" AS v', __METHOD__ )->fetchObject();
$this->assertSame( 'x', $row->v, "Recovered" );
$adminConn->close( __METHOD__ );
}
public function testConnectionLossScopedLock() {
$row = $this->conn->query( 'SELECT connection_id() AS id', __METHOD__ )->fetchObject();
$encId = intval( $row->id );
try {
( function () use ( $encId ) {
$unlocker = $this->conn->getScopedLockAndFlush( 'x', 'fn', 1 );
$adminConn = $this->newConnection();
$adminConn->query( "KILL $encId" );
$this->conn->query( "SELECT 1" );
} )();
$this->fail( "Expected DBQueryDisconnectedError" );
} catch ( DBQueryDisconnectedError $e ) {
// This should report the explicit query that failed,
// instead of the (later) implicit query from the $unlocker deref.
$this->assertStringContainsString( "SELECT 1", $e->getMessage() );
}
$this->conn->rollback( __METHOD__ );
$this->conn->unlock( 'x', __METHOD__ );
}
public function testTransactionError() {
$this->assertSame( TransactionManager::STATUS_TRX_NONE, $this->conn->trxStatus() );
$this->conn->startAtomic( __METHOD__, IDatabase::ATOMIC_CANCELABLE );
$this->assertSame( TransactionManager::STATUS_TRX_OK, $this->conn->trxStatus() );
$row = $this->conn->query( 'SELECT connection_id() AS id', __METHOD__ )->fetchObject();
$encId = intval( $row->id );
try {
$this->conn->lock( 'trx_lock_' . mt_rand(), __METHOD__, 0 );
$this->assertSame( TransactionManager::STATUS_TRX_OK, $this->conn->trxStatus() );
$this->conn->query( "SELECT invalid query()", __METHOD__ );
$this->fail( "No DBQueryError caught" );
} catch ( DBQueryError $e ) {
$this->assertInstanceOf( DBQueryError::class, $e );
}
$this->assertSame( TransactionManager::STATUS_TRX_ERROR, $this->conn->trxStatus() );
try {
$this->conn->query( 'SELECT "x" AS v', __METHOD__ )->fetchObject();
$this->fail( "No DBTransactionStateError caught" );
} catch ( DBTransactionStateError $e ) {
$this->assertInstanceOf( DBTransactionStateError::class, $e );
$this->assertSame( TransactionManager::STATUS_TRX_ERROR, $this->conn->trxStatus() );
$this->assertSame( 1, $this->conn->trxLevel(), "Transaction remains" );
$this->assertTrue( $this->conn->isOpen(), "Connection remains" );
}
$adminConn = $this->newConnection();
$adminConn->query( "KILL $encId", __METHOD__ );
$this->assertSame( TransactionManager::STATUS_TRX_ERROR, $this->conn->trxStatus() );
try {
$this->conn->query( 'SELECT 1', __METHOD__ );
$this->fail( "No DBTransactionStateError caught" );
} catch ( DBTransactionStateError $e ) {
$this->assertInstanceOf( DBTransactionStateError::class, $e );
}
$this->assertSame(
1,
$this->conn->trxLevel(),
"Transaction loss not yet detected (due to STATUS_TRX_ERROR)"
);
$this->assertTrue(
$this->conn->isOpen(),
"Connection loss not detected (due to STATUS_TRX_ERROR)"
);
$this->conn->cancelAtomic( __METHOD__ );
$this->assertSame( 0, $this->conn->trxLevel(), "No transaction remains" );
$this->assertTrue( $this->conn->isOpen(), "Reconnected" );
$row = $this->conn->query( 'SELECT "x" AS v', __METHOD__ )->fetchObject();
$this->assertSame( 'x', $row->v, "Recovered" );
$this->conn->rollback( __METHOD__ );
$adminConn->close( __METHOD__ );
}
private function newConnection(): DatabaseMySQL {
$lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
$dbFactory = MediaWikiServices::getInstance()->getDatabaseFactory();
/** @var DatabaseMySQL $conn */
$conn = $dbFactory->create(
'mysql',
array_merge(
$lb->getServerInfo( 0 ),
[
'dbname' => null,
'schema' => null,
'tablePrefix' => '',
]
)
);
return $conn;
}
public function testInsertIdAfterInsert() {
$dTable = $this->createDestTable();
$rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
$this->conn->insert( $dTable, $rows, __METHOD__ );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 1, $this->conn->insertId() );
$this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 0, $this->conn->insertId() );
$this->dropDestTable();
}
public function testInsertIdAfterInsertIgnore() {
$dTable = $this->createDestTable();
$rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
$this->conn->insert( $dTable, $rows, __METHOD__, 'IGNORE' );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 1, $this->conn->insertId() );
$this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
$this->conn->insert( $dTable, $rows, __METHOD__, 'IGNORE' );
$this->assertSame( 0, $this->conn->affectedRows() );
$this->assertSame( 0, $this->conn->insertId() );
$this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 0, $this->conn->insertId() );
$this->dropDestTable();
}
public function testInsertIdAfterReplace() {
$dTable = $this->createDestTable();
$rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
$this->conn->replace( $dTable, 'k', $rows, __METHOD__ );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 1, $this->conn->insertId() );
$this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
$this->conn->replace( $dTable, 'k', $rows, __METHOD__ );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 2, $this->conn->insertId() );
$this->assertSame( 2, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 0, $this->conn->insertId() );
$this->dropDestTable();
}
public function testInsertIdAfterUpsert() {
$dTable = $this->createDestTable();
$rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
$set = [
'v = ' . $this->conn->buildExcludedValue( 'v' ),
't = ' . $this->conn->buildExcludedValue( 't' ) . ' + 1'
];
$this->conn->upsert( $dTable, $rows, 'k', $set, __METHOD__ );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 1, $this->conn->insertId() );
$this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
$this->conn->upsert( $dTable, $rows, 'k', $set, __METHOD__ );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 1, $this->conn->insertId() );
$this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 0, $this->conn->insertId() );
$this->dropDestTable();
}
public function testInsertIdAfterInsertSelect() {
$sTable = $this->createSourceTable();
$dTable = $this->createDestTable();
$rows = [ [ 'sk' => 'Luca', 'sv' => mt_rand( 1, 100 ), 'st' => time() ] ];
$this->conn->insert( $sTable, $rows, __METHOD__, 'IGNORE' );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 1, $this->conn->insertId() );
$this->assertSame( 1, (int)$this->conn->selectField( $sTable, 'sn', [ 'sk' => 'Luca' ] ) );
$this->conn->insertSelect(
$dTable,
$sTable,
[ 'k' => 'sk', 'v' => 'sv', 't' => 'st' ],
[ 'sk' => 'Luca' ],
__METHOD__,
'IGNORE'
);
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 1, $this->conn->insertId() );
$this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 0, $this->conn->insertId() );
$this->dropSourceTable();
$this->dropDestTable();
}
public function testInsertIdAfterInsertSelectIgnore() {
$sTable = $this->createSourceTable();
$dTable = $this->createDestTable();
$rows = [ [ 'sk' => 'Luca', 'sv' => mt_rand( 1, 100 ), 'st' => time() ] ];
$this->conn->insert( $sTable, $rows, __METHOD__, 'IGNORE' );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 1, $this->conn->insertId() );
$this->assertSame( 1, (int)$this->conn->selectField( $sTable, 'sn', [ 'sk' => 'Luca' ] ) );
$this->conn->insertSelect(
$dTable,
$sTable,
[ 'k' => 'sk', 'v' => 'sv', 't' => 'st' ],
[ 'sk' => 'Luca' ],
__METHOD__,
'IGNORE'
);
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 1, $this->conn->insertId() );
$this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
$this->conn->insertSelect(
$dTable,
$sTable,
[ 'k' => 'sk', 'v' => 'sv', 't' => 'st' ],
[ 'sk' => 'Luca' ],
__METHOD__,
'IGNORE'
);
$this->assertSame( 0, $this->conn->affectedRows() );
$this->assertSame( 0, $this->conn->insertId() );
$this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
$this->assertSame( 1, $this->conn->affectedRows() );
$this->assertSame( 0, $this->conn->insertId() );
$this->dropSourceTable();
$this->dropDestTable();
}
/**
* @coversNothing
*/
public function testAffectedRowsAfterUpdateIgnore() {
$sTable = $this->createSourceTable();
$rows = [ [ 'sk' => 'Luca', 'sv' => mt_rand( 1, 100 ), 'st' => time() ],
[ 'sk' => 'Test', 'sv' => mt_rand( 1, 100 ), 'st' => time() ] ];
$this->conn->insert( $sTable, $rows, __METHOD__, 'IGNORE' );
$this->assertSame( 2, $this->conn->affectedRows(), 'Test inserted' );
// Test changing something
$this->conn->update(
$sTable,
[ 'sk' => 'TestRow' ],
[ 'sk' => 'Test' ],
__METHOD__,
[ 'IGNORE' ]
);
$this->assertSame( 1, $this->conn->affectedRows(), 'Updated' );
// Test changing nothing
$this->conn->update(
$sTable,
[ 'sk' => 'TestRow' ],
[ 'sk' => 'TestRow' ],
__METHOD__,
[ 'IGNORE' ]
);
$this->assertSame( 1, $this->conn->affectedRows(), 'Nothing changed' );
// Test nothing found
$this->conn->update(
$sTable,
[ 'sk' => 'TestExistingRow' ],
[ 'sk' => 'TestNonexistingRow' ],
__METHOD__,
[ 'IGNORE' ]
);
$this->assertSame( 0, $this->conn->affectedRows(), 'Not found' );
// Test key conflict on the unique sk field
$this->conn->update(
$sTable,
[ 'sk' => 'TestRow' ],
[ 'sk' => 'Luca' ],
__METHOD__,
[ 'IGNORE' ]
);
$this->assertSame( 1, $this->conn->affectedRows(), 'Key conflict, nothing changed on database' );
}
public function testFieldAndIndexInfo() {
global $wgDBname;
$this->conn->selectDomain( $wgDBname );
$this->conn->query(
"CREATE TEMPORARY TABLE tmp_schema_tbl (" .
"n integer not null auto_increment, " .
"k varchar(255), " .
"v integer, " .
"t integer," .
"PRIMARY KEY (n)," .
"UNIQUE INDEX k (k)," .
"INDEX t (t)" .
")"
);
$this->assertTrue( $this->conn->fieldExists( 'tmp_schema_tbl', 'n' ) );
$this->assertTrue( $this->conn->fieldExists( 'tmp_schema_tbl', 'k' ) );
$this->assertTrue( $this->conn->fieldExists( 'tmp_schema_tbl', 'v' ) );
$this->assertTrue( $this->conn->fieldExists( 'tmp_schema_tbl', 't' ) );
$this->assertFalse( $this->conn->fieldExists( 'tmp_schema_tbl', 'x' ) );
$this->assertTrue( $this->conn->indexExists( 'tmp_schema_tbl', 'k' ) );
$this->assertTrue( $this->conn->indexExists( 'tmp_schema_tbl', 't' ) );
$this->assertFalse( $this->conn->indexExists( 'tmp_schema_tbl', 'x' ) );
$this->assertTrue( $this->conn->indexExists( 'tmp_schema_tbl', 'PRIMARY' ) );
$this->assertTrue( $this->conn->indexUnique( 'tmp_schema_tbl', 'k' ) );
$this->assertFalse( $this->conn->indexUnique( 'tmp_schema_tbl', 't' ) );
$this->assertNull( $this->conn->indexUnique( 'tmp_schema_tbl', 'x' ) );
$this->assertTrue( $this->conn->indexUnique( 'tmp_schema_tbl', 'PRIMARY' ) );
}
private function createSourceTable() {
global $wgDBname;
$this->conn->query( "DROP TABLE IF EXISTS `$wgDBname`.`tmp_src_tbl`" );
$this->conn->query(
"CREATE TEMPORARY TABLE `$wgDBname`.`tmp_src_tbl` (" .
"sn integer not null unique key auto_increment, " .
"sk varchar(255) unique, " .
"sv integer, " .
"st integer" .
")"
);
return "$wgDBname.tmp_src_tbl";
}
private function createDestTable() {
global $wgDBname;
$this->conn->query( "DROP TABLE IF EXISTS `$wgDBname`.`tmp_dst_tbl`" );
$this->conn->query(
"CREATE TEMPORARY TABLE `$wgDBname`.`tmp_dst_tbl` (" .
"n integer not null unique key auto_increment, " .
"k varchar(255) unique, " .
"v integer, " .
"t integer" .
")"
);
return "$wgDBname.tmp_dst_tbl";
}
private function dropSourceTable() {
global $wgDBname;
$this->conn->query( "DROP TEMPORARY TABLE IF EXISTS `$wgDBname`.`tmp_src_tbl`" );
}
private function dropDestTable() {
global $wgDBname;
$this->conn->query( "DROP TEMPORARY TABLE IF EXISTS `$wgDBname`.`tmp_dst_tbl`" );
}
/**
* Insert a null value into a field that is not nullable using INSERT IGNORE
*/
public function testInsertIgnoreNull() {
$this->expectException( DBQueryError::class );
$this->conn->newInsertQueryBuilder()
->insertInto( 'log_search' )
->ignore()
->row( [ 'ls_field' => 'test', 'ls_value' => null, 'ls_log_id' => 1 ] )
->execute();
}
/**
* @covers \Wikimedia\Rdbms\DatabaseMySQL::query
* @covers \Wikimedia\Rdbms\DatabaseMySQL::dropTable
*/
public function testTempTableDomains() {
global $wgDBname;
$table = 'temp_test_tbl';
$this->conn->selectDomain( $wgDBname . '-xxx1_' );
$this->assertFalse( $this->conn->tableExists( $table ) );
$query = new Query(
"CREATE TEMPORARY TABLE " . $this->conn->tableName( $table ) .
" (n integer not null unique key)",
ISQLPlatform::QUERY_CHANGE_SCHEMA,
"CREATE TEMPORARY",
$table
);
$this->conn->query( $query, __METHOD__ );
$this->assertTrue( $this->conn->tableExists( $table ) );
$this->conn->selectDomain( $wgDBname . '-xxx2_' );
$this->assertFalse( $this->conn->tableExists( $table ) );
$query = new Query(
"CREATE TEMPORARY TABLE " . $this->conn->tableName( $table ) .
" (n integer not null unique key)",
ISQLPlatform::QUERY_CHANGE_SCHEMA,
"CREATE TEMPORARY",
$table
);
$this->conn->query( $query, __METHOD__ );
$this->assertTrue( $this->conn->tableExists( $table ) );
$this->conn->selectDomain( $wgDBname . '-xxx1_' );
$this->conn->dropTable( $table );
$this->assertFalse( $this->conn->tableExists( $table ) );
$this->conn->selectDomain( $wgDBname . '-xxx2_' );
$this->conn->dropTable( $table );
$this->assertFalse( $this->conn->tableExists( $table ) );
}
/**
* @covers \Wikimedia\Rdbms\DatabaseMySQL::query
* @covers \Wikimedia\Rdbms\DatabaseMySQL::dropTable
*/
public function testTempTableDomainsTableWithDot() {
global $wgDBname;
$table = '.temp_test_tbl';
$this->conn->selectDomain( $wgDBname . '-xxx1_' );
$this->assertFalse( $this->conn->tableExists( $table ) );
$query = new Query(
"CREATE TEMPORARY TABLE " . $this->conn->tableName( $table ) .
" (n integer not null unique key)",
ISQLPlatform::QUERY_CHANGE_SCHEMA,
"CREATE TEMPORARY",
$table
);
$this->conn->query( $query, __METHOD__ );
$this->assertTrue( $this->conn->tableExists( $table ) );
$this->conn->selectDomain( $wgDBname . '-xxx2_' );
$this->assertTrue( $this->conn->tableExists( $table ) );
$this->conn->dropTable( $table );
$this->assertFalse( $this->conn->tableExists( $table ) );
$this->conn->selectDomain( $wgDBname . '-xxx1_' );
$this->assertFalse( $this->conn->tableExists( $table ) );
}
}