Passed
Push — master ( af5a07...246690 )
by Roeland
15:23 queued 13s
created
lib/private/DB/Connection.php 1 patch
Indentation   +432 added lines, -432 removed lines patch added patch discarded remove patch
@@ -49,436 +49,436 @@
 block discarded – undo
49 49
 use OCP\PreConditionNotMetException;
50 50
 
51 51
 class Connection extends ReconnectWrapper implements IDBConnection {
52
-	/** @var string */
53
-	protected $tablePrefix;
54
-
55
-	/** @var \OC\DB\Adapter $adapter */
56
-	protected $adapter;
57
-
58
-	/** @var SystemConfig */
59
-	private $systemConfig;
60
-
61
-	/** @var ILogger */
62
-	private $logger;
63
-
64
-	protected $lockedTable = null;
65
-
66
-	/** @var int */
67
-	protected $queriesBuilt = 0;
68
-
69
-	/** @var int */
70
-	protected $queriesExecuted = 0;
71
-
72
-	public function connect() {
73
-		try {
74
-			return parent::connect();
75
-		} catch (DBALException $e) {
76
-			// throw a new exception to prevent leaking info from the stacktrace
77
-			throw new DBALException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode());
78
-		}
79
-	}
80
-
81
-	public function getStats(): array {
82
-		return [
83
-			'built' => $this->queriesBuilt,
84
-			'executed' => $this->queriesExecuted,
85
-		];
86
-	}
87
-
88
-	/**
89
-	 * Returns a QueryBuilder for the connection.
90
-	 *
91
-	 * @return \OCP\DB\QueryBuilder\IQueryBuilder
92
-	 */
93
-	public function getQueryBuilder() {
94
-		$this->queriesBuilt++;
95
-		return new QueryBuilder(
96
-			$this,
97
-			$this->systemConfig,
98
-			$this->logger
99
-		);
100
-	}
101
-
102
-	/**
103
-	 * Gets the QueryBuilder for the connection.
104
-	 *
105
-	 * @return \Doctrine\DBAL\Query\QueryBuilder
106
-	 * @deprecated please use $this->getQueryBuilder() instead
107
-	 */
108
-	public function createQueryBuilder() {
109
-		$backtrace = $this->getCallerBacktrace();
110
-		\OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
111
-		$this->queriesBuilt++;
112
-		return parent::createQueryBuilder();
113
-	}
114
-
115
-	/**
116
-	 * Gets the ExpressionBuilder for the connection.
117
-	 *
118
-	 * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder
119
-	 * @deprecated please use $this->getQueryBuilder()->expr() instead
120
-	 */
121
-	public function getExpressionBuilder() {
122
-		$backtrace = $this->getCallerBacktrace();
123
-		\OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
124
-		$this->queriesBuilt++;
125
-		return parent::getExpressionBuilder();
126
-	}
127
-
128
-	/**
129
-	 * Get the file and line that called the method where `getCallerBacktrace()` was used
130
-	 *
131
-	 * @return string
132
-	 */
133
-	protected function getCallerBacktrace() {
134
-		$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
135
-
136
-		// 0 is the method where we use `getCallerBacktrace`
137
-		// 1 is the target method which uses the method we want to log
138
-		if (isset($traces[1])) {
139
-			return $traces[1]['file'] . ':' . $traces[1]['line'];
140
-		}
141
-
142
-		return '';
143
-	}
144
-
145
-	/**
146
-	 * @return string
147
-	 */
148
-	public function getPrefix() {
149
-		return $this->tablePrefix;
150
-	}
151
-
152
-	/**
153
-	 * Initializes a new instance of the Connection class.
154
-	 *
155
-	 * @param array $params  The connection parameters.
156
-	 * @param \Doctrine\DBAL\Driver $driver
157
-	 * @param \Doctrine\DBAL\Configuration $config
158
-	 * @param \Doctrine\Common\EventManager $eventManager
159
-	 * @throws \Exception
160
-	 */
161
-	public function __construct(array $params, Driver $driver, Configuration $config = null,
162
-		EventManager $eventManager = null) {
163
-		if (!isset($params['adapter'])) {
164
-			throw new \Exception('adapter not set');
165
-		}
166
-		if (!isset($params['tablePrefix'])) {
167
-			throw new \Exception('tablePrefix not set');
168
-		}
169
-		parent::__construct($params, $driver, $config, $eventManager);
170
-		$this->adapter = new $params['adapter']($this);
171
-		$this->tablePrefix = $params['tablePrefix'];
172
-
173
-		$this->systemConfig = \OC::$server->getSystemConfig();
174
-		$this->logger = \OC::$server->getLogger();
175
-	}
176
-
177
-	/**
178
-	 * Prepares an SQL statement.
179
-	 *
180
-	 * @param string $statement The SQL statement to prepare.
181
-	 * @param int $limit
182
-	 * @param int $offset
183
-	 * @return \Doctrine\DBAL\Driver\Statement The prepared statement.
184
-	 */
185
-	public function prepare($statement, $limit = null, $offset = null) {
186
-		if ($limit === -1) {
187
-			$limit = null;
188
-		}
189
-		if (!is_null($limit)) {
190
-			$platform = $this->getDatabasePlatform();
191
-			$statement = $platform->modifyLimitQuery($statement, $limit, $offset);
192
-		}
193
-		$statement = $this->replaceTablePrefix($statement);
194
-		$statement = $this->adapter->fixupStatement($statement);
195
-
196
-		return parent::prepare($statement);
197
-	}
198
-
199
-	/**
200
-	 * Executes an, optionally parametrized, SQL query.
201
-	 *
202
-	 * If the query is parametrized, a prepared statement is used.
203
-	 * If an SQLLogger is configured, the execution is logged.
204
-	 *
205
-	 * @param string                                      $sql  The SQL query to execute.
206
-	 * @param array                                       $params The parameters to bind to the query, if any.
207
-	 * @param array                                       $types  The types the previous parameters are in.
208
-	 * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp    The query cache profile, optional.
209
-	 *
210
-	 * @return \Doctrine\DBAL\Driver\Statement The executed statement.
211
-	 *
212
-	 * @throws \Doctrine\DBAL\DBALException
213
-	 */
214
-	public function executeQuery($sql, array $params = [], $types = [], QueryCacheProfile $qcp = null) {
215
-		$sql = $this->replaceTablePrefix($sql);
216
-		$sql = $this->adapter->fixupStatement($sql);
217
-		$this->queriesExecuted++;
218
-		return parent::executeQuery($sql, $params, $types, $qcp);
219
-	}
220
-
221
-	public function executeUpdate($sql, array $params = [], array $types = []) {
222
-		$sql = $this->replaceTablePrefix($sql);
223
-		$sql = $this->adapter->fixupStatement($sql);
224
-		$this->queriesExecuted++;
225
-		return parent::executeUpdate($sql, $params, $types);
226
-	}
227
-
228
-	/**
229
-	 * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
230
-	 * and returns the number of affected rows.
231
-	 *
232
-	 * This method supports PDO binding types as well as DBAL mapping types.
233
-	 *
234
-	 * @param string $sql  The SQL query.
235
-	 * @param array  $params The query parameters.
236
-	 * @param array  $types  The parameter types.
237
-	 *
238
-	 * @return integer The number of affected rows.
239
-	 *
240
-	 * @throws \Doctrine\DBAL\DBALException
241
-	 */
242
-	public function executeStatement($sql, array $params = [], array $types = []) {
243
-		$sql = $this->replaceTablePrefix($sql);
244
-		$sql = $this->adapter->fixupStatement($sql);
245
-		$this->queriesExecuted++;
246
-		return parent::executeStatement($sql, $params, $types);
247
-	}
248
-
249
-	/**
250
-	 * Returns the ID of the last inserted row, or the last value from a sequence object,
251
-	 * depending on the underlying driver.
252
-	 *
253
-	 * Note: This method may not return a meaningful or consistent result across different drivers,
254
-	 * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
255
-	 * columns or sequences.
256
-	 *
257
-	 * @param string $seqName Name of the sequence object from which the ID should be returned.
258
-	 * @return string A string representation of the last inserted ID.
259
-	 */
260
-	public function lastInsertId($seqName = null) {
261
-		if ($seqName) {
262
-			$seqName = $this->replaceTablePrefix($seqName);
263
-		}
264
-		return $this->adapter->lastInsertId($seqName);
265
-	}
266
-
267
-	// internal use
268
-	public function realLastInsertId($seqName = null) {
269
-		return parent::lastInsertId($seqName);
270
-	}
271
-
272
-	/**
273
-	 * Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
274
-	 * it is needed that there is also a unique constraint on the values. Then this method will
275
-	 * catch the exception and return 0.
276
-	 *
277
-	 * @param string $table The table name (will replace *PREFIX* with the actual prefix)
278
-	 * @param array $input data that should be inserted into the table  (column name => value)
279
-	 * @param array|null $compare List of values that should be checked for "if not exists"
280
-	 *				If this is null or an empty array, all keys of $input will be compared
281
-	 *				Please note: text fields (clob) must not be used in the compare array
282
-	 * @return int number of inserted rows
283
-	 * @throws \Doctrine\DBAL\DBALException
284
-	 * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371
285
-	 */
286
-	public function insertIfNotExist($table, $input, array $compare = null) {
287
-		return $this->adapter->insertIfNotExist($table, $input, $compare);
288
-	}
289
-
290
-	public function insertIgnoreConflict(string $table, array $values) : int {
291
-		return $this->adapter->insertIgnoreConflict($table, $values);
292
-	}
293
-
294
-	private function getType($value) {
295
-		if (is_bool($value)) {
296
-			return IQueryBuilder::PARAM_BOOL;
297
-		} elseif (is_int($value)) {
298
-			return IQueryBuilder::PARAM_INT;
299
-		} else {
300
-			return IQueryBuilder::PARAM_STR;
301
-		}
302
-	}
303
-
304
-	/**
305
-	 * Insert or update a row value
306
-	 *
307
-	 * @param string $table
308
-	 * @param array $keys (column name => value)
309
-	 * @param array $values (column name => value)
310
-	 * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
311
-	 * @return int number of new rows
312
-	 * @throws \Doctrine\DBAL\DBALException
313
-	 * @throws PreConditionNotMetException
314
-	 */
315
-	public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
316
-		try {
317
-			$insertQb = $this->getQueryBuilder();
318
-			$insertQb->insert($table)
319
-				->values(
320
-					array_map(function ($value) use ($insertQb) {
321
-						return $insertQb->createNamedParameter($value, $this->getType($value));
322
-					}, array_merge($keys, $values))
323
-				);
324
-			return $insertQb->execute();
325
-		} catch (ConstraintViolationException $e) {
326
-			// value already exists, try update
327
-			$updateQb = $this->getQueryBuilder();
328
-			$updateQb->update($table);
329
-			foreach ($values as $name => $value) {
330
-				$updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value)));
331
-			}
332
-			$where = $updateQb->expr()->andX();
333
-			$whereValues = array_merge($keys, $updatePreconditionValues);
334
-			foreach ($whereValues as $name => $value) {
335
-				$where->add($updateQb->expr()->eq(
336
-					$name,
337
-					$updateQb->createNamedParameter($value, $this->getType($value)),
338
-					$this->getType($value)
339
-				));
340
-			}
341
-			$updateQb->where($where);
342
-			$affected = $updateQb->execute();
343
-
344
-			if ($affected === 0 && !empty($updatePreconditionValues)) {
345
-				throw new PreConditionNotMetException();
346
-			}
347
-
348
-			return 0;
349
-		}
350
-	}
351
-
352
-	/**
353
-	 * Create an exclusive read+write lock on a table
354
-	 *
355
-	 * @param string $tableName
356
-	 * @throws \BadMethodCallException When trying to acquire a second lock
357
-	 * @since 9.1.0
358
-	 */
359
-	public function lockTable($tableName) {
360
-		if ($this->lockedTable !== null) {
361
-			throw new \BadMethodCallException('Can not lock a new table until the previous lock is released.');
362
-		}
363
-
364
-		$tableName = $this->tablePrefix . $tableName;
365
-		$this->lockedTable = $tableName;
366
-		$this->adapter->lockTable($tableName);
367
-	}
368
-
369
-	/**
370
-	 * Release a previous acquired lock again
371
-	 *
372
-	 * @since 9.1.0
373
-	 */
374
-	public function unlockTable() {
375
-		$this->adapter->unlockTable();
376
-		$this->lockedTable = null;
377
-	}
378
-
379
-	/**
380
-	 * returns the error code and message as a string for logging
381
-	 * works with DoctrineException
382
-	 * @return string
383
-	 */
384
-	public function getError() {
385
-		$msg = $this->errorCode() . ': ';
386
-		$errorInfo = $this->errorInfo();
387
-		if (is_array($errorInfo)) {
388
-			$msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
389
-			$msg .= 'Driver Code = '.$errorInfo[1] . ', ';
390
-			$msg .= 'Driver Message = '.$errorInfo[2];
391
-		}
392
-		return $msg;
393
-	}
394
-
395
-	/**
396
-	 * Drop a table from the database if it exists
397
-	 *
398
-	 * @param string $table table name without the prefix
399
-	 */
400
-	public function dropTable($table) {
401
-		$table = $this->tablePrefix . trim($table);
402
-		$schema = $this->getSchemaManager();
403
-		if ($schema->tablesExist([$table])) {
404
-			$schema->dropTable($table);
405
-		}
406
-	}
407
-
408
-	/**
409
-	 * Check if a table exists
410
-	 *
411
-	 * @param string $table table name without the prefix
412
-	 * @return bool
413
-	 */
414
-	public function tableExists($table) {
415
-		$table = $this->tablePrefix . trim($table);
416
-		$schema = $this->getSchemaManager();
417
-		return $schema->tablesExist([$table]);
418
-	}
419
-
420
-	// internal use
421
-	/**
422
-	 * @param string $statement
423
-	 * @return string
424
-	 */
425
-	protected function replaceTablePrefix($statement) {
426
-		return str_replace('*PREFIX*', $this->tablePrefix, $statement);
427
-	}
428
-
429
-	/**
430
-	 * Check if a transaction is active
431
-	 *
432
-	 * @return bool
433
-	 * @since 8.2.0
434
-	 */
435
-	public function inTransaction() {
436
-		return $this->getTransactionNestingLevel() > 0;
437
-	}
438
-
439
-	/**
440
-	 * Escape a parameter to be used in a LIKE query
441
-	 *
442
-	 * @param string $param
443
-	 * @return string
444
-	 */
445
-	public function escapeLikeParameter($param) {
446
-		return addcslashes($param, '\\_%');
447
-	}
448
-
449
-	/**
450
-	 * Check whether or not the current database support 4byte wide unicode
451
-	 *
452
-	 * @return bool
453
-	 * @since 11.0.0
454
-	 */
455
-	public function supports4ByteText() {
456
-		if (!$this->getDatabasePlatform() instanceof MySqlPlatform) {
457
-			return true;
458
-		}
459
-		return $this->getParams()['charset'] === 'utf8mb4';
460
-	}
461
-
462
-
463
-	/**
464
-	 * Create the schema of the connected database
465
-	 *
466
-	 * @return Schema
467
-	 */
468
-	public function createSchema() {
469
-		$schemaManager = new MDB2SchemaManager($this);
470
-		$migrator = $schemaManager->getMigrator();
471
-		return $migrator->createSchema();
472
-	}
473
-
474
-	/**
475
-	 * Migrate the database to the given schema
476
-	 *
477
-	 * @param Schema $toSchema
478
-	 */
479
-	public function migrateToSchema(Schema $toSchema) {
480
-		$schemaManager = new MDB2SchemaManager($this);
481
-		$migrator = $schemaManager->getMigrator();
482
-		$migrator->migrate($toSchema);
483
-	}
52
+    /** @var string */
53
+    protected $tablePrefix;
54
+
55
+    /** @var \OC\DB\Adapter $adapter */
56
+    protected $adapter;
57
+
58
+    /** @var SystemConfig */
59
+    private $systemConfig;
60
+
61
+    /** @var ILogger */
62
+    private $logger;
63
+
64
+    protected $lockedTable = null;
65
+
66
+    /** @var int */
67
+    protected $queriesBuilt = 0;
68
+
69
+    /** @var int */
70
+    protected $queriesExecuted = 0;
71
+
72
+    public function connect() {
73
+        try {
74
+            return parent::connect();
75
+        } catch (DBALException $e) {
76
+            // throw a new exception to prevent leaking info from the stacktrace
77
+            throw new DBALException('Failed to connect to the database: ' . $e->getMessage(), $e->getCode());
78
+        }
79
+    }
80
+
81
+    public function getStats(): array {
82
+        return [
83
+            'built' => $this->queriesBuilt,
84
+            'executed' => $this->queriesExecuted,
85
+        ];
86
+    }
87
+
88
+    /**
89
+     * Returns a QueryBuilder for the connection.
90
+     *
91
+     * @return \OCP\DB\QueryBuilder\IQueryBuilder
92
+     */
93
+    public function getQueryBuilder() {
94
+        $this->queriesBuilt++;
95
+        return new QueryBuilder(
96
+            $this,
97
+            $this->systemConfig,
98
+            $this->logger
99
+        );
100
+    }
101
+
102
+    /**
103
+     * Gets the QueryBuilder for the connection.
104
+     *
105
+     * @return \Doctrine\DBAL\Query\QueryBuilder
106
+     * @deprecated please use $this->getQueryBuilder() instead
107
+     */
108
+    public function createQueryBuilder() {
109
+        $backtrace = $this->getCallerBacktrace();
110
+        \OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
111
+        $this->queriesBuilt++;
112
+        return parent::createQueryBuilder();
113
+    }
114
+
115
+    /**
116
+     * Gets the ExpressionBuilder for the connection.
117
+     *
118
+     * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder
119
+     * @deprecated please use $this->getQueryBuilder()->expr() instead
120
+     */
121
+    public function getExpressionBuilder() {
122
+        $backtrace = $this->getCallerBacktrace();
123
+        \OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
124
+        $this->queriesBuilt++;
125
+        return parent::getExpressionBuilder();
126
+    }
127
+
128
+    /**
129
+     * Get the file and line that called the method where `getCallerBacktrace()` was used
130
+     *
131
+     * @return string
132
+     */
133
+    protected function getCallerBacktrace() {
134
+        $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
135
+
136
+        // 0 is the method where we use `getCallerBacktrace`
137
+        // 1 is the target method which uses the method we want to log
138
+        if (isset($traces[1])) {
139
+            return $traces[1]['file'] . ':' . $traces[1]['line'];
140
+        }
141
+
142
+        return '';
143
+    }
144
+
145
+    /**
146
+     * @return string
147
+     */
148
+    public function getPrefix() {
149
+        return $this->tablePrefix;
150
+    }
151
+
152
+    /**
153
+     * Initializes a new instance of the Connection class.
154
+     *
155
+     * @param array $params  The connection parameters.
156
+     * @param \Doctrine\DBAL\Driver $driver
157
+     * @param \Doctrine\DBAL\Configuration $config
158
+     * @param \Doctrine\Common\EventManager $eventManager
159
+     * @throws \Exception
160
+     */
161
+    public function __construct(array $params, Driver $driver, Configuration $config = null,
162
+        EventManager $eventManager = null) {
163
+        if (!isset($params['adapter'])) {
164
+            throw new \Exception('adapter not set');
165
+        }
166
+        if (!isset($params['tablePrefix'])) {
167
+            throw new \Exception('tablePrefix not set');
168
+        }
169
+        parent::__construct($params, $driver, $config, $eventManager);
170
+        $this->adapter = new $params['adapter']($this);
171
+        $this->tablePrefix = $params['tablePrefix'];
172
+
173
+        $this->systemConfig = \OC::$server->getSystemConfig();
174
+        $this->logger = \OC::$server->getLogger();
175
+    }
176
+
177
+    /**
178
+     * Prepares an SQL statement.
179
+     *
180
+     * @param string $statement The SQL statement to prepare.
181
+     * @param int $limit
182
+     * @param int $offset
183
+     * @return \Doctrine\DBAL\Driver\Statement The prepared statement.
184
+     */
185
+    public function prepare($statement, $limit = null, $offset = null) {
186
+        if ($limit === -1) {
187
+            $limit = null;
188
+        }
189
+        if (!is_null($limit)) {
190
+            $platform = $this->getDatabasePlatform();
191
+            $statement = $platform->modifyLimitQuery($statement, $limit, $offset);
192
+        }
193
+        $statement = $this->replaceTablePrefix($statement);
194
+        $statement = $this->adapter->fixupStatement($statement);
195
+
196
+        return parent::prepare($statement);
197
+    }
198
+
199
+    /**
200
+     * Executes an, optionally parametrized, SQL query.
201
+     *
202
+     * If the query is parametrized, a prepared statement is used.
203
+     * If an SQLLogger is configured, the execution is logged.
204
+     *
205
+     * @param string                                      $sql  The SQL query to execute.
206
+     * @param array                                       $params The parameters to bind to the query, if any.
207
+     * @param array                                       $types  The types the previous parameters are in.
208
+     * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp    The query cache profile, optional.
209
+     *
210
+     * @return \Doctrine\DBAL\Driver\Statement The executed statement.
211
+     *
212
+     * @throws \Doctrine\DBAL\DBALException
213
+     */
214
+    public function executeQuery($sql, array $params = [], $types = [], QueryCacheProfile $qcp = null) {
215
+        $sql = $this->replaceTablePrefix($sql);
216
+        $sql = $this->adapter->fixupStatement($sql);
217
+        $this->queriesExecuted++;
218
+        return parent::executeQuery($sql, $params, $types, $qcp);
219
+    }
220
+
221
+    public function executeUpdate($sql, array $params = [], array $types = []) {
222
+        $sql = $this->replaceTablePrefix($sql);
223
+        $sql = $this->adapter->fixupStatement($sql);
224
+        $this->queriesExecuted++;
225
+        return parent::executeUpdate($sql, $params, $types);
226
+    }
227
+
228
+    /**
229
+     * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
230
+     * and returns the number of affected rows.
231
+     *
232
+     * This method supports PDO binding types as well as DBAL mapping types.
233
+     *
234
+     * @param string $sql  The SQL query.
235
+     * @param array  $params The query parameters.
236
+     * @param array  $types  The parameter types.
237
+     *
238
+     * @return integer The number of affected rows.
239
+     *
240
+     * @throws \Doctrine\DBAL\DBALException
241
+     */
242
+    public function executeStatement($sql, array $params = [], array $types = []) {
243
+        $sql = $this->replaceTablePrefix($sql);
244
+        $sql = $this->adapter->fixupStatement($sql);
245
+        $this->queriesExecuted++;
246
+        return parent::executeStatement($sql, $params, $types);
247
+    }
248
+
249
+    /**
250
+     * Returns the ID of the last inserted row, or the last value from a sequence object,
251
+     * depending on the underlying driver.
252
+     *
253
+     * Note: This method may not return a meaningful or consistent result across different drivers,
254
+     * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
255
+     * columns or sequences.
256
+     *
257
+     * @param string $seqName Name of the sequence object from which the ID should be returned.
258
+     * @return string A string representation of the last inserted ID.
259
+     */
260
+    public function lastInsertId($seqName = null) {
261
+        if ($seqName) {
262
+            $seqName = $this->replaceTablePrefix($seqName);
263
+        }
264
+        return $this->adapter->lastInsertId($seqName);
265
+    }
266
+
267
+    // internal use
268
+    public function realLastInsertId($seqName = null) {
269
+        return parent::lastInsertId($seqName);
270
+    }
271
+
272
+    /**
273
+     * Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
274
+     * it is needed that there is also a unique constraint on the values. Then this method will
275
+     * catch the exception and return 0.
276
+     *
277
+     * @param string $table The table name (will replace *PREFIX* with the actual prefix)
278
+     * @param array $input data that should be inserted into the table  (column name => value)
279
+     * @param array|null $compare List of values that should be checked for "if not exists"
280
+     *				If this is null or an empty array, all keys of $input will be compared
281
+     *				Please note: text fields (clob) must not be used in the compare array
282
+     * @return int number of inserted rows
283
+     * @throws \Doctrine\DBAL\DBALException
284
+     * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371
285
+     */
286
+    public function insertIfNotExist($table, $input, array $compare = null) {
287
+        return $this->adapter->insertIfNotExist($table, $input, $compare);
288
+    }
289
+
290
+    public function insertIgnoreConflict(string $table, array $values) : int {
291
+        return $this->adapter->insertIgnoreConflict($table, $values);
292
+    }
293
+
294
+    private function getType($value) {
295
+        if (is_bool($value)) {
296
+            return IQueryBuilder::PARAM_BOOL;
297
+        } elseif (is_int($value)) {
298
+            return IQueryBuilder::PARAM_INT;
299
+        } else {
300
+            return IQueryBuilder::PARAM_STR;
301
+        }
302
+    }
303
+
304
+    /**
305
+     * Insert or update a row value
306
+     *
307
+     * @param string $table
308
+     * @param array $keys (column name => value)
309
+     * @param array $values (column name => value)
310
+     * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
311
+     * @return int number of new rows
312
+     * @throws \Doctrine\DBAL\DBALException
313
+     * @throws PreConditionNotMetException
314
+     */
315
+    public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
316
+        try {
317
+            $insertQb = $this->getQueryBuilder();
318
+            $insertQb->insert($table)
319
+                ->values(
320
+                    array_map(function ($value) use ($insertQb) {
321
+                        return $insertQb->createNamedParameter($value, $this->getType($value));
322
+                    }, array_merge($keys, $values))
323
+                );
324
+            return $insertQb->execute();
325
+        } catch (ConstraintViolationException $e) {
326
+            // value already exists, try update
327
+            $updateQb = $this->getQueryBuilder();
328
+            $updateQb->update($table);
329
+            foreach ($values as $name => $value) {
330
+                $updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value)));
331
+            }
332
+            $where = $updateQb->expr()->andX();
333
+            $whereValues = array_merge($keys, $updatePreconditionValues);
334
+            foreach ($whereValues as $name => $value) {
335
+                $where->add($updateQb->expr()->eq(
336
+                    $name,
337
+                    $updateQb->createNamedParameter($value, $this->getType($value)),
338
+                    $this->getType($value)
339
+                ));
340
+            }
341
+            $updateQb->where($where);
342
+            $affected = $updateQb->execute();
343
+
344
+            if ($affected === 0 && !empty($updatePreconditionValues)) {
345
+                throw new PreConditionNotMetException();
346
+            }
347
+
348
+            return 0;
349
+        }
350
+    }
351
+
352
+    /**
353
+     * Create an exclusive read+write lock on a table
354
+     *
355
+     * @param string $tableName
356
+     * @throws \BadMethodCallException When trying to acquire a second lock
357
+     * @since 9.1.0
358
+     */
359
+    public function lockTable($tableName) {
360
+        if ($this->lockedTable !== null) {
361
+            throw new \BadMethodCallException('Can not lock a new table until the previous lock is released.');
362
+        }
363
+
364
+        $tableName = $this->tablePrefix . $tableName;
365
+        $this->lockedTable = $tableName;
366
+        $this->adapter->lockTable($tableName);
367
+    }
368
+
369
+    /**
370
+     * Release a previous acquired lock again
371
+     *
372
+     * @since 9.1.0
373
+     */
374
+    public function unlockTable() {
375
+        $this->adapter->unlockTable();
376
+        $this->lockedTable = null;
377
+    }
378
+
379
+    /**
380
+     * returns the error code and message as a string for logging
381
+     * works with DoctrineException
382
+     * @return string
383
+     */
384
+    public function getError() {
385
+        $msg = $this->errorCode() . ': ';
386
+        $errorInfo = $this->errorInfo();
387
+        if (is_array($errorInfo)) {
388
+            $msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
389
+            $msg .= 'Driver Code = '.$errorInfo[1] . ', ';
390
+            $msg .= 'Driver Message = '.$errorInfo[2];
391
+        }
392
+        return $msg;
393
+    }
394
+
395
+    /**
396
+     * Drop a table from the database if it exists
397
+     *
398
+     * @param string $table table name without the prefix
399
+     */
400
+    public function dropTable($table) {
401
+        $table = $this->tablePrefix . trim($table);
402
+        $schema = $this->getSchemaManager();
403
+        if ($schema->tablesExist([$table])) {
404
+            $schema->dropTable($table);
405
+        }
406
+    }
407
+
408
+    /**
409
+     * Check if a table exists
410
+     *
411
+     * @param string $table table name without the prefix
412
+     * @return bool
413
+     */
414
+    public function tableExists($table) {
415
+        $table = $this->tablePrefix . trim($table);
416
+        $schema = $this->getSchemaManager();
417
+        return $schema->tablesExist([$table]);
418
+    }
419
+
420
+    // internal use
421
+    /**
422
+     * @param string $statement
423
+     * @return string
424
+     */
425
+    protected function replaceTablePrefix($statement) {
426
+        return str_replace('*PREFIX*', $this->tablePrefix, $statement);
427
+    }
428
+
429
+    /**
430
+     * Check if a transaction is active
431
+     *
432
+     * @return bool
433
+     * @since 8.2.0
434
+     */
435
+    public function inTransaction() {
436
+        return $this->getTransactionNestingLevel() > 0;
437
+    }
438
+
439
+    /**
440
+     * Escape a parameter to be used in a LIKE query
441
+     *
442
+     * @param string $param
443
+     * @return string
444
+     */
445
+    public function escapeLikeParameter($param) {
446
+        return addcslashes($param, '\\_%');
447
+    }
448
+
449
+    /**
450
+     * Check whether or not the current database support 4byte wide unicode
451
+     *
452
+     * @return bool
453
+     * @since 11.0.0
454
+     */
455
+    public function supports4ByteText() {
456
+        if (!$this->getDatabasePlatform() instanceof MySqlPlatform) {
457
+            return true;
458
+        }
459
+        return $this->getParams()['charset'] === 'utf8mb4';
460
+    }
461
+
462
+
463
+    /**
464
+     * Create the schema of the connected database
465
+     *
466
+     * @return Schema
467
+     */
468
+    public function createSchema() {
469
+        $schemaManager = new MDB2SchemaManager($this);
470
+        $migrator = $schemaManager->getMigrator();
471
+        return $migrator->createSchema();
472
+    }
473
+
474
+    /**
475
+     * Migrate the database to the given schema
476
+     *
477
+     * @param Schema $toSchema
478
+     */
479
+    public function migrateToSchema(Schema $toSchema) {
480
+        $schemaManager = new MDB2SchemaManager($this);
481
+        $migrator = $schemaManager->getMigrator();
482
+        $migrator->migrate($toSchema);
483
+    }
484 484
 }
Please login to merge, or discard this patch.