Passed
Push — master ( 3bae59...c1000f )
by Christoph
14:32 queued 16s
created
lib/private/DB/Connection.php 1 patch
Indentation   +516 added lines, -516 removed lines patch added patch discarded remove patch
@@ -56,520 +56,520 @@
 block discarded – undo
56 56
 use OCP\PreConditionNotMetException;
57 57
 
58 58
 class Connection extends \Doctrine\DBAL\Connection {
59
-	/** @var string */
60
-	protected $tablePrefix;
61
-
62
-	/** @var \OC\DB\Adapter $adapter */
63
-	protected $adapter;
64
-
65
-	/** @var SystemConfig */
66
-	private $systemConfig;
67
-
68
-	/** @var ILogger */
69
-	private $logger;
70
-
71
-	protected $lockedTable = null;
72
-
73
-	/** @var int */
74
-	protected $queriesBuilt = 0;
75
-
76
-	/** @var int */
77
-	protected $queriesExecuted = 0;
78
-
79
-	/**
80
-	 * @throws Exception
81
-	 */
82
-	public function connect() {
83
-		try {
84
-			return parent::connect();
85
-		} catch (Exception $e) {
86
-			// throw a new exception to prevent leaking info from the stacktrace
87
-			throw new Exception('Failed to connect to the database: ' . $e->getMessage(), $e->getCode());
88
-		}
89
-	}
90
-
91
-	public function getStats(): array {
92
-		return [
93
-			'built' => $this->queriesBuilt,
94
-			'executed' => $this->queriesExecuted,
95
-		];
96
-	}
97
-
98
-	/**
99
-	 * Returns a QueryBuilder for the connection.
100
-	 */
101
-	public function getQueryBuilder(): IQueryBuilder {
102
-		$this->queriesBuilt++;
103
-		return new QueryBuilder(
104
-			new ConnectionAdapter($this),
105
-			$this->systemConfig,
106
-			$this->logger
107
-		);
108
-	}
109
-
110
-	/**
111
-	 * Gets the QueryBuilder for the connection.
112
-	 *
113
-	 * @return \Doctrine\DBAL\Query\QueryBuilder
114
-	 * @deprecated please use $this->getQueryBuilder() instead
115
-	 */
116
-	public function createQueryBuilder() {
117
-		$backtrace = $this->getCallerBacktrace();
118
-		\OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
119
-		$this->queriesBuilt++;
120
-		return parent::createQueryBuilder();
121
-	}
122
-
123
-	/**
124
-	 * Gets the ExpressionBuilder for the connection.
125
-	 *
126
-	 * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder
127
-	 * @deprecated please use $this->getQueryBuilder()->expr() instead
128
-	 */
129
-	public function getExpressionBuilder() {
130
-		$backtrace = $this->getCallerBacktrace();
131
-		\OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
132
-		$this->queriesBuilt++;
133
-		return parent::getExpressionBuilder();
134
-	}
135
-
136
-	/**
137
-	 * Get the file and line that called the method where `getCallerBacktrace()` was used
138
-	 *
139
-	 * @return string
140
-	 */
141
-	protected function getCallerBacktrace() {
142
-		$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
143
-
144
-		// 0 is the method where we use `getCallerBacktrace`
145
-		// 1 is the target method which uses the method we want to log
146
-		if (isset($traces[1])) {
147
-			return $traces[1]['file'] . ':' . $traces[1]['line'];
148
-		}
149
-
150
-		return '';
151
-	}
152
-
153
-	/**
154
-	 * @return string
155
-	 */
156
-	public function getPrefix() {
157
-		return $this->tablePrefix;
158
-	}
159
-
160
-	/**
161
-	 * Initializes a new instance of the Connection class.
162
-	 *
163
-	 * @param array $params  The connection parameters.
164
-	 * @param \Doctrine\DBAL\Driver $driver
165
-	 * @param \Doctrine\DBAL\Configuration $config
166
-	 * @param \Doctrine\Common\EventManager $eventManager
167
-	 * @throws \Exception
168
-	 */
169
-	public function __construct(array $params, Driver $driver, Configuration $config = null,
170
-		EventManager $eventManager = null) {
171
-		if (!isset($params['adapter'])) {
172
-			throw new \Exception('adapter not set');
173
-		}
174
-		if (!isset($params['tablePrefix'])) {
175
-			throw new \Exception('tablePrefix not set');
176
-		}
177
-		/**
178
-		 * @psalm-suppress InternalMethod
179
-		 */
180
-		parent::__construct($params, $driver, $config, $eventManager);
181
-		$this->adapter = new $params['adapter']($this);
182
-		$this->tablePrefix = $params['tablePrefix'];
183
-
184
-		$this->systemConfig = \OC::$server->getSystemConfig();
185
-		$this->logger = \OC::$server->getLogger();
186
-	}
187
-
188
-	/**
189
-	 * Prepares an SQL statement.
190
-	 *
191
-	 * @param string $statement The SQL statement to prepare.
192
-	 * @param int|null $limit
193
-	 * @param int|null $offset
194
-	 *
195
-	 * @return Statement The prepared statement.
196
-	 * @throws Exception
197
-	 */
198
-	public function prepare($statement, $limit = null, $offset = null): Statement {
199
-		if ($limit === -1 || $limit === null) {
200
-			$limit = null;
201
-		} else {
202
-			$limit = (int) $limit;
203
-		}
204
-		if ($offset !== null) {
205
-			$offset = (int) $offset;
206
-		}
207
-		if (!is_null($limit)) {
208
-			$platform = $this->getDatabasePlatform();
209
-			$statement = $platform->modifyLimitQuery($statement, $limit, $offset);
210
-		}
211
-		$statement = $this->replaceTablePrefix($statement);
212
-		$statement = $this->adapter->fixupStatement($statement);
213
-
214
-		return parent::prepare($statement);
215
-	}
216
-
217
-	/**
218
-	 * Executes an, optionally parametrized, SQL query.
219
-	 *
220
-	 * If the query is parametrized, a prepared statement is used.
221
-	 * If an SQLLogger is configured, the execution is logged.
222
-	 *
223
-	 * @param string                                      $sql  The SQL query to execute.
224
-	 * @param array                                       $params The parameters to bind to the query, if any.
225
-	 * @param array                                       $types  The types the previous parameters are in.
226
-	 * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp    The query cache profile, optional.
227
-	 *
228
-	 * @return Result The executed statement.
229
-	 *
230
-	 * @throws \Doctrine\DBAL\Exception
231
-	 */
232
-	public function executeQuery(string $sql, array $params = [], $types = [], QueryCacheProfile $qcp = null): Result {
233
-		$sql = $this->replaceTablePrefix($sql);
234
-		$sql = $this->adapter->fixupStatement($sql);
235
-		$this->queriesExecuted++;
236
-		$this->logQueryToFile($sql);
237
-		return parent::executeQuery($sql, $params, $types, $qcp);
238
-	}
239
-
240
-	/**
241
-	 * @throws Exception
242
-	 */
243
-	public function executeUpdate(string $sql, array $params = [], array $types = []): int {
244
-		$sql = $this->replaceTablePrefix($sql);
245
-		$sql = $this->adapter->fixupStatement($sql);
246
-		$this->queriesExecuted++;
247
-		$this->logQueryToFile($sql);
248
-		return parent::executeUpdate($sql, $params, $types);
249
-	}
250
-
251
-	/**
252
-	 * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
253
-	 * and returns the number of affected rows.
254
-	 *
255
-	 * This method supports PDO binding types as well as DBAL mapping types.
256
-	 *
257
-	 * @param string $sql  The SQL query.
258
-	 * @param array  $params The query parameters.
259
-	 * @param array  $types  The parameter types.
260
-	 *
261
-	 * @return int The number of affected rows.
262
-	 *
263
-	 * @throws \Doctrine\DBAL\Exception
264
-	 */
265
-	public function executeStatement($sql, array $params = [], array $types = []): int {
266
-		$sql = $this->replaceTablePrefix($sql);
267
-		$sql = $this->adapter->fixupStatement($sql);
268
-		$this->queriesExecuted++;
269
-		$this->logQueryToFile($sql);
270
-		return parent::executeStatement($sql, $params, $types);
271
-	}
272
-
273
-	protected function logQueryToFile(string $sql): void {
274
-		$logFile = $this->systemConfig->getValue('query_log_file', '');
275
-		if ($logFile !== '' && is_writable(dirname($logFile)) && (!file_exists($logFile) || is_writable($logFile))) {
276
-			file_put_contents(
277
-				$this->systemConfig->getValue('query_log_file', ''),
278
-				$sql . "\n",
279
-				FILE_APPEND
280
-			);
281
-		}
282
-	}
283
-
284
-	/**
285
-	 * Returns the ID of the last inserted row, or the last value from a sequence object,
286
-	 * depending on the underlying driver.
287
-	 *
288
-	 * Note: This method may not return a meaningful or consistent result across different drivers,
289
-	 * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
290
-	 * columns or sequences.
291
-	 *
292
-	 * @param string $seqName Name of the sequence object from which the ID should be returned.
293
-	 *
294
-	 * @return string the last inserted ID.
295
-	 * @throws Exception
296
-	 */
297
-	public function lastInsertId($seqName = null) {
298
-		if ($seqName) {
299
-			$seqName = $this->replaceTablePrefix($seqName);
300
-		}
301
-		return $this->adapter->lastInsertId($seqName);
302
-	}
303
-
304
-	/**
305
-	 * @internal
306
-	 * @throws Exception
307
-	 */
308
-	public function realLastInsertId($seqName = null) {
309
-		return parent::lastInsertId($seqName);
310
-	}
311
-
312
-	/**
313
-	 * Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
314
-	 * it is needed that there is also a unique constraint on the values. Then this method will
315
-	 * catch the exception and return 0.
316
-	 *
317
-	 * @param string $table The table name (will replace *PREFIX* with the actual prefix)
318
-	 * @param array $input data that should be inserted into the table  (column name => value)
319
-	 * @param array|null $compare List of values that should be checked for "if not exists"
320
-	 *				If this is null or an empty array, all keys of $input will be compared
321
-	 *				Please note: text fields (clob) must not be used in the compare array
322
-	 * @return int number of inserted rows
323
-	 * @throws \Doctrine\DBAL\Exception
324
-	 * @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
325
-	 */
326
-	public function insertIfNotExist($table, $input, array $compare = null) {
327
-		return $this->adapter->insertIfNotExist($table, $input, $compare);
328
-	}
329
-
330
-	public function insertIgnoreConflict(string $table, array $values) : int {
331
-		return $this->adapter->insertIgnoreConflict($table, $values);
332
-	}
333
-
334
-	private function getType($value) {
335
-		if (is_bool($value)) {
336
-			return IQueryBuilder::PARAM_BOOL;
337
-		} elseif (is_int($value)) {
338
-			return IQueryBuilder::PARAM_INT;
339
-		} else {
340
-			return IQueryBuilder::PARAM_STR;
341
-		}
342
-	}
343
-
344
-	/**
345
-	 * Insert or update a row value
346
-	 *
347
-	 * @param string $table
348
-	 * @param array $keys (column name => value)
349
-	 * @param array $values (column name => value)
350
-	 * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
351
-	 * @return int number of new rows
352
-	 * @throws \Doctrine\DBAL\Exception
353
-	 * @throws PreConditionNotMetException
354
-	 */
355
-	public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
356
-		try {
357
-			$insertQb = $this->getQueryBuilder();
358
-			$insertQb->insert($table)
359
-				->values(
360
-					array_map(function ($value) use ($insertQb) {
361
-						return $insertQb->createNamedParameter($value, $this->getType($value));
362
-					}, array_merge($keys, $values))
363
-				);
364
-			return $insertQb->execute();
365
-		} catch (NotNullConstraintViolationException $e) {
366
-			throw $e;
367
-		} catch (ConstraintViolationException $e) {
368
-			// value already exists, try update
369
-			$updateQb = $this->getQueryBuilder();
370
-			$updateQb->update($table);
371
-			foreach ($values as $name => $value) {
372
-				$updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value)));
373
-			}
374
-			$where = $updateQb->expr()->andX();
375
-			$whereValues = array_merge($keys, $updatePreconditionValues);
376
-			foreach ($whereValues as $name => $value) {
377
-				if ($value === '') {
378
-					$where->add($updateQb->expr()->emptyString(
379
-						$name
380
-					));
381
-				} else {
382
-					$where->add($updateQb->expr()->eq(
383
-						$name,
384
-						$updateQb->createNamedParameter($value, $this->getType($value)),
385
-						$this->getType($value)
386
-					));
387
-				}
388
-			}
389
-			$updateQb->where($where);
390
-			$affected = $updateQb->execute();
391
-
392
-			if ($affected === 0 && !empty($updatePreconditionValues)) {
393
-				throw new PreConditionNotMetException();
394
-			}
395
-
396
-			return 0;
397
-		}
398
-	}
399
-
400
-	/**
401
-	 * Create an exclusive read+write lock on a table
402
-	 *
403
-	 * @param string $tableName
404
-	 *
405
-	 * @throws \BadMethodCallException When trying to acquire a second lock
406
-	 * @throws Exception
407
-	 * @since 9.1.0
408
-	 */
409
-	public function lockTable($tableName) {
410
-		if ($this->lockedTable !== null) {
411
-			throw new \BadMethodCallException('Can not lock a new table until the previous lock is released.');
412
-		}
413
-
414
-		$tableName = $this->tablePrefix . $tableName;
415
-		$this->lockedTable = $tableName;
416
-		$this->adapter->lockTable($tableName);
417
-	}
418
-
419
-	/**
420
-	 * Release a previous acquired lock again
421
-	 *
422
-	 * @throws Exception
423
-	 * @since 9.1.0
424
-	 */
425
-	public function unlockTable() {
426
-		$this->adapter->unlockTable();
427
-		$this->lockedTable = null;
428
-	}
429
-
430
-	/**
431
-	 * returns the error code and message as a string for logging
432
-	 * works with DoctrineException
433
-	 * @return string
434
-	 */
435
-	public function getError() {
436
-		$msg = $this->errorCode() . ': ';
437
-		$errorInfo = $this->errorInfo();
438
-		if (!empty($errorInfo)) {
439
-			$msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
440
-			$msg .= 'Driver Code = '.$errorInfo[1] . ', ';
441
-			$msg .= 'Driver Message = '.$errorInfo[2];
442
-		}
443
-		return $msg;
444
-	}
445
-
446
-	public function errorCode() {
447
-		return -1;
448
-	}
449
-
450
-	public function errorInfo() {
451
-		return [];
452
-	}
453
-
454
-	/**
455
-	 * Drop a table from the database if it exists
456
-	 *
457
-	 * @param string $table table name without the prefix
458
-	 *
459
-	 * @throws Exception
460
-	 */
461
-	public function dropTable($table) {
462
-		$table = $this->tablePrefix . trim($table);
463
-		$schema = $this->getSchemaManager();
464
-		if ($schema->tablesExist([$table])) {
465
-			$schema->dropTable($table);
466
-		}
467
-	}
468
-
469
-	/**
470
-	 * Check if a table exists
471
-	 *
472
-	 * @param string $table table name without the prefix
473
-	 *
474
-	 * @return bool
475
-	 * @throws Exception
476
-	 */
477
-	public function tableExists($table) {
478
-		$table = $this->tablePrefix . trim($table);
479
-		$schema = $this->getSchemaManager();
480
-		return $schema->tablesExist([$table]);
481
-	}
482
-
483
-	// internal use
484
-	/**
485
-	 * @param string $statement
486
-	 * @return string
487
-	 */
488
-	protected function replaceTablePrefix($statement) {
489
-		return str_replace('*PREFIX*', $this->tablePrefix, $statement);
490
-	}
491
-
492
-	/**
493
-	 * Check if a transaction is active
494
-	 *
495
-	 * @return bool
496
-	 * @since 8.2.0
497
-	 */
498
-	public function inTransaction() {
499
-		return $this->getTransactionNestingLevel() > 0;
500
-	}
501
-
502
-	/**
503
-	 * Escape a parameter to be used in a LIKE query
504
-	 *
505
-	 * @param string $param
506
-	 * @return string
507
-	 */
508
-	public function escapeLikeParameter($param) {
509
-		return addcslashes($param, '\\_%');
510
-	}
511
-
512
-	/**
513
-	 * Check whether or not the current database support 4byte wide unicode
514
-	 *
515
-	 * @return bool
516
-	 * @since 11.0.0
517
-	 */
518
-	public function supports4ByteText() {
519
-		if (!$this->getDatabasePlatform() instanceof MySQLPlatform) {
520
-			return true;
521
-		}
522
-		return $this->getParams()['charset'] === 'utf8mb4';
523
-	}
524
-
525
-
526
-	/**
527
-	 * Create the schema of the connected database
528
-	 *
529
-	 * @return Schema
530
-	 * @throws Exception
531
-	 */
532
-	public function createSchema() {
533
-		$migrator = $this->getMigrator();
534
-		return $migrator->createSchema();
535
-	}
536
-
537
-	/**
538
-	 * Migrate the database to the given schema
539
-	 *
540
-	 * @param Schema $toSchema
541
-	 * @param bool $dryRun If true, will return the sql queries instead of running them.
542
-	 *
543
-	 * @throws Exception
544
-	 *
545
-	 * @return string|null Returns a string only if $dryRun is true.
546
-	 */
547
-	public function migrateToSchema(Schema $toSchema, bool $dryRun = false) {
548
-		$migrator = $this->getMigrator();
549
-
550
-		if ($dryRun) {
551
-			return $migrator->generateChangeScript($toSchema);
552
-		} else {
553
-			$migrator->migrate($toSchema);
554
-		}
555
-	}
556
-
557
-	private function getMigrator() {
558
-		// TODO properly inject those dependencies
559
-		$random = \OC::$server->getSecureRandom();
560
-		$platform = $this->getDatabasePlatform();
561
-		$config = \OC::$server->getConfig();
562
-		$dispatcher = \OC::$server->getEventDispatcher();
563
-		if ($platform instanceof SqlitePlatform) {
564
-			return new SQLiteMigrator($this, $config, $dispatcher);
565
-		} elseif ($platform instanceof OraclePlatform) {
566
-			return new OracleMigrator($this, $config, $dispatcher);
567
-		} elseif ($platform instanceof MySQLPlatform) {
568
-			return new MySQLMigrator($this, $config, $dispatcher);
569
-		} elseif ($platform instanceof PostgreSQL94Platform) {
570
-			return new PostgreSqlMigrator($this, $config, $dispatcher);
571
-		} else {
572
-			return new Migrator($this, $config, $dispatcher);
573
-		}
574
-	}
59
+    /** @var string */
60
+    protected $tablePrefix;
61
+
62
+    /** @var \OC\DB\Adapter $adapter */
63
+    protected $adapter;
64
+
65
+    /** @var SystemConfig */
66
+    private $systemConfig;
67
+
68
+    /** @var ILogger */
69
+    private $logger;
70
+
71
+    protected $lockedTable = null;
72
+
73
+    /** @var int */
74
+    protected $queriesBuilt = 0;
75
+
76
+    /** @var int */
77
+    protected $queriesExecuted = 0;
78
+
79
+    /**
80
+     * @throws Exception
81
+     */
82
+    public function connect() {
83
+        try {
84
+            return parent::connect();
85
+        } catch (Exception $e) {
86
+            // throw a new exception to prevent leaking info from the stacktrace
87
+            throw new Exception('Failed to connect to the database: ' . $e->getMessage(), $e->getCode());
88
+        }
89
+    }
90
+
91
+    public function getStats(): array {
92
+        return [
93
+            'built' => $this->queriesBuilt,
94
+            'executed' => $this->queriesExecuted,
95
+        ];
96
+    }
97
+
98
+    /**
99
+     * Returns a QueryBuilder for the connection.
100
+     */
101
+    public function getQueryBuilder(): IQueryBuilder {
102
+        $this->queriesBuilt++;
103
+        return new QueryBuilder(
104
+            new ConnectionAdapter($this),
105
+            $this->systemConfig,
106
+            $this->logger
107
+        );
108
+    }
109
+
110
+    /**
111
+     * Gets the QueryBuilder for the connection.
112
+     *
113
+     * @return \Doctrine\DBAL\Query\QueryBuilder
114
+     * @deprecated please use $this->getQueryBuilder() instead
115
+     */
116
+    public function createQueryBuilder() {
117
+        $backtrace = $this->getCallerBacktrace();
118
+        \OC::$server->getLogger()->debug('Doctrine QueryBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
119
+        $this->queriesBuilt++;
120
+        return parent::createQueryBuilder();
121
+    }
122
+
123
+    /**
124
+     * Gets the ExpressionBuilder for the connection.
125
+     *
126
+     * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder
127
+     * @deprecated please use $this->getQueryBuilder()->expr() instead
128
+     */
129
+    public function getExpressionBuilder() {
130
+        $backtrace = $this->getCallerBacktrace();
131
+        \OC::$server->getLogger()->debug('Doctrine ExpressionBuilder retrieved in {backtrace}', ['app' => 'core', 'backtrace' => $backtrace]);
132
+        $this->queriesBuilt++;
133
+        return parent::getExpressionBuilder();
134
+    }
135
+
136
+    /**
137
+     * Get the file and line that called the method where `getCallerBacktrace()` was used
138
+     *
139
+     * @return string
140
+     */
141
+    protected function getCallerBacktrace() {
142
+        $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
143
+
144
+        // 0 is the method where we use `getCallerBacktrace`
145
+        // 1 is the target method which uses the method we want to log
146
+        if (isset($traces[1])) {
147
+            return $traces[1]['file'] . ':' . $traces[1]['line'];
148
+        }
149
+
150
+        return '';
151
+    }
152
+
153
+    /**
154
+     * @return string
155
+     */
156
+    public function getPrefix() {
157
+        return $this->tablePrefix;
158
+    }
159
+
160
+    /**
161
+     * Initializes a new instance of the Connection class.
162
+     *
163
+     * @param array $params  The connection parameters.
164
+     * @param \Doctrine\DBAL\Driver $driver
165
+     * @param \Doctrine\DBAL\Configuration $config
166
+     * @param \Doctrine\Common\EventManager $eventManager
167
+     * @throws \Exception
168
+     */
169
+    public function __construct(array $params, Driver $driver, Configuration $config = null,
170
+        EventManager $eventManager = null) {
171
+        if (!isset($params['adapter'])) {
172
+            throw new \Exception('adapter not set');
173
+        }
174
+        if (!isset($params['tablePrefix'])) {
175
+            throw new \Exception('tablePrefix not set');
176
+        }
177
+        /**
178
+         * @psalm-suppress InternalMethod
179
+         */
180
+        parent::__construct($params, $driver, $config, $eventManager);
181
+        $this->adapter = new $params['adapter']($this);
182
+        $this->tablePrefix = $params['tablePrefix'];
183
+
184
+        $this->systemConfig = \OC::$server->getSystemConfig();
185
+        $this->logger = \OC::$server->getLogger();
186
+    }
187
+
188
+    /**
189
+     * Prepares an SQL statement.
190
+     *
191
+     * @param string $statement The SQL statement to prepare.
192
+     * @param int|null $limit
193
+     * @param int|null $offset
194
+     *
195
+     * @return Statement The prepared statement.
196
+     * @throws Exception
197
+     */
198
+    public function prepare($statement, $limit = null, $offset = null): Statement {
199
+        if ($limit === -1 || $limit === null) {
200
+            $limit = null;
201
+        } else {
202
+            $limit = (int) $limit;
203
+        }
204
+        if ($offset !== null) {
205
+            $offset = (int) $offset;
206
+        }
207
+        if (!is_null($limit)) {
208
+            $platform = $this->getDatabasePlatform();
209
+            $statement = $platform->modifyLimitQuery($statement, $limit, $offset);
210
+        }
211
+        $statement = $this->replaceTablePrefix($statement);
212
+        $statement = $this->adapter->fixupStatement($statement);
213
+
214
+        return parent::prepare($statement);
215
+    }
216
+
217
+    /**
218
+     * Executes an, optionally parametrized, SQL query.
219
+     *
220
+     * If the query is parametrized, a prepared statement is used.
221
+     * If an SQLLogger is configured, the execution is logged.
222
+     *
223
+     * @param string                                      $sql  The SQL query to execute.
224
+     * @param array                                       $params The parameters to bind to the query, if any.
225
+     * @param array                                       $types  The types the previous parameters are in.
226
+     * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp    The query cache profile, optional.
227
+     *
228
+     * @return Result The executed statement.
229
+     *
230
+     * @throws \Doctrine\DBAL\Exception
231
+     */
232
+    public function executeQuery(string $sql, array $params = [], $types = [], QueryCacheProfile $qcp = null): Result {
233
+        $sql = $this->replaceTablePrefix($sql);
234
+        $sql = $this->adapter->fixupStatement($sql);
235
+        $this->queriesExecuted++;
236
+        $this->logQueryToFile($sql);
237
+        return parent::executeQuery($sql, $params, $types, $qcp);
238
+    }
239
+
240
+    /**
241
+     * @throws Exception
242
+     */
243
+    public function executeUpdate(string $sql, array $params = [], array $types = []): int {
244
+        $sql = $this->replaceTablePrefix($sql);
245
+        $sql = $this->adapter->fixupStatement($sql);
246
+        $this->queriesExecuted++;
247
+        $this->logQueryToFile($sql);
248
+        return parent::executeUpdate($sql, $params, $types);
249
+    }
250
+
251
+    /**
252
+     * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
253
+     * and returns the number of affected rows.
254
+     *
255
+     * This method supports PDO binding types as well as DBAL mapping types.
256
+     *
257
+     * @param string $sql  The SQL query.
258
+     * @param array  $params The query parameters.
259
+     * @param array  $types  The parameter types.
260
+     *
261
+     * @return int The number of affected rows.
262
+     *
263
+     * @throws \Doctrine\DBAL\Exception
264
+     */
265
+    public function executeStatement($sql, array $params = [], array $types = []): int {
266
+        $sql = $this->replaceTablePrefix($sql);
267
+        $sql = $this->adapter->fixupStatement($sql);
268
+        $this->queriesExecuted++;
269
+        $this->logQueryToFile($sql);
270
+        return parent::executeStatement($sql, $params, $types);
271
+    }
272
+
273
+    protected function logQueryToFile(string $sql): void {
274
+        $logFile = $this->systemConfig->getValue('query_log_file', '');
275
+        if ($logFile !== '' && is_writable(dirname($logFile)) && (!file_exists($logFile) || is_writable($logFile))) {
276
+            file_put_contents(
277
+                $this->systemConfig->getValue('query_log_file', ''),
278
+                $sql . "\n",
279
+                FILE_APPEND
280
+            );
281
+        }
282
+    }
283
+
284
+    /**
285
+     * Returns the ID of the last inserted row, or the last value from a sequence object,
286
+     * depending on the underlying driver.
287
+     *
288
+     * Note: This method may not return a meaningful or consistent result across different drivers,
289
+     * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
290
+     * columns or sequences.
291
+     *
292
+     * @param string $seqName Name of the sequence object from which the ID should be returned.
293
+     *
294
+     * @return string the last inserted ID.
295
+     * @throws Exception
296
+     */
297
+    public function lastInsertId($seqName = null) {
298
+        if ($seqName) {
299
+            $seqName = $this->replaceTablePrefix($seqName);
300
+        }
301
+        return $this->adapter->lastInsertId($seqName);
302
+    }
303
+
304
+    /**
305
+     * @internal
306
+     * @throws Exception
307
+     */
308
+    public function realLastInsertId($seqName = null) {
309
+        return parent::lastInsertId($seqName);
310
+    }
311
+
312
+    /**
313
+     * Insert a row if the matching row does not exists. To accomplish proper race condition avoidance
314
+     * it is needed that there is also a unique constraint on the values. Then this method will
315
+     * catch the exception and return 0.
316
+     *
317
+     * @param string $table The table name (will replace *PREFIX* with the actual prefix)
318
+     * @param array $input data that should be inserted into the table  (column name => value)
319
+     * @param array|null $compare List of values that should be checked for "if not exists"
320
+     *				If this is null or an empty array, all keys of $input will be compared
321
+     *				Please note: text fields (clob) must not be used in the compare array
322
+     * @return int number of inserted rows
323
+     * @throws \Doctrine\DBAL\Exception
324
+     * @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
325
+     */
326
+    public function insertIfNotExist($table, $input, array $compare = null) {
327
+        return $this->adapter->insertIfNotExist($table, $input, $compare);
328
+    }
329
+
330
+    public function insertIgnoreConflict(string $table, array $values) : int {
331
+        return $this->adapter->insertIgnoreConflict($table, $values);
332
+    }
333
+
334
+    private function getType($value) {
335
+        if (is_bool($value)) {
336
+            return IQueryBuilder::PARAM_BOOL;
337
+        } elseif (is_int($value)) {
338
+            return IQueryBuilder::PARAM_INT;
339
+        } else {
340
+            return IQueryBuilder::PARAM_STR;
341
+        }
342
+    }
343
+
344
+    /**
345
+     * Insert or update a row value
346
+     *
347
+     * @param string $table
348
+     * @param array $keys (column name => value)
349
+     * @param array $values (column name => value)
350
+     * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
351
+     * @return int number of new rows
352
+     * @throws \Doctrine\DBAL\Exception
353
+     * @throws PreConditionNotMetException
354
+     */
355
+    public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
356
+        try {
357
+            $insertQb = $this->getQueryBuilder();
358
+            $insertQb->insert($table)
359
+                ->values(
360
+                    array_map(function ($value) use ($insertQb) {
361
+                        return $insertQb->createNamedParameter($value, $this->getType($value));
362
+                    }, array_merge($keys, $values))
363
+                );
364
+            return $insertQb->execute();
365
+        } catch (NotNullConstraintViolationException $e) {
366
+            throw $e;
367
+        } catch (ConstraintViolationException $e) {
368
+            // value already exists, try update
369
+            $updateQb = $this->getQueryBuilder();
370
+            $updateQb->update($table);
371
+            foreach ($values as $name => $value) {
372
+                $updateQb->set($name, $updateQb->createNamedParameter($value, $this->getType($value)));
373
+            }
374
+            $where = $updateQb->expr()->andX();
375
+            $whereValues = array_merge($keys, $updatePreconditionValues);
376
+            foreach ($whereValues as $name => $value) {
377
+                if ($value === '') {
378
+                    $where->add($updateQb->expr()->emptyString(
379
+                        $name
380
+                    ));
381
+                } else {
382
+                    $where->add($updateQb->expr()->eq(
383
+                        $name,
384
+                        $updateQb->createNamedParameter($value, $this->getType($value)),
385
+                        $this->getType($value)
386
+                    ));
387
+                }
388
+            }
389
+            $updateQb->where($where);
390
+            $affected = $updateQb->execute();
391
+
392
+            if ($affected === 0 && !empty($updatePreconditionValues)) {
393
+                throw new PreConditionNotMetException();
394
+            }
395
+
396
+            return 0;
397
+        }
398
+    }
399
+
400
+    /**
401
+     * Create an exclusive read+write lock on a table
402
+     *
403
+     * @param string $tableName
404
+     *
405
+     * @throws \BadMethodCallException When trying to acquire a second lock
406
+     * @throws Exception
407
+     * @since 9.1.0
408
+     */
409
+    public function lockTable($tableName) {
410
+        if ($this->lockedTable !== null) {
411
+            throw new \BadMethodCallException('Can not lock a new table until the previous lock is released.');
412
+        }
413
+
414
+        $tableName = $this->tablePrefix . $tableName;
415
+        $this->lockedTable = $tableName;
416
+        $this->adapter->lockTable($tableName);
417
+    }
418
+
419
+    /**
420
+     * Release a previous acquired lock again
421
+     *
422
+     * @throws Exception
423
+     * @since 9.1.0
424
+     */
425
+    public function unlockTable() {
426
+        $this->adapter->unlockTable();
427
+        $this->lockedTable = null;
428
+    }
429
+
430
+    /**
431
+     * returns the error code and message as a string for logging
432
+     * works with DoctrineException
433
+     * @return string
434
+     */
435
+    public function getError() {
436
+        $msg = $this->errorCode() . ': ';
437
+        $errorInfo = $this->errorInfo();
438
+        if (!empty($errorInfo)) {
439
+            $msg .= 'SQLSTATE = '.$errorInfo[0] . ', ';
440
+            $msg .= 'Driver Code = '.$errorInfo[1] . ', ';
441
+            $msg .= 'Driver Message = '.$errorInfo[2];
442
+        }
443
+        return $msg;
444
+    }
445
+
446
+    public function errorCode() {
447
+        return -1;
448
+    }
449
+
450
+    public function errorInfo() {
451
+        return [];
452
+    }
453
+
454
+    /**
455
+     * Drop a table from the database if it exists
456
+     *
457
+     * @param string $table table name without the prefix
458
+     *
459
+     * @throws Exception
460
+     */
461
+    public function dropTable($table) {
462
+        $table = $this->tablePrefix . trim($table);
463
+        $schema = $this->getSchemaManager();
464
+        if ($schema->tablesExist([$table])) {
465
+            $schema->dropTable($table);
466
+        }
467
+    }
468
+
469
+    /**
470
+     * Check if a table exists
471
+     *
472
+     * @param string $table table name without the prefix
473
+     *
474
+     * @return bool
475
+     * @throws Exception
476
+     */
477
+    public function tableExists($table) {
478
+        $table = $this->tablePrefix . trim($table);
479
+        $schema = $this->getSchemaManager();
480
+        return $schema->tablesExist([$table]);
481
+    }
482
+
483
+    // internal use
484
+    /**
485
+     * @param string $statement
486
+     * @return string
487
+     */
488
+    protected function replaceTablePrefix($statement) {
489
+        return str_replace('*PREFIX*', $this->tablePrefix, $statement);
490
+    }
491
+
492
+    /**
493
+     * Check if a transaction is active
494
+     *
495
+     * @return bool
496
+     * @since 8.2.0
497
+     */
498
+    public function inTransaction() {
499
+        return $this->getTransactionNestingLevel() > 0;
500
+    }
501
+
502
+    /**
503
+     * Escape a parameter to be used in a LIKE query
504
+     *
505
+     * @param string $param
506
+     * @return string
507
+     */
508
+    public function escapeLikeParameter($param) {
509
+        return addcslashes($param, '\\_%');
510
+    }
511
+
512
+    /**
513
+     * Check whether or not the current database support 4byte wide unicode
514
+     *
515
+     * @return bool
516
+     * @since 11.0.0
517
+     */
518
+    public function supports4ByteText() {
519
+        if (!$this->getDatabasePlatform() instanceof MySQLPlatform) {
520
+            return true;
521
+        }
522
+        return $this->getParams()['charset'] === 'utf8mb4';
523
+    }
524
+
525
+
526
+    /**
527
+     * Create the schema of the connected database
528
+     *
529
+     * @return Schema
530
+     * @throws Exception
531
+     */
532
+    public function createSchema() {
533
+        $migrator = $this->getMigrator();
534
+        return $migrator->createSchema();
535
+    }
536
+
537
+    /**
538
+     * Migrate the database to the given schema
539
+     *
540
+     * @param Schema $toSchema
541
+     * @param bool $dryRun If true, will return the sql queries instead of running them.
542
+     *
543
+     * @throws Exception
544
+     *
545
+     * @return string|null Returns a string only if $dryRun is true.
546
+     */
547
+    public function migrateToSchema(Schema $toSchema, bool $dryRun = false) {
548
+        $migrator = $this->getMigrator();
549
+
550
+        if ($dryRun) {
551
+            return $migrator->generateChangeScript($toSchema);
552
+        } else {
553
+            $migrator->migrate($toSchema);
554
+        }
555
+    }
556
+
557
+    private function getMigrator() {
558
+        // TODO properly inject those dependencies
559
+        $random = \OC::$server->getSecureRandom();
560
+        $platform = $this->getDatabasePlatform();
561
+        $config = \OC::$server->getConfig();
562
+        $dispatcher = \OC::$server->getEventDispatcher();
563
+        if ($platform instanceof SqlitePlatform) {
564
+            return new SQLiteMigrator($this, $config, $dispatcher);
565
+        } elseif ($platform instanceof OraclePlatform) {
566
+            return new OracleMigrator($this, $config, $dispatcher);
567
+        } elseif ($platform instanceof MySQLPlatform) {
568
+            return new MySQLMigrator($this, $config, $dispatcher);
569
+        } elseif ($platform instanceof PostgreSQL94Platform) {
570
+            return new PostgreSqlMigrator($this, $config, $dispatcher);
571
+        } else {
572
+            return new Migrator($this, $config, $dispatcher);
573
+        }
574
+    }
575 575
 }
