MediaWiki  1.28.1
DatabaseTest.php
Go to the documentation of this file.
1 <?php
2 
11  protected $db;
12 
13  private $functionTest = false;
14 
15  protected function setUp() {
16  parent::setUp();
17  $this->db = wfGetDB( DB_MASTER );
18  }
19 
20  protected function tearDown() {
21  parent::tearDown();
22  if ( $this->functionTest ) {
23  $this->dropFunctions();
24  $this->functionTest = false;
25  }
26  $this->db->restoreFlags( IDatabase::RESTORE_INITIAL );
27  }
28 
32  public function testAddQuotesNull() {
33  $check = "NULL";
34  if ( $this->db->getType() === 'sqlite' || $this->db->getType() === 'oracle' ) {
35  $check = "''";
36  }
37  $this->assertEquals( $check, $this->db->addQuotes( null ) );
38  }
39 
40  public function testAddQuotesInt() {
41  # returning just "1234" should be ok too, though...
42  # maybe
43  $this->assertEquals(
44  "'1234'",
45  $this->db->addQuotes( 1234 ) );
46  }
47 
48  public function testAddQuotesFloat() {
49  # returning just "1234.5678" would be ok too, though
50  $this->assertEquals(
51  "'1234.5678'",
52  $this->db->addQuotes( 1234.5678 ) );
53  }
54 
55  public function testAddQuotesString() {
56  $this->assertEquals(
57  "'string'",
58  $this->db->addQuotes( 'string' ) );
59  }
60 
61  public function testAddQuotesStringQuote() {
62  $check = "'string''s cause trouble'";
63  if ( $this->db->getType() === 'mysql' ) {
64  $check = "'string\'s cause trouble'";
65  }
66  $this->assertEquals(
67  $check,
68  $this->db->addQuotes( "string's cause trouble" ) );
69  }
70 
71  private function getSharedTableName( $table, $database, $prefix, $format = 'quoted' ) {
73 
74  $this->db->setTableAliases( [
75  $table => [
76  'dbname' => $database,
77  'schema' => null,
78  'prefix' => $prefix
79  ]
80  ] );
81 
82  $ret = $this->db->tableName( $table, $format );
83 
84  $this->db->setTableAliases( array_fill_keys(
85  $wgSharedDB ? $wgSharedTables : [],
86  [
87  'dbname' => $wgSharedDB,
88  'schema' => $wgSharedSchema,
89  'prefix' => $wgSharedPrefix
90  ]
91  ) );
92 
93  return $ret;
94  }
95 
96  private function prefixAndQuote( $table, $database = null, $prefix = null, $format = 'quoted' ) {
97  if ( $this->db->getType() === 'sqlite' || $format !== 'quoted' ) {
98  $quote = '';
99  } elseif ( $this->db->getType() === 'mysql' ) {
100  $quote = '`';
101  } elseif ( $this->db->getType() === 'oracle' ) {
102  $quote = '/*Q*/';
103  } else {
104  $quote = '"';
105  }
106 
107  if ( $database !== null ) {
108  if ( $this->db->getType() === 'oracle' ) {
109  $database = $quote . $database . '.';
110  } else {
111  $database = $quote . $database . $quote . '.';
112  }
113  }
114 
115  if ( $prefix === null ) {
116  $prefix = $this->dbPrefix();
117  }
118 
119  if ( $this->db->getType() === 'oracle' ) {
120  return strtoupper( $database . $quote . $prefix . $table );
121  } else {
122  return $database . $quote . $prefix . $table . $quote;
123  }
124  }
125 
126  public function testTableNameLocal() {
127  $this->assertEquals(
128  $this->prefixAndQuote( 'tablename' ),
129  $this->db->tableName( 'tablename' )
130  );
131  }
132 
133  public function testTableNameRawLocal() {
134  $this->assertEquals(
135  $this->prefixAndQuote( 'tablename', null, null, 'raw' ),
136  $this->db->tableName( 'tablename', 'raw' )
137  );
138  }
139 
140  public function testTableNameShared() {
141  $this->assertEquals(
142  $this->prefixAndQuote( 'tablename', 'sharedatabase', 'sh_' ),
143  $this->getSharedTableName( 'tablename', 'sharedatabase', 'sh_' )
144  );
145 
146  $this->assertEquals(
147  $this->prefixAndQuote( 'tablename', 'sharedatabase', null ),
148  $this->getSharedTableName( 'tablename', 'sharedatabase', null )
149  );
150  }
151 
152  public function testTableNameRawShared() {
153  $this->assertEquals(
154  $this->prefixAndQuote( 'tablename', 'sharedatabase', 'sh_', 'raw' ),
155  $this->getSharedTableName( 'tablename', 'sharedatabase', 'sh_', 'raw' )
156  );
157 
158  $this->assertEquals(
159  $this->prefixAndQuote( 'tablename', 'sharedatabase', null, 'raw' ),
160  $this->getSharedTableName( 'tablename', 'sharedatabase', null, 'raw' )
161  );
162  }
163 
164  public function testTableNameForeign() {
165  $this->assertEquals(
166  $this->prefixAndQuote( 'tablename', 'databasename', '' ),
167  $this->db->tableName( 'databasename.tablename' )
168  );
169  }
170 
171  public function testTableNameRawForeign() {
172  $this->assertEquals(
173  $this->prefixAndQuote( 'tablename', 'databasename', '', 'raw' ),
174  $this->db->tableName( 'databasename.tablename', 'raw' )
175  );
176  }
177 
178  public function testStoredFunctions() {
179  if ( !in_array( wfGetDB( DB_MASTER )->getType(), [ 'mysql', 'postgres' ] ) ) {
180  $this->markTestSkipped( 'MySQL or Postgres required' );
181  }
182  global $IP;
183  $this->dropFunctions();
184  $this->functionTest = true;
185  $this->assertTrue(
186  $this->db->sourceFile( "$IP/tests/phpunit/data/db/{$this->db->getType()}/functions.sql" )
187  );
188  $res = $this->db->query( 'SELECT mw_test_function() AS test', __METHOD__ );
189  $this->assertEquals( 42, $res->fetchObject()->test );
190  }
191 
192  private function dropFunctions() {
193  $this->db->query( 'DROP FUNCTION IF EXISTS mw_test_function'
194  . ( $this->db->getType() == 'postgres' ? '()' : '' )
195  );
196  }
197 
199  $res = $this->db->select( 'page', '*', [ 'page_id' => 1 ] );
200  $this->assertFalse( $this->db->tableExists( 'foobarbaz' ) );
201  $this->assertInternalType( 'int', $res->numRows() );
202  }
203 
204  public function testTransactionIdle() {
205  $db = $this->db;
206 
207  $db->setFlag( DBO_TRX );
208  $called = false;
209  $flagSet = null;
211  function () use ( $db, &$flagSet, &$called ) {
212  $called = true;
213  $flagSet = $db->getFlag( DBO_TRX );
214  },
215  __METHOD__
216  );
217  $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
218  $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
219  $this->assertTrue( $called, 'Callback reached' );
220 
221  $db->clearFlag( DBO_TRX );
222  $flagSet = null;
224  function () use ( $db, &$flagSet ) {
225  $flagSet = $db->getFlag( DBO_TRX );
226  },
227  __METHOD__
228  );
229  $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
230  $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
231 
232  $db->clearFlag( DBO_TRX );
234  function () use ( $db ) {
235  $db->setFlag( DBO_TRX );
236  },
237  __METHOD__
238  );
239  $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
240  }
241 
242  public function testTransactionResolution() {
243  $db = $this->db;
244 
245  $db->clearFlag( DBO_TRX );
246  $db->begin( __METHOD__ );
247  $called = false;
248  $db->onTransactionResolution( function () use ( $db, &$called ) {
249  $called = true;
250  $db->setFlag( DBO_TRX );
251  } );
252  $db->commit( __METHOD__ );
253  $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
254  $this->assertTrue( $called, 'Callback reached' );
255 
256  $db->clearFlag( DBO_TRX );
257  $db->begin( __METHOD__ );
258  $called = false;
259  $db->onTransactionResolution( function () use ( $db, &$called ) {
260  $called = true;
261  $db->setFlag( DBO_TRX );
262  } );
263  $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
264  $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
265  $this->assertTrue( $called, 'Callback reached' );
266  }
267 
271  public function testTransactionListener() {
272  $db = $this->db;
273 
274  $db->setTransactionListener( 'ping', function () use ( $db, &$called ) {
275  $called = true;
276  } );
277 
278  $called = false;
279  $db->begin( __METHOD__ );
280  $db->commit( __METHOD__ );
281  $this->assertTrue( $called, 'Callback reached' );
282 
283  $called = false;
284  $db->begin( __METHOD__ );
285  $db->commit( __METHOD__ );
286  $this->assertTrue( $called, 'Callback still reached' );
287 
288  $called = false;
289  $db->begin( __METHOD__ );
290  $db->rollback( __METHOD__ );
291  $this->assertTrue( $called, 'Callback reached' );
292 
293  $db->setTransactionListener( 'ping', null );
294  $called = false;
295  $db->begin( __METHOD__ );
296  $db->commit( __METHOD__ );
297  $this->assertFalse( $called, 'Callback not reached' );
298  }
299 
303  public function testFlushSnapshot() {
304  $db = $this->db;
305 
306  $db->flushSnapshot( __METHOD__ ); // ok
307  $db->flushSnapshot( __METHOD__ ); // ok
308 
309  $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
310  $db->query( 'SELECT 1', __METHOD__ );
311  $this->assertTrue( (bool)$db->trxLevel(), "Transaction started." );
312  $db->flushSnapshot( __METHOD__ ); // ok
313  $db->restoreFlags( $db::RESTORE_PRIOR );
314 
315  $this->assertFalse( (bool)$db->trxLevel(), "Transaction cleared." );
316  }
317 
318  public function testGetScopedLock() {
319  $db = $this->db;
320 
321  $db->setFlag( DBO_TRX );
322  try {
323  $this->badLockingMethodImplicit( $db );
324  } catch ( RunTimeException $e ) {
325  $this->assertTrue( $db->trxLevel() > 0, "Transaction not committed." );
326  }
327  $db->clearFlag( DBO_TRX );
328  $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
329  $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
330 
331  try {
332  $this->badLockingMethodExplicit( $db );
333  } catch ( RunTimeException $e ) {
334  $this->assertTrue( $db->trxLevel() > 0, "Transaction not committed." );
335  }
336  $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
337  $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
338  }
339 
340  private function badLockingMethodImplicit( IDatabase $db ) {
341  $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
342  $db->query( "SELECT 1" ); // trigger DBO_TRX
343  throw new RunTimeException( "Uh oh!" );
344  }
345 
346  private function badLockingMethodExplicit( IDatabase $db ) {
347  $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
348  $db->begin( __METHOD__ );
349  throw new RunTimeException( "Uh oh!" );
350  }
351 
357  public function testFlagSetting() {
358  $db = $this->db;
359  $origTrx = $db->getFlag( DBO_TRX );
360  $origSsl = $db->getFlag( DBO_SSL );
361 
362  $origTrx
363  ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
364  : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
365  $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
366 
367  $origSsl
368  ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
369  : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
370  $this->assertEquals( !$origSsl, $db->getFlag( DBO_SSL ) );
371 
372  $db->restoreFlags( $db::RESTORE_INITIAL );
373  $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
374  $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
375 
376  $origTrx
377  ? $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR )
378  : $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
379  $origSsl
380  ? $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR )
381  : $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
382 
383  $db->restoreFlags();
384  $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
385  $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
386 
387  $db->restoreFlags();
388  $this->assertEquals( $origSsl, $db->getFlag( DBO_SSL ) );
389  $this->assertEquals( $origTrx, $db->getFlag( DBO_TRX ) );
390  }
391 
396  public function testMutators() {
397  $old = $this->db->tablePrefix();
398  $this->assertType( 'string', $old, 'Prefix is string' );
399  $this->assertEquals( $old, $this->db->tablePrefix(), "Prefix unchanged" );
400  $this->assertEquals( $old, $this->db->tablePrefix( 'xxx' ) );
401  $this->assertEquals( 'xxx', $this->db->tablePrefix(), "Prefix set" );
402  $this->db->tablePrefix( $old );
403  $this->assertNotEquals( 'xxx', $this->db->tablePrefix() );
404 
405  $old = $this->db->dbSchema();
406  $this->assertType( 'string', $old, 'Schema is string' );
407  $this->assertEquals( $old, $this->db->dbSchema(), "Schema unchanged" );
408  $this->assertEquals( $old, $this->db->dbSchema( 'xxx' ) );
409  $this->assertEquals( 'xxx', $this->db->dbSchema(), "Schema set" );
410  $this->db->dbSchema( $old );
411  $this->assertNotEquals( 'xxx', $this->db->dbSchema() );
412  }
413 }
rollback($fname=__METHOD__, $flush= '')
Rollback a transaction previously started using begin().
Definition: Database.php:2813
badLockingMethodExplicit(IDatabase $db)
testAddQuotesNull()
Database::dropTable.
commit($fname=__METHOD__, $flush= '')
Commits a transaction previously started using begin().
Definition: Database.php:2752
wfGetDB($db, $groups=[], $wiki=false)
Get a Database object.
trxLevel()
Gets the current transaction level.
Definition: Database.php:440
$called
$called tracks whether the setUp and tearDown method has been called.
$IP
Definition: WebStart.php:58
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1936
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:2102
prefixAndQuote($table, $database=null, $prefix=null, $format= 'quoted')
getSharedTableName($table, $database, $prefix, $format= 'quoted')
query($sql, $fname=__METHOD__, $tempIgnore=false)
Run an SQL query and return the result.
restoreFlags($state=self::RESTORE_PRIOR)
Restore the flags to their prior state before the last setFlag/clearFlag call.
Definition: Database.php:595
testTransactionListener()
Database::setTransactionListener()
$wgSharedTables
testMutators()
Database::tablePrefix() Database::dbSchema()
assertType($type, $actual, $message= '')
Asserts the type of the provided value.
testFlushSnapshot()
Database::flushSnapshot()
setFlag($flag, $remember=self::REMEMBER_NOTHING)
Set a flag for this connection.
Definition: Database.php:581
when a variable name is used in a it is silently declared as a new local masking the global
Definition: design.txt:93
badLockingMethodImplicit(IDatabase $db)
lockIsFree($lockName, $method)
Check to see if a named lock is available (non-blocking)
Definition: Database.php:3267
const DB_MASTER
Definition: defines.php:23
setTransactionListener($name, callable $callback=null)
Run a callback each time any transaction commits or rolls back.
Definition: Database.php:2520
begin($fname=__METHOD__, $mode=self::TRANSACTION_EXPLICIT)
Begin a transaction.
Definition: Database.php:2692
onTransactionIdle(callable $callback, $fname=__METHOD__)
Run a callback as soon as there is no transaction pending.
Definition: Database.php:2497
$res
Definition: database.txt:21
getFlag($flag)
Returns a boolean whether the flag $flag is set for this connection.
Definition: Database.php:608
testUnknownTableCorruptsResults()
clearFlag($flag, $remember=self::REMEMBER_NOTHING)
Clear a flag for this connection.
Definition: Database.php:588
$wgSharedDB
Shared database for multiple wikis.
Database $db
begin($fname=__METHOD__, $mode=self::TRANSACTION_EXPLICIT)
Begin a transaction.
const DBO_TRX
Definition: defines.php:9
flushSnapshot($fname=__METHOD__)
Commit any transaction but error out if writes or callbacks are pending.
Definition: Database.php:2862
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
testAddQuotesStringQuote()
onTransactionResolution(callable $callback, $fname=__METHOD__)
Run a callback as soon as the current transaction commits or rolls back.
Definition: Database.php:2490
$wgSharedPrefix
const DBO_SSL
Definition: defines.php:14
$wgSharedSchema
testTransactionResolution()
getScopedLockAndFlush($lockKey, $fname, $timeout)
Acquire a named lock, flush any transaction, and return an RAII style unlocker object.
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:34
Database Database.
Definition: DatabaseTest.php:7
query($sql, $fname=__METHOD__, $tempIgnore=false)
Run an SQL query and return the result.
Definition: Database.php:829
testFlagSetting()
Database::getFlag( Database::setFlag() Database::restoreFlags()