9 use Wikimedia\TestingAccessWrapper;
17 use MediaWikiCoversValidator;
28 $m = Database::NEW_UNCONNECTED;
29 $p = [
'host' =>
'localhost',
'user' =>
'me',
'password' =>
'myself',
'dbname' =>
'i' ];
37 $x = $p + [
'port' => 10000,
'UseWindowsAuth' =>
false ];
40 $x = $p + [
'dbFilePath' =>
'some/file.sqlite' ];
42 $x = $p + [
'dbDirectory' =>
'some/file' ];
50 [ 1234.5678,
"'1234.5678'" ],
51 [
'string',
"'string'" ],
52 [
'string\'s cause trouble',
"'string\'s cause trouble'" ],
61 $this->assertEquals( $expected, $this->db->addQuotes(
$input ) );
82 [
'dbname' =>
'sharedb',
'schema' =>
null,
'prefix' =>
'' ],
88 [
'dbname' =>
'sharedb',
'schema' =>
null,
'prefix' =>
'' ],
91 'sharedb.sh_tablename',
94 [
'dbname' =>
'sharedb',
'schema' =>
null,
'prefix' =>
'sh_' ],
96 'shared-prefix-raw' => [
97 'sharedb.sh_tablename',
100 [
'dbname' =>
'sharedb',
'schema' =>
null,
'prefix' =>
'sh_' ],
103 'databasename.tablename',
104 'databasename.tablename',
108 'databasename.tablename',
109 'databasename.tablename',
121 $this->db->setTableAliases( [ $table => $alias ] );
125 $this->db->tableName( $table, $format ?:
'quoted' )
131 'one-element array' => [
132 [
'table' ], [],
'table '
135 [
'table1',
'table2' ], [],
'table1,table2 '
138 [
'table1',
'table2' ],
139 [
'table2' => [
'LEFT JOIN',
't1_id = t2_id' ] ],
140 'table1 LEFT JOIN table2 ON ((t1_id = t2_id))'
142 'real join with multiple conditionals' => [
143 [
'table1',
'table2' ],
144 [
'table2' => [
'LEFT JOIN', [
't1_id = t2_id',
't2_x = \'X\'' ] ] ],
145 'table1 LEFT JOIN table2 ON ((t1_id = t2_id) AND (t2_x = \'X\'))'
147 'join with parenthesized group' => [
148 [
'table1',
'n' => [
'table2',
'table3' ] ],
150 'table3' => [
'JOIN',
't2_id = t3_id' ],
151 'n' => [
'LEFT JOIN',
't1_id = t2_id' ],
153 'table1 LEFT JOIN (table2 JOIN table3 ON ((t2_id = t3_id))) ON ((t1_id = t2_id))'
155 'join with degenerate parenthesized group' => [
156 [
'table1',
'n' => [
't2' =>
'table2' ] ],
158 'n' => [
'LEFT JOIN',
't1_id = t2_id' ],
160 'table1 LEFT JOIN table2 t2 ON ((t1_id = t2_id))'
170 $clause = TestingAccessWrapper::newFromObject( $this->db )
171 ->tableNamesWithIndexClauseOrJOIN(
$tables, [], [], $join_conds );
172 $this->assertSame( $expect, $clause );
185 $callback =
function ( $trigger,
IDatabase $db )
use ( &$flagSet, &$called ) {
187 $flagSet = $db->getFlag(
DBO_TRX );
190 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
191 $this->assertTrue( $called,
'Callback reached' );
192 $this->assertFalse( $flagSet,
'DBO_TRX off in callback' );
193 $this->assertFalse( $db->getFlag(
DBO_TRX ),
'DBO_TRX still default' );
197 $db->startAtomic( __METHOD__ );
198 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
199 $this->assertFalse( $called,
'Callback not reached during TRX' );
200 $db->endAtomic( __METHOD__ );
202 $this->assertTrue( $called,
'Callback reached after COMMIT' );
203 $this->assertFalse( $flagSet,
'DBO_TRX off in callback' );
204 $this->assertFalse( $db->getFlag(
DBO_TRX ),
'DBO_TRX restored to default' );
207 $db->onTransactionCommitOrIdle(
213 $this->assertFalse( $db->
getFlag(
DBO_TRX ),
'DBO_TRX restored to default' );
221 $db = $this->
getMockDB( [
'isOpen',
'ping',
'getDBname' ] );
222 $db->method(
'isOpen' )->willReturn(
true );
223 $db->method(
'ping' )->willReturn(
true );
224 $db->method(
'getDBname' )->willReturn(
'' );
227 $lbFactory = LBFactorySingle::newFromConnection( $db );
230 $lb = $lbFactory->getMainLB();
231 $conn = $lb->openConnection( $lb->getWriterIndex() );
232 $this->assertSame( $db, $conn,
'Same DB instance' );
233 $this->assertTrue( $db->getFlag(
DBO_TRX ),
'DBO_TRX is set' );
237 $callback =
function ()
use ( $db, &$flagSet, &$called ) {
239 $flagSet = $db->getFlag(
DBO_TRX );
242 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
243 $this->assertTrue( $called,
'Called when idle if DBO_TRX is set' );
244 $this->assertFalse( $flagSet,
'DBO_TRX off in callback' );
245 $this->assertTrue( $db->getFlag(
DBO_TRX ),
'DBO_TRX still default' );
248 $lbFactory->beginMasterChanges( __METHOD__ );
249 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
250 $this->assertFalse( $called,
'Not called when lb-transaction is active' );
252 $lbFactory->commitMasterChanges( __METHOD__ );
253 $this->assertTrue( $called,
'Called when lb-transaction is committed' );
256 $lbFactory->beginMasterChanges( __METHOD__ );
257 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
258 $this->assertFalse( $called,
'Not called when lb-transaction is active' );
260 $lbFactory->rollbackMasterChanges( __METHOD__ );
261 $this->assertFalse( $called,
'Not called when lb-transaction is rolled back' );
263 $lbFactory->commitMasterChanges( __METHOD__ );
264 $this->assertFalse( $called,
'Not called in next round commit' );
268 $db->onTransactionCommitOrIdle(
function () {
269 throw new RuntimeException(
'test' );
271 $this->fail(
"Exception not thrown" );
272 }
catch ( RuntimeException
$e ) {
273 $this->assertTrue( $db->getFlag(
DBO_TRX ) );
283 $db->method(
'isOpen' )->willReturn(
true );
286 $this->assertFalse( $db->getFlag(
DBO_TRX ),
'DBO_TRX is not set' );
289 $db->onTransactionPreCommitOrIdle(
295 $this->assertTrue( $called,
'Called when idle' );
297 $db->
begin( __METHOD__ );
305 $this->assertFalse( $called,
'Not called when transaction is active' );
306 $db->
commit( __METHOD__ );
307 $this->assertTrue( $called,
'Called when transaction is committed' );
315 $db = $this->
getMockDB( [
'isOpen',
'ping',
'getDBname' ] );
316 $db->method(
'isOpen' )->willReturn(
true );
317 $db->method(
'ping' )->willReturn(
true );
318 $db->method(
'getDBname' )->willReturn(
'unittest' );
321 $lbFactory = LBFactorySingle::newFromConnection( $db );
324 $lb = $lbFactory->getMainLB();
325 $conn = $lb->openConnection( $lb->getWriterIndex() );
326 $this->assertSame( $db, $conn,
'Same DB instance' );
328 $this->assertFalse( $lb->hasMasterChanges() );
329 $this->assertTrue( $db->getFlag(
DBO_TRX ),
'DBO_TRX is set' );
331 $callback =
function (
IDatabase $db )
use ( &$called ) {
334 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
335 $this->assertTrue( $called,
'Called when idle if DBO_TRX is set' );
337 $lbFactory->commitMasterChanges();
338 $this->assertFalse( $called );
341 $lbFactory->beginMasterChanges( __METHOD__ );
342 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
343 $this->assertFalse( $called,
'Not called when lb-transaction is active' );
344 $lbFactory->commitMasterChanges( __METHOD__ );
345 $this->assertTrue( $called,
'Called when lb-transaction is committed' );
348 $lbFactory->beginMasterChanges( __METHOD__ );
349 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
350 $this->assertFalse( $called,
'Not called when lb-transaction is active' );
352 $lbFactory->rollbackMasterChanges( __METHOD__ );
353 $this->assertFalse( $called,
'Not called when lb-transaction is rolled back' );
355 $lbFactory->commitMasterChanges( __METHOD__ );
356 $this->assertFalse( $called,
'Not called in next round commit' );
367 $db->begin( __METHOD__ );
369 $db->onTransactionResolution(
function ( $trigger,
IDatabase $db )
use ( &$called ) {
373 $db->
commit( __METHOD__ );
374 $this->assertFalse( $db->
getFlag(
DBO_TRX ),
'DBO_TRX restored to default' );
375 $this->assertTrue( $called,
'Callback reached' );
378 $db->
begin( __METHOD__ );
385 $this->assertFalse( $db->
getFlag(
DBO_TRX ),
'DBO_TRX restored to default' );
386 $this->assertTrue( $called,
'Callback reached' );
395 $db->setTransactionListener(
'ping',
function ()
use ( $db, &$called ) {
400 $db->begin( __METHOD__ );
401 $db->commit( __METHOD__ );
402 $this->assertTrue( $called,
'Callback reached' );
405 $db->begin( __METHOD__ );
406 $db->commit( __METHOD__ );
407 $this->assertTrue( $called,
'Callback still reached' );
410 $db->begin( __METHOD__ );
411 $db->rollback( __METHOD__ );
412 $this->assertTrue( $called,
'Callback reached' );
414 $db->setTransactionListener(
'ping',
null );
416 $db->begin( __METHOD__ );
417 $db->commit( __METHOD__ );
418 $this->assertFalse( $called,
'Callback not reached' );
431 static $abstractMethods = [
432 'fetchAffectedRowCount',
436 'fetchObject',
'fetchRow',
437 'fieldInfo',
'fieldName',
438 'getSoftwareLink',
'getServerVersion',
442 'lastError',
'lastErrno',
443 'numFields',
'numRows',
449 ->disableOriginalConstructor()
450 ->setMethods( array_values( array_unique( array_merge(
455 $wdb = TestingAccessWrapper::newFromObject( $db );
457 $wdb->connLogger = new \Psr\Log\NullLogger();
458 $wdb->queryLogger = new \Psr\Log\NullLogger();
459 $wdb->currentDomain = DatabaseDomain::newUnspecified();
468 $db->method(
'isOpen' )->willReturn(
true );
470 $db->flushSnapshot( __METHOD__ );
471 $db->flushSnapshot( __METHOD__ );
473 $db->setFlag(
DBO_TRX, $db::REMEMBER_PRIOR );
474 $db->query(
'SELECT 1', __METHOD__ );
475 $this->assertTrue( (
bool)$db->trxLevel(),
"Transaction started." );
476 $db->flushSnapshot( __METHOD__ );
477 $db->restoreFlags( $db::RESTORE_PRIOR );
479 $this->assertFalse( (
bool)$db->trxLevel(),
"Transaction cleared." );
489 $db = $this->
getMockDB( [
'isOpen',
'getDBname' ] );
490 $db->method(
'isOpen' )->willReturn(
true );
491 $db->method(
'getDBname' )->willReturn(
'unittest' );
493 $this->assertEquals( 0, $db->trxLevel() );
494 $this->assertEquals(
true, $db->lockIsFree(
'x', __METHOD__ ) );
495 $this->assertEquals(
true, $db->lock(
'x', __METHOD__ ) );
496 $this->assertEquals(
false, $db->lockIsFree(
'x', __METHOD__ ) );
497 $this->assertEquals(
true, $db->unlock(
'x', __METHOD__ ) );
498 $this->assertEquals(
true, $db->lockIsFree(
'x', __METHOD__ ) );
499 $this->assertEquals( 0, $db->trxLevel() );
502 $this->assertEquals(
true, $db->lockIsFree(
'x', __METHOD__ ) );
503 $this->assertEquals(
true, $db->lock(
'x', __METHOD__ ) );
504 $this->assertEquals(
false, $db->lockIsFree(
'x', __METHOD__ ) );
505 $this->assertEquals(
true, $db->unlock(
'x', __METHOD__ ) );
506 $this->assertEquals(
true, $db->lockIsFree(
'x', __METHOD__ ) );
510 $this->assertEquals( 0, $db->trxLevel() );
511 $this->assertTrue( $db->lockIsFree(
'meow', __METHOD__ ) );
513 $db->query(
"DELETE FROM test WHERE t = 1" );
515 $lock = $db->getScopedLockAndFlush(
'meow', __METHOD__, 1 );
516 $this->fail(
"Exception not reached" );
518 $this->assertEquals( 1, $db->trxLevel(),
"Transaction not committed." );
519 $this->assertTrue( $db->lockIsFree(
'meow', __METHOD__ ),
'Lock not acquired' );
521 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
524 $this->assertEquals( 0, $db->trxLevel() );
525 $this->assertTrue( $db->lockIsFree(
'meow2', __METHOD__ ) );
526 $db->begin( __METHOD__ );
527 $db->query(
"DELETE FROM test WHERE t = 1" );
529 $lock = $db->getScopedLockAndFlush(
'meow2', __METHOD__, 1 );
530 $this->fail(
"Exception not reached" );
532 $this->assertEquals( 1, $db->trxLevel(),
"Transaction not committed." );
533 $this->assertTrue( $db->lockIsFree(
'meow2', __METHOD__ ),
'Lock not acquired' );
535 $db->rollback( __METHOD__ );
538 $this->assertEquals( 0, $db->trxLevel() );
539 $this->assertTrue( $db->lockIsFree(
'wuff', __METHOD__ ) );
540 $db->query(
"SELECT 1", __METHOD__ );
541 $this->assertEquals( 1, $db->trxLevel() );
542 $lock = $db->getScopedLockAndFlush(
'wuff', __METHOD__, 1 );
543 $this->assertEquals( 0, $db->trxLevel() );
544 $this->assertFalse( $db->lockIsFree(
'wuff', __METHOD__ ),
'Lock already acquired' );
545 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
548 $this->assertEquals( 0, $db->trxLevel() );
549 $this->assertTrue( $db->lockIsFree(
'wuff2', __METHOD__ ) );
550 $db->begin( __METHOD__ );
552 $lock = $db->getScopedLockAndFlush(
'wuff2', __METHOD__, 1 );
553 $this->fail(
"Exception not reached" );
555 $this->assertEquals( 1, $db->trxLevel(),
"Transaction not committed." );
556 $this->assertFalse( $db->lockIsFree(
'wuff2', __METHOD__ ),
'Lock not acquired' );
558 $db->rollback( __METHOD__ );
568 $origTrx = $db->getFlag(
DBO_TRX );
569 $origSsl = $db->getFlag(
DBO_SSL );
572 ? $db->clearFlag(
DBO_TRX, $db::REMEMBER_PRIOR )
573 : $db->setFlag(
DBO_TRX, $db::REMEMBER_PRIOR );
574 $this->assertEquals( !$origTrx, $db->getFlag(
DBO_TRX ) );
577 ? $db->clearFlag(
DBO_SSL, $db::REMEMBER_PRIOR )
578 : $db->setFlag(
DBO_SSL, $db::REMEMBER_PRIOR );
579 $this->assertEquals( !$origSsl, $db->getFlag(
DBO_SSL ) );
581 $db->restoreFlags( $db::RESTORE_INITIAL );
582 $this->assertEquals( $origTrx, $db->getFlag(
DBO_TRX ) );
583 $this->assertEquals( $origSsl, $db->getFlag(
DBO_SSL ) );
586 ? $db->clearFlag(
DBO_TRX, $db::REMEMBER_PRIOR )
587 : $db->setFlag(
DBO_TRX, $db::REMEMBER_PRIOR );
589 ? $db->clearFlag(
DBO_SSL, $db::REMEMBER_PRIOR )
590 : $db->setFlag(
DBO_SSL, $db::REMEMBER_PRIOR );
593 $this->assertEquals( $origSsl, $db->getFlag(
DBO_SSL ) );
594 $this->assertEquals( !$origTrx, $db->getFlag(
DBO_TRX ) );
597 $this->assertEquals( $origSsl, $db->getFlag(
DBO_SSL ) );
598 $this->assertEquals( $origTrx, $db->getFlag(
DBO_TRX ) );
607 ->disableOriginalConstructor()
620 ->disableOriginalConstructor()
632 $old = $this->db->tablePrefix();
633 $oldDomain = $this->db->getDomainId();
634 $this->assertInternalType(
'string', $old,
'Prefix is string' );
635 $this->assertSame( $old, $this->db->tablePrefix(),
"Prefix unchanged" );
636 $this->assertSame( $old, $this->db->tablePrefix(
'xxx' ) );
637 $this->assertSame(
'xxx', $this->db->tablePrefix(),
"Prefix set" );
638 $this->db->tablePrefix( $old );
639 $this->assertNotEquals(
'xxx', $this->db->tablePrefix() );
640 $this->assertSame( $oldDomain, $this->db->getDomainId() );
642 $old = $this->db->dbSchema();
643 $oldDomain = $this->db->getDomainId();
644 $this->assertInternalType(
'string', $old,
'Schema is string' );
645 $this->assertSame( $old, $this->db->dbSchema(),
"Schema unchanged" );
646 $this->assertSame( $old, $this->db->dbSchema(
'xxx' ) );
647 $this->assertSame(
'xxx', $this->db->dbSchema(),
"Schema set" );
648 $this->db->dbSchema( $old );
649 $this->assertNotEquals(
'xxx', $this->db->dbSchema() );
650 $this->assertSame( $oldDomain, $this->db->getDomainId() );
657 $oldDomain = $this->db->getDomainId();
658 $oldDatabase = $this->db->getDBname();
659 $oldSchema = $this->db->dbSchema();
660 $oldPrefix = $this->db->tablePrefix();
662 $this->db->selectDomain(
'testselectdb-xxx' );
663 $this->assertSame(
'testselectdb', $this->db->getDBname() );
664 $this->assertSame(
'', $this->db->dbSchema() );
665 $this->assertSame(
'xxx', $this->db->tablePrefix() );
667 $this->db->selectDomain( $oldDomain );
668 $this->assertSame( $oldDatabase, $this->db->getDBname() );
669 $this->assertSame( $oldSchema, $this->db->dbSchema() );
670 $this->assertSame( $oldPrefix, $this->db->tablePrefix() );
671 $this->assertSame( $oldDomain, $this->db->getDomainId() );
673 $this->db->selectDomain(
'testselectdb-schema-xxx' );
674 $this->assertSame(
'testselectdb', $this->db->getDBname() );
675 $this->assertSame(
'schema', $this->db->dbSchema() );
676 $this->assertSame(
'xxx', $this->db->tablePrefix() );
678 $this->db->selectDomain( $oldDomain );
679 $this->assertSame( $oldDatabase, $this->db->getDBname() );
680 $this->assertSame( $oldSchema, $this->db->dbSchema() );
681 $this->assertSame( $oldPrefix, $this->db->tablePrefix() );
682 $this->assertSame( $oldDomain, $this->db->getDomainId() );