Please login to merge, or discard this patch.
core/Command/Db/AddMissingColumns.php 1 patch
Indentation   +53 added lines, -53 removed lines patch added patch discarded remove patch
@@ -46,67 +46,67 @@
 block discarded – undo
46 46
  */
47 47
 class AddMissingColumns extends Command {
48 48
 
49
-	/** @var Connection */
50
-	private $connection;
49
+    /** @var Connection */
50
+    private $connection;
51 51
 
52
-	/** @var EventDispatcherInterface */
53
-	private $dispatcher;
52
+    /** @var EventDispatcherInterface */
53
+    private $dispatcher;
54 54
 
55
-	public function __construct(Connection $connection, EventDispatcherInterface $dispatcher) {
56
-		parent::__construct();
55
+    public function __construct(Connection $connection, EventDispatcherInterface $dispatcher) {
56
+        parent::__construct();
57 57
 
58
-		$this->connection = $connection;
59
-		$this->dispatcher = $dispatcher;
60
-	}
58
+        $this->connection = $connection;
59
+        $this->dispatcher = $dispatcher;
60
+    }
61 61
 
62
-	protected function configure() {
63
-		$this
64
-			->setName('db:add-missing-columns')
65
-			->setDescription('Add missing optional columns to the database tables')
66
-			->addOption('dry-run', null, InputOption::VALUE_NONE, "Output the SQL queries instead of running them.");
67
-	}
62
+    protected function configure() {
63
+        $this
64
+            ->setName('db:add-missing-columns')
65
+            ->setDescription('Add missing optional columns to the database tables')
66
+            ->addOption('dry-run', null, InputOption::VALUE_NONE, "Output the SQL queries instead of running them.");
67
+    }
68 68
 
69
-	protected function execute(InputInterface $input, OutputInterface $output): int {
70
-		$this->addCoreColumns($output, $input->getOption('dry-run'));
69
+    protected function execute(InputInterface $input, OutputInterface $output): int {
70
+        $this->addCoreColumns($output, $input->getOption('dry-run'));
71 71
 
72
-		// Dispatch event so apps can also update columns if needed
73
-		$event = new GenericEvent($output);
74
-		$this->dispatcher->dispatch(IDBConnection::ADD_MISSING_COLUMNS_EVENT, $event);
75
-		return 0;
76
-	}
72
+        // Dispatch event so apps can also update columns if needed
73
+        $event = new GenericEvent($output);
74
+        $this->dispatcher->dispatch(IDBConnection::ADD_MISSING_COLUMNS_EVENT, $event);
75
+        return 0;
76
+    }
77 77
 
78
-	/**
79
-	 * add missing indices to the share table
80
-	 *
81
-	 * @param OutputInterface $output
82
-	 * @param bool $dryRun If true, will return the sql queries instead of running them.
83
-	 * @throws \Doctrine\DBAL\Schema\SchemaException
84
-	 */
85
-	private function addCoreColumns(OutputInterface $output, bool $dryRun): void {
86
-		$output->writeln('<info>Check columns of the comments table.</info>');
78
+    /**
79
+     * add missing indices to the share table
80
+     *
81
+     * @param OutputInterface $output
82
+     * @param bool $dryRun If true, will return the sql queries instead of running them.
83
+     * @throws \Doctrine\DBAL\Schema\SchemaException
84
+     */
85
+    private function addCoreColumns(OutputInterface $output, bool $dryRun): void {
86
+        $output->writeln('<info>Check columns of the comments table.</info>');
87 87
 
88
-		$schema = new SchemaWrapper($this->connection);
89
-		$updated = false;
88
+        $schema = new SchemaWrapper($this->connection);
89
+        $updated = false;
90 90
 
91
-		if ($schema->hasTable('comments')) {
92
-			$table = $schema->getTable('comments');
93
-			if (!$table->hasColumn('reference_id')) {
94
-				$output->writeln('<info>Adding additional reference_id column to the comments table, this can take some time...</info>');
95
-				$table->addColumn('reference_id', 'string', [
96
-					'notnull' => false,
97
-					'length' => 64,
98
-				]);
99
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
100
-				if ($dryRun && $sqlQueries !== null) {
101
-					$output->writeln($sqlQueries);
102
-				}
103
-				$updated = true;
104
-				$output->writeln('<info>Comments table updated successfully.</info>');
105
-			}
106
-		}
91
+        if ($schema->hasTable('comments')) {
92
+            $table = $schema->getTable('comments');
93
+            if (!$table->hasColumn('reference_id')) {
94
+                $output->writeln('<info>Adding additional reference_id column to the comments table, this can take some time...</info>');
95
+                $table->addColumn('reference_id', 'string', [
96
+                    'notnull' => false,
97
+                    'length' => 64,
98
+                ]);
99
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
100
+                if ($dryRun && $sqlQueries !== null) {
101
+                    $output->writeln($sqlQueries);
102
+                }
103
+                $updated = true;
104
+                $output->writeln('<info>Comments table updated successfully.</info>');
105
+            }
106
+        }
107 107
 
108
-		if (!$updated) {
109
-			$output->writeln('<info>Done.</info>');
110
-		}
111
-	}
108
+        if (!$updated) {
109
+            $output->writeln('<info>Done.</info>');
110
+        }
111
+    }
112 112
 }
