Completed
Push — master ( b068cc...8887b2 )
by mw
12s
created

Database::resetTransactionProfiler()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
eloc 3
c 1
b 0
f 1
nc 2
nop 0
dl 0
loc 5
ccs 0
cts 4
cp 0
crap 6
rs 9.4285
1
<?php
2
3
namespace SMW\MediaWiki;
4
5
use DBError;
6
use ResultWrapper;
7
use RuntimeException;
8
use SMW\DBConnectionProvider;
9
use UnexpectedValueException;
10
11
/**
12
 * This adapter class covers MW DB specific operations. Changes to the
13
 * interface are likely therefore this class should not be used other than by
14
 * SMW itself.
15
 *
16
 * @license GNU GPL v2+
17
 * @since 1.9
18
 *
19
 * @author mwjames
20
 */
21
class Database {
22
23
	/**
24
	 * @var DBConnectionProvider
25
	 */
26
	private $readConnectionProvider = null;
27
28
	/**
29
	 * @var DBConnectionProvider
30
	 */
31
	private $writeConnectionProvider = null;
32
33
	/**
34
	 * @var array
35
	 */
36
	private $transactionQueue = array();
37
38
	/**
39
	 * @var string
40
	 */
41
	private $dbPrefix = '';
42
43
	/**
44
	 * @since 1.9
45
	 *
46
	 * @param DBConnectionProvider $readConnectionProvider
47
	 * @param DBConnectionProvider|null $writeConnectionProvider
48
	 */
49 241
	public function __construct( DBConnectionProvider $readConnectionProvider, DBConnectionProvider $writeConnectionProvider = null ) {
50 241
		$this->readConnectionProvider = $readConnectionProvider;
51 241
		$this->writeConnectionProvider = $writeConnectionProvider;
52 241
	}
53
54
	/**
55
	 * @see DatabaseBase::getType
56
	 *
57
	 * @since 1.9.1
58
	 *
59
	 * @return string
60
	 */
61 247
	public function getType() {
62 247
		return $this->readConnection()->getType();
63
	}
64
65
	/**
66
	 * @since 2.1
67
	 *
68
	 * @param string $dbPrefix
69
	 */
70 227
	public function setDBPrefix( $dbPrefix ) {
71 227
		$this->dbPrefix = $dbPrefix;
72 227
	}
73
74
	/**
75
	 * @see DatabaseBase::tableName
76
	 *
77
	 * @since 1.9.0.2
78
	 *
79
	 * @param string $tableName
80
	 *
81
	 * @return string
82
	 */
83 238
	public function tableName( $tableName ) {
84
85 238
		if ( $this->getType() === 'sqlite' ) {
86 1
			return $this->dbPrefix . $tableName;
87
		}
88
89 237
		return $this->readConnection()->tableName( $tableName );
90
	}
91
92
	/**
93
	 * @see DatabaseBase::addQuotes
94
	 *
95
	 * @since 1.9.0.2
96
	 *
97
	 * @param string $tableName
0 ignored issues
show
Bug introduced by
There is no parameter named $tableName. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
98
	 *
99
	 * @return string
100
	 */
101 232
	public function addQuotes( $value ) {
102 232
		return $this->readConnection()->addQuotes( $value );
103
	}
104
105
	/**
106
	 * @see DatabaseBase::fetchObject
107
	 *
108
	 * @since 1.9.1
109
	 *
110
	 * @param ResultWrapper $res
111
	 *
112
	 * @return string
113
	 */
114 136
	public function fetchObject( $res ) {
115 136
		return $this->readConnection()->fetchObject( $res );
116
	}
117
118
	/**
119
	 * @see DatabaseBase::numRows
120
	 *
121
	 * @since 1.9.0.2
122
	 *
123
	 * @param mixed $results
124
	 *
125
	 * @return integer
126
	 */
127 16
	public function numRows( $results ) {
128 16
		return $this->readConnection()->numRows( $results );
129
	}
130
131
	/**
132
	 * @see DatabaseBase::freeResult
133
	 *
134
	 * @since 1.9.0.2
135
	 *
136
	 * @param ResultWrapper $res
137
	 */
138 231
	public function freeResult( $res ) {
139 231
		$this->readConnection()->freeResult( $res );
140 231
	}
141
142
	/**
143
	 * @see DatabaseBase::select
144
	 *
145
	 * @since 1.9.0.2
146
	 *
147
	 * @param string $tableName
148
	 * @param $fields
149
	 * @param $conditions
150
	 * @param array $options
151
	 * @param array $joinConditions
152
	 *
153
	 * @return ResultWrapper
154
	 * @throws UnexpectedValueException
155
	 */
156 236
	public function select( $tableName, $fields, $conditions = '', $fname, array $options = array(), $joinConditions = array() ) {
157
158 236
		$tablePrefix = null;
159
160
		// MW's SQLite implementation adds an auto prefix to the tableName but
161
		// not to the conditions and since ::tableName will handle prefixing
162
		// consistently ensure that the select doesn't add an extra prefix
163 236
		if ( $this->getType() === 'sqlite' ) {
164
			$tablePrefix = $this->readConnection()->tablePrefix( '' );
165
166
			if ( isset( $options['ORDER BY'] ) ) {
167
				$options['ORDER BY'] = str_replace( 'RAND', 'RANDOM', $options['ORDER BY'] );
168
			}
169
		}
170
171
		try {
172 236
			$results = $this->readConnection()->select(
173
				$tableName,
174
				$fields,
175
				$conditions,
176
				$fname,
177
				$options,
178
				$joinConditions
179
			);
180
		} catch  ( DBError $e ) {
0 ignored issues
show
Bug introduced by
The class DBError does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
181
			throw new RuntimeException (
182
				$e->getMessage() . "\n" .
183
				$e->getTraceAsString()
184
			);
185
		}
186
187 236
		if ( $tablePrefix !== null ) {
188
			$this->readConnection()->tablePrefix( $tablePrefix );
189
		}
190
191 236
		if ( $results instanceof ResultWrapper ) {
0 ignored issues
show
Bug introduced by
The class ResultWrapper does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
192 235
			return $results;
193
		}
194
195 1
		throw new UnexpectedValueException (
196
			'Expected a ResultWrapper for ' . "\n" .
197 1
			$tableName . "\n" .
198 1
			$fields . "\n" .
199 1
			$conditions
200
		);
201
	}
202
203
	/**
204
	 * @see DatabaseBase::query
205
	 *
206
	 * @since 1.9.1
207
	 *
208
	 * @param string $sql
209
	 * @param $fname
210
	 * @param $ignoreException
211
	 *
212
	 * @return ResultWrapper
213
	 * @throws RuntimeException
214
	 */
215 52
	public function query( $sql, $fname = __METHOD__, $ignoreException = false ) {
216
217 52
		if ( $this->getType() !== 'postgres' ) {
218 52
			$sql = str_replace( '@INT', '', $sql );
219
		}
220
221 52
		if ( $this->getType() == 'postgres' ) {
222
			$sql = str_replace( '@INT', '::integer', $sql );
223
			$sql = str_replace( 'IGNORE', '', $sql );
224
			$sql = str_replace( 'DROP TEMPORARY TABLE', 'DROP TABLE IF EXISTS', $sql );
225
			$sql = str_replace( 'RAND()', ( strpos( $sql, 'DISTINCT' ) !== false ? '' : 'RANDOM()' ), $sql );
226
		}
227
228 52
		if ( $this->getType() == 'sqlite' ) {
229 4
			$sql = str_replace( 'IGNORE', '', $sql );
230 4
			$sql = str_replace( 'TEMPORARY', 'TEMP', $sql );
231 4
			$sql = str_replace( 'ENGINE=MEMORY', '', $sql );
232 4
			$sql = str_replace( 'DROP TEMP', 'DROP', $sql );
233 4
			$sql = str_replace( 'TRUNCATE TABLE', 'DELETE FROM', $sql );
234 4
			$sql = str_replace( 'RAND', 'RANDOM', $sql );
235
		}
236
237
		try {
238 52
			$results = $this->writeConnection()->query(
239
				$sql,
240
				$fname,
241
				$ignoreException
242
			);
243 1
		} catch ( DBError $e ) {
0 ignored issues
show
Bug introduced by
The class DBError does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
244
			throw new RuntimeException (
245
				$e->getMessage() . "\n" .
246
				$e->getTraceAsString()
247
			);
248
		}
249
250 51
		return $results;
251
	}
252
253
	/**
254
	 * @see DatabaseBase::selectRow
255
	 *
256
	 * @since 1.9.1
257
	 */
258 236
	public function selectRow( $table, $vars, $conds, $fname = __METHOD__,
259
		$options = array(), $joinConditions = array() ) {
260
261 236
		return $this->readConnection()->selectRow(
262
			$table,
263
			$vars,
264
			$conds,
265
			$fname,
266
			$options,
267
			$joinConditions
268
		);
269
	}
270
271
	/**
272
	 * @see DatabaseBase::affectedRows
273
	 *
274
	 * @since 1.9.1
275
	 *
276
	 * @return int
277
	 */
278 11
	function affectedRows() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
279 11
		return $this->readConnection()->affectedRows();
280
	}
