f08b3767dd610480b1761127fcf236fd35253ecc
[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::onTransactionIdle
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->onTransactionIdle( $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->onTransactionIdle( $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->onTransactionIdle(
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::onTransactionIdle
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->onTransactionIdle( $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->onTransactionIdle( $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->onTransactionIdle( $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
265 /**
266 * @covers Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle
267 * @covers Wikimedia\Rdbms\Database::runOnTransactionPreCommitCallbacks
268 */
269 public function testTransactionPreCommitOrIdle() {
270 $db = $this->getMockDB( [ 'isOpen' ] );
271 $db->method( 'isOpen' )->willReturn( true );
272 $db->clearFlag( DBO_TRX );
273
274 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX is not set' );
275
276 $called = false;
277 $db->onTransactionPreCommitOrIdle(
278 function ( IDatabase $db ) use ( &$called ) {
279 $called = true;
280 },
281 __METHOD__
282 );
283 $this->assertTrue( $called, 'Called when idle' );
284
285 $db->begin( __METHOD__ );
286 $called = false;
287 $db->onTransactionPreCommitOrIdle(
288 function ( IDatabase $db ) use ( &$called ) {
289 $called = true;
290 },
291 __METHOD__
292 );
293 $this->assertFalse( $called, 'Not called when transaction is active' );
294 $db->commit( __METHOD__ );
295 $this->assertTrue( $called, 'Called when transaction is committed' );
296 }
297
298 /**
299 * @covers Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle
300 * @covers Wikimedia\Rdbms\Database::runOnTransactionPreCommitCallbacks
301 */
302 public function testTransactionPreCommitOrIdle_TRX() {
303 $db = $this->getMockDB( [ 'isOpen', 'ping' ] );
304 $db->method( 'isOpen' )->willReturn( true );
305 $db->method( 'ping' )->willReturn( true );
306 $db->setFlag( DBO_TRX );
307
308 $lbFactory = LBFactorySingle::newFromConnection( $db );
309 // Ask for the connection so that LB sets internal state
310 // about this connection being the master connection
311 $lb = $lbFactory->getMainLB();
312 $conn = $lb->openConnection( $lb->getWriterIndex() );
313 $this->assertSame( $db, $conn, 'Same DB instance' );
314
315 $this->assertFalse( $lb->hasMasterChanges() );
316 $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX is set' );
317 $called = false;
318 $callback = function ( IDatabase $db ) use ( &$called ) {
319 $called = true;
320 };
321 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
322 $this->assertTrue( $called, 'Called when idle if DBO_TRX is set' );
323 $called = false;
324 $lbFactory->commitMasterChanges();
325 $this->assertFalse( $called );
326
327 $called = false;
328 $lbFactory->beginMasterChanges( __METHOD__ );
329 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
330 $this->assertFalse( $called, 'Not called when lb-transaction is active' );
331 $lbFactory->commitMasterChanges( __METHOD__ );
332 $this->assertTrue( $called, 'Called when lb-transaction is committed' );
333
334 $called = false;
335 $lbFactory->beginMasterChanges( __METHOD__ );
336 $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
337 $this->assertFalse( $called, 'Not called when lb-transaction is active' );
338
339 $lbFactory->rollbackMasterChanges( __METHOD__ );
340 $this->assertFalse( $called, 'Not called when lb-transaction is rolled back' );
341
342 $lbFactory->commitMasterChanges( __METHOD__ );
343 $this->assertFalse( $called, 'Not called in next round commit' );
344 }
345
346 /**
347 * @covers Wikimedia\Rdbms\Database::onTransactionResolution
348 * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
349 */
350 public function testTransactionResolution() {
351 $db = $this->db;
352
353 $db->clearFlag( DBO_TRX );
354 $db->begin( __METHOD__ );
355 $called = false;
356 $db->onTransactionResolution( function ( $trigger, IDatabase $db ) use ( &$called ) {
357 $called = true;
358 $db->setFlag( DBO_TRX );
359 } );
360 $db->commit( __METHOD__ );
361 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
362 $this->assertTrue( $called, 'Callback reached' );
363
364 $db->clearFlag( DBO_TRX );
365 $db->begin( __METHOD__ );
366 $called = false;
367 $db->onTransactionResolution( function ( $trigger, IDatabase $db ) use ( &$called ) {
368 $called = true;
369 $db->setFlag( DBO_TRX );
370 } );
371 $db->rollback( __METHOD__ );
372 $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
373 $this->assertTrue( $called, 'Callback reached' );
374 }
375
376 /**
377 * @covers Wikimedia\Rdbms\Database::setTransactionListener
378 */
379 public function testTransactionListener() {
380 $db = $this->db;
381
382 $db->setTransactionListener( 'ping', function () use ( $db, &$called ) {
383 $called = true;
384 } );
385
386 $called = false;
387 $db->begin( __METHOD__ );
388 $db->commit( __METHOD__ );
389 $this->assertTrue( $called, 'Callback reached' );
390
391 $called = false;
392 $db->begin( __METHOD__ );
393 $db->commit( __METHOD__ );
394 $this->assertTrue( $called, 'Callback still reached' );
395
396 $called = false;
397 $db->begin( __METHOD__ );
398 $db->rollback( __METHOD__ );
399 $this->assertTrue( $called, 'Callback reached' );
400
401 $db->setTransactionListener( 'ping', null );
402 $called = false;
403 $db->begin( __METHOD__ );
404 $db->commit( __METHOD__ );
405 $this->assertFalse( $called, 'Callback not reached' );
406 }
407
408 /**
409 * Use this mock instead of DatabaseTestHelper for cases where
410 * DatabaseTestHelper is too inflexibile due to mocking too much
411 * or being too restrictive about fname matching (e.g. for tests
412 * that assert behaviour when the name is a mismatch, we need to
413 * catch the error here instead of there).
414 *
415 * @return Database
416 */
417 private function getMockDB( $methods = [] ) {
418 static $abstractMethods = [
419 'fetchAffectedRowCount',
420 'closeConnection',
421 'dataSeek',
422 'doQuery',
423 'fetchObject', 'fetchRow',
424 'fieldInfo', 'fieldName',
425 'getSoftwareLink', 'getServerVersion',
426 'getType',
427 'indexInfo',
428 'insertId',
429 'lastError', 'lastErrno',
430 'numFields', 'numRows',
431 'open',
432 'strencode',
433 ];
434 $db = $this->getMockBuilder( Database::class )
435 ->disableOriginalConstructor()
436 ->setMethods( array_values( array_unique( array_merge(
437 $abstractMethods,
438 $methods
439 ) ) ) )
440 ->getMock();
441 $wdb = TestingAccessWrapper::newFromObject( $db );
442 $wdb->trxProfiler = new TransactionProfiler();
443 $wdb->connLogger = new \Psr\Log\NullLogger();
444 $wdb->queryLogger = new \Psr\Log\NullLogger();
445 return $db;
446 }
447
448 /**
449 * @covers Wikimedia\Rdbms\Database::flushSnapshot
450 */
451 public function testFlushSnapshot() {
452 $db = $this->getMockDB( [ 'isOpen' ] );
453 $db->method( 'isOpen' )->willReturn( true );
454
455 $db->flushSnapshot( __METHOD__ ); // ok
456 $db->flushSnapshot( __METHOD__ ); // ok
457
458 $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
459 $db->query( 'SELECT 1', __METHOD__ );
460 $this->assertTrue( (bool)$db->trxLevel(), "Transaction started." );
461 $db->flushSnapshot( __METHOD__ ); // ok
462 $db->restoreFlags( $db::RESTORE_PRIOR );
463
464 $this->assertFalse( (bool)$db->trxLevel(), "Transaction cleared." );
465 }
466
467 /**
468 * @covers Wikimedia\Rdbms\Database::getScopedLockAndFlush
469 * @covers Wikimedia\Rdbms\Database::lock
470 * @covers Wikimedia\Rdbms\Database::unlock
471 * @covers Wikimedia\Rdbms\Database::lockIsFree
472 */
473 public function testGetScopedLock() {
474 $db = $this->getMockDB( [ 'isOpen' ] );
475 $db->method( 'isOpen' )->willReturn( true );
476
477 $this->assertEquals( 0, $db->trxLevel() );
478 $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
479 $this->assertEquals( true, $db->lock( 'x', __METHOD__ ) );
480 $this->assertEquals( false, $db->lockIsFree( 'x', __METHOD__ ) );
481 $this->assertEquals( true, $db->unlock( 'x', __METHOD__ ) );
482 $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
483 $this->assertEquals( 0, $db->trxLevel() );
484
485 $db->setFlag( DBO_TRX );
486 $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
487 $this->assertEquals( true, $db->lock( 'x', __METHOD__ ) );
488 $this->assertEquals( false, $db->lockIsFree( 'x', __METHOD__ ) );
489 $this->assertEquals( true, $db->unlock( 'x', __METHOD__ ) );
490 $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
491 $db->clearFlag( DBO_TRX );
492
493 // Pending writes with DBO_TRX
494 $this->assertEquals( 0, $db->trxLevel() );
495 $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
496 $db->setFlag( DBO_TRX );
497 $db->query( "DELETE FROM test WHERE t = 1" ); // trigger DBO_TRX transaction before lock
498 try {
499 $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
500 $this->fail( "Exception not reached" );
501 } catch ( DBUnexpectedError $e ) {
502 $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
503 $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ), 'Lock not acquired' );
504 }
505 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
506 // Pending writes without DBO_TRX
507 $db->clearFlag( DBO_TRX );
508 $this->assertEquals( 0, $db->trxLevel() );
509 $this->assertTrue( $db->lockIsFree( 'meow2', __METHOD__ ) );
510 $db->begin( __METHOD__ );
511 $db->query( "DELETE FROM test WHERE t = 1" ); // trigger DBO_TRX transaction before lock
512 try {
513 $lock = $db->getScopedLockAndFlush( 'meow2', __METHOD__, 1 );
514 $this->fail( "Exception not reached" );
515 } catch ( DBUnexpectedError $e ) {
516 $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
517 $this->assertTrue( $db->lockIsFree( 'meow2', __METHOD__ ), 'Lock not acquired' );
518 }
519 $db->rollback( __METHOD__ );
520 // No pending writes, with DBO_TRX
521 $db->setFlag( DBO_TRX );
522 $this->assertEquals( 0, $db->trxLevel() );
523 $this->assertTrue( $db->lockIsFree( 'wuff', __METHOD__ ) );
524 $db->query( "SELECT 1", __METHOD__ );
525 $this->assertEquals( 1, $db->trxLevel() );
526 $lock = $db->getScopedLockAndFlush( 'wuff', __METHOD__, 1 );
527 $this->assertEquals( 0, $db->trxLevel() );
528 $this->assertFalse( $db->lockIsFree( 'wuff', __METHOD__ ), 'Lock already acquired' );
529 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
530 // No pending writes, without DBO_TRX
531 $db->clearFlag( DBO_TRX );
532 $this->assertEquals( 0, $db->trxLevel() );
533 $this->assertTrue( $db->lockIsFree( 'wuff2', __METHOD__ ) );
534 $db->begin( __METHOD__ );
535 try {
536 $lock = $db->getScopedLockAndFlush( 'wuff2', __METHOD__, 1 );
537 $this->fail( "Exception not reached" );
538 } catch ( DBUnexpectedError $e ) {
539 $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
540 $this->assertFalse( $db->lockIsFree( 'wuff2', __METHOD__ ), 'Lock not acquired' );
541 }
542 $db->rollback( __METHOD__ );
543 }
544
545 /**
546 * @covers Wikimedia\Rdbms\Database::getFlag
547 * @covers Wikimedia\Rdbms\Database::setFlag
548 * @covers Wikimedia\Rdbms\Database::restoreFlags
549 */
550 public function testFlagSetting() {
551 $db = $this->db;
552 $origTrx = $db->getFlag( DBO_TRX );
553 $origSsl = $db->getFlag( DBO_SSL );
554
555 $origTrx
556 ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
557 : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
558 $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
559
560 $origSsl
561 ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
562 : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
563 $this->assertEquals( !$origSsl, $db->getFlag( DBO_SSL ) );
564
565 $db->restoreFlags( $db::RESTORE_INITIAL );
566 $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
567 $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
568
569 $origTrx
570 ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
571 : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
572 $origSsl
573 ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
574 : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
575
576 $db->restoreFlags();
577 $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
578 $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
579
580 $db->restoreFlags();
581 $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
582 $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
583 }
584
585 /**
586 * @expectedException UnexpectedValueException
587 * @covers Wikimedia\Rdbms\Database::setFlag
588 */
589 public function testDBOIgnoreSet() {
590 $db = $this->getMockBuilder( DatabaseMysqli::class )
591 ->disableOriginalConstructor()
592 ->setMethods( null )
593 ->getMock();
594
595 $db->setFlag( Database::DBO_IGNORE );
596 }
597
598 /**
599 * @expectedException UnexpectedValueException
600 * @covers Wikimedia\Rdbms\Database::clearFlag
601 */
602 public function testDBOIgnoreClear() {
603 $db = $this->getMockBuilder( DatabaseMysqli::class )
604 ->disableOriginalConstructor()
605 ->setMethods( null )
606 ->getMock();
607
608 $db->clearFlag( Database::DBO_IGNORE );
609 }
610
611 /**
612 * @covers Wikimedia\Rdbms\Database::tablePrefix
613 * @covers Wikimedia\Rdbms\Database::dbSchema
614 */
615 public function testMutators() {
616 $old = $this->db->tablePrefix();
617 $this->assertInternalType( 'string', $old, 'Prefix is string' );
618 $this->assertEquals( $old, $this->db->tablePrefix(), "Prefix unchanged" );
619 $this->assertEquals( $old, $this->db->tablePrefix( 'xxx' ) );
620 $this->assertEquals( 'xxx', $this->db->tablePrefix(), "Prefix set" );
621 $this->db->tablePrefix( $old );
622 $this->assertNotEquals( 'xxx', $this->db->tablePrefix() );
623
624 $old = $this->db->dbSchema();
625 $this->assertInternalType( 'string', $old, 'Schema is string' );
626 $this->assertEquals( $old, $this->db->dbSchema(), "Schema unchanged" );
627 $this->assertEquals( $old, $this->db->dbSchema( 'xxx' ) );
628 $this->assertEquals( 'xxx', $this->db->dbSchema(), "Schema set" );
629 $this->db->dbSchema( $old );
630 $this->assertNotEquals( 'xxx', $this->db->dbSchema() );
631 }
632 }