MediaWiki  master
DatabaseTest.php
Go to the documentation of this file.
1 <?php
2 
14 
17  private $db;
18 
20 
21  protected function setUp() {
22  $this->db = new DatabaseTestHelper( __CLASS__ . '::' . $this->getName() );
23  }
24 
29  public function testFactory() {
30  $m = Database::NEW_UNCONNECTED; // no-connect mode
31  $p = [ 'host' => 'localhost', 'user' => 'me', 'password' => 'myself', 'dbname' => 'i' ];
32 
33  $this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'mysqli', $p, $m ) );
34  $this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySqli', $p, $m ) );
35  $this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySQLi', $p, $m ) );
36  $this->assertInstanceOf( DatabasePostgres::class, Database::factory( 'postgres', $p, $m ) );
37  $this->assertInstanceOf( DatabasePostgres::class, Database::factory( 'Postgres', $p, $m ) );
38 
39  $x = $p + [ 'port' => 10000, 'UseWindowsAuth' => false ];
40  $this->assertInstanceOf( DatabaseMssql::class, Database::factory( 'mssql', $x, $m ) );
41 
42  $x = $p + [ 'dbFilePath' => 'some/file.sqlite' ];
43  $this->assertInstanceOf( DatabaseSqlite::class, Database::factory( 'sqlite', $x, $m ) );
44  $x = $p + [ 'dbDirectory' => 'some/file' ];
45  $this->assertInstanceOf( DatabaseSqlite::class, Database::factory( 'sqlite', $x, $m ) );
46  }
47 
48  public static function provideAddQuotes() {
49  return [
50  [ null, 'NULL' ],
51  [ 1234, "'1234'" ],
52  [ 1234.5678, "'1234.5678'" ],
53  [ 'string', "'string'" ],
54  [ 'string\'s cause trouble', "'string\'s cause trouble'" ],
55  ];
56  }
57 
62  public function testAddQuotes( $input, $expected ) {
63  $this->assertEquals( $expected, $this->db->addQuotes( $input ) );
64  }
65 
66  public static function provideTableName() {
67  // Formatting is mostly ignored since addIdentifierQuotes is abstract.
68  // For testing of addIdentifierQuotes, see actual Database subclas tests.
69  return [
70  'local' => [
71  'tablename',
72  'tablename',
73  'quoted',
74  ],
75  'local-raw' => [
76  'tablename',
77  'tablename',
78  'raw',
79  ],
80  'shared' => [
81  'sharedb.tablename',
82  'tablename',
83  'quoted',
84  [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => '' ],
85  ],
86  'shared-raw' => [
87  'sharedb.tablename',
88  'tablename',
89  'raw',
90  [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => '' ],
91  ],
92  'shared-prefix' => [
93  'sharedb.sh_tablename',
94  'tablename',
95  'quoted',
96  [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => 'sh_' ],
97  ],
98  'shared-prefix-raw' => [
99  'sharedb.sh_tablename',
100  'tablename',
101  'raw',
102  [ 'dbname' => 'sharedb', 'schema' => null, 'prefix' => 'sh_' ],
103  ],
104  'foreign' => [
105  'databasename.tablename',
106  'databasename.tablename',
107  'quoted',
108  ],
109  'foreign-raw' => [
110  'databasename.tablename',
111  'databasename.tablename',
112  'raw',
113  ],
114  ];
115  }
116 
121  public function testTableName( $expected, $table, $format, array $alias = null ) {
122  if ( $alias ) {
123  $this->db->setTableAliases( [ $table => $alias ] );
124  }
125  $this->assertEquals(
126  $expected,
127  $this->db->tableName( $table, $format ?: 'quoted' )
128  );
129  }
130 
132  return [
133  'one-element array' => [
134  [ 'table' ], [], 'table '
135  ],
136  'comma join' => [
137  [ 'table1', 'table2' ], [], 'table1,table2 '
138  ],
139  'real join' => [
140  [ 'table1', 'table2' ],
141  [ 'table2' => [ 'LEFT JOIN', 't1_id = t2_id' ] ],
142  'table1 LEFT JOIN table2 ON ((t1_id = t2_id))'
143  ],
144  'real join with multiple conditionals' => [
145  [ 'table1', 'table2' ],
146  [ 'table2' => [ 'LEFT JOIN', [ 't1_id = t2_id', 't2_x = \'X\'' ] ] ],
147  'table1 LEFT JOIN table2 ON ((t1_id = t2_id) AND (t2_x = \'X\'))'
148  ],
149  'join with parenthesized group' => [
150  [ 'table1', 'n' => [ 'table2', 'table3' ] ],
151  [
152  'table3' => [ 'JOIN', 't2_id = t3_id' ],
153  'n' => [ 'LEFT JOIN', 't1_id = t2_id' ],
154  ],
155  'table1 LEFT JOIN (table2 JOIN table3 ON ((t2_id = t3_id))) ON ((t1_id = t2_id))'
156  ],
157  'join with degenerate parenthesized group' => [
158  [ 'table1', 'n' => [ 't2' => 'table2' ] ],
159  [
160  'n' => [ 'LEFT JOIN', 't1_id = t2_id' ],
161  ],
162  'table1 LEFT JOIN table2 t2 ON ((t1_id = t2_id))'
163  ],
164  ];
165  }
166 
171  public function testTableNamesWithIndexClauseOrJOIN( $tables, $join_conds, $expect ) {
172  $clause = TestingAccessWrapper::newFromObject( $this->db )
173  ->tableNamesWithIndexClauseOrJOIN( $tables, [], [], $join_conds );
174  $this->assertSame( $expect, $clause );
175  }
176 
181  public function testTransactionIdle() {
182  $db = $this->db;
183 
184  $db->clearFlag( DBO_TRX );
185  $called = false;
186  $flagSet = null;
187  $callback = function ( $trigger, IDatabase $db ) use ( &$flagSet, &$called ) {
188  $called = true;
189  $flagSet = $db->getFlag( DBO_TRX );
190  };
191 
192  $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
193  $this->assertTrue( $called, 'Callback reached' );
194  $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
195  $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX still default' );
196 
197  $flagSet = null;
198  $called = false;
199  $db->startAtomic( __METHOD__ );
200  $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
201  $this->assertFalse( $called, 'Callback not reached during TRX' );
202  $db->endAtomic( __METHOD__ );
203 
204  $this->assertTrue( $called, 'Callback reached after COMMIT' );
205  $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
206  $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
207 
208  $db->clearFlag( DBO_TRX );
210  function ( $trigger, IDatabase $db ) {
211  $db->setFlag( DBO_TRX );
212  },
213  __METHOD__
214  );
215  $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
216  }
217 
222  public function testTransactionIdle_TRX() {
223  $db = $this->getMockDB( [ 'isOpen', 'ping', 'getDBname' ] );
224  $db->method( 'isOpen' )->willReturn( true );
225  $db->method( 'ping' )->willReturn( true );
226  $db->method( 'getDBname' )->willReturn( '' );
227  $db->setFlag( DBO_TRX );
228 
229  $lbFactory = LBFactorySingle::newFromConnection( $db );
230  // Ask for the connection so that LB sets internal state
231  // about this connection being the master connection
232  $lb = $lbFactory->getMainLB();
233  $conn = $lb->openConnection( $lb->getWriterIndex() );
234  $this->assertSame( $db, $conn, 'Same DB instance' );
235  $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX is set' );
236 
237  $called = false;
238  $flagSet = null;
239  $callback = function () use ( $db, &$flagSet, &$called ) {
240  $called = true;
241  $flagSet = $db->getFlag( DBO_TRX );
242  };
243 
244  $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
245  $this->assertTrue( $called, 'Called when idle if DBO_TRX is set' );
246  $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
247  $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX still default' );
248 
249  $called = false;
250  $lbFactory->beginMasterChanges( __METHOD__ );
251  $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
252  $this->assertFalse( $called, 'Not called when lb-transaction is active' );
253 
254  $lbFactory->commitMasterChanges( __METHOD__ );
255  $this->assertTrue( $called, 'Called when lb-transaction is committed' );
256 
257  $called = false;
258  $lbFactory->beginMasterChanges( __METHOD__ );
259  $db->onTransactionCommitOrIdle( $callback, __METHOD__ );
260  $this->assertFalse( $called, 'Not called when lb-transaction is active' );
261 
262  $lbFactory->rollbackMasterChanges( __METHOD__ );
263  $this->assertFalse( $called, 'Not called when lb-transaction is rolled back' );
264 
265  $lbFactory->commitMasterChanges( __METHOD__ );
266  $this->assertFalse( $called, 'Not called in next round commit' );
267 
268  $db->setFlag( DBO_TRX );
269  try {
270  $db->onTransactionCommitOrIdle( function () {
271  throw new RuntimeException( 'test' );
272  } );
273  $this->fail( "Exception not thrown" );
274  } catch ( RuntimeException $e ) {
275  $this->assertTrue( $db->getFlag( DBO_TRX ) );
276  }
277  }
278 
283  public function testTransactionPreCommitOrIdle() {
284  $db = $this->getMockDB( [ 'isOpen' ] );
285  $db->method( 'isOpen' )->willReturn( true );
286  $db->clearFlag( DBO_TRX );
287 
288  $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX is not set' );
289 
290  $called = false;
292  function ( IDatabase $db ) use ( &$called ) {
293  $called = true;
294  },
295  __METHOD__
296  );
297  $this->assertTrue( $called, 'Called when idle' );
298 
299  $db->begin( __METHOD__ );
300  $called = false;
302  function ( IDatabase $db ) use ( &$called ) {
303  $called = true;
304  },
305  __METHOD__
306  );
307  $this->assertFalse( $called, 'Not called when transaction is active' );
308  $db->commit( __METHOD__ );
309  $this->assertTrue( $called, 'Called when transaction is committed' );
310  }
311 
317  $db = $this->getMockDB( [ 'isOpen', 'ping', 'getDBname' ] );
318  $db->method( 'isOpen' )->willReturn( true );
319  $db->method( 'ping' )->willReturn( true );
320  $db->method( 'getDBname' )->willReturn( 'unittest' );
321  $db->setFlag( DBO_TRX );
322 
323  $lbFactory = LBFactorySingle::newFromConnection( $db );
324  // Ask for the connection so that LB sets internal state
325  // about this connection being the master connection
326  $lb = $lbFactory->getMainLB();
327  $conn = $lb->openConnection( $lb->getWriterIndex() );
328  $this->assertSame( $db, $conn, 'Same DB instance' );
329 
330  $this->assertFalse( $lb->hasMasterChanges() );
331  $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX is set' );
332  $called = false;
333  $callback = function ( IDatabase $db ) use ( &$called ) {
334  $called = true;
335  };
336  $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
337  $this->assertTrue( $called, 'Called when idle if DBO_TRX is set' );
338  $called = false;
339  $lbFactory->commitMasterChanges();
340  $this->assertFalse( $called );
341 
342  $called = false;
343  $lbFactory->beginMasterChanges( __METHOD__ );
344  $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
345  $this->assertFalse( $called, 'Not called when lb-transaction is active' );
346  $lbFactory->commitMasterChanges( __METHOD__ );
347  $this->assertTrue( $called, 'Called when lb-transaction is committed' );
348 
349  $called = false;
350  $lbFactory->beginMasterChanges( __METHOD__ );
351  $db->onTransactionPreCommitOrIdle( $callback, __METHOD__ );
352  $this->assertFalse( $called, 'Not called when lb-transaction is active' );
353 
354  $lbFactory->rollbackMasterChanges( __METHOD__ );
355  $this->assertFalse( $called, 'Not called when lb-transaction is rolled back' );
356 
357  $lbFactory->commitMasterChanges( __METHOD__ );
358  $this->assertFalse( $called, 'Not called in next round commit' );
359  }
360 
365  public function testTransactionResolution() {
366  $db = $this->db;
367 
368  $db->clearFlag( DBO_TRX );
369  $db->begin( __METHOD__ );
370  $called = false;
371  $db->onTransactionResolution( function ( $trigger, IDatabase $db ) use ( &$called ) {
372  $called = true;
373  $db->setFlag( DBO_TRX );
374  } );
375  $db->commit( __METHOD__ );
376  $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
377  $this->assertTrue( $called, 'Callback reached' );
378 
379  $db->clearFlag( DBO_TRX );
380  $db->begin( __METHOD__ );
381  $called = false;
382  $db->onTransactionResolution( function ( $trigger, IDatabase $db ) use ( &$called ) {
383  $called = true;
384  $db->setFlag( DBO_TRX );
385  } );
386  $db->rollback( __METHOD__ );
387  $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
388  $this->assertTrue( $called, 'Callback reached' );
389  }
390 
394  public function testTransactionListener() {
395  $db = $this->db;
396 
397  $db->setTransactionListener( 'ping', function () use ( $db, &$called ) {
398  $called = true;
399  } );
400 
401  $called = false;
402  $db->begin( __METHOD__ );
403  $db->commit( __METHOD__ );
404  $this->assertTrue( $called, 'Callback reached' );
405 
406  $called = false;
407  $db->begin( __METHOD__ );
408  $db->commit( __METHOD__ );
409  $this->assertTrue( $called, 'Callback still reached' );
410 
411  $called = false;
412  $db->begin( __METHOD__ );
413  $db->rollback( __METHOD__ );
414  $this->assertTrue( $called, 'Callback reached' );
415 
416  $db->setTransactionListener( 'ping', null );
417  $called = false;
418  $db->begin( __METHOD__ );
419  $db->commit( __METHOD__ );
420  $this->assertFalse( $called, 'Callback not reached' );
421  }
422 
432  private function getMockDB( $methods = [] ) {
433  static $abstractMethods = [
434  'fetchAffectedRowCount',
435  'closeConnection',
436  'dataSeek',
437  'doQuery',
438  'fetchObject', 'fetchRow',
439  'fieldInfo', 'fieldName',
440  'getSoftwareLink', 'getServerVersion',
441  'getType',
442  'indexInfo',
443  'insertId',
444  'lastError', 'lastErrno',
445  'numFields', 'numRows',
446  'open',
447  'strencode',
448  'tableExists'
449  ];
450  $db = $this->getMockBuilder( Database::class )
451  ->disableOriginalConstructor()
452  ->setMethods( array_values( array_unique( array_merge(
453  $abstractMethods,
454  $methods
455  ) ) ) )
456  ->getMock();
457  $wdb = TestingAccessWrapper::newFromObject( $db );
458  $wdb->trxProfiler = new TransactionProfiler();
459  $wdb->connLogger = new \Psr\Log\NullLogger();
460  $wdb->queryLogger = new \Psr\Log\NullLogger();
461  $wdb->currentDomain = DatabaseDomain::newUnspecified();
462  return $db;
463  }
464 
468  public function testFlushSnapshot() {
469  $db = $this->getMockDB( [ 'isOpen' ] );
470  $db->method( 'isOpen' )->willReturn( true );
471 
472  $db->flushSnapshot( __METHOD__ ); // ok
473  $db->flushSnapshot( __METHOD__ ); // ok
474 
475  $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
476  $db->query( 'SELECT 1', __METHOD__ );
477  $this->assertTrue( (bool)$db->trxLevel(), "Transaction started." );
478  $db->flushSnapshot( __METHOD__ ); // ok
479  $db->restoreFlags( $db::RESTORE_PRIOR );
480 
481  $this->assertFalse( (bool)$db->trxLevel(), "Transaction cleared." );
482  }
483 
490  public function testGetScopedLock() {
491  $db = $this->getMockDB( [ 'isOpen', 'getDBname' ] );
492  $db->method( 'isOpen' )->willReturn( true );
493  $db->method( 'getDBname' )->willReturn( 'unittest' );
494 
495  $this->assertEquals( 0, $db->trxLevel() );
496  $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
497  $this->assertEquals( true, $db->lock( 'x', __METHOD__ ) );
498  $this->assertEquals( false, $db->lockIsFree( 'x', __METHOD__ ) );
499  $this->assertEquals( true, $db->unlock( 'x', __METHOD__ ) );
500  $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
501  $this->assertEquals( 0, $db->trxLevel() );
502 
503  $db->setFlag( DBO_TRX );
504  $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
505  $this->assertEquals( true, $db->lock( 'x', __METHOD__ ) );
506  $this->assertEquals( false, $db->lockIsFree( 'x', __METHOD__ ) );
507  $this->assertEquals( true, $db->unlock( 'x', __METHOD__ ) );
508  $this->assertEquals( true, $db->lockIsFree( 'x', __METHOD__ ) );
509  $db->clearFlag( DBO_TRX );
510 
511  // Pending writes with DBO_TRX
512  $this->assertEquals( 0, $db->trxLevel() );
513  $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
514  $db->setFlag( DBO_TRX );
515  $db->query( "DELETE FROM test WHERE t = 1" ); // trigger DBO_TRX transaction before lock
516  try {
517  $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
518  $this->fail( "Exception not reached" );
519  } catch ( DBUnexpectedError $e ) {
520  $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
521  $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ), 'Lock not acquired' );
522  }
523  $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
524  // Pending writes without DBO_TRX
525  $db->clearFlag( DBO_TRX );
526  $this->assertEquals( 0, $db->trxLevel() );
527  $this->assertTrue( $db->lockIsFree( 'meow2', __METHOD__ ) );
528  $db->begin( __METHOD__ );
529  $db->query( "DELETE FROM test WHERE t = 1" ); // trigger DBO_TRX transaction before lock
530  try {
531  $lock = $db->getScopedLockAndFlush( 'meow2', __METHOD__, 1 );
532  $this->fail( "Exception not reached" );
533  } catch ( DBUnexpectedError $e ) {
534  $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
535  $this->assertTrue( $db->lockIsFree( 'meow2', __METHOD__ ), 'Lock not acquired' );
536  }
537  $db->rollback( __METHOD__ );
538  // No pending writes, with DBO_TRX
539  $db->setFlag( DBO_TRX );
540  $this->assertEquals( 0, $db->trxLevel() );
541  $this->assertTrue( $db->lockIsFree( 'wuff', __METHOD__ ) );
542  $db->query( "SELECT 1", __METHOD__ );
543  $this->assertEquals( 1, $db->trxLevel() );
544  $lock = $db->getScopedLockAndFlush( 'wuff', __METHOD__, 1 );
545  $this->assertEquals( 0, $db->trxLevel() );
546  $this->assertFalse( $db->lockIsFree( 'wuff', __METHOD__ ), 'Lock already acquired' );
547  $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
548  // No pending writes, without DBO_TRX
549  $db->clearFlag( DBO_TRX );
550  $this->assertEquals( 0, $db->trxLevel() );
551  $this->assertTrue( $db->lockIsFree( 'wuff2', __METHOD__ ) );
552  $db->begin( __METHOD__ );
553  try {
554  $lock = $db->getScopedLockAndFlush( 'wuff2', __METHOD__, 1 );
555  $this->fail( "Exception not reached" );
556  } catch ( DBUnexpectedError $e ) {
557  $this->assertEquals( 1, $db->trxLevel(), "Transaction not committed." );
558  $this->assertFalse( $db->lockIsFree( 'wuff2', __METHOD__ ), 'Lock not acquired' );
559  }
560  $db->rollback( __METHOD__ );
561  }
562 
568  public function testFlagSetting() {
569  $db = $this->db;
570  $origTrx = $db->getFlag( DBO_TRX );
571  $origSsl = $db->getFlag( DBO_SSL );
572 
573  $origTrx
574  ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
575  : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
576  $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
577 
578  $origSsl
579  ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
580  : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
581  $this->assertEquals( !$origSsl, $db->getFlag( DBO_SSL ) );
582 
583  $db->restoreFlags( $db::RESTORE_INITIAL );
584  $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
585  $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
586 
587  $origTrx
588  ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
589  : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
590  $origSsl
591  ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
592  : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
593 
594  $db->restoreFlags();
595  $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
596  $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
597 
598  $db->restoreFlags();
599  $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
600  $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
601  }
602 
607  public function testDBOIgnoreSet() {
608  $db = $this->getMockBuilder( DatabaseMysqli::class )
609  ->disableOriginalConstructor()
610  ->setMethods( null )
611  ->getMock();
612 
614  }
615 
620  public function testDBOIgnoreClear() {
621  $db = $this->getMockBuilder( DatabaseMysqli::class )
622  ->disableOriginalConstructor()
623  ->setMethods( null )
624  ->getMock();
625 
627  }
628 
633  public function testSchemaAndPrefixMutators() {
634  $ud = DatabaseDomain::newUnspecified();
635 
636  $this->assertEquals( $ud->getId(), $this->db->getDomainID() );
637 
638  $old = $this->db->tablePrefix();
639  $oldDomain = $this->db->getDomainId();
640  $this->assertInternalType( 'string', $old, 'Prefix is string' );
641  $this->assertSame( $old, $this->db->tablePrefix(), "Prefix unchanged" );
642  $this->assertSame( $old, $this->db->tablePrefix( 'xxx_' ) );
643  $this->assertSame( 'xxx_', $this->db->tablePrefix(), "Prefix set" );
644  $this->db->tablePrefix( $old );
645  $this->assertNotEquals( 'xxx_', $this->db->tablePrefix() );
646  $this->assertSame( $oldDomain, $this->db->getDomainId() );
647 
648  $old = $this->db->dbSchema();
649  $oldDomain = $this->db->getDomainId();
650  $this->assertInternalType( 'string', $old, 'Schema is string' );
651  $this->assertSame( $old, $this->db->dbSchema(), "Schema unchanged" );
652 
653  $this->db->selectDB( 'y' );
654  $this->assertSame( $old, $this->db->dbSchema( 'xxx' ) );
655  $this->assertSame( 'xxx', $this->db->dbSchema(), "Schema set" );
656  $this->db->dbSchema( $old );
657  $this->assertNotEquals( 'xxx', $this->db->dbSchema() );
658  $this->assertSame( "y", $this->db->getDomainId() );
659  }
660 
666  public function testSchemaWithNoDB() {
667  $ud = DatabaseDomain::newUnspecified();
668 
669  $this->assertEquals( $ud->getId(), $this->db->getDomainID() );
670  $this->assertSame( '', $this->db->dbSchema() );
671 
672  $this->db->dbSchema( 'xxx' );
673  }
674 
678  public function testSelectDomain() {
679  $oldDomain = $this->db->getDomainId();
680  $oldDatabase = $this->db->getDBname();
681  $oldSchema = $this->db->dbSchema();
682  $oldPrefix = $this->db->tablePrefix();
683 
684  $this->db->selectDomain( 'testselectdb-xxx_' );
685  $this->assertSame( 'testselectdb', $this->db->getDBname() );
686  $this->assertSame( '', $this->db->dbSchema() );
687  $this->assertSame( 'xxx_', $this->db->tablePrefix() );
688 
689  $this->db->selectDomain( $oldDomain );
690  $this->assertSame( $oldDatabase, $this->db->getDBname() );
691  $this->assertSame( $oldSchema, $this->db->dbSchema() );
692  $this->assertSame( $oldPrefix, $this->db->tablePrefix() );
693  $this->assertSame( $oldDomain, $this->db->getDomainId() );
694 
695  $this->db->selectDomain( 'testselectdb-schema-xxx_' );
696  $this->assertSame( 'testselectdb', $this->db->getDBname() );
697  $this->assertSame( 'schema', $this->db->dbSchema() );
698  $this->assertSame( 'xxx_', $this->db->tablePrefix() );
699 
700  $this->db->selectDomain( $oldDomain );
701  $this->assertSame( $oldDatabase, $this->db->getDBname() );
702  $this->assertSame( $oldSchema, $this->db->dbSchema() );
703  $this->assertSame( $oldPrefix, $this->db->tablePrefix() );
704  $this->assertSame( $oldDomain, $this->db->getDomainId() );
705  }
706 
711  public function testGetSetLBInfo() {
712  $db = $this->getMockDB();
713 
714  $this->assertEquals( [], $db->getLBInfo() );
715  $this->assertNull( $db->getLBInfo( 'pringles' ) );
716 
717  $db->setLBInfo( 'soda', 'water' );
718  $this->assertEquals( [ 'soda' => 'water' ], $db->getLBInfo() );
719  $this->assertNull( $db->getLBInfo( 'pringles' ) );
720  $this->assertEquals( 'water', $db->getLBInfo( 'soda' ) );
721 
722  $db->setLBInfo( 'basketball', 'Lebron' );
723  $this->assertEquals( [ 'soda' => 'water', 'basketball' => 'Lebron' ], $db->getLBInfo() );
724  $this->assertEquals( 'water', $db->getLBInfo( 'soda' ) );
725  $this->assertEquals( 'Lebron', $db->getLBInfo( 'basketball' ) );
726 
727  $db->setLBInfo( 'soda', null );
728  $this->assertEquals( [ 'basketball' => 'Lebron' ], $db->getLBInfo() );
729 
730  $db->setLBInfo( [ 'King' => 'James' ] );
731  $this->assertNull( $db->getLBInfo( 'basketball' ) );
732  $this->assertEquals( [ 'King' => 'James' ], $db->getLBInfo() );
733  }
734 }
Helper class that detects high-contention DB queries via profiling calls.
getMockDB( $methods=[])
Use this mock instead of DatabaseTestHelper for cases where DatabaseTestHelper is too inflexibile due...
rollback( $fname=__METHOD__, $flush=self::FLUSHING_ONE)
Rollback a transaction previously started using begin().
Definition: Database.php:4101
const DBO_IGNORE
Definition: defines.php:11
clearFlag( $flag, $remember=self::REMEMBER_NOTHING)
Clear a flag for this connection.
Definition: Database.php:754
getFlag( $flag)
Returns a boolean whether the flag $flag is set for this connection.
Definition: Database.php:778
flushSnapshot( $fname=__METHOD__, $flush=self::FLUSHING_ONE)
Commit any transaction but error out if writes or callbacks are pending.
Definition: Database.php:4172
static provideAddQuotes()
if(is_array( $mode)) switch( $mode) $input
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
setLBInfo( $nameOrArray, $value=null)
Set the entire array or a particular key of the managing load balancer info array.
Definition: Database.php:589
begin( $fname=__METHOD__, $mode=self::TRANSACTION_EXPLICIT)
Begin a transaction.
Definition: Database.php:3963
static provideTableName()
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2147
getScopedLockAndFlush( $lockKey, $fname, $timeout)
Acquire a named lock, flush any transaction, and return an RAII style unlocker object.
Definition: Database.php:4664
testTransactionListener()
Wikimedia\Rdbms\Database::setTransactionListener.
commit( $fname=__METHOD__, $flush=self::FLUSHING_ONE)
Commits a transaction previously started using begin().
trxLevel()
Gets the current transaction level.
Definition: Database.php:523
startAtomic( $fname=__METHOD__, $cancelable=self::ATOMIC_NOT_CANCELABLE)
Begin an atomic section of SQL statements.
Definition: Database.php:3798
provideTableNamesWithIndexClauseOrJOIN()
testFlushSnapshot()
Wikimedia\Rdbms\Database::flushSnapshot.
testSchemaWithNoDB()
Wikimedia\Rdbms\Database::tablePrefix Wikimedia\Rdbms\Database::dbSchema DBUnexpectedError.
endAtomic( $fname=__METHOD__)
Ends an atomic section of SQL statements.
Definition: Database.php:3828
getLBInfo( $name=null)
Get properties passed down from the server info array of the load balancer.
Definition: Database.php:577
testTableNamesWithIndexClauseOrJOIN( $tables, $join_conds, $expect)
provideTableNamesWithIndexClauseOrJOIN Wikimedia\Rdbms\Database::tableNamesWithIndexClauseOrJOIN ...
this hook is for auditing only RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist & $tables
Definition: hooks.txt:960
begin( $fname=__METHOD__, $mode=self::TRANSACTION_EXPLICIT)
Begin a transaction.
DatabaseTestHelper $db
rollback( $fname=__METHOD__, $flush=self::FLUSHING_ONE)
Rollback a transaction previously started using begin().
query( $sql, $fname='', $flags=0)
Run an SQL query and return the result.
testDBOIgnoreClear()
UnexpectedValueException Wikimedia\Rdbms\Database::clearFlag.
testTransactionPreCommitOrIdle_TRX()
Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle Wikimedia\Rdbms\Database::runOnTransactionPreC...
Helper for testing the methods from the Database class.
testAddQuotes( $input, $expected)
provideAddQuotes Wikimedia\Rdbms\Database::addQuotes
testFactory()
provideAddQuotes Wikimedia\Rdbms\Database::factory
onTransactionCommitOrIdle(callable $callback, $fname=__METHOD__)
Run a callback as soon as there is no transaction pending.
Definition: Database.php:3396
testGetSetLBInfo()
Wikimedia\Rdbms\Database::getLBInfo Wikimedia\Rdbms\Database::setLBInfo.
onTransactionPreCommitOrIdle(callable $callback, $fname=__METHOD__)
Run a callback before the current transaction commits or now if there is none.
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:767
setFlag( $flag, $remember=self::REMEMBER_NOTHING)
Set a flag for this connection.
Definition: Database.php:743
testTransactionIdle()
Wikimedia\Rdbms\Database::onTransactionCommitOrIdle Wikimedia\Rdbms\Database::runOnTransactionIdleCal...
testTableName( $expected, $table, $format, array $alias=null)
provideTableName Wikimedia\Rdbms\Database::tableName
restoreFlags( $state=self::RESTORE_PRIOR)
Restore the flags to their prior state before the last setFlag/clearFlag call.
Definition: Database.php:765
testGetScopedLock()
Wikimedia\Rdbms\Database::getScopedLockAndFlush Wikimedia\Rdbms\Database::lock Wikimedia\Rdbms\Databa...
const DBO_TRX
Definition: defines.php:12
setFlag( $flag, $remember=self::REMEMBER_NOTHING)
Set a flag for this connection.
unlock( $lockName, $method)
Release a lock.
Definition: Database.php:4658
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
testTransactionIdle_TRX()
Wikimedia\Rdbms\Database::onTransactionCommitOrIdle Wikimedia\Rdbms\Database::runOnTransactionIdleCal...
testSchemaAndPrefixMutators()
Wikimedia\Rdbms\Database::tablePrefix Wikimedia\Rdbms\Database::dbSchema.
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
getFlag( $flag)
Returns a boolean whether the flag $flag is set for this connection.
commit( $fname=__METHOD__, $flush=self::FLUSHING_ONE)
Commits a transaction previously started using begin().
Definition: Database.php:4026
onTransactionResolution(callable $callback, $fname=__METHOD__)
Run a callback as soon as the current transaction commits or rolls back.
Definition: Database.php:3389
setTransactionListener( $name, callable $callback=null)
Run a callback after each time any transaction commits or rolls back.
Definition: Database.php:3541
lock( $lockName, $method, $timeout=5)
Acquire a named lock.
Definition: Database.php:4652
onTransactionResolution(callable $callback, $fname=__METHOD__)
Run a callback as soon as the current transaction commits or rolls back.
testSelectDomain()
Wikimedia\Rdbms\Database::selectDomain.
lockIsFree( $lockName, $method)
Check to see if a named lock is not locked by any thread (non-blocking)
Definition: Database.php:4645
const DBO_SSL
Definition: defines.php:17
clearFlag( $flag, $remember=self::REMEMBER_NOTHING)
Clear a flag for this connection.
testDBOIgnoreSet()
UnexpectedValueException Wikimedia\Rdbms\Database::setFlag.
testTransactionResolution()
Wikimedia\Rdbms\Database::onTransactionResolution Wikimedia\Rdbms\Database::runOnTransactionIdleCallb...
testTransactionPreCommitOrIdle()
Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle Wikimedia\Rdbms\Database::runOnTransactionPreC...
testFlagSetting()
Wikimedia\Rdbms\Database::getFlag Wikimedia\Rdbms\Database::setFlag Wikimedia\Rdbms\Database::restore...
onTransactionPreCommitOrIdle(callable $callback, $fname=__METHOD__)
Run a callback before the current transaction commits or now if there is none.
Definition: Database.php:3413