Database: Allow selectFieldValues() to accept SQL fragments
[lhc/web/wiklou.git] / tests / phpunit / includes / libs / rdbms / database / DatabaseTest.php
1 <?php
2
3 use Wikimedia\Rdbms\Database;
4 use Wikimedia\Rdbms\IDatabase;
5 use Wikimedia\Rdbms\DatabaseDomain;
6 use Wikimedia\Rdbms\DatabaseMysqli;
7 use Wikimedia\Rdbms\LBFactorySingle;
8 use Wikimedia\Rdbms\TransactionProfiler;
9 use Wikimedia\TestingAccessWrapper;
10 use Wikimedia\Rdbms\DatabaseSqlite;
11 use Wikimedia\Rdbms\DatabasePostgres;
12 use Wikimedia\Rdbms\DatabaseMssql;
13 use Wikimedia\Rdbms\DBUnexpectedError;
14
15 class DatabaseTest extends PHPUnit\Framework\TestCase {
16
17 use MediaWikiCoversValidator;
18
19 protected function setUp() {
20 $this->db = new DatabaseTestHelper( __CLASS__ . '::' . $this->getName() );
21 }
22
23 /**
24 * @dataProvider provideAddQuotes
25 * @covers Wikimedia\Rdbms\Database::factory
26 */
27 public function testFactory() {
28 $m = Database::NEW_UNCONNECTED; // no-connect mode
29 $p = [ 'host' => 'localhost', 'user' => 'me', 'password' => 'myself', 'dbname' => 'i' ];
30
31 $this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'mysqli', $p, $m ) );
32 $this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySqli', $p, $m ) );
33 $this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySQLi', $p, $m ) );
34 $this->assertInstanceOf( DatabasePostgres::class, Database::factory( 'postgres', $p, $m ) );
35 $this->assertInstanceOf( DatabasePostgres::class, Database::factory( 'Postgres', $p, $m ) );
36
37 $x = $p + [ 'port' => 10000, 'UseWindowsAuth' => false ];
38 $this->assertInstanceOf( DatabaseMssql::class, Database::factory( 'mssql', $x, $m ) );
39
40 $x = $p + [ 'dbFilePath' => 'some/file.sqlite' ];
41 $this->assertInstanceOf( DatabaseSqlite::class, Database::factory( 'sqlite', $x, $m ) );
42 $x = $p + [ 'dbDirectory' => 'some/file' ];
43 $this->assertInstanceOf( DatabaseSqlite::class, Database::factory( 'sqlite', $x, $m ) );
44 }
45
46 public static function provideAddQuotes() {
47 return [
48 [ null, 'NULL' ],
49 [ 1234, "'1234'" ],
50 [ 1234.5678, "'1234.5678'" ],
51 [ 'string', "'string'" ],
52 [ 'string\'s cause trouble', "'string\'s cause trouble'" ],
53 ];
54 }
55
56 /**
57 * @dataProvider provideAddQuotes
58 * @covers Wikimedia\Rdbms\Database::addQuotes
59 */
60 public function testAddQuotes( $input, $expected ) {
61 $this->assertEquals( $expected, $this->db->addQuotes( $input ) );
62 }
63
64 public static function provideTableName() {
65 // Formatting is mostly ignored since addIdentifierQuotes is abstract.
66 // For testing of addIdentifierQuotes, see actual Database subclas tests.
67 return [
68 'local' => [
69 'tablename',
70 'tablename',
71 'quoted',
72 ],
73 'local-raw' => [
74 'tablename',
75 'tablename',
76 'raw',
77 ],
78 'shared' => [
79 'sharedb.tablename',
80 'tablename',
81 'quoted',
82 [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => '' ],
83 ],
84 'shared-raw' => [
85 'sharedb.tablename',
86 'tablename',
87 'raw',
88 [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => '' ],
89 ],
90 'shared-prefix' => [
91 'sharedb.sh_tablename',
92 'tablename',
93 'quoted',
94 [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => 'sh_' ],
95 ],
96 'shared-prefix-raw' => [
97 'sharedb.sh_tablename',
98 'tablename',
99 'raw',
100 [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => 'sh_' ],
101 ],
102 'foreign' => [
103 'databasename.tablename',
104 'databasename.tablename',
105 'quoted',
106 ],
107 'foreign-raw' => [
108 'databasename.tablename',
109 'databasename.tablename',
110 'raw',
111 ],
112 ];
113 }
114
115 /**
116 * @dataProvider provideTableName
117 * @covers Wikimedia\Rdbms\Database::tableName
118 */
119 public function testTableName( $expected, $table, $format, array $alias = null ) {
120 if ( $alias ) {
121 $this->db->setTableAliases( [ $table => $alias ] );
122 }
123 $this->assertEquals(
124 $expected,
125 $this->db->tableName( $table, $format ?: 'quoted' )
126 );
127 }
128
129 public function provideTableNamesWithIndexClauseOrJOIN() {
130 return [
131 'one-element array' => [
132 [ 'table' ], [], 'table '
133 ],
134 'comma join' => [
135 [ 'table1', 'table2' ], [], 'table1,table2 '
136 ],
137 'real join' => [
138 [ 'table1', 'table2' ],
139 [ 'table2' => [ 'LEFT JOIN', 't1_id = t2_id' ] ],
140 'table1 LEFT JOIN table2 ON ((t1_id = t2_id))'
141 ],
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\'))'
146 ],
147 'join with parenthesized group' => [
148 [ 'table1', 'n' => [ 'table2', 'table3' ] ],
149 [
150 'table3' => [ 'JOIN', 't2_id = t3_id' ],
151 'n' => [ 'LEFT JOIN', 't1_id = t2_id' ],
152 ],
153 'table1 LEFT JOIN (table2 JOIN table3 ON ((t2_id = t3_id))) ON ((t1_id = t2_id))'
154 ],
155 'join with degenerate parenthesized group' => [
156 [ 'table1', 'n' => [ 't2' => 'table2' ] ],
157 [
158 'n' => [ 'LEFT JOIN', 't1_id = t2_id' ],
159 ],
160 'table1 LEFT JOIN table2 t2 ON ((t1_id = t2_id))'
161 ],
162 ];
163 }
164
165 /**
166 * @dataProvider provideTableNamesWithIndexClauseOrJOIN
167 * @covers Wikimedia\Rdbms\Database::tableNamesWithIndexClauseOrJOIN
168 */
169 public function testTableNamesWithIndexClauseOrJOIN( $tables, $join_conds, $expect ) {
170 $clause = TestingAccessWrapper::newFromObject( $this->db )
171 ->tableNamesWithIndexClauseOrJOIN( $tables, [], [], $join_conds );
172 $this->assertSame( $expect, $clause );
173 }
174
175 /**
176 * @covers Wikimedia\Rdbms\Database::onTransactionCommitOrIdle
177 * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
178 */
179 public function testTransactionIdle() {
180 $db = $this->db;
181
182 $db->clearFlag( DBO_TRX );
183 $called = false;
184 $flagSet = null;
185 $callback = function ( $trigger, IDatabase $db ) use ( &$flagSet, &$called ) {
186 $called = true;
187 $flagSet = $db->getFlag( DBO_TRX );
188 };
189
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' );
194
195 $flagSet = null;
196 $called = false;
197 $db->startAtomic( __METHOD__ );
198 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
199 $this->assertFalse( $called, 'Callback not reached during TRX' );
200 $db->endAtomic( __METHOD__ );
201
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' );
205
206 $db->clearFlag( DBO_TRX );
207 $db->onTransactionCommitOrIdle(
208 function ( $trigger, IDatabase $db ) {
209 $db->setFlag( DBO_TRX );
210 },
211 __METHOD__
212 );
213 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
214 }
215
216 /**
217 * @covers Wikimedia\Rdbms\Database::onTransactionCommitOrIdle
218 * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
219 */
220 public function testTransactionIdle_TRX() {
221 $db = $this->getMockDB( [ 'isOpen', 'ping', 'getDBname' ] );
222 $db->method( 'isOpen' )->willReturn( true );
223 $db->method( 'ping' )->willReturn( true );
224 $db->method( 'getDBname' )->willReturn( '' );
225 $db->setFlag( DBO_TRX );
226
227 $lbFactory = LBFactorySingle::newFromConnection( $db );
228 // Ask for the connection so that LB sets internal state
229 // about this connection being the master connection
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' );
234
235 $called = false;
236 $flagSet = null;
237 $callback = function () use ( $db, &$flagSet, &$called ) {
238 $called = true;
239 $flagSet = $db->getFlag( DBO_TRX );
240 };
241
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' );
246
247 $called = false;
248 $lbFactory->beginMasterChanges( __METHOD__ );
249 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
250 $this->assertFalse( $called, 'Not called when lb-transaction is active' );
251
252 $lbFactory->commitMasterChanges( __METHOD__ );
253 $this->assertTrue( $called, 'Called when lb-transaction is committed' );
254
255 $called = false;
256 $lbFactory->beginMasterChanges( __METHOD__ );
257 $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
258 $this->assertFalse( $called, 'Not called when lb-transaction is active' );
259
260 $lbFactory->rollbackMasterChanges( __METHOD__ );
261 $this->assertFalse( $called, 'Not called when lb-transaction is rolled back' );
262
263 $lbFactory->commitMasterChanges( __METHOD__ );
264 $this->assertFalse( $called, 'Not called in next round commit' );
265
266 $db->setFlag( DBO_TRX );
267 try {
268 $db->onTransactionCommitOrIdle( function () {
269 throw new RuntimeException( 'test' );
270 } );
271 $this->fail( "Exception not thrown" );
272 } catch ( RuntimeException $e ) {
273 $this->assertTrue( $db->getFlag( DBO_TRX ) );
274 }
275 }
276
277 /**
278 * @covers Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle
279 * @covers Wikimedia\Rdbms\Database::runOnTransactionPreCommitCallbacks
280 */
281 public function testTransactionPreCommitOrIdle() {
282 $db = $this->getMockDB( [ 'isOpen' ] );
283 $db->method( 'isOpen' )->willReturn( true );
284 $db->clearFlag( DBO_TRX );
285
286 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX is not set' );
287
288 $called = false;
289 $db->onTransactionPreCommitOrIdle(
290 function ( IDatabase $db ) use ( &$called ) {
291 $called = true;
292 },
293 __METHOD__
294 );
295 $this->assertTrue( $called, 'Called when idle' );
296
297 $db->begin( __METHOD__ );
298 $called = false;
299 $db->onTransactionPreCommitOrIdle(
300 function ( IDatabase $db ) use ( &$called ) {
301 $called = true;
302 },
303 __METHOD__
304 );
305 $this->assertFalse( $called, 'Not called when transaction is active' );
306 $db->commit( __METHOD__ );
307 $this->assertTrue( $called, 'Called when transaction is committed' );
308 }
309
310 /**
311 * @covers Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle
312 * @covers Wikimedia\Rdbms\Database::runOnTransactionPreCommitCallbacks
313 */
314 public function testTransactionPreCommitOrIdle_TRX() {
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' );
319 $db->setFlag( DBO_TRX );
320
321 $lbFactory = LBFactorySingle::newFromConnection( $db );
322 // Ask for the connection so that LB sets internal state
323 // about this connection being the master connection
324 $lb = $lbFactory->getMainLB();
325 $conn = $lb->openConnection( $lb->getWriterIndex() );
326 $this->assertSame( $db, $conn, 'Same DB instance' );
327
328 $this->assertFalse( $lb->hasMasterChanges() );
329 $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX is set' );
330 $called = false;
331 $callback = function ( IDatabase $db ) use ( &$called ) {
332 $called = true;
333 };
334 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
335 $this->assertTrue( $called, 'Called when idle if DBO_TRX is set' );
336 $called = false;
337 $lbFactory->commitMasterChanges();
338 $this->assertFalse( $called );
339
340 $called = false;
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' );
346
347 $called = false;
348 $lbFactory->beginMasterChanges( __METHOD__ );
349 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
350 $this->assertFalse( $called, 'Not called when lb-transaction is active' );
351
352 $lbFactory->rollbackMasterChanges( __METHOD__ );
353 $this->assertFalse( $called, 'Not called when lb-transaction is rolled back' );
354
355 $lbFactory->commitMasterChanges( __METHOD__ );
356 $this->assertFalse( $called, 'Not called in next round commit' );
357 }
358
359 /**
360 * @covers Wikimedia\Rdbms\Database::onTransactionResolution
361 * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
362 */
363 public function testTransactionResolution() {
364 $db = $this->db;
365
366 $db->clearFlag( DBO_TRX );
367 $db->begin( __METHOD__ );
368 $called = false;
369 $db->onTransactionResolution( function ( $trigger, IDatabase $db ) use ( &$called ) {
370 $called = true;
371 $db->setFlag( DBO_TRX );
372 } );
373 $db->commit( __METHOD__ );
374 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
375 $this->assertTrue( $called, 'Callback reached' );
376
377 $db->clearFlag( DBO_TRX );
378 $db->begin( __METHOD__ );
379 $called = false;
380 $db->onTransactionResolution( function ( $trigger, IDatabase $db ) use ( &$called ) {
381 $called = true;
382 $db->setFlag( DBO_TRX );
383 } );
384 $db->rollback( __METHOD__ );
385 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
386 $this->assertTrue( $called, 'Callback reached' );
387 }
388
389 /**
390 * @covers Wikimedia\Rdbms\Database::setTransactionListener
391 */
392 public function testTransactionListener() {
393 $db = $this->db;
394
395 $db->setTransactionListener( 'ping', function () use ( $db, &$called ) {
396 $called = true;
397 } );
398
399 $called = false;
400 $db->begin( __METHOD__ );
401 $db->commit( __METHOD__ );
402 $this->assertTrue( $called, 'Callback reached' );
403
404 $called = false;
405 $db->begin( __METHOD__ );
406 $db->commit( __METHOD__ );
407 $this->assertTrue( $called, 'Callback still reached' );
408
409 $called = false;
410 $db->begin( __METHOD__ );
411 $db->rollback( __METHOD__ );
412 $this->assertTrue( $called, 'Callback reached' );
413
414 $db->setTransactionListener( 'ping', null );
415 $called = false;
416 $db->begin( __METHOD__ );
417 $db->commit( __METHOD__ );
418 $this->assertFalse( $called, 'Callback not reached' );
419 }
420
421 /**
422 * Use this mock instead of DatabaseTestHelper for cases where
423 * DatabaseTestHelper is too inflexibile due to mocking too much
424 * or being too restrictive about fname matching (e.g. for tests
425 * that assert behaviour when the name is a mismatch, we need to
426 * catch the error here instead of there).
427 *
428 * @return Database
429 */
430 private function getMockDB( $methods = [] ) {
431 static $abstractMethods = [
432 'fetchAffectedRowCount',
433 'closeConnection',
434 'dataSeek',
435 'doQuery',
436 'fetchObject', 'fetchRow',
437 'fieldInfo', 'fieldName',
438 'getSoftwareLink', 'getServerVersion',
439 'getType',
440 'indexInfo',
441 'insertId',
442 'lastError', 'lastErrno',
443 'numFields', 'numRows',
444 'open',
445 'strencode',
446 'tableExists'
447 ];
448 $db = $this->getMockBuilder( Database::class )
449 ->disableOriginalConstructor()
450 ->setMethods( array_values( array_unique( array_merge(
451 $abstractMethods,
452 $methods
453 ) ) ) )
454 ->getMock();
455 $wdb = TestingAccessWrapper::newFromObject( $db );
456 $wdb->trxProfiler = new TransactionProfiler();
457 $wdb->connLogger = new \Psr\Log\NullLogger();
458 $wdb->queryLogger = new \Psr\Log\NullLogger();
459 $wdb->currentDomain = DatabaseDomain::newUnspecified();
460 return $db;
461 }
462
463 /**
464 * @covers Wikimedia\Rdbms\Database::flushSnapshot
465 */
466 public function testFlushSnapshot() {
467 $db = $this->getMockDB( [ 'isOpen' ] );
468 $db->method( 'isOpen' )->willReturn( true );
469
470 $db->flushSnapshot( __METHOD__ ); // ok
471 $db->flushSnapshot( __METHOD__ ); // ok
472
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__ ); // ok
477 $db->restoreFlags( $db::RESTORE_PRIOR );
478
479 $this->assertFalse( (bool)$db->trxLevel(), "Transaction cleared." );
480 }
481
482 /**
483 * @covers Wikimedia\Rdbms\Database::getScopedLockAndFlush
484 * @covers Wikimedia\Rdbms\Database::lock
485 * @covers Wikimedia\Rdbms\Database::unlock
486 * @covers Wikimedia\Rdbms\Database::lockIsFree
487 */
488 public function testGetScopedLock() {
489 $db = $this->getMockDB( [ 'isOpen', 'getDBname' ] );
490 $db->method( 'isOpen' )->willReturn( true );
491 $db->method( 'getDBname' )->willReturn( 'unittest' );
492
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() );
500
501 $db->setFlag( DBO_TRX );
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__ ) );
507 $db->clearFlag( DBO_TRX );
508
509 // Pending writes with DBO_TRX
510 $this->assertEquals( 0, $db->trxLevel() );
511 $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
512 $db->setFlag( DBO_TRX );
513 $db->query( "DELETE FROM test WHERE t = 1" ); // trigger DBO_TRX transaction before lock
514 try {
515 $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
516 $this->fail( "Exception not reached" );
517 } catch ( DBUnexpectedError $e ) {
518 $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
519 $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ), 'Lock not acquired' );
520 }
521 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
522 // Pending writes without DBO_TRX
523 $db->clearFlag( DBO_TRX );
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" ); // trigger DBO_TRX transaction before lock
528 try {
529 $lock = $db->getScopedLockAndFlush( 'meow2', __METHOD__, 1 );
530 $this->fail( "Exception not reached" );
531 } catch ( DBUnexpectedError $e ) {
532 $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
533 $this->assertTrue( $db->lockIsFree( 'meow2', __METHOD__ ), 'Lock not acquired' );
534 }
535 $db->rollback( __METHOD__ );
536 // No pending writes, with DBO_TRX
537 $db->setFlag( DBO_TRX );
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 );
546 // No pending writes, without DBO_TRX
547 $db->clearFlag( DBO_TRX );
548 $this->assertEquals( 0, $db->trxLevel() );
549 $this->assertTrue( $db->lockIsFree( 'wuff2', __METHOD__ ) );
550 $db->begin( __METHOD__ );
551 try {
552 $lock = $db->getScopedLockAndFlush( 'wuff2', __METHOD__, 1 );
553 $this->fail( "Exception not reached" );
554 } catch ( DBUnexpectedError $e ) {
555 $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
556 $this->assertFalse( $db->lockIsFree( 'wuff2', __METHOD__ ), 'Lock not acquired' );
557 }
558 $db->rollback( __METHOD__ );
559 }
560
561 /**
562 * @covers Wikimedia\Rdbms\Database::getFlag
563 * @covers Wikimedia\Rdbms\Database::setFlag
564 * @covers Wikimedia\Rdbms\Database::restoreFlags
565 */
566 public function testFlagSetting() {
567 $db = $this->db;
568 $origTrx = $db->getFlag( DBO_TRX );
569 $origSsl = $db->getFlag( DBO_SSL );
570
571 $origTrx
572 ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
573 : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
574 $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
575
576 $origSsl
577 ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
578 : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
579 $this->assertEquals( !$origSsl, $db->getFlag( DBO_SSL ) );
580
581 $db->restoreFlags( $db::RESTORE_INITIAL );
582 $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
583 $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
584
585 $origTrx
586 ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
587 : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
588 $origSsl
589 ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
590 : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
591
592 $db->restoreFlags();
593 $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
594 $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
595
596 $db->restoreFlags();
597 $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
598 $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
599 }
600
601 /**
602 * @expectedException UnexpectedValueException
603 * @covers Wikimedia\Rdbms\Database::setFlag
604 */
605 public function testDBOIgnoreSet() {
606 $db = $this->getMockBuilder( DatabaseMysqli::class )
607 ->disableOriginalConstructor()
608 ->setMethods( null )
609 ->getMock();
610
611 $db->setFlag( Database::DBO_IGNORE );
612 }
613
614 /**
615 * @expectedException UnexpectedValueException
616 * @covers Wikimedia\Rdbms\Database::clearFlag
617 */
618 public function testDBOIgnoreClear() {
619 $db = $this->getMockBuilder( DatabaseMysqli::class )
620 ->disableOriginalConstructor()
621 ->setMethods( null )
622 ->getMock();
623
624 $db->clearFlag( Database::DBO_IGNORE );
625 }
626
627 /**
628 * @covers Wikimedia\Rdbms\Database::tablePrefix
629 * @covers Wikimedia\Rdbms\Database::dbSchema
630 */
631 public function testSchemaAndPrefixMutators() {
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() );
641
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() );
651 }
652
653 /**
654 * @covers Wikimedia\Rdbms\Database::selectDomain
655 */
656 public function testSelectDomain() {
657 $oldDomain = $this->db->getDomainId();
658 $oldDatabase = $this->db->getDBname();
659 $oldSchema = $this->db->dbSchema();
660 $oldPrefix = $this->db->tablePrefix();
661
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() );
666
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() );
672
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() );
677
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() );
683 }
684
685 }