Please login to merge, or discard this patch.
core/Command/Db/AddMissingPrimaryKeys.php 1 patch
Indentation   +148 added lines, -148 removed lines patch added patch discarded remove patch
@@ -46,152 +46,152 @@
 block discarded – undo
46 46
  */
47 47
 class AddMissingPrimaryKeys extends Command {
48 48
 
49
-	/** @var Connection */
50
-	private $connection;
51
-
52
-	/** @var EventDispatcherInterface */
53
-	private $dispatcher;
54
-
55
-	public function __construct(Connection $connection, EventDispatcherInterface $dispatcher) {
56
-		parent::__construct();
57
-
58
-		$this->connection = $connection;
59
-		$this->dispatcher = $dispatcher;
60
-	}
61
-
62
-	protected function configure() {
63
-		$this
64
-			->setName('db:add-missing-primary-keys')
65
-			->setDescription('Add missing primary keys to the database tables')
66
-			->addOption('dry-run', null, InputOption::VALUE_NONE, "Output the SQL queries instead of running them.");
67
-	}
68
-
69
-	protected function execute(InputInterface $input, OutputInterface $output): int {
70
-		$this->addCorePrimaryKeys($output, $input->getOption('dry-run'));
71
-
72
-		// Dispatch event so apps can also update indexes if needed
73
-		$event = new GenericEvent($output);
74
-		$this->dispatcher->dispatch(IDBConnection::ADD_MISSING_PRIMARY_KEYS_EVENT, $event);
75
-		return 0;
76
-	}
77
-
78
-	/**
79
-	 * add missing indices to the share table
80
-	 *
81
-	 * @param OutputInterface $output
82
-	 * @param bool $dryRun If true, will return the sql queries instead of running them.
83
-	 * @throws \Doctrine\DBAL\Schema\SchemaException
84
-	 */
85
-	private function addCorePrimaryKeys(OutputInterface $output, bool $dryRun): void {
86
-		$output->writeln('<info>Check primary keys.</info>');
87
-
88
-		$schema = new SchemaWrapper($this->connection);
89
-		$updated = false;
90
-
91
-		if ($schema->hasTable('federated_reshares')) {
92
-			$table = $schema->getTable('federated_reshares');
93
-			if (!$table->hasPrimaryKey()) {
94
-				$output->writeln('<info>Adding primary key to the federated_reshares table, this can take some time...</info>');
95
-				$table->setPrimaryKey(['share_id'], 'federated_res_pk');
96
-				if ($table->hasIndex('share_id_index')) {
97
-					$table->dropIndex('share_id_index');
98
-				}
99
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
100
-				if ($dryRun && $sqlQueries !== null) {
101
-					$output->writeln($sqlQueries);
102
-				}
103
-				$updated = true;
104
-				$output->writeln('<info>federated_reshares table updated successfully.</info>');
105
-			}
106
-		}
107
-
108
-		if ($schema->hasTable('systemtag_object_mapping')) {
109
-			$table = $schema->getTable('systemtag_object_mapping');
110
-			if (!$table->hasPrimaryKey()) {
111
-				$output->writeln('<info>Adding primary key to the systemtag_object_mapping table, this can take some time...</info>');
112
-				$table->setPrimaryKey(['objecttype', 'objectid', 'systemtagid'], 'som_pk');
113
-				if ($table->hasIndex('mapping')) {
114
-					$table->dropIndex('mapping');
115
-				}
116
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
117
-				if ($dryRun && $sqlQueries !== null) {
118
-					$output->writeln($sqlQueries);
119
-				}
120
-				$updated = true;
121
-				$output->writeln('<info>systemtag_object_mapping table updated successfully.</info>');
122
-			}
123
-		}
124
-
125
-		if ($schema->hasTable('comments_read_markers')) {
126
-			$table = $schema->getTable('comments_read_markers');
127
-			if (!$table->hasPrimaryKey()) {
128
-				$output->writeln('<info>Adding primary key to the comments_read_markers table, this can take some time...</info>');
129
-				$table->setPrimaryKey(['user_id', 'object_type', 'object_id'], 'crm_pk');
130
-				if ($table->hasIndex('comments_marker_index')) {
131
-					$table->dropIndex('comments_marker_index');
132
-				}
133
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
134
-				if ($dryRun && $sqlQueries !== null) {
135
-					$output->writeln($sqlQueries);
136
-				}
137
-				$updated = true;
138
-				$output->writeln('<info>comments_read_markers table updated successfully.</info>');
139
-			}
140
-		}
141
-
142
-		if ($schema->hasTable('collres_resources')) {
143
-			$table = $schema->getTable('collres_resources');
144
-			if (!$table->hasPrimaryKey()) {
145
-				$output->writeln('<info>Adding primary key to the collres_resources table, this can take some time...</info>');
146
-				$table->setPrimaryKey(['collection_id', 'resource_type', 'resource_id'], 'crr_pk');
147
-				if ($table->hasIndex('collres_unique_res')) {
148
-					$table->dropIndex('collres_unique_res');
149
-				}
150
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
151
-				if ($dryRun && $sqlQueries !== null) {
152
-					$output->writeln($sqlQueries);
153
-				}
154
-				$updated = true;
155
-				$output->writeln('<info>collres_resources table updated successfully.</info>');
156
-			}
157
-		}
158
-
159
-		if ($schema->hasTable('collres_accesscache')) {
160
-			$table = $schema->getTable('collres_accesscache');
161
-			if (!$table->hasPrimaryKey()) {
162
-				$output->writeln('<info>Adding primary key to the collres_accesscache table, this can take some time...</info>');
163
-				$table->setPrimaryKey(['user_id', 'collection_id', 'resource_type', 'resource_id'], 'cra_pk');
164
-				if ($table->hasIndex('collres_unique_user')) {
165
-					$table->dropIndex('collres_unique_user');
166
-				}
167
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
168
-				if ($dryRun && $sqlQueries !== null) {
169
-					$output->writeln($sqlQueries);
170
-				}
171
-				$updated = true;
172
-				$output->writeln('<info>collres_accesscache table updated successfully.</info>');
173
-			}
174
-		}
175
-
176
-		if ($schema->hasTable('filecache_extended')) {
177
-			$table = $schema->getTable('filecache_extended');
178
-			if (!$table->hasPrimaryKey()) {
179
-				$output->writeln('<info>Adding primary key to the filecache_extended table, this can take some time...</info>');
180
-				$table->setPrimaryKey(['fileid'], 'fce_pk');
181
-				if ($table->hasIndex('fce_fileid_idx')) {
182
-					$table->dropIndex('fce_fileid_idx');
183
-				}
184
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
185
-				if ($dryRun && $sqlQueries !== null) {
186
-					$output->writeln($sqlQueries);
187
-				}
188
-				$updated = true;
189
-				$output->writeln('<info>filecache_extended table updated successfully.</info>');
190
-			}
191
-		}
192
-
193
-		if (!$updated) {
194
-			$output->writeln('<info>Done.</info>');
195
-		}
196
-	}
49
+    /** @var Connection */
50
+    private $connection;
51
+
52
+    /** @var EventDispatcherInterface */
53
+    private $dispatcher;
54
+
55
+    public function __construct(Connection $connection, EventDispatcherInterface $dispatcher) {
56
+        parent::__construct();
57
+
58
+        $this->connection = $connection;
59
+        $this->dispatcher = $dispatcher;
60
+    }
61
+
62
+    protected function configure() {
63
+        $this
64
+            ->setName('db:add-missing-primary-keys')
65
+            ->setDescription('Add missing primary keys to the database tables')
66
+            ->addOption('dry-run', null, InputOption::VALUE_NONE, "Output the SQL queries instead of running them.");
67
+    }
68
+
69
+    protected function execute(InputInterface $input, OutputInterface $output): int {
70
+        $this->addCorePrimaryKeys($output, $input->getOption('dry-run'));
71
+
72
+        // Dispatch event so apps can also update indexes if needed
73
+        $event = new GenericEvent($output);
74
+        $this->dispatcher->dispatch(IDBConnection::ADD_MISSING_PRIMARY_KEYS_EVENT, $event);
75
+        return 0;
76
+    }
77
+
78
+    /**
79
+     * add missing indices to the share table
80
+     *
81
+     * @param OutputInterface $output
82
+     * @param bool $dryRun If true, will return the sql queries instead of running them.
83
+     * @throws \Doctrine\DBAL\Schema\SchemaException
84
+     */
85
+    private function addCorePrimaryKeys(OutputInterface $output, bool $dryRun): void {
86
+        $output->writeln('<info>Check primary keys.</info>');
87
+
88
+        $schema = new SchemaWrapper($this->connection);
89
+        $updated = false;
90
+
91
+        if ($schema->hasTable('federated_reshares')) {
92
+            $table = $schema->getTable('federated_reshares');
93
+            if (!$table->hasPrimaryKey()) {
94
+                $output->writeln('<info>Adding primary key to the federated_reshares table, this can take some time...</info>');
95
+                $table->setPrimaryKey(['share_id'], 'federated_res_pk');
96
+                if ($table->hasIndex('share_id_index')) {
97
+                    $table->dropIndex('share_id_index');
98
+                }
99
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
100
+                if ($dryRun && $sqlQueries !== null) {
101
+                    $output->writeln($sqlQueries);
102
+                }
103
+                $updated = true;
104
+                $output->writeln('<info>federated_reshares table updated successfully.</info>');
105
+            }
106
+        }
107
+
108
+        if ($schema->hasTable('systemtag_object_mapping')) {
109
+            $table = $schema->getTable('systemtag_object_mapping');
110
+            if (!$table->hasPrimaryKey()) {
111
+                $output->writeln('<info>Adding primary key to the systemtag_object_mapping table, this can take some time...</info>');
112
+                $table->setPrimaryKey(['objecttype', 'objectid', 'systemtagid'], 'som_pk');
113
+                if ($table->hasIndex('mapping')) {
114
+                    $table->dropIndex('mapping');
115
+                }
116
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
117
+                if ($dryRun && $sqlQueries !== null) {
118
+                    $output->writeln($sqlQueries);
119
+                }
120
+                $updated = true;
121
+                $output->writeln('<info>systemtag_object_mapping table updated successfully.</info>');
122
+            }
123
+        }
124
+
125
+        if ($schema->hasTable('comments_read_markers')) {
126
+            $table = $schema->getTable('comments_read_markers');
127
+            if (!$table->hasPrimaryKey()) {
128
+                $output->writeln('<info>Adding primary key to the comments_read_markers table, this can take some time...</info>');
129
+                $table->setPrimaryKey(['user_id', 'object_type', 'object_id'], 'crm_pk');
130
+                if ($table->hasIndex('comments_marker_index')) {
131
+                    $table->dropIndex('comments_marker_index');
132
+                }
133
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
134
+                if ($dryRun && $sqlQueries !== null) {
135
+                    $output->writeln($sqlQueries);
136
+                }
137
+                $updated = true;
138
+                $output->writeln('<info>comments_read_markers table updated successfully.</info>');
139
+            }
140
+        }
141
+
142
+        if ($schema->hasTable('collres_resources')) {
143
+            $table = $schema->getTable('collres_resources');
144
+            if (!$table->hasPrimaryKey()) {
145
+                $output->writeln('<info>Adding primary key to the collres_resources table, this can take some time...</info>');
146
+                $table->setPrimaryKey(['collection_id', 'resource_type', 'resource_id'], 'crr_pk');
147
+                if ($table->hasIndex('collres_unique_res')) {
148
+                    $table->dropIndex('collres_unique_res');
149
+                }
150
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
151
+                if ($dryRun && $sqlQueries !== null) {
152
+                    $output->writeln($sqlQueries);
153
+                }
154
+                $updated = true;
155
+                $output->writeln('<info>collres_resources table updated successfully.</info>');
156
+            }
157
+        }
158
+
159
+        if ($schema->hasTable('collres_accesscache')) {
160
+            $table = $schema->getTable('collres_accesscache');
161
+            if (!$table->hasPrimaryKey()) {
162
+                $output->writeln('<info>Adding primary key to the collres_accesscache table, this can take some time...</info>');
163
+                $table->setPrimaryKey(['user_id', 'collection_id', 'resource_type', 'resource_id'], 'cra_pk');
164
+                if ($table->hasIndex('collres_unique_user')) {
165
+                    $table->dropIndex('collres_unique_user');
166
+                }
167
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
168
+                if ($dryRun && $sqlQueries !== null) {
169
+                    $output->writeln($sqlQueries);
170
+                }
171
+                $updated = true;
172
+                $output->writeln('<info>collres_accesscache table updated successfully.</info>');
173
+            }
174
+        }
175
+
176
+        if ($schema->hasTable('filecache_extended')) {
177
+            $table = $schema->getTable('filecache_extended');
178
+            if (!$table->hasPrimaryKey()) {
179
+                $output->writeln('<info>Adding primary key to the filecache_extended table, this can take some time...</info>');
180
+                $table->setPrimaryKey(['fileid'], 'fce_pk');
181
+                if ($table->hasIndex('fce_fileid_idx')) {
182
+                    $table->dropIndex('fce_fileid_idx');
183
+                }
184
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
185
+                if ($dryRun && $sqlQueries !== null) {
186
+                    $output->writeln($sqlQueries);
187
+                }
188
+                $updated = true;
189
+                $output->writeln('<info>filecache_extended table updated successfully.</info>');
190
+            }
191
+        }
192
+
193
+        if (!$updated) {
194
+            $output->writeln('<info>Done.</info>');
195
+        }
196
+    }
197 197
 }
