8 use Wikimedia\TestingAccessWrapper;
15 use MediaWikiCoversValidator;
26 $m = Database::NEW_UNCONNECTED;
27 $p = [
'host' =>
'localhost',
'user' =>
'me',
'password' =>
'myself',
'dbname' =>
'i' ];
35 $x = $p + [
'port' => 10000,
'UseWindowsAuth' =>
false ];
38 $x = $p + [
'dbFilePath' =>
'some/file.sqlite' ];
40 $x = $p + [
'dbDirectory' =>
'some/file' ];
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',
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',
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 ) );
571 ->disableOriginalConstructor()
584 ->disableOriginalConstructor()
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() );