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