281
282
	/**
283
	 * @see DatabaseBase::makeSelectOptions
284
	 *
285
	 * @since 1.9.1
286
	 *
287
	 * @param array $options
288
	 *
289
	 * @return array
290
	 */
291 1
	public function makeSelectOptions( $options ) {
292 1
		return $this->readConnection()->makeSelectOptions( $options );
293
	}
294
295
	/**
296
	 * @see DatabaseBase::nextSequenceValue
297
	 *
298
	 * @since 1.9.1
299
	 *
300
	 * @param string $seqName
301
	 *
302
	 * @return int|null
303
	 */
304 217
	public function nextSequenceValue( $seqName ) {
305 217
		return $this->writeConnection()->nextSequenceValue( $seqName );
306
	}
307
308
	/**
309
	 * @see DatabaseBase::insertId
310
	 *
311
	 * @since 1.9.1
312
	 *
313
	 * @return int
314
	 */
315 217
	function insertId() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
316 217
		return (int)$this->writeConnection()->insertId();
317
	}
318
319
	/**
320
	 * @note Use a blank trx profiler to ignore expections
321
	 * @since 2.4
322
	 */
323
	function resetTransactionProfiler() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
324
		if ( method_exists( $this->writeConnection(), 'setTransactionProfiler' ) ) {
325
			$this->writeConnection()->setTransactionProfiler( new \TransactionProfiler() );
326
		}
