Completed
Pull Request — stable8.2 (#26410)
by Thomas
38:55
created

Connection::escapeLikeParameter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Bart Visscher <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Morris Jobke <[email protected]>
6
 * @author Robin Appelman <[email protected]>
7
 * @author Thomas Müller <[email protected]>
8
 *
9
 * @copyright Copyright (c) 2015, ownCloud, Inc.
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
26
namespace OC\DB;
27
use Doctrine\DBAL\DBALException;
28
use Doctrine\DBAL\Driver;
29
use Doctrine\DBAL\Configuration;
30
use Doctrine\DBAL\Cache\QueryCacheProfile;
31
use Doctrine\Common\EventManager;
32
use OC\DB\QueryBuilder\ExpressionBuilder;
33
use OC\DB\QueryBuilder\QueryBuilder;
34
use OCP\IDBConnection;
35
36
class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
0 ignored issues
show
Bug introduced by
There is at least one abstract method in this class. Maybe declare it as abstract, or implement the remaining methods: beginTransaction, close, commit, errorCode, errorInfo, getDatabasePlatform, quote, rollBack
Loading history...
37
	/**
38
	 * @var string $tablePrefix
39
	 */
40
	protected $tablePrefix;
41
42
	/**
43
	 * @var \OC\DB\Adapter $adapter
44
	 */
45
	protected $adapter;
46
47 1752
	public function connect() {
48
		try {
49 1752
			return parent::connect();
50
		} catch (DBALException $e) {
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\DBALException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
51
			// throw a new exception to prevent leaking info from the stacktrace
52
			throw new DBALException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode());
53
		}
54
	}
55
56
	/**
57
	 * Returns a QueryBuilder for the connection.
58
	 *
59
	 * @return \OCP\DB\QueryBuilder\IQueryBuilder
60
	 */
61 244
	public function getQueryBuilder() {
62 244
		return new QueryBuilder($this);
63
	}
64
65
	/**
66
	 * Gets the QueryBuilder for the connection.
67
	 *
68
	 * @return \Doctrine\DBAL\Query\QueryBuilder
69
	 * @deprecated please use $this->getQueryBuilder() instead
70
	 */
71 63
	public function createQueryBuilder() {
72 63
		$backtrace = $this->getCallerBacktrace();
73 63
		\OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
74 63
		return parent::createQueryBuilder();
75
	}
76
77
	/**
78
	 * Gets the ExpressionBuilder for the connection.
79
	 *
80
	 * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder
81
	 * @deprecated please use $this->getQueryBuilder()->expr() instead
82
	 */
83 63
	public function getExpressionBuilder() {
84 63
		$backtrace = $this->getCallerBacktrace();
85 63
		\OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
86 63
		return parent::getExpressionBuilder();
87
	}
88
89
	/**
90
	 * Get the file and line that called the method where `getCallerBacktrace()` was used
91
	 *
92
	 * @return string
93
	 */
94 63
	protected function getCallerBacktrace() {
95 63
		$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
96
97
		// 0 is the method where we use `getCallerBacktrace`
98
		// 1 is the target method which uses the method we want to log
99 63
		if (isset($traces[1])) {
100 63
			return $traces[1]['file'] . ':' . $traces[1]['line'];
101
		}
102
103
		return '';
104
	}
105
106
	/**
107
	 * @return string
108
	 */
109 1
	public function getPrefix() {
110 1
		return $this->tablePrefix;
111
	}
112
113
	/**
114
	 * Initializes a new instance of the Connection class.
115
	 *
116
	 * @param array $params  The connection parameters.
117
	 * @param \Doctrine\DBAL\Driver $driver
118
	 * @param \Doctrine\DBAL\Configuration $config
119
	 * @param \Doctrine\Common\EventManager $eventManager
120
	 * @throws \Exception
121
	 */
122 12
	public function __construct(array $params, Driver $driver, Configuration $config = null,
123
		EventManager $eventManager = null)
124
	{
125 12
		if (!isset($params['adapter'])) {
126
			throw new \Exception('adapter not set');
127
		}
128 12
		if (!isset($params['tablePrefix'])) {
129
			throw new \Exception('tablePrefix not set');
130
		}
131 12
		parent::__construct($params, $driver, $config, $eventManager);
132 12
		$this->adapter = new $params['adapter']($this);
133 12
		$this->tablePrefix = $params['tablePrefix'];
134
135 12
		parent::setTransactionIsolation(parent::TRANSACTION_READ_COMMITTED);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (setTransactionIsolation() instead of __construct()). Are you sure this is correct? If so, you might want to change this to $this->setTransactionIsolation().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
136 12
	}
137
138
	/**
139
	 * Prepares an SQL statement.
140
	 *
141
	 * @param string $statement The SQL statement to prepare.
142
	 * @param int $limit
143
	 * @param int $offset
144
	 * @return \Doctrine\DBAL\Driver\Statement The prepared statement.
145
	 */
146 1591
	public function prepare( $statement, $limit=null, $offset=null ) {
147 1591
		if ($limit === -1) {
148
			$limit = null;
149
		}
150 1591
		if (!is_null($limit)) {
151 91
			$platform = $this->getDatabasePlatform();
152 91
			$statement = $platform->modifyLimitQuery($statement, $limit, $offset);
153 91
		}
154 1591
		$statement = $this->replaceTablePrefix($statement);
155 1591
		$statement = $this->adapter->fixupStatement($statement);
156
157 1591
		if(\OC_Config::getValue( 'log_query', false)) {
158
			\OCP\Util::writeLog('core', 'DB prepare : '.$statement, \OCP\Util::DEBUG);
159
		}
160 1591
		return parent::prepare($statement);
161
	}
