8use Wikimedia\TestingAccessWrapper;
15 use MediaWikiCoversValidator;
26 $m = Database::NEW_UNCONNECTED;
27 $p = [
'host' =>
'localhost',
'user' =>
'me',
'password' =>
'myself',
'dbname' =>
'i' ];
29 $this->assertInstanceOf( DatabaseMysqli::class, Database::factory(
'mysqli', $p, $m ) );
30 $this->assertInstanceOf( DatabaseMysqli::class, Database::factory(
'MySqli', $p, $m ) );
31 $this->assertInstanceOf( DatabaseMysqli::class, Database::factory(
'MySQLi', $p, $m ) );
32 $this->assertInstanceOf( DatabasePostgres::class, Database::factory(
'postgres', $p, $m ) );
33 $this->assertInstanceOf( DatabasePostgres::class, Database::factory(
'Postgres', $p, $m ) );
35 $x = $p + [
'port' => 10000,
'UseWindowsAuth' =>
false ];
36 $this->assertInstanceOf( DatabaseMssql::class, Database::factory(
'mssql', $x, $m ) );
38 $x = $p + [
'dbFilePath' =>
'some/file.sqlite' ];
39 $this->assertInstanceOf( DatabaseSqlite::class, Database::factory(
'sqlite', $x, $m ) );
40 $x = $p + [
'dbDirectory' =>
'some/file' ];
41 $this->assertInstanceOf( DatabaseSqlite::class, Database::factory(
'sqlite', $x, $m ) );
48 [ 1234.5678,
"'1234.5678'" ],
49 [
'string',
"'string'" ],
50 [
'string\'s cause trouble',
"'string\'s cause trouble'" ],
59 $this->assertEquals( $expected, $this->db->addQuotes(
$input ) );
80 [
'dbname' =>
'sharedb',
'schema' =>
null,
'prefix' =>
'' ],
86 [
'dbname' =>
'sharedb',
'schema' =>
null,
'prefix' =>
'' ],
89 'sharedb.sh_tablename',
92 [
'dbname' =>
'sharedb',
'schema' =>
null,
'prefix' =>
'sh_' ],
94 'shared-prefix-raw' => [
95 'sharedb.sh_tablename',
98 [
'dbname' =>
'sharedb',
'schema' =>
null,
'prefix' =>
'sh_' ],
101 'databasename.tablename',
102 'databasename.tablename',
106 'databasename.tablename',
107 'databasename.tablename',
117 public function testTableName( $expected, $table, $format, array $alias =
null ) {
119 $this->db->setTableAliases( [ $table => $alias ] );
123 $this->db->tableName( $table, $format ?:
'quoted' )
129 'one-element array' => [
130 [
'table' ], [],
'table '
133 [
'table1',
'table2' ], [],
'table1,table2 '
136 [
'table1',
'table2' ],
137 [
'table2' => [
'LEFT JOIN',
't1_id = t2_id' ] ],
138 'table1 LEFT JOIN table2 ON ((t1_id = t2_id))'
140 'real join with multiple conditionals' => [
141 [
'table1',
'table2' ],
142 [
'table2' => [
'LEFT JOIN', [
't1_id = t2_id',
't2_x = \'X\'' ] ] ],
143 'table1 LEFT JOIN table2 ON ((t1_id = t2_id) AND (t2_x = \'X\'))'
145 'join with parenthesized group' => [
146 [
'table1',
'n' => [
'table2',
'table3' ] ],
148 'table3' => [
'JOIN',
't2_id = t3_id' ],
149 'n' => [
'LEFT JOIN',
't1_id = t2_id' ],
151 'table1 LEFT JOIN (table2 JOIN table3 ON ((t2_id = t3_id))) ON ((t1_id = t2_id))'
153 'join with degenerate parenthesized group' => [
154 [
'table1',
'n' => [
't2' =>
'table2' ] ],
156 'n' => [
'LEFT JOIN',
't1_id = t2_id' ],
158 'table1 LEFT JOIN table2 t2 ON ((t1_id = t2_id))'
168 $clause = TestingAccessWrapper::newFromObject( $this->db )
169 ->tableNamesWithIndexClauseOrJOIN(
$tables, [], [], $join_conds );
170 $this->assertSame( $expect, $clause );
183 $callback =
function () use ( $db, &$flagSet, &$called ) {
185 $flagSet = $db->getFlag(
DBO_TRX );
188 $db->onTransactionIdle( $callback, __METHOD__ );
189 $this->assertTrue( $called,
'Callback reached' );
190 $this->assertFalse( $flagSet,
'DBO_TRX off in callback' );
191 $this->assertFalse( $db->getFlag(
DBO_TRX ),
'DBO_TRX still default' );
195 $db->startAtomic( __METHOD__ );
196 $db->onTransactionIdle( $callback, __METHOD__ );
197 $this->assertFalse( $called,
'Callback not reached during TRX' );
198 $db->endAtomic( __METHOD__ );
200 $this->assertTrue( $called,
'Callback reached after COMMIT' );
201 $this->assertFalse( $flagSet,
'DBO_TRX off in callback' );
202 $this->assertFalse( $db->getFlag(
DBO_TRX ),
'DBO_TRX restored to default' );
205 $db->onTransactionIdle(
206 function () use ( $db ) {
211 $this->assertFalse( $db->getFlag(
DBO_TRX ),
'DBO_TRX restored to default' );
219 $db = $this->
getMockDB( [
'isOpen',
'ping' ] );
220 $db->method(
'isOpen' )->willReturn(
true );
221 $db->method(
'ping' )->willReturn(
true );
224 $lbFactory = LBFactorySingle::newFromConnection( $db );
227 $lb = $lbFactory->getMainLB();
228 $conn = $lb->openConnection( $lb->getWriterIndex() );
229 $this->assertSame( $db, $conn,
'Same DB instance' );
230 $this->assertTrue( $db->getFlag(
DBO_TRX ),
'DBO_TRX is set' );
234 $callback =
function () use ( $db, &$flagSet, &$called ) {
236 $flagSet = $db->getFlag(
DBO_TRX );
239 $db->onTransactionIdle( $callback, __METHOD__ );
240 $this->assertTrue( $called,
'Called when idle if DBO_TRX is set' );
241 $this->assertFalse( $flagSet,
'DBO_TRX off in callback' );
242 $this->assertTrue( $db->getFlag(
DBO_TRX ),
'DBO_TRX still default' );
245 $lbFactory->beginMasterChanges( __METHOD__ );
246 $db->onTransactionIdle( $callback, __METHOD__ );
247 $this->assertFalse( $called,
'Not called when lb-transaction is active' );
249 $lbFactory->commitMasterChanges( __METHOD__ );
250 $this->assertTrue( $called,
'Called when lb-transaction is committed' );
253 $lbFactory->beginMasterChanges( __METHOD__ );
254 $db->onTransactionIdle( $callback, __METHOD__ );
255 $this->assertFalse( $called,
'Not called when lb-transaction is active' );
257 $lbFactory->rollbackMasterChanges( __METHOD__ );
258 $this->assertFalse( $called,
'Not called when lb-transaction is rolled back' );
260 $lbFactory->commitMasterChanges( __METHOD__ );
261 $this->assertFalse( $called,
'Not called in next round commit' );
270 $db->method(
'isOpen' )->willReturn(
true );
273 $this->assertFalse( $db->getFlag(
DBO_TRX ),
'DBO_TRX is not set' );
276 $db->onTransactionPreCommitOrIdle(
277 function () use ( &$called ) {
282 $this->assertTrue( $called,
'Called when idle' );
284 $db->begin( __METHOD__ );
286 $db->onTransactionPreCommitOrIdle(
287 function () use ( &$called ) {
292 $this->assertFalse( $called,
'Not called when transaction is active' );
293 $db->commit( __METHOD__ );
294 $this->assertTrue( $called,
'Called when transaction is committed' );
302 $db = $this->
getMockDB( [
'isOpen',
'ping' ] );
303 $db->method(
'isOpen' )->willReturn(
true );
304 $db->method(
'ping' )->willReturn(
true );
307 $lbFactory = LBFactorySingle::newFromConnection( $db );
310 $lb = $lbFactory->getMainLB();
311 $conn = $lb->openConnection( $lb->getWriterIndex() );
312 $this->assertSame( $db, $conn,
'Same DB instance' );
314 $this->assertFalse( $lb->hasMasterChanges() );
315 $this->assertTrue( $db->getFlag(
DBO_TRX ),
'DBO_TRX is set' );
317 $callback =
function () use ( &$called ) {
320 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
321 $this->assertTrue( $called,
'Called when idle if DBO_TRX is set' );
323 $lbFactory->commitMasterChanges();
324 $this->assertFalse( $called );
327 $lbFactory->beginMasterChanges( __METHOD__ );
328 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
329 $this->assertFalse( $called,
'Not called when lb-transaction is active' );
330 $lbFactory->commitMasterChanges( __METHOD__ );
331 $this->assertTrue( $called,
'Called when lb-transaction is committed' );
334 $lbFactory->beginMasterChanges( __METHOD__ );
335 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
336 $this->assertFalse( $called,
'Not called when lb-transaction is active' );
338 $lbFactory->rollbackMasterChanges( __METHOD__ );
339 $this->assertFalse( $called,
'Not called when lb-transaction is rolled back' );
341 $lbFactory->commitMasterChanges( __METHOD__ );
342 $this->assertFalse( $called,
'Not called in next round commit' );
353 $db->begin( __METHOD__ );
355 $db->onTransactionResolution(
function () use ( $db, &$called ) {
359 $db->commit( __METHOD__ );
360 $this->assertFalse( $db->getFlag(
DBO_TRX ),
'DBO_TRX restored to default' );
361 $this->assertTrue( $called,
'Callback reached' );
364 $db->begin( __METHOD__ );
366 $db->onTransactionResolution(
function () use ( $db, &$called ) {
370 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
371 $this->assertFalse( $db->getFlag(
DBO_TRX ),
'DBO_TRX restored to default' );
372 $this->assertTrue( $called,
'Callback reached' );
381 $db->setTransactionListener(
'ping',
function () use ( $db, &$called ) {
386 $db->begin( __METHOD__ );
387 $db->commit( __METHOD__ );
388 $this->assertTrue( $called,
'Callback reached' );
391 $db->begin( __METHOD__ );
392 $db->commit( __METHOD__ );
393 $this->assertTrue( $called,
'Callback still reached' );
396 $db->begin( __METHOD__ );
397 $db->rollback( __METHOD__ );
398 $this->assertTrue( $called,
'Callback reached' );
400 $db->setTransactionListener(
'ping',
null );
402 $db->begin( __METHOD__ );
403 $db->commit( __METHOD__ );
404 $this->assertFalse( $called,
'Callback not reached' );
417 static $abstractMethods = [
418 'fetchAffectedRowCount',
422 'fetchObject',
'fetchRow',
423 'fieldInfo',
'fieldName',
424 'getSoftwareLink',
'getServerVersion',
428 'lastError',
'lastErrno',
429 'numFields',
'numRows',
433 $db = $this->getMockBuilder( Database::class )
434 ->disableOriginalConstructor()
435 ->setMethods( array_values( array_unique( array_merge(
440 $wdb = TestingAccessWrapper::newFromObject( $db );
442 $wdb->connLogger = new \Psr\Log\NullLogger();
443 $wdb->queryLogger = new \Psr\Log\NullLogger();
452 $db->method(
'isOpen' )->willReturn(
true );
454 $db->flushSnapshot( __METHOD__ );
455 $db->flushSnapshot( __METHOD__ );
457 $db->setFlag(
DBO_TRX, $db::REMEMBER_PRIOR );
458 $db->query(
'SELECT 1', __METHOD__ );
459 $this->assertTrue( (
bool)$db->trxLevel(),
"Transaction started." );
460 $db->flushSnapshot( __METHOD__ );
461 $db->restoreFlags( $db::RESTORE_PRIOR );
463 $this->assertFalse( (
bool)$db->trxLevel(),
"Transaction cleared." );
474 $db->method(
'isOpen' )->willReturn(
true );
476 $this->assertEquals( 0, $db->trxLevel() );
477 $this->assertEquals(
true, $db->lockIsFree(
'x', __METHOD__ ) );
478 $this->assertEquals(
true, $db->lock(
'x', __METHOD__ ) );
479 $this->assertEquals(
false, $db->lockIsFree(
'x', __METHOD__ ) );
480 $this->assertEquals(
true, $db->unlock(
'x', __METHOD__ ) );
481 $this->assertEquals(
true, $db->lockIsFree(
'x', __METHOD__ ) );
482 $this->assertEquals( 0, $db->trxLevel() );
485 $this->assertEquals(
true, $db->lockIsFree(
'x', __METHOD__ ) );
486 $this->assertEquals(
true, $db->lock(
'x', __METHOD__ ) );
487 $this->assertEquals(
false, $db->lockIsFree(
'x', __METHOD__ ) );
488 $this->assertEquals(
true, $db->unlock(
'x', __METHOD__ ) );
489 $this->assertEquals(
true, $db->lockIsFree(
'x', __METHOD__ ) );
492 $this->assertEquals( 0, $db->trxLevel() );
497 }
catch ( RunTimeException
$e ) {
498 $this->assertTrue( $db->trxLevel() > 0,
"Transaction not committed." );
501 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
502 $this->assertTrue( $db->lockIsFree(
'meow', __METHOD__ ) );
506 }
catch ( RunTimeException
$e ) {
507 $this->assertTrue( $db->trxLevel() > 0,
"Transaction not committed." );
509 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
510 $this->assertTrue( $db->lockIsFree(
'meow', __METHOD__ ) );
515 $db->
query(
"SELECT 1" );
516 throw new RunTimeException(
"Uh oh!" );
521 $db->
begin( __METHOD__ );
522 throw new RunTimeException(
"Uh oh!" );
532 $origTrx = $db->getFlag(
DBO_TRX );
533 $origSsl = $db->getFlag(
DBO_SSL );
536 ? $db->clearFlag(
DBO_TRX, $db::REMEMBER_PRIOR )
537 : $db->setFlag(
DBO_TRX, $db::REMEMBER_PRIOR );
538 $this->assertEquals( !$origTrx, $db->getFlag(
DBO_TRX ) );
541 ? $db->clearFlag(
DBO_SSL, $db::REMEMBER_PRIOR )
542 : $db->setFlag(
DBO_SSL, $db::REMEMBER_PRIOR );
543 $this->assertEquals( !$origSsl, $db->getFlag(
DBO_SSL ) );
545 $db->restoreFlags( $db::RESTORE_INITIAL );
546 $this->assertEquals( $origTrx, $db->getFlag(
DBO_TRX ) );
547 $this->assertEquals( $origSsl, $db->getFlag(
DBO_SSL ) );
550 ? $db->clearFlag(
DBO_TRX, $db::REMEMBER_PRIOR )
551 : $db->setFlag(
DBO_TRX, $db::REMEMBER_PRIOR );
553 ? $db->clearFlag(
DBO_SSL, $db::REMEMBER_PRIOR )
554 : $db->setFlag(
DBO_SSL, $db::REMEMBER_PRIOR );
557 $this->assertEquals( $origSsl, $db->getFlag(
DBO_SSL ) );
558 $this->assertEquals( !$origTrx, $db->getFlag(
DBO_TRX ) );
561 $this->assertEquals( $origSsl, $db->getFlag(
DBO_SSL ) );
562 $this->assertEquals( $origTrx, $db->getFlag(
DBO_TRX ) );
570 $db = $this->getMockBuilder( DatabaseMysqli::class )
571 ->disableOriginalConstructor()
575 $db->setFlag( Database::DBO_IGNORE );
583 $db = $this->getMockBuilder( DatabaseMysqli::class )
584 ->disableOriginalConstructor()
588 $db->clearFlag( Database::DBO_IGNORE );
596 $old = $this->db->tablePrefix();
597 $this->assertInternalType(
'string', $old,
'Prefix is string' );
598 $this->assertEquals( $old, $this->db->tablePrefix(),
"Prefix unchanged" );
599 $this->assertEquals( $old, $this->db->tablePrefix(
'xxx' ) );
600 $this->assertEquals(
'xxx', $this->db->tablePrefix(),
"Prefix set" );
601 $this->db->tablePrefix( $old );
602 $this->assertNotEquals(
'xxx', $this->db->tablePrefix() );
604 $old = $this->db->dbSchema();
605 $this->assertInternalType(
'string', $old,
'Schema is string' );
606 $this->assertEquals( $old, $this->db->dbSchema(),
"Schema unchanged" );
607 $this->assertEquals( $old, $this->db->dbSchema(
'xxx' ) );
608 $this->assertEquals(
'xxx', $this->db->dbSchema(),
"Schema set" );
609 $this->db->dbSchema( $old );
610 $this->assertNotEquals(
'xxx', $this->db->dbSchema() );
Helper for testing the methods from the Database class.
testTransactionPreCommitOrIdle_TRX()
Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle Wikimedia\Rdbms\Database::runOnTransactionPreC...
badLockingMethodImplicit(IDatabase $db)
testTransactionIdle()
Wikimedia\Rdbms\Database::onTransactionIdle Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks.
badLockingMethodExplicit(IDatabase $db)
testTableName( $expected, $table, $format, array $alias=null)
provideTableName Wikimedia\Rdbms\Database::tableName
provideTableNamesWithIndexClauseOrJOIN()
testTransactionListener()
Wikimedia\Rdbms\Database::setTransactionListener.
testMutators()
Wikimedia\Rdbms\Database::tablePrefix Wikimedia\Rdbms\Database::dbSchema.
testFlagSetting()
Wikimedia\Rdbms\Database::getFlag Wikimedia\Rdbms\Database::setFlag Wikimedia\Rdbms\Database::restore...
testTableNamesWithIndexClauseOrJOIN( $tables, $join_conds, $expect)
provideTableNamesWithIndexClauseOrJOIN Wikimedia\Rdbms\Database::tableNamesWithIndexClauseOrJOIN
testGetScopedLock()
Wikimedia\Rdbms\Database::getScopedLockAndFlush Wikimedia\Rdbms\Database::lock Wikimedia\Rdbms\Databa...
static provideAddQuotes()
testFlushSnapshot()
Wikimedia\Rdbms\Database::flushSnapshot.
testTransactionResolution()
Wikimedia\Rdbms\Database::onTransactionResolution Wikimedia\Rdbms\Database::runOnTransactionIdleCallb...
static provideTableName()
testTransactionPreCommitOrIdle()
Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle Wikimedia\Rdbms\Database::runOnTransactionPreC...
testDBOIgnoreClear()
UnexpectedValueException Wikimedia\Rdbms\Database::clearFlag.
testAddQuotes( $input, $expected)
provideAddQuotes Wikimedia\Rdbms\Database::addQuotes
getMockDB( $methods=[])
Use this mock instead of DatabaseTestHelper for cases where DatabaseTestHelper is too inflexibile due...
testDBOIgnoreSet()
UnexpectedValueException Wikimedia\Rdbms\Database::setFlag.
testTransactionIdle_TRX()
Wikimedia\Rdbms\Database::onTransactionIdle Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks.
testFactory()
provideAddQuotes Wikimedia\Rdbms\Database::factory
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist & $tables
processing should stop and the error should be shown to the user * false
returning false will NOT prevent logging $e
if(is_array($mode)) switch( $mode) $input