327
	}
328
329
	/**
330
	 * @see DatabaseBase::clearFlag
331
	 *
332
	 * @since 2.4
333
	 */
334
	function clearFlag( $flag ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
335
		$this->writeConnection()->clearFlag( $flag );
336
	}
337
338
	/**
339
	 * @see DatabaseBase::insert
340
	 *
341
	 * @since 1.9.1
342
	 */
343 217
	public function insert( $table, $rows, $fname = __METHOD__, $options = array() ) {
344 217
		return $this->writeConnection()->insert( $table, $rows, $fname, $options );
345
	}
346
347
	/**
348
	 * @see DatabaseBase::update
349
	 *
350
	 * @since 1.9.1
351
	 */
352 210
	function update( $table, $values, $conds, $fname = __METHOD__, $options = array() ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
353 210
		return $this->writeConnection()->update( $table, $values, $conds, $fname, $options );
354
	}
355
356
	/**
357
	 * @see DatabaseBase::delete
358
	 *
359
	 * @since 1.9.1
360
	 */
361 198
	public function delete( $table, $conds, $fname = __METHOD__ ) {
362 198
		return $this->writeConnection()->delete( $table, $conds, $fname );
363
	}
364
365
	/**
366
	 * @see DatabaseBase::makeList
367
	 *
368
	 * @since 1.9.1
369
	 */
370 198
	public function makeList( $data, $mode ) {
371 198
		return $this->writeConnection()->makeList( $data, $mode );
372
	}
373
374
	/**
375
	 * @see DatabaseBase::tableExists
376
	 *
377
	 * @since 1.9.1
378
	 *
379
	 * @param string $table
380
	 * @param string $fname
381
	 *
382
	 * @return bool
383
	 */