162
163
	/**
164
	 * Executes an, optionally parametrized, SQL query.
165
	 *
166
	 * If the query is parametrized, a prepared statement is used.
167
	 * If an SQLLogger is configured, the execution is logged.
168
	 *
169
	 * @param string                                      $query  The SQL query to execute.
170
	 * @param array                                       $params The parameters to bind to the query, if any.
171
	 * @param array                                       $types  The types the previous parameters are in.
172
	 * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp    The query cache profile, optional.
173
	 *
174
	 * @return \Doctrine\DBAL\Driver\Statement The executed statement.
175
	 *
176
	 * @throws \Doctrine\DBAL\DBALException
177
	 */
178 1048
	public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null)
179
	{
180 1048
		$query = $this->replaceTablePrefix($query);
181 1048
		$query = $this->adapter->fixupStatement($query);
182 1048
		return parent::executeQuery($query, $params, $types, $qcp);
183
	}
184
185
	/**
186
	 * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
187
	 * and returns the number of affected rows.
188
	 *
189
	 * This method supports PDO binding types as well as DBAL mapping types.
190
	 *
191
	 * @param string $query  The SQL query.
192
	 * @param array  $params The query parameters.
193
	 * @param array  $types  The parameter types.
194
	 *
195
	 * @return integer The number of affected rows.
196
	 *
197
	 * @throws \Doctrine\DBAL\DBALException
198
	 */
199 1451
	public function executeUpdate($query, array $params = array(), array $types = array())
200
	{
201 1451
		$query = $this->replaceTablePrefix($query);
202 1451
		$query = $this->adapter->fixupStatement($query);
203 1451
		return parent::executeUpdate($query, $params, $types);
204
	}
205
206
	/**
207
	 * Returns the ID of the last inserted row, or the last value from a sequence object,
208
	 * depending on the underlying driver.
209
	 *
210
	 * Note: This method may not return a meaningful or consistent result across different drivers,
211
	 * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
212
	 * columns or sequences.
213
	 *
214
	 * @param string $seqName Name of the sequence object from which the ID should be returned.
215
	 * @return string A string representation of the last inserted ID.
216
	 */
217 1136
	public function lastInsertId($seqName = null)
218
	{
219 1136
		if ($seqName) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $seqName of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
220 1110
			$seqName = $this->replaceTablePrefix($seqName);
221 1110
		}
222 1136
		return $this->adapter->lastInsertId($seqName);
223
	}
224
225
	// internal use
226 1136
	public function realLastInsertId($seqName = null) {
227 1136
		return parent::lastInsertId($seqName);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (lastInsertId() instead of realLastInsertId()). Are you sure this is correct? If so, you might want to change this to $this->lastInsertId().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
228
	}
229
230
	/**
231
	 * Insert a row if the matching row does not exists.
232
	 *
233
	 * @param string $table The table name (will replace *PREFIX* with the actual prefix)
234
	 * @param array $input data that should be inserted into the table  (column name => value)
235
	 * @param array|null $compare List of values that should be checked for "if not exists"
236
	 *				If this is null or an empty array, all keys of $input will be compared
237
	 *				Please note: text fields (clob) must not be used in the compare array
238
	 * @return int number of inserted rows
239
	 * @throws \Doctrine\DBAL\DBALException
240
	 */
241 1164
	public function insertIfNotExist($table, $input, array $compare = null) {
242 1164
		return $this->adapter->insertIfNotExist($table, $input, $compare);
243
	}
244
245
	/**
246
	 * returns the error code and message as a string for logging
247
	 * works with DoctrineException
248
	 * @return string
249
	 */
250
	public function getError() {
251
		$msg = $this->errorCode() . ': ';
252
		$errorInfo = $this->errorInfo();
253
		if (is_array($errorInfo)) {
254
			$msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
255
			$msg .= 'Driver Code = '.$errorInfo[1] . ', ';
256
			$msg .= 'Driver Message = '.$errorInfo[2];
257
		}
258
		return $msg;
259
	}
260
261
	/**
262
	 * Drop a table from the database if it exists
263
	 *
264
	 * @param string $table table name without the prefix
265
	 */
266 3 View Code Duplication
	public function dropTable($table) {
267 3
		$table = $this->tablePrefix . trim($table);
268 3
		$schema = $this->getSchemaManager();
269 3
		if($schema->tablesExist(array($table))) {
270 3
			$schema->dropTable($table);
271 3
		}
272 3
	}
273
274
	/**
275
	 * Check if a table exists
276
	 *
277
	 * @param string $table table name without the prefix
278
	 * @return bool
279
	 */
280 2
	public function tableExists($table){
281 2
		$table = $this->tablePrefix . trim($table);
282 2
		$schema = $this->getSchemaManager();
283 2
		return $schema->tablesExist(array($table));
284
	}
285
286
	// internal use
287
	/**
288
	 * @param string $statement
289
	 * @return string
290
	 */
291 1704
	protected function replaceTablePrefix($statement) {
292 1704
		return str_replace( '*PREFIX*', $this->tablePrefix, $statement );
293
	}
294
295
	/**
296
	 * Check if a transaction is active
297
	 *
298
	 * @return bool
299
	 * @since 8.2.0
300
	 */
301
	public function inTransaction() {
302
		return $this->getTransactionNestingLevel() > 0;
303
	}
304
305
	/**
306
	 * Espace a parameter to be used in a LIKE query
307
	 *
308
	 * @param string $param
309
	 * @return string
310
	 */
311
	public function escapeLikeParameter($param) {
312
		return addcslashes($param, '\\_%');
313
	}
314
}
315