Please login to merge, or discard this patch.
core/Command/Db/AddMissingIndices.php 1 patch
Indentation   +381 added lines, -381 removed lines patch added patch discarded remove patch
@@ -54,385 +54,385 @@
 block discarded – undo
54 54
  */
55 55
 class AddMissingIndices extends Command {
56 56
 
57
-	/** @var Connection */
58
-	private $connection;
59
-
60
-	/** @var EventDispatcherInterface */
61
-	private $dispatcher;
62
-
63
-	public function __construct(Connection $connection, EventDispatcherInterface $dispatcher) {
64
-		parent::__construct();
65
-
66
-		$this->connection = $connection;
67
-		$this->dispatcher = $dispatcher;
68
-	}
69
-
70
-	protected function configure() {
71
-		$this
72
-			->setName('db:add-missing-indices')
73
-			->setDescription('Add missing indices to the database tables')
74
-			->addOption('dry-run', null, InputOption::VALUE_NONE, "Output the SQL queries instead of running them.");
75
-	}
76
-
77
-	protected function execute(InputInterface $input, OutputInterface $output): int {
78
-		$this->addCoreIndexes($output, $input->getOption('dry-run'));
79
-
80
-		// Dispatch event so apps can also update indexes if needed
81
-		$event = new GenericEvent($output);
82
-		$this->dispatcher->dispatch(IDBConnection::ADD_MISSING_INDEXES_EVENT, $event);
83
-		return 0;
84
-	}
85
-
86
-	/**
87
-	 * add missing indices to the share table
88
-	 *
89
-	 * @param OutputInterface $output
90
-	 * @param bool $dryRun If true, will return the sql queries instead of running them.
91
-	 * @throws \Doctrine\DBAL\Schema\SchemaException
92
-	 */
93
-	private function addCoreIndexes(OutputInterface $output, bool $dryRun): void {
94
-		$output->writeln('<info>Check indices of the share table.</info>');
95
-
96
-		$schema = new SchemaWrapper($this->connection);
97
-		$updated = false;
98
-
99
-		if ($schema->hasTable('share')) {
100
-			$table = $schema->getTable('share');
101
-			if (!$table->hasIndex('share_with_index')) {
102
-				$output->writeln('<info>Adding additional share_with index to the share table, this can take some time...</info>');
103
-				$table->addIndex(['share_with'], 'share_with_index');
104
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
105
-				if ($dryRun && $sqlQueries !== null) {
106
-					$output->writeln($sqlQueries);
107
-				}
108
-				$updated = true;
109
-				$output->writeln('<info>Share table updated successfully.</info>');
110
-			}
111
-
112
-			if (!$table->hasIndex('parent_index')) {
113
-				$output->writeln('<info>Adding additional parent index to the share table, this can take some time...</info>');
114
-				$table->addIndex(['parent'], 'parent_index');
115
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
116
-				if ($dryRun && $sqlQueries !== null) {
117
-					$output->writeln($sqlQueries);
118
-				}
119
-				$updated = true;
120
-				$output->writeln('<info>Share table updated successfully.</info>');
121
-			}
122
-
123
-			if (!$table->hasIndex('owner_index')) {
124
-				$output->writeln('<info>Adding additional owner index to the share table, this can take some time...</info>');
125
-				$table->addIndex(['uid_owner'], 'owner_index');
126
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
127
-				if ($dryRun && $sqlQueries !== null) {
128
-					$output->writeln($sqlQueries);
129
-				}
130
-				$updated = true;
131
-				$output->writeln('<info>Share table updated successfully.</info>');
132
-			}
133
-
134
-			if (!$table->hasIndex('initiator_index')) {
135
-				$output->writeln('<info>Adding additional initiator index to the share table, this can take some time...</info>');
136
-				$table->addIndex(['uid_initiator'], 'initiator_index');
137
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
138
-				if ($dryRun && $sqlQueries !== null) {
139
-					$output->writeln($sqlQueries);
140
-				}
141
-				$updated = true;
142
-				$output->writeln('<info>Share table updated successfully.</info>');
143
-			}
144
-		}
145
-
146
-		$output->writeln('<info>Check indices of the filecache table.</info>');
147
-		if ($schema->hasTable('filecache')) {
148
-			$table = $schema->getTable('filecache');
149
-			if (!$table->hasIndex('fs_mtime')) {
150
-				$output->writeln('<info>Adding additional mtime index to the filecache table, this can take some time...</info>');
151
-				$table->addIndex(['mtime'], 'fs_mtime');
152
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
153
-				if ($dryRun && $sqlQueries !== null) {
154
-					$output->writeln($sqlQueries);
155
-				}
156
-				$updated = true;
157
-				$output->writeln('<info>Filecache table updated successfully.</info>');
158
-			}
159
-			if (!$table->hasIndex('fs_size')) {
160
-				$output->writeln('<info>Adding additional size index to the filecache table, this can take some time...</info>');
161
-				$table->addIndex(['size'], 'fs_size');
162
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
163
-				if ($dryRun && $sqlQueries !== null) {
164
-					$output->writeln($sqlQueries);
165
-				}
166
-				$updated = true;
167
-				$output->writeln('<info>Filecache table updated successfully.</info>');
168
-			}
169
-			if (!$table->hasIndex('fs_id_storage_size')) {
170
-				$output->writeln('<info>Adding additional size index to the filecache table, this can take some time...</info>');
171
-				$table->addIndex(['fileid', 'storage', 'size'], 'fs_id_storage_size');
172
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
173
-				if ($dryRun && $sqlQueries !== null) {
174
-					$output->writeln($sqlQueries);
175
-				}
176
-				$updated = true;
177
-				$output->writeln('<info>Filecache table updated successfully.</info>');
178
-			}
179
-			if (!$table->hasIndex('fs_storage_path_prefix') && !$schema->getDatabasePlatform() instanceof PostgreSQL94Platform) {
180
-				$output->writeln('<info>Adding additional path index to the filecache table, this can take some time...</info>');
181
-				$table->addIndex(['storage', 'path'], 'fs_storage_path_prefix', [], ['lengths' => [null, 64]]);
182
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
183
-				if ($dryRun && $sqlQueries !== null) {
184
-					$output->writeln($sqlQueries);
185
-				}
186
-				$updated = true;
187
-				$output->writeln('<info>Filecache table updated successfully.</info>');
188
-			}
189
-		}
190
-
191
-		$output->writeln('<info>Check indices of the twofactor_providers table.</info>');
192
-		if ($schema->hasTable('twofactor_providers')) {
193
-			$table = $schema->getTable('twofactor_providers');
194
-			if (!$table->hasIndex('twofactor_providers_uid')) {
195
-				$output->writeln('<info>Adding additional twofactor_providers_uid index to the twofactor_providers table, this can take some time...</info>');
196
-				$table->addIndex(['uid'], 'twofactor_providers_uid');
197
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
198
-				if ($dryRun && $sqlQueries !== null) {
199
-					$output->writeln($sqlQueries);
200
-				}
201
-				$updated = true;
202
-				$output->writeln('<info>Twofactor_providers table updated successfully.</info>');
203
-			}
204
-		}
205
-
206
-		$output->writeln('<info>Check indices of the login_flow_v2 table.</info>');
207
-		if ($schema->hasTable('login_flow_v2')) {
208
-			$table = $schema->getTable('login_flow_v2');
209
-			if (!$table->hasIndex('poll_token')) {
210
-				$output->writeln('<info>Adding additional indeces to the login_flow_v2 table, this can take some time...</info>');
211
-
212
-				foreach ($table->getIndexes() as $index) {
213
-					$columns = $index->getColumns();
214
-					if ($columns === ['poll_token'] ||
215
-						$columns === ['login_token'] ||
216
-						$columns === ['timestamp']) {
217
-						$table->dropIndex($index->getName());
218
-					}
219
-				}
220
-
221
-				$table->addUniqueIndex(['poll_token'], 'poll_token');
222
-				$table->addUniqueIndex(['login_token'], 'login_token');
223
-				$table->addIndex(['timestamp'], 'timestamp');
224
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
225
-				if ($dryRun && $sqlQueries !== null) {
226
-					$output->writeln($sqlQueries);
227
-				}
228
-				$updated = true;
229
-				$output->writeln('<info>login_flow_v2 table updated successfully.</info>');
230
-			}
231
-		}
232
-
233
-		$output->writeln('<info>Check indices of the whats_new table.</info>');
234
-		if ($schema->hasTable('whats_new')) {
235
-			$table = $schema->getTable('whats_new');
236
-			if (!$table->hasIndex('version')) {
237
-				$output->writeln('<info>Adding version index to the whats_new table, this can take some time...</info>');
238
-
239
-				foreach ($table->getIndexes() as $index) {
240
-					if ($index->getColumns() === ['version']) {
241
-						$table->dropIndex($index->getName());
242
-					}
243
-				}
244
-
245
-				$table->addUniqueIndex(['version'], 'version');
246
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
247
-				if ($dryRun && $sqlQueries !== null) {
248
-					$output->writeln($sqlQueries);
249
-				}
250
-				$updated = true;
251
-				$output->writeln('<info>whats_new table updated successfully.</info>');
252
-			}
253
-		}
254
-
255
-		$output->writeln('<info>Check indices of the cards table.</info>');
256
-		$cardsUpdated = false;
257
-		if ($schema->hasTable('cards')) {
258
-			$table = $schema->getTable('cards');
259
-
260
-			if ($table->hasIndex('addressbookid_uri_index')) {
261
-				$output->writeln('<info>Renaming addressbookid_uri_index index to  to the cards table, this can take some time...</info>');
262
-
263
-				foreach ($table->getIndexes() as $index) {
264
-					if ($index->getColumns() === ['addressbookid', 'uri']) {
265
-						$table->renameIndex('addressbookid_uri_index', 'cards_abiduri');
266
-					}
267
-				}
268
-
269
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
270
-				if ($dryRun && $sqlQueries !== null) {
271
-					$output->writeln($sqlQueries);
272
-				}
273
-				$cardsUpdated = true;
274
-			}
275
-
276
-			if (!$table->hasIndex('cards_abid')) {
277
-				$output->writeln('<info>Adding cards_abid index to the cards table, this can take some time...</info>');
278
-
279
-				foreach ($table->getIndexes() as $index) {
280
-					if ($index->getColumns() === ['addressbookid']) {
281
-						$table->dropIndex($index->getName());
282
-					}
283
-				}
284
-
285
-				$table->addIndex(['addressbookid'], 'cards_abid');
286
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
287
-				if ($dryRun && $sqlQueries !== null) {
288
-					$output->writeln($sqlQueries);
289
-				}
290
-				$cardsUpdated = true;
291
-			}
292
-
293
-			if (!$table->hasIndex('cards_abiduri')) {
294
-				$output->writeln('<info>Adding cards_abiduri index to the cards table, this can take some time...</info>');
295
-
296
-				foreach ($table->getIndexes() as $index) {
297
-					if ($index->getColumns() === ['addressbookid', 'uri']) {
298
-						$table->dropIndex($index->getName());
299
-					}
300
-				}
301
-
302
-				$table->addIndex(['addressbookid', 'uri'], 'cards_abiduri');
303
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
304
-				if ($dryRun && $sqlQueries !== null) {
305
-					$output->writeln($sqlQueries);
306
-				}
307
-				$cardsUpdated = true;
308
-			}
309
-
310
-			if ($cardsUpdated) {
311
-				$updated = true;
312
-				$output->writeln('<info>cards table updated successfully.</info>');
313
-			}
314
-		}
315
-
316
-		$output->writeln('<info>Check indices of the cards_properties table.</info>');
317
-		if ($schema->hasTable('cards_properties')) {
318
-			$table = $schema->getTable('cards_properties');
319
-			if (!$table->hasIndex('cards_prop_abid')) {
320
-				$output->writeln('<info>Adding cards_prop_abid index to the cards_properties table, this can take some time...</info>');
321
-
322
-				foreach ($table->getIndexes() as $index) {
323
-					if ($index->getColumns() === ['addressbookid']) {
324
-						$table->dropIndex($index->getName());
325
-					}
326
-				}
327
-
328
-				$table->addIndex(['addressbookid'], 'cards_prop_abid');
329
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
330
-				if ($dryRun && $sqlQueries !== null) {
331
-					$output->writeln($sqlQueries);
332
-				}
333
-				$updated = true;
334
-				$output->writeln('<info>cards_properties table updated successfully.</info>');
335
-			}
336
-		}
337
-
338
-		$output->writeln('<info>Check indices of the calendarobjects_props table.</info>');
339
-		if ($schema->hasTable('calendarobjects_props')) {
340
-			$table = $schema->getTable('calendarobjects_props');
341
-			if (!$table->hasIndex('calendarobject_calid_index')) {
342
-				$output->writeln('<info>Adding calendarobject_calid_index index to the calendarobjects_props table, this can take some time...</info>');
343
-
344
-				$table->addIndex(['calendarid', 'calendartype'], 'calendarobject_calid_index');
345
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
346
-				if ($dryRun && $sqlQueries !== null) {
347
-					$output->writeln($sqlQueries);
348
-				}
349
-				$updated = true;
350
-				$output->writeln('<info>calendarobjects_props table updated successfully.</info>');
351
-			}
352
-		}
353
-
354
-		$output->writeln('<info>Check indices of the schedulingobjects table.</info>');
355
-		if ($schema->hasTable('schedulingobjects')) {
356
-			$table = $schema->getTable('schedulingobjects');
357
-			if (!$table->hasIndex('schedulobj_principuri_index')) {
358
-				$output->writeln('<info>Adding schedulobj_principuri_index index to the schedulingobjects table, this can take some time...</info>');
359
-
360
-				$table->addIndex(['principaluri'], 'schedulobj_principuri_index');
361
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
362
-				if ($dryRun && $sqlQueries !== null) {
363
-					$output->writeln($sqlQueries);
364
-				}
365
-				$updated = true;
366
-				$output->writeln('<info>schedulingobjects table updated successfully.</info>');
367
-			}
368
-		}
369
-
370
-		$output->writeln('<info>Check indices of the oc_properties table.</info>');
371
-		if ($schema->hasTable('properties')) {
372
-			$table = $schema->getTable('properties');
373
-			$propertiesUpdated = false;
374
-
375
-			if (!$table->hasIndex('properties_path_index')) {
376
-				$output->writeln('<info>Adding properties_path_index index to the oc_properties table, this can take some time...</info>');
377
-
378
-				$table->addIndex(['userid', 'propertypath'], 'properties_path_index');
379
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
380
-				if ($dryRun && $sqlQueries !== null) {
381
-					$output->writeln($sqlQueries);
382
-				}
383
-				$propertiesUpdated = true;
384
-			}
385
-			if (!$table->hasIndex('properties_pathonly_index')) {
386
-				$output->writeln('<info>Adding properties_pathonly_index index to the oc_properties table, this can take some time...</info>');
387
-
388
-				$table->addIndex(['propertypath'], 'properties_pathonly_index');
389
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
390
-				if ($dryRun && $sqlQueries !== null) {
391
-					$output->writeln($sqlQueries);
392
-				}
393
-				$propertiesUpdated = true;
394
-			}
395
-
396
-			if ($propertiesUpdated) {
397
-				$updated = true;
398
-				$output->writeln('<info>oc_properties table updated successfully.</info>');
399
-			}
400
-		}
401
-
402
-		$output->writeln('<info>Check indices of the oc_jobs table.</info>');
403
-		if ($schema->hasTable('jobs')) {
404
-			$table = $schema->getTable('jobs');
405
-			if (!$table->hasIndex('job_lastcheck_reserved')) {
406
-				$output->writeln('<info>Adding job_lastcheck_reserved index to the oc_jobs table, this can take some time...</info>');
407
-
408
-				$table->addIndex(['last_checked', 'reserved_at'], 'job_lastcheck_reserved');
409
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
410
-				if ($dryRun && $sqlQueries !== null) {
411
-					$output->writeln($sqlQueries);
412
-				}
413
-				$updated = true;
414
-				$output->writeln('<info>oc_properties table updated successfully.</info>');
415
-			}
416
-		}
417
-
418
-		$output->writeln('<info>Check indices of the oc_direct_edit table.</info>');
419
-		if ($schema->hasTable('direct_edit')) {
420
-			$table = $schema->getTable('direct_edit');
421
-			if (!$table->hasIndex('direct_edit_timestamp')) {
422
-				$output->writeln('<info>Adding direct_edit_timestamp index to the oc_direct_edit table, this can take some time...</info>');
423
-
424
-				$table->addIndex(['timestamp'], 'direct_edit_timestamp');
425
-				$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
426
-				if ($dryRun && $sqlQueries !== null) {
427
-					$output->writeln($sqlQueries);
428
-				}
429
-				$updated = true;
430
-				$output->writeln('<info>oc_direct_edit table updated successfully.</info>');
431
-			}
432
-		}
433
-
434
-		if (!$updated) {
435
-			$output->writeln('<info>Done.</info>');
436
-		}
437
-	}
57
+    /** @var Connection */
58
+    private $connection;
59
+
60
+    /** @var EventDispatcherInterface */
61
+    private $dispatcher;
62
+
63
+    public function __construct(Connection $connection, EventDispatcherInterface $dispatcher) {
64
+        parent::__construct();
65
+
66
+        $this->connection = $connection;
67
+        $this->dispatcher = $dispatcher;
68
+    }
69
+
70
+    protected function configure() {
71
+        $this
72
+            ->setName('db:add-missing-indices')
73
+            ->setDescription('Add missing indices to the database tables')
74
+            ->addOption('dry-run', null, InputOption::VALUE_NONE, "Output the SQL queries instead of running them.");
75
+    }
76
+
77
+    protected function execute(InputInterface $input, OutputInterface $output): int {
78
+        $this->addCoreIndexes($output, $input->getOption('dry-run'));
79
+
80
+        // Dispatch event so apps can also update indexes if needed
81
+        $event = new GenericEvent($output);
82
+        $this->dispatcher->dispatch(IDBConnection::ADD_MISSING_INDEXES_EVENT, $event);
83
+        return 0;
84
+    }
85
+
86
+    /**
87
+     * add missing indices to the share table
88
+     *
89
+     * @param OutputInterface $output
90
+     * @param bool $dryRun If true, will return the sql queries instead of running them.
91
+     * @throws \Doctrine\DBAL\Schema\SchemaException
92
+     */
93
+    private function addCoreIndexes(OutputInterface $output, bool $dryRun): void {
94
+        $output->writeln('<info>Check indices of the share table.</info>');
95
+
96
+        $schema = new SchemaWrapper($this->connection);
97
+        $updated = false;
98
+
99
+        if ($schema->hasTable('share')) {
100
+            $table = $schema->getTable('share');
101
+            if (!$table->hasIndex('share_with_index')) {
102
+                $output->writeln('<info>Adding additional share_with index to the share table, this can take some time...</info>');
103
+                $table->addIndex(['share_with'], 'share_with_index');
104
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
105
+                if ($dryRun && $sqlQueries !== null) {
106
+                    $output->writeln($sqlQueries);
107
+                }
108
+                $updated = true;
109
+                $output->writeln('<info>Share table updated successfully.</info>');
110
+            }
111
+
112
+            if (!$table->hasIndex('parent_index')) {
113
+                $output->writeln('<info>Adding additional parent index to the share table, this can take some time...</info>');
114
+                $table->addIndex(['parent'], 'parent_index');
115
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
116
+                if ($dryRun && $sqlQueries !== null) {
117
+                    $output->writeln($sqlQueries);
118
+                }
119
+                $updated = true;
120
+                $output->writeln('<info>Share table updated successfully.</info>');
121
+            }
122
+
123
+            if (!$table->hasIndex('owner_index')) {
124
+                $output->writeln('<info>Adding additional owner index to the share table, this can take some time...</info>');
125
+                $table->addIndex(['uid_owner'], 'owner_index');
126
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
127
+                if ($dryRun && $sqlQueries !== null) {
128
+                    $output->writeln($sqlQueries);
129
+                }
130
+                $updated = true;
131
+                $output->writeln('<info>Share table updated successfully.</info>');
132
+            }
133
+
134
+            if (!$table->hasIndex('initiator_index')) {
135
+                $output->writeln('<info>Adding additional initiator index to the share table, this can take some time...</info>');
136
+                $table->addIndex(['uid_initiator'], 'initiator_index');
137
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
138
+                if ($dryRun && $sqlQueries !== null) {
139
+                    $output->writeln($sqlQueries);
140
+                }
141
+                $updated = true;
142
+                $output->writeln('<info>Share table updated successfully.</info>');
143
+            }
144
+        }
145
+
146
+        $output->writeln('<info>Check indices of the filecache table.</info>');
147
+        if ($schema->hasTable('filecache')) {
148
+            $table = $schema->getTable('filecache');
149
+            if (!$table->hasIndex('fs_mtime')) {
150
+                $output->writeln('<info>Adding additional mtime index to the filecache table, this can take some time...</info>');
151
+                $table->addIndex(['mtime'], 'fs_mtime');
152
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
153
+                if ($dryRun && $sqlQueries !== null) {
154
+                    $output->writeln($sqlQueries);
155
+                }
156
+                $updated = true;
157
+                $output->writeln('<info>Filecache table updated successfully.</info>');
158
+            }
159
+            if (!$table->hasIndex('fs_size')) {
160
+                $output->writeln('<info>Adding additional size index to the filecache table, this can take some time...</info>');
161
+                $table->addIndex(['size'], 'fs_size');
162
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
163
+                if ($dryRun && $sqlQueries !== null) {
164
+                    $output->writeln($sqlQueries);
165
+                }
166
+                $updated = true;
167
+                $output->writeln('<info>Filecache table updated successfully.</info>');
168
+            }
169
+            if (!$table->hasIndex('fs_id_storage_size')) {
170
+                $output->writeln('<info>Adding additional size index to the filecache table, this can take some time...</info>');
171
+                $table->addIndex(['fileid', 'storage', 'size'], 'fs_id_storage_size');
172
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
173
+                if ($dryRun && $sqlQueries !== null) {
174
+                    $output->writeln($sqlQueries);
175
+                }
176
+                $updated = true;
177
+                $output->writeln('<info>Filecache table updated successfully.</info>');
178
+            }
179
+            if (!$table->hasIndex('fs_storage_path_prefix') && !$schema->getDatabasePlatform() instanceof PostgreSQL94Platform) {
180
+                $output->writeln('<info>Adding additional path index to the filecache table, this can take some time...</info>');
181
+                $table->addIndex(['storage', 'path'], 'fs_storage_path_prefix', [], ['lengths' => [null, 64]]);
182
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
183
+                if ($dryRun && $sqlQueries !== null) {
184
+                    $output->writeln($sqlQueries);
185
+                }
186
+                $updated = true;
187
+                $output->writeln('<info>Filecache table updated successfully.</info>');
188
+            }
189
+        }
190
+
191
+        $output->writeln('<info>Check indices of the twofactor_providers table.</info>');
192
+        if ($schema->hasTable('twofactor_providers')) {
193
+            $table = $schema->getTable('twofactor_providers');
194
+            if (!$table->hasIndex('twofactor_providers_uid')) {
195
+                $output->writeln('<info>Adding additional twofactor_providers_uid index to the twofactor_providers table, this can take some time...</info>');
196
+                $table->addIndex(['uid'], 'twofactor_providers_uid');
197
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
198
+                if ($dryRun && $sqlQueries !== null) {
199
+                    $output->writeln($sqlQueries);
200
+                }
201
+                $updated = true;
202
+                $output->writeln('<info>Twofactor_providers table updated successfully.</info>');
203
+            }
204
+        }
205
+
206
+        $output->writeln('<info>Check indices of the login_flow_v2 table.</info>');
207
+        if ($schema->hasTable('login_flow_v2')) {
208
+            $table = $schema->getTable('login_flow_v2');
209
+            if (!$table->hasIndex('poll_token')) {
210
+                $output->writeln('<info>Adding additional indeces to the login_flow_v2 table, this can take some time...</info>');
211
+
212
+                foreach ($table->getIndexes() as $index) {
213
+                    $columns = $index->getColumns();
214
+                    if ($columns === ['poll_token'] ||
215
+                        $columns === ['login_token'] ||
216
+                        $columns === ['timestamp']) {
217
+                        $table->dropIndex($index->getName());
218
+                    }
219
+                }
220
+
221
+                $table->addUniqueIndex(['poll_token'], 'poll_token');
222
+                $table->addUniqueIndex(['login_token'], 'login_token');
223
+                $table->addIndex(['timestamp'], 'timestamp');
224
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
225
+                if ($dryRun && $sqlQueries !== null) {
226
+                    $output->writeln($sqlQueries);
227
+                }
228
+                $updated = true;
229
+                $output->writeln('<info>login_flow_v2 table updated successfully.</info>');
230
+            }
231
+        }
232
+
233
+        $output->writeln('<info>Check indices of the whats_new table.</info>');
234
+        if ($schema->hasTable('whats_new')) {
235
+            $table = $schema->getTable('whats_new');
236
+            if (!$table->hasIndex('version')) {
237
+                $output->writeln('<info>Adding version index to the whats_new table, this can take some time...</info>');
238
+
239
+                foreach ($table->getIndexes() as $index) {
240
+                    if ($index->getColumns() === ['version']) {
241
+                        $table->dropIndex($index->getName());
242
+                    }
243
+                }
244
+
245
+                $table->addUniqueIndex(['version'], 'version');
246
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
247
+                if ($dryRun && $sqlQueries !== null) {
248
+                    $output->writeln($sqlQueries);
249
+                }
250
+                $updated = true;
251
+                $output->writeln('<info>whats_new table updated successfully.</info>');
252
+            }
253
+        }
254
+
255
+        $output->writeln('<info>Check indices of the cards table.</info>');
256
+        $cardsUpdated = false;
257
+        if ($schema->hasTable('cards')) {
258
+            $table = $schema->getTable('cards');
259
+
260
+            if ($table->hasIndex('addressbookid_uri_index')) {
261
+                $output->writeln('<info>Renaming addressbookid_uri_index index to  to the cards table, this can take some time...</info>');
262
+
263
+                foreach ($table->getIndexes() as $index) {
264
+                    if ($index->getColumns() === ['addressbookid', 'uri']) {
265
+                        $table->renameIndex('addressbookid_uri_index', 'cards_abiduri');
266
+                    }
267
+                }
268
+
269
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
270
+                if ($dryRun && $sqlQueries !== null) {
271
+                    $output->writeln($sqlQueries);
272
+                }
273
+                $cardsUpdated = true;
274
+            }
275
+
276
+            if (!$table->hasIndex('cards_abid')) {
277
+                $output->writeln('<info>Adding cards_abid index to the cards table, this can take some time...</info>');
278
+
279
+                foreach ($table->getIndexes() as $index) {
280
+                    if ($index->getColumns() === ['addressbookid']) {
281
+                        $table->dropIndex($index->getName());
282
+                    }
283
+                }
284
+
285
+                $table->addIndex(['addressbookid'], 'cards_abid');
286
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
287
+                if ($dryRun && $sqlQueries !== null) {
288
+                    $output->writeln($sqlQueries);
289
+                }
290
+                $cardsUpdated = true;
291
+            }
292
+
293
+            if (!$table->hasIndex('cards_abiduri')) {
294
+                $output->writeln('<info>Adding cards_abiduri index to the cards table, this can take some time...</info>');
295
+
296
+                foreach ($table->getIndexes() as $index) {
297
+                    if ($index->getColumns() === ['addressbookid', 'uri']) {
298
+                        $table->dropIndex($index->getName());
299
+                    }
300
+                }
301
+
302
+                $table->addIndex(['addressbookid', 'uri'], 'cards_abiduri');
303
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
304
+                if ($dryRun && $sqlQueries !== null) {
305
+                    $output->writeln($sqlQueries);
306
+                }
307
+                $cardsUpdated = true;
308
+            }
309
+
310
+            if ($cardsUpdated) {
311
+                $updated = true;
312
+                $output->writeln('<info>cards table updated successfully.</info>');
313
+            }
314
+        }
315
+
316
+        $output->writeln('<info>Check indices of the cards_properties table.</info>');
317
+        if ($schema->hasTable('cards_properties')) {
318
+            $table = $schema->getTable('cards_properties');
319
+            if (!$table->hasIndex('cards_prop_abid')) {
320
+                $output->writeln('<info>Adding cards_prop_abid index to the cards_properties table, this can take some time...</info>');
321
+
322
+                foreach ($table->getIndexes() as $index) {
323
+                    if ($index->getColumns() === ['addressbookid']) {
324
+                        $table->dropIndex($index->getName());
325
+                    }
326
+                }
327
+
328
+                $table->addIndex(['addressbookid'], 'cards_prop_abid');
329
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
330
+                if ($dryRun && $sqlQueries !== null) {
331
+                    $output->writeln($sqlQueries);
332
+                }
333
+                $updated = true;
334
+                $output->writeln('<info>cards_properties table updated successfully.</info>');
335
+            }
336
+        }
337
+
338
+        $output->writeln('<info>Check indices of the calendarobjects_props table.</info>');
339
+        if ($schema->hasTable('calendarobjects_props')) {
340
+            $table = $schema->getTable('calendarobjects_props');
341
+            if (!$table->hasIndex('calendarobject_calid_index')) {
342
+                $output->writeln('<info>Adding calendarobject_calid_index index to the calendarobjects_props table, this can take some time...</info>');
343
+
344
+                $table->addIndex(['calendarid', 'calendartype'], 'calendarobject_calid_index');
345
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
346
+                if ($dryRun && $sqlQueries !== null) {
347
+                    $output->writeln($sqlQueries);
348
+                }
349
+                $updated = true;
350
+                $output->writeln('<info>calendarobjects_props table updated successfully.</info>');
351
+            }
352
+        }
353
+
354
+        $output->writeln('<info>Check indices of the schedulingobjects table.</info>');
355
+        if ($schema->hasTable('schedulingobjects')) {
356
+            $table = $schema->getTable('schedulingobjects');
357
+            if (!$table->hasIndex('schedulobj_principuri_index')) {
358
+                $output->writeln('<info>Adding schedulobj_principuri_index index to the schedulingobjects table, this can take some time...</info>');
359
+
360
+                $table->addIndex(['principaluri'], 'schedulobj_principuri_index');
361
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
362
+                if ($dryRun && $sqlQueries !== null) {
363
+                    $output->writeln($sqlQueries);
364
+                }
365
+                $updated = true;
366
+                $output->writeln('<info>schedulingobjects table updated successfully.</info>');
367
+            }
368
+        }
369
+
370
+        $output->writeln('<info>Check indices of the oc_properties table.</info>');
371
+        if ($schema->hasTable('properties')) {
372
+            $table = $schema->getTable('properties');
373
+            $propertiesUpdated = false;
374
+
375
+            if (!$table->hasIndex('properties_path_index')) {
376
+                $output->writeln('<info>Adding properties_path_index index to the oc_properties table, this can take some time...</info>');
377
+
378
+                $table->addIndex(['userid', 'propertypath'], 'properties_path_index');
379
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
380
+                if ($dryRun && $sqlQueries !== null) {
381
+                    $output->writeln($sqlQueries);
382
+                }
383
+                $propertiesUpdated = true;
384
+            }
385
+            if (!$table->hasIndex('properties_pathonly_index')) {
386
+                $output->writeln('<info>Adding properties_pathonly_index index to the oc_properties table, this can take some time...</info>');
387
+
388
+                $table->addIndex(['propertypath'], 'properties_pathonly_index');
389
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
390
+                if ($dryRun && $sqlQueries !== null) {
391
+                    $output->writeln($sqlQueries);
392
+                }
393
+                $propertiesUpdated = true;
394
+            }
395
+
396
+            if ($propertiesUpdated) {
397
+                $updated = true;
398
+                $output->writeln('<info>oc_properties table updated successfully.</info>');
399
+            }
400
+        }
401
+
402
+        $output->writeln('<info>Check indices of the oc_jobs table.</info>');
403
+        if ($schema->hasTable('jobs')) {
404
+            $table = $schema->getTable('jobs');
405
+            if (!$table->hasIndex('job_lastcheck_reserved')) {
406
+                $output->writeln('<info>Adding job_lastcheck_reserved index to the oc_jobs table, this can take some time...</info>');
407
+
408
+                $table->addIndex(['last_checked', 'reserved_at'], 'job_lastcheck_reserved');
409
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
410
+                if ($dryRun && $sqlQueries !== null) {
411
+                    $output->writeln($sqlQueries);
412
+                }
413
+                $updated = true;
414
+                $output->writeln('<info>oc_properties table updated successfully.</info>');
415
+            }
416
+        }
417
+
418
+        $output->writeln('<info>Check indices of the oc_direct_edit table.</info>');
419
+        if ($schema->hasTable('direct_edit')) {
420
+            $table = $schema->getTable('direct_edit');
421
+            if (!$table->hasIndex('direct_edit_timestamp')) {
422
+                $output->writeln('<info>Adding direct_edit_timestamp index to the oc_direct_edit table, this can take some time...</info>');
423
+
424
+                $table->addIndex(['timestamp'], 'direct_edit_timestamp');
425
+                $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
426
+                if ($dryRun && $sqlQueries !== null) {
427
+                    $output->writeln($sqlQueries);
428
+                }
429
+                $updated = true;
430
+                $output->writeln('<info>oc_direct_edit table updated successfully.</info>');
431
+            }
432
+        }
433
+
434
+        if (!$updated) {
435
+            $output->writeln('<info>Done.</info>');
436
+        }
437
+    }
438 438
 }
Please login to merge, or discard this patch.