384 1
	public function tableExists( $table, $fname = __METHOD__ ) {
385 1
		return $this->writeConnection()->tableExists( $table, $fname );
386
	}
387
388
	/**
389
	 * @see DatabaseBase::selectField
390
	 *
391
	 * @since 1.9.2
392
	 */
393 9
	public function selectField( $table, $fieldName, $conditions = '', $fname = __METHOD__, $options = array() ) {
394 9
		return $this->readConnection()->selectField( $table, $fieldName, $conditions, $fname, $options );
395
	}
396
397
	/**
398
	 * @see DatabaseBase::estimateRowCount
399
	 *
400
	 * @since 2.1
401
	 */
402 1
	public function estimateRowCount( $table, $vars = '*', $conditions = '', $fname = __METHOD__, $options = array() ) {
403 1
		return $this->readConnection()->estimateRowCount(
404
			$table,
405
			$vars,
406
			$conditions,
407
			$fname,
408
			$options
409
		);
410
	}
411
412
	/**
413
	 * @since 2.1
414
	 *
415
	 * @param string $fname
416
	 */
417 2
	public function beginTransaction( $fname = __METHOD__  ) {
418
419
		// If a transaction is being added for an uncommitted
420
		// queue entry then a transaction for the same instance
421
		// and name is being omitted
422 2
		if ( isset( $this->transactionQueue[$fname] ) ) {
423
			return;
424
		}
425
426 2
		$this->transactionQueue[$fname] = true;
427
428
		try {
429 2
			$this->writeConnection()->begin( $fname );
430 2
		} catch ( \Exception $exception ) {
431 2
			unset( $this->transactionQueue[$fname] );
432 2
			wfDebug( __METHOD__ . ' exception caused by ' . $exception->getMessage() );
433
		}
434 2
	}
435
436
	/**
437
	 * @since 2.1
438
	 *
439
	 * @param string $fname
440
	 */
441 1
	public function commitTransaction( $fname = __METHOD__  ) {
442
443 1
		if ( !isset( $this->transactionQueue[$fname] ) ) {
444 1
			return;
445
		}
446
447
		try {
448
			$this->writeConnection()->commit( $fname );
449
		} catch ( \Exception $exception ) {
450
			$this->writeConnection()->rollback( $fname );
451
			wfDebug( __METHOD__ . ' rollback because of ' . $exception->getMessage() );
452
		}
453
454
		unset( $this->transactionQueue[$fname] );
455
	}
456
457
	/**
458
	 * @since 2.3
459
	 *
460
	 * @param string $fname
461
	 */
462 224
	public function beginAtomicTransaction( $fname = __METHOD__ ) {
463
464
		// MW 1.23
465 224
		if ( !method_exists( $this->writeConnection(), 'startAtomic' ) ) {
466
			return null;
467
		}
468
469 224
		$this->writeConnection()->startAtomic( $fname );
470 224
	}
471
472
	/**
473
	 * @since 2.3
474
	 *
475
	 * @param string $fname
476
	 */
477 224
	public function endAtomicTransaction( $fname = __METHOD__ ) {
478
479
		// MW 1.23
480 224
		if ( !method_exists( $this->writeConnection(), 'endAtomic' ) ) {
481
			return null;
482
		}
483
484 224
		$this->writeConnection()->endAtomic( $fname );
485 224
	}
486
487
	/**
488
	 * @since 2.3
489
	 *
490
	 * @param callable $callback
491
	 */
492
	public function onTransactionIdle( $callback ) {
493
494
		// FIXME For 1.19 it is an unknown method hence execute without idle
495
		if ( !method_exists( $this->readConnection(), 'onTransactionIdle' ) ) {
496
			return call_user_func( $callback );
497
		}
498
499
		$this->readConnection()->onTransactionIdle( $callback );
500
	}
501
502 259
	private function readConnection() {
503 259
		return $this->readConnectionProvider->getConnection();
504
	}
505
506 232
	private function writeConnection() {
507
508 232
		if ( $this->writeConnectionProvider instanceof DBConnectionProvider ) {
509 230
			return $this->writeConnectionProvider->getConnection();
510
		}
511
512 2
		throw new RuntimeException( 'Expected a DBConnectionProvider instance' );
513
	}
514
515
}
516