Passed
Push — master ( 11fca4...69a6b1 )
by Roeland
13:58 queued 11s
created
lib/private/DB/Connection.php 1 patch
Indentation   +425 added lines, -425 removed lines patch added patch discarded remove patch
@@ -49,429 +49,429 @@
 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                                      $query  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($query, array $params = [], $types = [], QueryCacheProfile $qcp = null) {
215
-		$query = $this->replaceTablePrefix($query);
216
-		$query = $this->adapter->fixupStatement($query);
217
-		$this->queriesExecuted++;
218
-		return parent::executeQuery($query, $params, $types, $qcp);
219
-	}
220
-
221
-	/**
222
-	 * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
223
-	 * and returns the number of affected rows.
224
-	 *
225
-	 * This method supports PDO binding types as well as DBAL mapping types.
226
-	 *
227
-	 * @param string $query  The SQL query.
228
-	 * @param array  $params The query parameters.
229
-	 * @param array  $types  The parameter types.
230
-	 *
231
-	 * @return integer The number of affected rows.
232
-	 *
233
-	 * @throws \Doctrine\DBAL\DBALException
234
-	 */
235
-	public function executeUpdate($query, array $params = [], array $types = []) {
236
-		$query = $this->replaceTablePrefix($query);
237
-		$query = $this->adapter->fixupStatement($query);
238
-		$this->queriesExecuted++;
239
-		return parent::executeUpdate($query, $params, $types);
240
-	}
241
-
242
-	/**
243
-	 * Returns the ID of the last inserted row, or the last value from a sequence object,
244
-	 * depending on the underlying driver.
245
-	 *
246
-	 * Note: This method may not return a meaningful or consistent result across different drivers,
247
-	 * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
248
-	 * columns or sequences.
249
-	 *
250
-	 * @param string $seqName Name of the sequence object from which the ID should be returned.
251
-	 * @return string A string representation of the last inserted ID.
252
-	 */
253
-	public function lastInsertId($seqName = null) {
254
-		if ($seqName) {
255
-			$seqName = $this->replaceTablePrefix($seqName);
256
-		}
257
-		return $this->adapter->lastInsertId($seqName);
258
-	}
259
-
260
-	// internal use
261
-	public function realLastInsertId($seqName = null) {
262
-		return parent::lastInsertId($seqName);
263
-	}
264
-
265
-	/**
266
-	 * Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
267
-	 * it is needed that there is also a unique constraint on the values. Then this method will
268
-	 * catch the exception and return 0.
269
-	 *
270
-	 * @param string $table The table name (will replace *PREFIX* with the actual prefix)
271
-	 * @param array $input data that should be inserted into the table  (column name => value)
272
-	 * @param array|null $compare List of values that should be checked for "if not exists"
273
-	 *				If this is null or an empty array, all keys of $input will be compared
274
-	 *				Please note: text fields (clob) must not be used in the compare array
275
-	 * @return int number of inserted rows
276
-	 * @throws \Doctrine\DBAL\DBALException
277
-	 * @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
278
-	 */
279
-	public function insertIfNotExist($table, $input, array $compare = null) {
280
-		return $this->adapter->insertIfNotExist($table, $input, $compare);
281
-	}
282
-
283
-	public function insertIgnoreConflict(string $table, array $values) : int {
284
-		return $this->adapter->insertIgnoreConflict($table, $values);
285
-	}
286
-
287
-	private function getType($value) {
288
-		if (is_bool($value)) {
289
-			return IQueryBuilder::PARAM_BOOL;
290
-		} elseif (is_int($value)) {
291
-			return IQueryBuilder::PARAM_INT;
292
-		} else {
293
-			return IQueryBuilder::PARAM_STR;
294
-		}
295
-	}
296
-
297
-	/**
298
-	 * Insert or update a row value
299
-	 *
300
-	 * @param string $table
301
-	 * @param array $keys (column name => value)
302
-	 * @param array $values (column name => value)
303
-	 * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
304
-	 * @return int number of new rows
305
-	 * @throws \Doctrine\DBAL\DBALException
306
-	 * @throws PreConditionNotMetException
307
-	 */
308
-	public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
309
-		try {
310
-			$insertQb = $this->getQueryBuilder();
311
-			$insertQb->insert($table)
312
-				->values(
313
-					array_map(function ($value) use ($insertQb) {
314
-						return $insertQb->createNamedParameter($value, $this->getType($value));
315
-					}, array_merge($keys, $values))
316
-				);
317
-			return $insertQb->execute();
318
-		} catch (ConstraintViolationException $e) {
319
-			// value already exists, try update
320
-			$updateQb = $this->getQueryBuilder();
321
-			$updateQb->update($table);
322
-			foreach ($values as $name => $value) {
323
-				$updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value)));
324
-			}
325
-			$where = $updateQb->expr()->andX();
326
-			$whereValues = array_merge($keys, $updatePreconditionValues);
327
-			foreach ($whereValues as $name => $value) {
328
-				$where->add($updateQb->expr()->eq(
329
-					$name,
330
-					$updateQb->createNamedParameter($value, $this->getType($value)),
331
-					$this->getType($value)
332
-				));
333
-			}
334
-			$updateQb->where($where);
335
-			$affected = $updateQb->execute();
336
-
337
-			if ($affected === 0 && !empty($updatePreconditionValues)) {
338
-				throw new PreConditionNotMetException();
339
-			}
340
-
341
-			return 0;
342
-		}
343
-	}
344
-
345
-	/**
346
-	 * Create an exclusive read+write lock on a table
347
-	 *
348
-	 * @param string $tableName
349
-	 * @throws \BadMethodCallException When trying to acquire a second lock
350
-	 * @since 9.1.0
351
-	 */
352
-	public function lockTable($tableName) {
353
-		if ($this->lockedTable !== null) {
354
-			throw new \BadMethodCallException('Can not lock a new table until the previous lock is released.');
355
-		}
356
-
357
-		$tableName = $this->tablePrefix . $tableName;
358
-		$this->lockedTable = $tableName;
359
-		$this->adapter->lockTable($tableName);
360
-	}
361
-
362
-	/**
363
-	 * Release a previous acquired lock again
364
-	 *
365
-	 * @since 9.1.0
366
-	 */
367
-	public function unlockTable() {
368
-		$this->adapter->unlockTable();
369
-		$this->lockedTable = null;
370
-	}
371
-
372
-	/**
373
-	 * returns the error code and message as a string for logging
374
-	 * works with DoctrineException
375
-	 * @return string
376
-	 */
377
-	public function getError() {
378
-		$msg = $this->errorCode() . ': ';
379
-		$errorInfo = $this->errorInfo();
380
-		if (is_array($errorInfo)) {
381
-			$msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
382
-			$msg .= 'Driver Code = '.$errorInfo[1] . ', ';
383
-			$msg .= 'Driver Message = '.$errorInfo[2];
384
-		}
385
-		return $msg;
386
-	}
387
-
388
-	/**
389
-	 * Drop a table from the database if it exists
390
-	 *
391
-	 * @param string $table table name without the prefix
392
-	 */
393
-	public function dropTable($table) {
394
-		$table = $this->tablePrefix . trim($table);
395
-		$schema = $this->getSchemaManager();
396
-		if ($schema->tablesExist([$table])) {
397
-			$schema->dropTable($table);
398
-		}
399
-	}
400
-
401
-	/**
402
-	 * Check if a table exists
403
-	 *
404
-	 * @param string $table table name without the prefix
405
-	 * @return bool
406
-	 */
407
-	public function tableExists($table) {
408
-		$table = $this->tablePrefix . trim($table);
409
-		$schema = $this->getSchemaManager();
410
-		return $schema->tablesExist([$table]);
411
-	}
412
-
413
-	// internal use
414
-	/**
415
-	 * @param string $statement
416
-	 * @return string
417
-	 */
418
-	protected function replaceTablePrefix($statement) {
419
-		return str_replace('*PREFIX*', $this->tablePrefix, $statement);
420
-	}
421
-
422
-	/**
423
-	 * Check if a transaction is active
424
-	 *
425
-	 * @return bool
426
-	 * @since 8.2.0
427
-	 */
428
-	public function inTransaction() {
429
-		return $this->getTransactionNestingLevel() > 0;
430
-	}
431
-
432
-	/**
433
-	 * Escape a parameter to be used in a LIKE query
434
-	 *
435
-	 * @param string $param
436
-	 * @return string
437
-	 */
438
-	public function escapeLikeParameter($param) {
439
-		return addcslashes($param, '\\_%');
440
-	}
441
-
442
-	/**
443
-	 * Check whether or not the current database support 4byte wide unicode
444
-	 *
445
-	 * @return bool
446
-	 * @since 11.0.0
447
-	 */
448
-	public function supports4ByteText() {
449
-		if (!$this->getDatabasePlatform() instanceof MySqlPlatform) {
450
-			return true;
451
-		}
452
-		return $this->getParams()['charset'] === 'utf8mb4';
453
-	}
454
-
455
-
456
-	/**
457
-	 * Create the schema of the connected database
458
-	 *
459
-	 * @return Schema
460
-	 */
461
-	public function createSchema() {
462
-		$schemaManager = new MDB2SchemaManager($this);
463
-		$migrator = $schemaManager->getMigrator();
464
-		return $migrator->createSchema();
465
-	}
466
-
467
-	/**
468
-	 * Migrate the database to the given schema
469
-	 *
470
-	 * @param Schema $toSchema
471
-	 */
472
-	public function migrateToSchema(Schema $toSchema) {
473
-		$schemaManager = new MDB2SchemaManager($this);
474
-		$migrator = $schemaManager->getMigrator();
475
-		$migrator->migrate($toSchema);
476
-	}
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                                      $query  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($query, array $params = [], $types = [], QueryCacheProfile $qcp = null) {
215
+        $query = $this->replaceTablePrefix($query);
216
+        $query = $this->adapter->fixupStatement($query);
217
+        $this->queriesExecuted++;
218
+        return parent::executeQuery($query, $params, $types, $qcp);
219
+    }
220
+
221
+    /**
222
+     * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
223
+     * and returns the number of affected rows.
224
+     *
225
+     * This method supports PDO binding types as well as DBAL mapping types.
226
+     *
227
+     * @param string $query  The SQL query.
228
+     * @param array  $params The query parameters.
229
+     * @param array  $types  The parameter types.
230
+     *
231
+     * @return integer The number of affected rows.
232
+     *
233
+     * @throws \Doctrine\DBAL\DBALException
234
+     */
235
+    public function executeUpdate($query, array $params = [], array $types = []) {
236
+        $query = $this->replaceTablePrefix($query);
237
+        $query = $this->adapter->fixupStatement($query);
238
+        $this->queriesExecuted++;
239
+        return parent::executeUpdate($query, $params, $types);
240
+    }
241
+
242
+    /**
243
+     * Returns the ID of the last inserted row, or the last value from a sequence object,
244
+     * depending on the underlying driver.
245
+     *
246
+     * Note: This method may not return a meaningful or consistent result across different drivers,
247
+     * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
248
+     * columns or sequences.
249
+     *
250
+     * @param string $seqName Name of the sequence object from which the ID should be returned.
251
+     * @return string A string representation of the last inserted ID.
252
+     */
253
+    public function lastInsertId($seqName = null) {
254
+        if ($seqName) {
255
+            $seqName = $this->replaceTablePrefix($seqName);
256
+        }
257
+        return $this->adapter->lastInsertId($seqName);
258
+    }
259
+
260
+    // internal use
261
+    public function realLastInsertId($seqName = null) {
262
+        return parent::lastInsertId($seqName);
263
+    }
264
+
265
+    /**
266
+     * Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
267
+     * it is needed that there is also a unique constraint on the values. Then this method will
268
+     * catch the exception and return 0.
269
+     *
270
+     * @param string $table The table name (will replace *PREFIX* with the actual prefix)
271
+     * @param array $input data that should be inserted into the table  (column name => value)
272
+     * @param array|null $compare List of values that should be checked for "if not exists"
273
+     *				If this is null or an empty array, all keys of $input will be compared
274
+     *				Please note: text fields (clob) must not be used in the compare array
275
+     * @return int number of inserted rows
276
+     * @throws \Doctrine\DBAL\DBALException
277
+     * @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
278
+     */
279
+    public function insertIfNotExist($table, $input, array $compare = null) {
280
+        return $this->adapter->insertIfNotExist($table, $input, $compare);
281
+    }
282
+
283
+    public function insertIgnoreConflict(string $table, array $values) : int {
284
+        return $this->adapter->insertIgnoreConflict($table, $values);
285
+    }
286
+
287
+    private function getType($value) {
288
+        if (is_bool($value)) {
289
+            return IQueryBuilder::PARAM_BOOL;
290
+        } elseif (is_int($value)) {
291
+            return IQueryBuilder::PARAM_INT;
292
+        } else {
293
+            return IQueryBuilder::PARAM_STR;
294
+        }
295
+    }
296
+
297
+    /**
298
+     * Insert or update a row value
299
+     *
300
+     * @param string $table
301
+     * @param array $keys (column name => value)
302
+     * @param array $values (column name => value)
303
+     * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
304
+     * @return int number of new rows
305
+     * @throws \Doctrine\DBAL\DBALException
306
+     * @throws PreConditionNotMetException
307
+     */
308
+    public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
309
+        try {
310
+            $insertQb = $this->getQueryBuilder();
311
+            $insertQb->insert($table)
312
+                ->values(
313
+                    array_map(function ($value) use ($insertQb) {
314
+                        return $insertQb->createNamedParameter($value, $this->getType($value));
315
+                    }, array_merge($keys, $values))
316
+                );
317
+            return $insertQb->execute();
318
+        } catch (ConstraintViolationException $e) {
319
+            // value already exists, try update
320
+            $updateQb = $this->getQueryBuilder();
321
+            $updateQb->update($table);
322
+            foreach ($values as $name => $value) {
323
+                $updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value)));
324
+            }
325
+            $where = $updateQb->expr()->andX();
326
+            $whereValues = array_merge($keys, $updatePreconditionValues);
327
+            foreach ($whereValues as $name => $value) {
328
+                $where->add($updateQb->expr()->eq(
329
+                    $name,
330
+                    $updateQb->createNamedParameter($value, $this->getType($value)),
331
+                    $this->getType($value)
332
+                ));
333
+            }
334
+            $updateQb->where($where);
335
+            $affected = $updateQb->execute();
336
+
337
+            if ($affected === 0 && !empty($updatePreconditionValues)) {
338
+                throw new PreConditionNotMetException();
339
+            }
340
+
341
+            return 0;
342
+        }
343
+    }
344
+
345
+    /**
346
+     * Create an exclusive read+write lock on a table
347
+     *
348
+     * @param string $tableName
349
+     * @throws \BadMethodCallException When trying to acquire a second lock
350
+     * @since 9.1.0
351
+     */
352
+    public function lockTable($tableName) {
353
+        if ($this->lockedTable !== null) {
354
+            throw new \BadMethodCallException('Can not lock a new table until the previous lock is released.');
355
+        }
356
+
357
+        $tableName = $this->tablePrefix . $tableName;
358
+        $this->lockedTable = $tableName;
359
+        $this->adapter->lockTable($tableName);
360
+    }
361
+
362
+    /**
363
+     * Release a previous acquired lock again
364
+     *
365
+     * @since 9.1.0
366
+     */
367
+    public function unlockTable() {
368
+        $this->adapter->unlockTable();
369
+        $this->lockedTable = null;
370
+    }
371
+
372
+    /**
373
+     * returns the error code and message as a string for logging
374
+     * works with DoctrineException
375
+     * @return string
376
+     */
377
+    public function getError() {
378
+        $msg = $this->errorCode() . ': ';
379
+        $errorInfo = $this->errorInfo();
380
+        if (is_array($errorInfo)) {
381
+            $msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
382
+            $msg .= 'Driver Code = '.$errorInfo[1] . ', ';
383
+            $msg .= 'Driver Message = '.$errorInfo[2];
384
+        }
385
+        return $msg;
386
+    }
387
+
388
+    /**
389
+     * Drop a table from the database if it exists
390
+     *
391
+     * @param string $table table name without the prefix
392
+     */
393
+    public function dropTable($table) {
394
+        $table = $this->tablePrefix . trim($table);
395
+        $schema = $this->getSchemaManager();
396
+        if ($schema->tablesExist([$table])) {
397
+            $schema->dropTable($table);
398
+        }
399
+    }
400
+
401
+    /**
402
+     * Check if a table exists
403
+     *
404
+     * @param string $table table name without the prefix
405
+     * @return bool
406
+     */
407
+    public function tableExists($table) {
408
+        $table = $this->tablePrefix . trim($table);
409
+        $schema = $this->getSchemaManager();
410
+        return $schema->tablesExist([$table]);
411
+    }
412
+
413
+    // internal use
414
+    /**
415
+     * @param string $statement
416
+     * @return string
417
+     */
418
+    protected function replaceTablePrefix($statement) {
419
+        return str_replace('*PREFIX*', $this->tablePrefix, $statement);
420
+    }
421
+
422
+    /**
423
+     * Check if a transaction is active
424
+     *
425
+     * @return bool
426
+     * @since 8.2.0
427
+     */
428
+    public function inTransaction() {
429
+        return $this->getTransactionNestingLevel() > 0;
430
+    }
431
+
432
+    /**
433
+     * Escape a parameter to be used in a LIKE query
434
+     *
435
+     * @param string $param
436
+     * @return string
437
+     */
438
+    public function escapeLikeParameter($param) {
439
+        return addcslashes($param, '\\_%');
440
+    }
441
+
442
+    /**
443
+     * Check whether or not the current database support 4byte wide unicode
444
+     *
445
+     * @return bool
446
+     * @since 11.0.0
447
+     */
448
+    public function supports4ByteText() {
449
+        if (!$this->getDatabasePlatform() instanceof MySqlPlatform) {
450
+            return true;
451
+        }
452
+        return $this->getParams()['charset'] === 'utf8mb4';
453
+    }
454
+
455
+
456
+    /**
457
+     * Create the schema of the connected database
458
+     *
459
+     * @return Schema
460
+     */
461
+    public function createSchema() {
462
+        $schemaManager = new MDB2SchemaManager($this);
463
+        $migrator = $schemaManager->getMigrator();
464
+        return $migrator->createSchema();
465
+    }
466
+
467
+    /**
468
+     * Migrate the database to the given schema
469
+     *
470
+     * @param Schema $toSchema
471
+     */
472
+    public function migrateToSchema(Schema $toSchema) {
473
+        $schemaManager = new MDB2SchemaManager($this);
474
+        $migrator = $schemaManager->getMigrator();
475
+        $migrator->migrate($toSchema);
476
+    }
477 477
 }
Please login to merge, or discard this patch.