MediaWiki REL1_28
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(
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
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
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
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
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 {
324 } catch ( RunTimeException $e ) {
325 $this->assertTrue( $db->trxLevel() > 0, "Transaction not committed." );
326 }
328 $db->rollback( __METHOD__, IDatabase::FLUSHING_ALL_PEERS );
329 $this->assertTrue( $db->lockIsFree( 'meow', __METHOD__ ) );
330
331 try {
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
341 $lock = $db->getScopedLockAndFlush( 'meow', __METHOD__, 1 );
342 $db->query( "SELECT 1" ); // trigger DBO_TRX
343 throw new RunTimeException( "Uh oh!" );
344 }
345
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}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
$wgSharedTables
$wgSharedDB
Shared database for multiple wikis.
$wgSharedSchema
$wgSharedPrefix
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
$IP
Definition WebStart.php:58
Database Database.
badLockingMethodImplicit(IDatabase $db)
badLockingMethodExplicit(IDatabase $db)
testTransactionListener()
Database::setTransactionListener()
testMutators()
Database::tablePrefix() Database::dbSchema()
testFlagSetting()
Database::getFlag( Database::setFlag() Database::restoreFlags()
testFlushSnapshot()
Database::flushSnapshot()
testAddQuotesNull()
Database::dropTable.
testUnknownTableCorruptsResults()
getSharedTableName( $table, $database, $prefix, $format='quoted')
prefixAndQuote( $table, $database=null, $prefix=null, $format='quoted')
Relational database abstraction object.
Definition Database.php:36
onTransactionResolution(callable $callback, $fname=__METHOD__)
Run a callback as soon as the current transaction commits or rolls back.
flushSnapshot( $fname=__METHOD__)
Commit any transaction but error out if writes or callbacks are pending.
getFlag( $flag)
Returns a boolean whether the flag $flag is set for this connection.
Definition Database.php:608
restoreFlags( $state=self::RESTORE_PRIOR)
Restore the flags to their prior state before the last setFlag/clearFlag call.
Definition Database.php:595
onTransactionIdle(callable $callback, $fname=__METHOD__)
Run a callback as soon as there is no transaction pending.
setTransactionListener( $name, callable $callback=null)
Run a callback each time any transaction commits or rolls back.
getScopedLockAndFlush( $lockKey, $fname, $timeout)
Acquire a named lock, flush any transaction, and return an RAII style unlocker object.
commit( $fname=__METHOD__, $flush='')
Commits a transaction previously started using begin().
query( $sql, $fname=__METHOD__, $tempIgnore=false)
Run an SQL query and return the result.
Definition Database.php:829
trxLevel()
Gets the current transaction level.
Definition Database.php:440
setFlag( $flag, $remember=self::REMEMBER_NOTHING)
Set a flag for this connection.
Definition Database.php:581
rollback( $fname=__METHOD__, $flush='')
Rollback a transaction previously started using begin().
begin( $fname=__METHOD__, $mode=self::TRANSACTION_EXPLICIT)
Begin a transaction.
clearFlag( $flag, $remember=self::REMEMBER_NOTHING)
Clear a flag for this connection.
Definition Database.php:588
lockIsFree( $lockName, $method)
Check to see if a named lock is available (non-blocking)
assertType( $type, $actual, $message='')
Asserts the type of the provided value.
$called
$called tracks whether the setUp and tearDown method has been called.
$res
Definition database.txt:21
when a variable name is used in a it is silently declared as a new local masking the global
Definition design.txt:95
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:1949
returning false will NOT prevent logging $e
Definition hooks.txt:2110
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:37
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:34
const DB_MASTER
Definition defines.php:23
const DBO_SSL
Definition defines.php:14
const DBO_TRX
Definition defines.php:9