Completed
Push — master ( 453450...6f0537 )
by
unknown
37:16
created
core/Command/Info/FileUtils.php 1 patch
Indentation   +272 added lines, -272 removed lines patch added patch discarded remove patch
@@ -33,293 +33,293 @@
 block discarded – undo
33 33
  * @psalm-type StorageInfo array{numeric_id: int, id: string, available: bool, last_checked: ?\DateTime, files: int, mount_id: ?int}
34 34
  */
35 35
 class FileUtils {
36
-	public function __construct(
37
-		private IRootFolder $rootFolder,
38
-		private IUserMountCache $userMountCache,
39
-		private IDBConnection $connection,
40
-	) {
41
-	}
36
+    public function __construct(
37
+        private IRootFolder $rootFolder,
38
+        private IUserMountCache $userMountCache,
39
+        private IDBConnection $connection,
40
+    ) {
41
+    }
42 42
 
43
-	/**
44
-	 * @param FileInfo $file
45
-	 * @return array<string, Node[]>
46
-	 * @throws NotPermittedException
47
-	 * @throws NoUserException
48
-	 */
49
-	public function getFilesByUser(FileInfo $file): array {
50
-		$id = $file->getId();
51
-		if (!$id) {
52
-			return [];
53
-		}
43
+    /**
44
+     * @param FileInfo $file
45
+     * @return array<string, Node[]>
46
+     * @throws NotPermittedException
47
+     * @throws NoUserException
48
+     */
49
+    public function getFilesByUser(FileInfo $file): array {
50
+        $id = $file->getId();
51
+        if (!$id) {
52
+            return [];
53
+        }
54 54
 
55
-		$mounts = $this->userMountCache->getMountsForFileId($id);
56
-		$result = [];
57
-		foreach ($mounts as $cachedMount) {
58
-			$mount = $this->rootFolder->getMount($cachedMount->getMountPoint());
59
-			$cache = $mount->getStorage()->getCache();
60
-			$cacheEntry = $cache->get($id);
61
-			$node = $this->rootFolder->getNodeFromCacheEntryAndMount($cacheEntry, $mount);
62
-			$result[$cachedMount->getUser()->getUID()][] = $node;
63
-		}
55
+        $mounts = $this->userMountCache->getMountsForFileId($id);
56
+        $result = [];
57
+        foreach ($mounts as $cachedMount) {
58
+            $mount = $this->rootFolder->getMount($cachedMount->getMountPoint());
59
+            $cache = $mount->getStorage()->getCache();
60
+            $cacheEntry = $cache->get($id);
61
+            $node = $this->rootFolder->getNodeFromCacheEntryAndMount($cacheEntry, $mount);
62
+            $result[$cachedMount->getUser()->getUID()][] = $node;
63
+        }
64 64
 
65
-		return $result;
66
-	}
65
+        return $result;
66
+    }
67 67
 
68
-	/**
69
-	 * Get file by either id of path
70
-	 *
71
-	 * @param string $fileInput
72
-	 * @return Node|null
73
-	 */
74
-	public function getNode(string $fileInput): ?Node {
75
-		if (is_numeric($fileInput)) {
76
-			$mounts = $this->userMountCache->getMountsForFileId((int)$fileInput);
77
-			if (!$mounts) {
78
-				return null;
79
-			}
80
-			$mount = reset($mounts);
81
-			$userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID());
82
-			return $userFolder->getFirstNodeById((int)$fileInput);
83
-		} else {
84
-			try {
85
-				return $this->rootFolder->get($fileInput);
86
-			} catch (NotFoundException $e) {
87
-				return null;
88
-			}
89
-		}
90
-	}
68
+    /**
69
+     * Get file by either id of path
70
+     *
71
+     * @param string $fileInput
72
+     * @return Node|null
73
+     */
74
+    public function getNode(string $fileInput): ?Node {
75
+        if (is_numeric($fileInput)) {
76
+            $mounts = $this->userMountCache->getMountsForFileId((int)$fileInput);
77
+            if (!$mounts) {
78
+                return null;
79
+            }
80
+            $mount = reset($mounts);
81
+            $userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID());
82
+            return $userFolder->getFirstNodeById((int)$fileInput);
83
+        } else {
84
+            try {
85
+                return $this->rootFolder->get($fileInput);
86
+            } catch (NotFoundException $e) {
87
+                return null;
88
+            }
89
+        }
90
+    }
91 91
 
92
-	public function formatPermissions(string $type, int $permissions): string {
93
-		if ($permissions == Constants::PERMISSION_ALL || ($type === 'file' && $permissions == (Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE))) {
94
-			return 'full permissions';
95
-		}
92
+    public function formatPermissions(string $type, int $permissions): string {
93
+        if ($permissions == Constants::PERMISSION_ALL || ($type === 'file' && $permissions == (Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE))) {
94
+            return 'full permissions';
95
+        }
96 96
 
97
-		$perms = [];
98
-		$allPerms = [Constants::PERMISSION_READ => 'read', Constants::PERMISSION_UPDATE => 'update', Constants::PERMISSION_CREATE => 'create', Constants::PERMISSION_DELETE => 'delete', Constants::PERMISSION_SHARE => 'share'];
99
-		foreach ($allPerms as $perm => $name) {
100
-			if (($permissions & $perm) === $perm) {
101
-				$perms[] = $name;
102
-			}
103
-		}
97
+        $perms = [];
98
+        $allPerms = [Constants::PERMISSION_READ => 'read', Constants::PERMISSION_UPDATE => 'update', Constants::PERMISSION_CREATE => 'create', Constants::PERMISSION_DELETE => 'delete', Constants::PERMISSION_SHARE => 'share'];
99
+        foreach ($allPerms as $perm => $name) {
100
+            if (($permissions & $perm) === $perm) {
101
+                $perms[] = $name;
102
+            }
103
+        }
104 104
 
105
-		return implode(', ', $perms);
106
-	}
105
+        return implode(', ', $perms);
106
+    }
107 107
 
108
-	/**
109
-	 * @psalm-suppress UndefinedClass
110
-	 * @psalm-suppress UndefinedInterfaceMethod
111
-	 */
112
-	public function formatMountType(IMountPoint $mountPoint): string {
113
-		$storage = $mountPoint->getStorage();
114
-		if ($storage && $storage->instanceOfStorage(IHomeStorage::class)) {
115
-			return 'home storage';
116
-		} elseif ($mountPoint instanceof SharedMount) {
117
-			$share = $mountPoint->getShare();
118
-			$shares = $mountPoint->getGroupedShares();
119
-			$sharedBy = array_map(function (IShare $share) {
120
-				$shareType = $this->formatShareType($share);
121
-				if ($shareType) {
122
-					return $share->getSharedBy() . ' (via ' . $shareType . ' ' . $share->getSharedWith() . ')';
123
-				} else {
124
-					return $share->getSharedBy();
125
-				}
126
-			}, $shares);
127
-			$description = 'shared by ' . implode(', ', $sharedBy);
128
-			if ($share->getSharedBy() !== $share->getShareOwner()) {
129
-				$description .= ' owned by ' . $share->getShareOwner();
130
-			}
131
-			return $description;
132
-		} elseif ($mountPoint instanceof GroupMountPoint) {
133
-			return 'groupfolder ' . $mountPoint->getFolderId();
134
-		} elseif ($mountPoint instanceof ExternalMountPoint) {
135
-			return 'external storage ' . $mountPoint->getStorageConfig()->getId();
136
-		} elseif ($mountPoint instanceof CircleMount) {
137
-			return 'circle';
138
-		}
139
-		return get_class($mountPoint);
140
-	}
108
+    /**
109
+     * @psalm-suppress UndefinedClass
110
+     * @psalm-suppress UndefinedInterfaceMethod
111
+     */
112
+    public function formatMountType(IMountPoint $mountPoint): string {
113
+        $storage = $mountPoint->getStorage();
114
+        if ($storage && $storage->instanceOfStorage(IHomeStorage::class)) {
115
+            return 'home storage';
116
+        } elseif ($mountPoint instanceof SharedMount) {
117
+            $share = $mountPoint->getShare();
118
+            $shares = $mountPoint->getGroupedShares();
119
+            $sharedBy = array_map(function (IShare $share) {
120
+                $shareType = $this->formatShareType($share);
121
+                if ($shareType) {
122
+                    return $share->getSharedBy() . ' (via ' . $shareType . ' ' . $share->getSharedWith() . ')';
123
+                } else {
124
+                    return $share->getSharedBy();
125
+                }
126
+            }, $shares);
127
+            $description = 'shared by ' . implode(', ', $sharedBy);
128
+            if ($share->getSharedBy() !== $share->getShareOwner()) {
129
+                $description .= ' owned by ' . $share->getShareOwner();
130
+            }
131
+            return $description;
132
+        } elseif ($mountPoint instanceof GroupMountPoint) {
133
+            return 'groupfolder ' . $mountPoint->getFolderId();
134
+        } elseif ($mountPoint instanceof ExternalMountPoint) {
135
+            return 'external storage ' . $mountPoint->getStorageConfig()->getId();
136
+        } elseif ($mountPoint instanceof CircleMount) {
137
+            return 'circle';
138
+        }
139
+        return get_class($mountPoint);
140
+    }
141 141
 
142
-	public function formatShareType(IShare $share): ?string {
143
-		switch ($share->getShareType()) {
144
-			case IShare::TYPE_GROUP:
145
-				return 'group';
146
-			case IShare::TYPE_CIRCLE:
147
-				return 'circle';
148
-			case IShare::TYPE_DECK:
149
-				return 'deck';
150
-			case IShare::TYPE_ROOM:
151
-				return 'room';
152
-			case IShare::TYPE_USER:
153
-				return null;
154
-			default:
155
-				return 'Unknown (' . $share->getShareType() . ')';
156
-		}
157
-	}
142
+    public function formatShareType(IShare $share): ?string {
143
+        switch ($share->getShareType()) {
144
+            case IShare::TYPE_GROUP:
145
+                return 'group';
146
+            case IShare::TYPE_CIRCLE:
147
+                return 'circle';
148
+            case IShare::TYPE_DECK:
149
+                return 'deck';
150
+            case IShare::TYPE_ROOM:
151
+                return 'room';
152
+            case IShare::TYPE_USER:
153
+                return null;
154
+            default:
155
+                return 'Unknown (' . $share->getShareType() . ')';
156
+        }
157
+    }
158 158
 
159
-	/**
160
-	 * Print out the largest count($sizeLimits) files in the directory tree
161
-	 *
162
-	 * @param OutputInterface $output
163
-	 * @param Folder $node
164
-	 * @param string $prefix
165
-	 * @param array $sizeLimits largest items that are still in the queue to be printed, ordered ascending
166
-	 * @return int how many items we've printed
167
-	 */
168
-	public function outputLargeFilesTree(
169
-		OutputInterface $output,
170
-		Folder $node,
171
-		string $prefix,
172
-		array &$sizeLimits,
173
-		bool $all,
174
-	): int {
175
-		/**
176
-		 * Algorithm to print the N largest items in a folder without requiring to query or sort the entire three
177
-		 *
178
-		 * This is done by keeping a list ($sizeLimits) of size N that contain the largest items outside of this
179
-		 * folders that are could be printed if there aren't enough items in this folder that are larger.
180
-		 *
181
-		 * We loop over the items in this folder by size descending until the size of the item falls before the smallest
182
-		 * size in $sizeLimits (at that point there are enough items outside this folder to complete the N items).
183
-		 *
184
-		 * When encountering a folder, we create an updated $sizeLimits with the largest items in the current folder still
185
-		 * remaining which we pass into the recursion. (We don't update the current $sizeLimits because that should only
186
-		 * hold items *outside* of the current folder.)
187
-		 *
188
-		 * For every item printed we remove the first item of $sizeLimits are there is no longer room in the output to print
189
-		 * items that small.
190
-		 */
159
+    /**
160
+     * Print out the largest count($sizeLimits) files in the directory tree
161
+     *
162
+     * @param OutputInterface $output
163
+     * @param Folder $node
164
+     * @param string $prefix
165
+     * @param array $sizeLimits largest items that are still in the queue to be printed, ordered ascending
166
+     * @return int how many items we've printed
167
+     */
168
+    public function outputLargeFilesTree(
169
+        OutputInterface $output,
170
+        Folder $node,
171
+        string $prefix,
172
+        array &$sizeLimits,
173
+        bool $all,
174
+    ): int {
175
+        /**
176
+         * Algorithm to print the N largest items in a folder without requiring to query or sort the entire three
177
+         *
178
+         * This is done by keeping a list ($sizeLimits) of size N that contain the largest items outside of this
179
+         * folders that are could be printed if there aren't enough items in this folder that are larger.
180
+         *
181
+         * We loop over the items in this folder by size descending until the size of the item falls before the smallest
182
+         * size in $sizeLimits (at that point there are enough items outside this folder to complete the N items).
183
+         *
184
+         * When encountering a folder, we create an updated $sizeLimits with the largest items in the current folder still
185
+         * remaining which we pass into the recursion. (We don't update the current $sizeLimits because that should only
186
+         * hold items *outside* of the current folder.)
187
+         *
188
+         * For every item printed we remove the first item of $sizeLimits are there is no longer room in the output to print
189
+         * items that small.
190
+         */
191 191
 
192
-		$count = 0;
193
-		$children = $node->getDirectoryListing();
194
-		usort($children, function (Node $a, Node $b) {
195
-			return $b->getSize() <=> $a->getSize();
196
-		});
197
-		foreach ($children as $i => $child) {
198
-			if (!$all) {
199
-				if (count($sizeLimits) === 0 || $child->getSize() < $sizeLimits[0]) {
200
-					return $count;
201
-				}
202
-				array_shift($sizeLimits);
203
-			}
204
-			$count += 1;
192
+        $count = 0;
193
+        $children = $node->getDirectoryListing();
194
+        usort($children, function (Node $a, Node $b) {
195
+            return $b->getSize() <=> $a->getSize();
196
+        });
197
+        foreach ($children as $i => $child) {
198
+            if (!$all) {
199
+                if (count($sizeLimits) === 0 || $child->getSize() < $sizeLimits[0]) {
200
+                    return $count;
201
+                }
202
+                array_shift($sizeLimits);
203
+            }
204
+            $count += 1;
205 205
 
206
-			/** @var Node $child */
207
-			$output->writeln("$prefix- " . $child->getName() . ': <info>' . Util::humanFileSize($child->getSize()) . '</info>');
208
-			if ($child instanceof Folder) {
209
-				$recurseSizeLimits = $sizeLimits;
210
-				if (!$all) {
211
-					for ($j = 0; $j < count($recurseSizeLimits); $j++) {
212
-						if (isset($children[$i + $j + 1])) {
213
-							$nextChildSize = $children[$i + $j + 1]->getSize();
214
-							if ($nextChildSize > $recurseSizeLimits[0]) {
215
-								array_shift($recurseSizeLimits);
216
-								$recurseSizeLimits[] = $nextChildSize;
217
-							}
218
-						}
219
-					}
220
-					sort($recurseSizeLimits);
221
-				}
222
-				$recurseCount = $this->outputLargeFilesTree($output, $child, $prefix . '  ', $recurseSizeLimits, $all);
223
-				$sizeLimits = array_slice($sizeLimits, $recurseCount);
224
-				$count += $recurseCount;
225
-			}
226
-		}
227
-		return $count;
228
-	}
206
+            /** @var Node $child */
207
+            $output->writeln("$prefix- " . $child->getName() . ': <info>' . Util::humanFileSize($child->getSize()) . '</info>');
208
+            if ($child instanceof Folder) {
209
+                $recurseSizeLimits = $sizeLimits;
210
+                if (!$all) {
211
+                    for ($j = 0; $j < count($recurseSizeLimits); $j++) {
212
+                        if (isset($children[$i + $j + 1])) {
213
+                            $nextChildSize = $children[$i + $j + 1]->getSize();
214
+                            if ($nextChildSize > $recurseSizeLimits[0]) {
215
+                                array_shift($recurseSizeLimits);
216
+                                $recurseSizeLimits[] = $nextChildSize;
217
+                            }
218
+                        }
219
+                    }
220
+                    sort($recurseSizeLimits);
221
+                }
222
+                $recurseCount = $this->outputLargeFilesTree($output, $child, $prefix . '  ', $recurseSizeLimits, $all);
223
+                $sizeLimits = array_slice($sizeLimits, $recurseCount);
224
+                $count += $recurseCount;
225
+            }
226
+        }
227
+        return $count;
228
+    }
229 229
 
230
-	public function getNumericStorageId(string $id): ?int {
231
-		if (is_numeric($id)) {
232
-			return (int)$id;
233
-		}
234
-		$query = $this->connection->getQueryBuilder();
235
-		$query->select('numeric_id')
236
-			->from('storages')
237
-			->where($query->expr()->eq('id', $query->createNamedParameter($id)));
238
-		$result = $query->executeQuery()->fetchOne();
239
-		return $result ? (int)$result : null;
240
-	}
230
+    public function getNumericStorageId(string $id): ?int {
231
+        if (is_numeric($id)) {
232
+            return (int)$id;
233
+        }
234
+        $query = $this->connection->getQueryBuilder();
235
+        $query->select('numeric_id')
236
+            ->from('storages')
237
+            ->where($query->expr()->eq('id', $query->createNamedParameter($id)));
238
+        $result = $query->executeQuery()->fetchOne();
239
+        return $result ? (int)$result : null;
240
+    }
241 241
 
242
-	/**
243
-	 * @param int|null $limit
244
-	 * @return ?StorageInfo
245
-	 * @throws \OCP\DB\Exception
246
-	 */
247
-	public function getStorage(int $id): ?array {
248
-		$query = $this->connection->getQueryBuilder();
249
-		$query->select('numeric_id', 's.id', 'available', 'last_checked', 'mount_id')
250
-			->selectAlias($query->func()->count('fileid'), 'files')
251
-			->from('storages', 's')
252
-			->innerJoin('s', 'filecache', 'f', $query->expr()->eq('f.storage', 's.numeric_id'))
253
-			->leftJoin('s', 'mounts', 'm', $query->expr()->eq('s.numeric_id', 'm.storage_id'))
254
-			->where($query->expr()->eq('s.numeric_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
255
-			->groupBy('s.numeric_id', 's.id', 's.available', 's.last_checked', 'mount_id');
256
-		$row = $query->executeQuery()->fetchAssociative();
257
-		if ($row) {
258
-			return [
259
-				'numeric_id' => $row['numeric_id'],
260
-				'id' => $row['id'],
261
-				'files' => $row['files'],
262
-				'available' => (bool)$row['available'],
263
-				'last_checked' => $row['last_checked'] ? new \DateTime('@' . $row['last_checked']) : null,
264
-				'mount_id' => $row['mount_id'],
265
-			];
266
-		} else {
267
-			return null;
268
-		}
269
-	}
242
+    /**
243
+     * @param int|null $limit
244
+     * @return ?StorageInfo
245
+     * @throws \OCP\DB\Exception
246
+     */
247
+    public function getStorage(int $id): ?array {
248
+        $query = $this->connection->getQueryBuilder();
249
+        $query->select('numeric_id', 's.id', 'available', 'last_checked', 'mount_id')
250
+            ->selectAlias($query->func()->count('fileid'), 'files')
251
+            ->from('storages', 's')
252
+            ->innerJoin('s', 'filecache', 'f', $query->expr()->eq('f.storage', 's.numeric_id'))
253
+            ->leftJoin('s', 'mounts', 'm', $query->expr()->eq('s.numeric_id', 'm.storage_id'))
254
+            ->where($query->expr()->eq('s.numeric_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
255
+            ->groupBy('s.numeric_id', 's.id', 's.available', 's.last_checked', 'mount_id');
256
+        $row = $query->executeQuery()->fetchAssociative();
257
+        if ($row) {
258
+            return [
259
+                'numeric_id' => $row['numeric_id'],
260
+                'id' => $row['id'],
261
+                'files' => $row['files'],
262
+                'available' => (bool)$row['available'],
263
+                'last_checked' => $row['last_checked'] ? new \DateTime('@' . $row['last_checked']) : null,
264
+                'mount_id' => $row['mount_id'],
265
+            ];
266
+        } else {
267
+            return null;
268
+        }
269
+    }
270 270
 
271
-	/**
272
-	 * @param int|null $limit
273
-	 * @return \Iterator<StorageInfo>
274
-	 * @throws \OCP\DB\Exception
275
-	 */
276
-	public function listStorages(?int $limit): \Iterator {
277
-		$query = $this->connection->getQueryBuilder();
278
-		$query->select('numeric_id', 's.id', 'available', 'last_checked', 'mount_id')
279
-			->selectAlias($query->func()->count('fileid'), 'files')
280
-			->from('storages', 's')
281
-			->innerJoin('s', 'filecache', 'f', $query->expr()->eq('f.storage', 's.numeric_id'))
282
-			->leftJoin('s', 'mounts', 'm', $query->expr()->eq('s.numeric_id', 'm.storage_id'))
283
-			->groupBy('s.numeric_id', 's.id', 's.available', 's.last_checked', 'mount_id')
284
-			->orderBy('files', 'DESC');
285
-		if ($limit !== null) {
286
-			$query->setMaxResults($limit);
287
-		}
288
-		$result = $query->executeQuery();
289
-		while ($row = $result->fetchAssociative()) {
290
-			yield [
291
-				'numeric_id' => $row['numeric_id'],
292
-				'id' => $row['id'],
293
-				'files' => $row['files'],
294
-				'available' => (bool)$row['available'],
295
-				'last_checked' => $row['last_checked'] ? new \DateTime('@' . $row['last_checked']) : null,
296
-				'mount_id' => $row['mount_id'],
297
-			];
298
-		}
299
-	}
271
+    /**
272
+     * @param int|null $limit
273
+     * @return \Iterator<StorageInfo>
274
+     * @throws \OCP\DB\Exception
275
+     */
276
+    public function listStorages(?int $limit): \Iterator {
277
+        $query = $this->connection->getQueryBuilder();
278
+        $query->select('numeric_id', 's.id', 'available', 'last_checked', 'mount_id')
279
+            ->selectAlias($query->func()->count('fileid'), 'files')
280
+            ->from('storages', 's')
281
+            ->innerJoin('s', 'filecache', 'f', $query->expr()->eq('f.storage', 's.numeric_id'))
282
+            ->leftJoin('s', 'mounts', 'm', $query->expr()->eq('s.numeric_id', 'm.storage_id'))
283
+            ->groupBy('s.numeric_id', 's.id', 's.available', 's.last_checked', 'mount_id')
284
+            ->orderBy('files', 'DESC');
285
+        if ($limit !== null) {
286
+            $query->setMaxResults($limit);
287
+        }
288
+        $result = $query->executeQuery();
289
+        while ($row = $result->fetchAssociative()) {
290
+            yield [
291
+                'numeric_id' => $row['numeric_id'],
292
+                'id' => $row['id'],
293
+                'files' => $row['files'],
294
+                'available' => (bool)$row['available'],
295
+                'last_checked' => $row['last_checked'] ? new \DateTime('@' . $row['last_checked']) : null,
296
+                'mount_id' => $row['mount_id'],
297
+            ];
298
+        }
299
+    }
300 300
 
301
-	/**
302
-	 * @param StorageInfo $storage
303
-	 * @return array
304
-	 */
305
-	public function formatStorage(array $storage): array {
306
-		return [
307
-			'numeric_id' => $storage['numeric_id'],
308
-			'id' => $storage['id'],
309
-			'files' => $storage['files'],
310
-			'available' => $storage['available'] ? 'true' : 'false',
311
-			'last_checked' => $storage['last_checked']?->format(\DATE_ATOM),
312
-			'external_mount_id' => $storage['mount_id'],
313
-		];
314
-	}
301
+    /**
302
+     * @param StorageInfo $storage
303
+     * @return array
304
+     */
305
+    public function formatStorage(array $storage): array {
306
+        return [
307
+            'numeric_id' => $storage['numeric_id'],
308
+            'id' => $storage['id'],
309
+            'files' => $storage['files'],
310
+            'available' => $storage['available'] ? 'true' : 'false',
311
+            'last_checked' => $storage['last_checked']?->format(\DATE_ATOM),
312
+            'external_mount_id' => $storage['mount_id'],
313
+        ];
314
+    }
315 315
 
316
-	/**
317
-	 * @param \Iterator<StorageInfo> $storages
318
-	 * @return \Iterator
319
-	 */
320
-	public function formatStorages(\Iterator $storages): \Iterator {
321
-		foreach ($storages as $storage) {
322
-			yield $this->formatStorage($storage);
323
-		}
324
-	}
316
+    /**
317
+     * @param \Iterator<StorageInfo> $storages
318
+     * @return \Iterator
319
+     */
320
+    public function formatStorages(\Iterator $storages): \Iterator {
321
+        foreach ($storages as $storage) {
322
+            yield $this->formatStorage($storage);
323
+        }
324
+    }
325 325
 }
Please login to merge, or discard this patch.
core/Command/Maintenance/RepairShareOwnership.php 1 patch
Indentation   +151 added lines, -151 removed lines patch added patch discarded remove patch
@@ -22,155 +22,155 @@
 block discarded – undo
22 22
 use Symfony\Component\Console\Question\ConfirmationQuestion;
23 23
 
24 24
 class RepairShareOwnership extends Command {
25
-	public function __construct(
26
-		private IDBConnection $dbConnection,
27
-		private IUserManager $userManager,
28
-	) {
29
-		parent::__construct();
30
-	}
31
-
32
-	protected function configure() {
33
-		$this
34
-			->setName('maintenance:repair-share-owner')
35
-			->setDescription('repair invalid share-owner entries in the database')
36
-			->addOption('no-confirm', 'y', InputOption::VALUE_NONE, "Don't ask for confirmation before repairing the shares")
37
-			->addArgument('user', InputArgument::OPTIONAL, 'User to fix incoming shares for, if omitted all users will be fixed');
38
-	}
39
-
40
-	protected function execute(InputInterface $input, OutputInterface $output): int {
41
-		$noConfirm = $input->getOption('no-confirm');
42
-		$userId = $input->getArgument('user');
43
-		if ($userId) {
44
-			$user = $this->userManager->get($userId);
45
-			if (!$user) {
46
-				$output->writeln("<error>user $userId not found</error>");
47
-				return 1;
48
-			}
49
-			$shares = $this->getWrongShareOwnershipForUser($user);
50
-		} else {
51
-			$shares = $this->getWrongShareOwnership();
52
-		}
53
-
54
-		if ($shares) {
55
-			$output->writeln('');
56
-			$output->writeln('Found ' . count($shares) . ' shares with invalid share owner');
57
-			foreach ($shares as $share) {
58
-				/** @var array{shareId: int, fileTarget: string, initiator: string, receiver: string, owner: string, mountOwner: string} $share */
59
-				$output->writeln(" - share {$share['shareId']} from \"{$share['initiator']}\" to \"{$share['receiver']}\" at \"{$share['fileTarget']}\", owned by \"{$share['owner']}\", that should be owned by \"{$share['mountOwner']}\"");
60
-			}
61
-			$output->writeln('');
62
-
63
-			if (!$noConfirm) {
64
-				/** @var QuestionHelper $helper */
65
-				$helper = $this->getHelper('question');
66
-				$question = new ConfirmationQuestion('Repair these shares? [y/N]', false);
67
-
68
-				if (!$helper->ask($input, $output, $question)) {
69
-					return 0;
70
-				}
71
-			}
72
-			$output->writeln('Repairing ' . count($shares) . ' shares');
73
-			$this->repairShares($shares);
74
-		} else {
75
-			$output->writeln('Found no shares with invalid share owner');
76
-		}
77
-
78
-		return 0;
79
-	}
80
-
81
-	/**
82
-	 * @return array{shareId: int, fileTarget: string, initiator: string, receiver: string, owner: string, mountOwner: string}[]
83
-	 * @throws \OCP\DB\Exception
84
-	 */
85
-	protected function getWrongShareOwnership(): array {
86
-		$qb = $this->dbConnection->getQueryBuilder();
87
-		$brokenShares = $qb
88
-			->select('s.id', 'm.user_id', 's.uid_owner', 's.uid_initiator', 's.share_with', 's.file_target')
89
-			->from('share', 's')
90
-			->join('s', 'filecache', 'f', $qb->expr()->eq($qb->expr()->castColumn('s.item_source', IQueryBuilder::PARAM_INT), 'f.fileid'))
91
-			->join('s', 'mounts', 'm', $qb->expr()->eq('f.storage', 'm.storage_id'))
92
-			->where($qb->expr()->neq('m.user_id', 's.uid_owner'))
93
-			->andWhere($qb->expr()->eq($qb->func()->concat($qb->expr()->literal('/'), 'm.user_id', $qb->expr()->literal('/')), 'm.mount_point'))
94
-			->executeQuery()
95
-			->fetchAllAssociative();
96
-
97
-		$found = [];
98
-
99
-		foreach ($brokenShares as $share) {
100
-			$found[] = [
101
-				'shareId' => (int)$share['id'],
102
-				'fileTarget' => $share['file_target'],
103
-				'initiator' => $share['uid_initiator'],
104
-				'receiver' => $share['share_with'],
105
-				'owner' => $share['uid_owner'],
106
-				'mountOwner' => $share['user_id'],
107
-			];
108
-		}
109
-
110
-		return $found;
111
-	}
112
-
113
-	/**
114
-	 * @param IUser $user
115
-	 * @return array{shareId: int, fileTarget: string, initiator: string, receiver: string, owner: string, mountOwner: string}[]
116
-	 * @throws \OCP\DB\Exception
117
-	 */
118
-	protected function getWrongShareOwnershipForUser(IUser $user): array {
119
-		$qb = $this->dbConnection->getQueryBuilder();
120
-		$brokenShares = $qb
121
-			->select('s.id', 'm.user_id', 's.uid_owner', 's.uid_initiator', 's.share_with', 's.file_target')
122
-			->from('share', 's')
123
-			->join('s', 'filecache', 'f', $qb->expr()->eq('s.item_source', $qb->expr()->castColumn('f.fileid', IQueryBuilder::PARAM_STR)))
124
-			->join('s', 'mounts', 'm', $qb->expr()->eq('f.storage', 'm.storage_id'))
125
-			->where($qb->expr()->neq('m.user_id', 's.uid_owner'))
126
-			->andWhere($qb->expr()->eq($qb->func()->concat($qb->expr()->literal('/'), 'm.user_id', $qb->expr()->literal('/')), 'm.mount_point'))
127
-			->andWhere($qb->expr()->eq('s.share_with', $qb->createNamedParameter($user->getUID())))
128
-			->executeQuery()
129
-			->fetchAllAssociative();
130
-
131
-		$found = [];
132
-
133
-		foreach ($brokenShares as $share) {
134
-			$found[] = [
135
-				'shareId' => (int)$share['id'],
136
-				'fileTarget' => $share['file_target'],
137
-				'initiator' => $share['uid_initiator'],
138
-				'receiver' => $share['share_with'],
139
-				'owner' => $share['uid_owner'],
140
-				'mountOwner' => $share['user_id'],
141
-			];
142
-		}
143
-
144
-		return $found;
145
-	}
146
-
147
-	/**
148
-	 * @param array{shareId: int, fileTarget: string, initiator: string, receiver: string, owner: string, mountOwner: string}[] $shares
149
-	 * @return void
150
-	 */
151
-	protected function repairShares(array $shares) {
152
-		$this->dbConnection->beginTransaction();
153
-
154
-		$update = $this->dbConnection->getQueryBuilder();
155
-		$update->update('share')
156
-			->set('uid_owner', $update->createParameter('share_owner'))
157
-			->set('uid_initiator', $update->createParameter('share_initiator'))
158
-			->where($update->expr()->eq('id', $update->createParameter('share_id')));
159
-
160
-		foreach ($shares as $share) {
161
-			/** @var array{shareId: int, fileTarget: string, initiator: string, receiver: string, owner: string, mountOwner: string} $share */
162
-			$update->setParameter('share_id', $share['shareId'], IQueryBuilder::PARAM_INT);
163
-			$update->setParameter('share_owner', $share['mountOwner']);
164
-
165
-			// if the broken owner is also the initiator it's safe to update them both, otherwise we don't touch the initiator
166
-			if ($share['initiator'] === $share['owner']) {
167
-				$update->setParameter('share_initiator', $share['mountOwner']);
168
-			} else {
169
-				$update->setParameter('share_initiator', $share['initiator']);
170
-			}
171
-			$update->executeStatement();
172
-		}
173
-
174
-		$this->dbConnection->commit();
175
-	}
25
+    public function __construct(
26
+        private IDBConnection $dbConnection,
27
+        private IUserManager $userManager,
28
+    ) {
29
+        parent::__construct();
30
+    }
31
+
32
+    protected function configure() {
33
+        $this
34
+            ->setName('maintenance:repair-share-owner')
35
+            ->setDescription('repair invalid share-owner entries in the database')
36
+            ->addOption('no-confirm', 'y', InputOption::VALUE_NONE, "Don't ask for confirmation before repairing the shares")
37
+            ->addArgument('user', InputArgument::OPTIONAL, 'User to fix incoming shares for, if omitted all users will be fixed');
38
+    }
39
+
40
+    protected function execute(InputInterface $input, OutputInterface $output): int {
41
+        $noConfirm = $input->getOption('no-confirm');
42
+        $userId = $input->getArgument('user');
43
+        if ($userId) {
44
+            $user = $this->userManager->get($userId);
45
+            if (!$user) {
46
+                $output->writeln("<error>user $userId not found</error>");
47
+                return 1;
48
+            }
49
+            $shares = $this->getWrongShareOwnershipForUser($user);
50
+        } else {
51
+            $shares = $this->getWrongShareOwnership();
52
+        }
53
+
54
+        if ($shares) {
55
+            $output->writeln('');
56
+            $output->writeln('Found ' . count($shares) . ' shares with invalid share owner');
57
+            foreach ($shares as $share) {
58
+                /** @var array{shareId: int, fileTarget: string, initiator: string, receiver: string, owner: string, mountOwner: string} $share */
59
+                $output->writeln(" - share {$share['shareId']} from \"{$share['initiator']}\" to \"{$share['receiver']}\" at \"{$share['fileTarget']}\", owned by \"{$share['owner']}\", that should be owned by \"{$share['mountOwner']}\"");
60
+            }
61
+            $output->writeln('');
62
+
63
+            if (!$noConfirm) {
64
+                /** @var QuestionHelper $helper */
65
+                $helper = $this->getHelper('question');
66
+                $question = new ConfirmationQuestion('Repair these shares? [y/N]', false);
67
+
68
+                if (!$helper->ask($input, $output, $question)) {
69
+                    return 0;
70
+                }
71
+            }
72
+            $output->writeln('Repairing ' . count($shares) . ' shares');
73
+            $this->repairShares($shares);
74
+        } else {
75
+            $output->writeln('Found no shares with invalid share owner');
76
+        }
77
+
78
+        return 0;
79
+    }
80
+
81
+    /**
82
+     * @return array{shareId: int, fileTarget: string, initiator: string, receiver: string, owner: string, mountOwner: string}[]
83
+     * @throws \OCP\DB\Exception
84
+     */
85
+    protected function getWrongShareOwnership(): array {
86
+        $qb = $this->dbConnection->getQueryBuilder();
87
+        $brokenShares = $qb
88
+            ->select('s.id', 'm.user_id', 's.uid_owner', 's.uid_initiator', 's.share_with', 's.file_target')
89
+            ->from('share', 's')
90
+            ->join('s', 'filecache', 'f', $qb->expr()->eq($qb->expr()->castColumn('s.item_source', IQueryBuilder::PARAM_INT), 'f.fileid'))
91
+            ->join('s', 'mounts', 'm', $qb->expr()->eq('f.storage', 'm.storage_id'))
92
+            ->where($qb->expr()->neq('m.user_id', 's.uid_owner'))
93
+            ->andWhere($qb->expr()->eq($qb->func()->concat($qb->expr()->literal('/'), 'm.user_id', $qb->expr()->literal('/')), 'm.mount_point'))
94
+            ->executeQuery()
95
+            ->fetchAllAssociative();
96
+
97
+        $found = [];
98
+
99
+        foreach ($brokenShares as $share) {
100
+            $found[] = [
101
+                'shareId' => (int)$share['id'],
102
+                'fileTarget' => $share['file_target'],
103
+                'initiator' => $share['uid_initiator'],
104
+                'receiver' => $share['share_with'],
105
+                'owner' => $share['uid_owner'],
106
+                'mountOwner' => $share['user_id'],
107
+            ];
108
+        }
109
+
110
+        return $found;
111
+    }
112
+
113
+    /**
114
+     * @param IUser $user
115
+     * @return array{shareId: int, fileTarget: string, initiator: string, receiver: string, owner: string, mountOwner: string}[]
116
+     * @throws \OCP\DB\Exception
117
+     */
118
+    protected function getWrongShareOwnershipForUser(IUser $user): array {
119
+        $qb = $this->dbConnection->getQueryBuilder();
120
+        $brokenShares = $qb
121
+            ->select('s.id', 'm.user_id', 's.uid_owner', 's.uid_initiator', 's.share_with', 's.file_target')
122
+            ->from('share', 's')
123
+            ->join('s', 'filecache', 'f', $qb->expr()->eq('s.item_source', $qb->expr()->castColumn('f.fileid', IQueryBuilder::PARAM_STR)))
124
+            ->join('s', 'mounts', 'm', $qb->expr()->eq('f.storage', 'm.storage_id'))
125
+            ->where($qb->expr()->neq('m.user_id', 's.uid_owner'))
126
+            ->andWhere($qb->expr()->eq($qb->func()->concat($qb->expr()->literal('/'), 'm.user_id', $qb->expr()->literal('/')), 'm.mount_point'))
127
+            ->andWhere($qb->expr()->eq('s.share_with', $qb->createNamedParameter($user->getUID())))
128
+            ->executeQuery()
129
+            ->fetchAllAssociative();
130
+
131
+        $found = [];
132
+
133
+        foreach ($brokenShares as $share) {
134
+            $found[] = [
135
+                'shareId' => (int)$share['id'],
136
+                'fileTarget' => $share['file_target'],
137
+                'initiator' => $share['uid_initiator'],
138
+                'receiver' => $share['share_with'],
139
+                'owner' => $share['uid_owner'],
140
+                'mountOwner' => $share['user_id'],
141
+            ];
142
+        }
143
+
144
+        return $found;
145
+    }
146
+
147
+    /**
148
+     * @param array{shareId: int, fileTarget: string, initiator: string, receiver: string, owner: string, mountOwner: string}[] $shares
149
+     * @return void
150
+     */
151
+    protected function repairShares(array $shares) {
152
+        $this->dbConnection->beginTransaction();
153
+
154
+        $update = $this->dbConnection->getQueryBuilder();
155
+        $update->update('share')
156
+            ->set('uid_owner', $update->createParameter('share_owner'))
157
+            ->set('uid_initiator', $update->createParameter('share_initiator'))
158
+            ->where($update->expr()->eq('id', $update->createParameter('share_id')));
159
+
160
+        foreach ($shares as $share) {
161
+            /** @var array{shareId: int, fileTarget: string, initiator: string, receiver: string, owner: string, mountOwner: string} $share */
162
+            $update->setParameter('share_id', $share['shareId'], IQueryBuilder::PARAM_INT);
163
+            $update->setParameter('share_owner', $share['mountOwner']);
164
+
165
+            // if the broken owner is also the initiator it's safe to update them both, otherwise we don't touch the initiator
166
+            if ($share['initiator'] === $share['owner']) {
167
+                $update->setParameter('share_initiator', $share['mountOwner']);
168
+            } else {
169
+                $update->setParameter('share_initiator', $share['initiator']);
170
+            }
171
+            $update->executeStatement();
172
+        }
173
+
174
+        $this->dbConnection->commit();
175
+    }
176 176
 }
Please login to merge, or discard this patch.
core/Command/Db/ConvertType.php 1 patch
Indentation   +428 added lines, -428 removed lines patch added patch discarded remove patch
@@ -35,432 +35,432 @@
 block discarded – undo
35 35
 use function preg_quote;
36 36
 
37 37
 class ConvertType extends Command implements CompletionAwareInterface {
38
-	protected array $columnTypes = [];
39
-
40
-	public function __construct(
41
-		protected IConfig $config,
42
-		protected ConnectionFactory $connectionFactory,
43
-		protected IAppManager $appManager,
44
-	) {
45
-		parent::__construct();
46
-	}
47
-
48
-	protected function configure() {
49
-		$this
50
-			->setName('db:convert-type')
51
-			->setDescription('Convert the Nextcloud database to the newly configured one')
52
-			->addArgument(
53
-				'type',
54
-				InputArgument::REQUIRED,
55
-				'the type of the database to convert to'
56
-			)
57
-			->addArgument(
58
-				'username',
59
-				InputArgument::REQUIRED,
60
-				'the username of the database to convert to'
61
-			)
62
-			->addArgument(
63
-				'hostname',
64
-				InputArgument::REQUIRED,
65
-				'the hostname of the database to convert to'
66
-			)
67
-			->addArgument(
68
-				'database',
69
-				InputArgument::REQUIRED,
70
-				'the name of the database to convert to'
71
-			)
72
-			->addOption(
73
-				'port',
74
-				null,
75
-				InputOption::VALUE_REQUIRED,
76
-				'the port of the database to convert to'
77
-			)
78
-			->addOption(
79
-				'password',
80
-				null,
81
-				InputOption::VALUE_REQUIRED,
82
-				'the password of the database to convert to. Will be asked when not specified. Can also be passed via stdin.'
83
-			)
84
-			->addOption(
85
-				'clear-schema',
86
-				null,
87
-				InputOption::VALUE_NONE,
88
-				'remove all tables from the destination database'
89
-			)
90
-			->addOption(
91
-				'all-apps',
92
-				null,
93
-				InputOption::VALUE_NONE,
94
-				'whether to create schema for all apps instead of only installed apps'
95
-			)
96
-			->addOption(
97
-				'chunk-size',
98
-				null,
99
-				InputOption::VALUE_REQUIRED,
100
-				'the maximum number of database rows to handle in a single query, bigger tables will be handled in chunks of this size. Lower this if the process runs out of memory during conversion.',
101
-				'1000'
102
-			)
103
-		;
104
-	}
105
-
106
-	protected function validateInput(InputInterface $input, OutputInterface $output) {
107
-		$type = $this->connectionFactory->normalizeType($input->getArgument('type'));
108
-		if ($type === 'sqlite3') {
109
-			throw new \InvalidArgumentException(
110
-				'Converting to SQLite (sqlite3) is currently not supported.'
111
-			);
112
-		}
113
-		if ($type === $this->config->getSystemValue('dbtype', '')) {
114
-			throw new \InvalidArgumentException(sprintf(
115
-				'Can not convert from %1$s to %1$s.',
116
-				$type
117
-			));
118
-		}
119
-		if ($type === 'oci' && $input->getOption('clear-schema')) {
120
-			// Doctrine unconditionally tries (at least in version 2.3)
121
-			// to drop sequence triggers when dropping a table, even though
122
-			// such triggers may not exist. This results in errors like
123
-			// "ORA-04080: trigger 'OC_STORAGES_AI_PK' does not exist".
124
-			throw new \InvalidArgumentException(
125
-				'The --clear-schema option is not supported when converting to Oracle (oci).'
126
-			);
127
-		}
128
-	}
129
-
130
-	protected function readPassword(InputInterface $input, OutputInterface $output) {
131
-		// Explicitly specified password
132
-		if ($input->getOption('password')) {
133
-			return;
134
-		}
135
-
136
-		// Read from stdin. stream_set_blocking is used to prevent blocking
137
-		// when nothing is passed via stdin.
138
-		stream_set_blocking(STDIN, 0);
139
-		$password = file_get_contents('php://stdin');
140
-		stream_set_blocking(STDIN, 1);
141
-		if (trim($password) !== '') {
142
-			$input->setOption('password', $password);
143
-			return;
144
-		}
145
-
146
-		// Read password by interacting
147
-		if ($input->isInteractive()) {
148
-			/** @var QuestionHelper $helper */
149
-			$helper = $this->getHelper('question');
150
-			$question = new Question('What is the database password (press <enter> for none)? ');
151
-			$question->setHidden(true);
152
-			$question->setHiddenFallback(false);
153
-			$password = $helper->ask($input, $output, $question);
154
-			if ($password === null) {
155
-				$password = ''; // possibly unnecessary
156
-			}
157
-			$input->setOption('password', $password);
158
-			return;
159
-		}
160
-	}
161
-
162
-	protected function execute(InputInterface $input, OutputInterface $output): int {
163
-		$this->validateInput($input, $output);
164
-		$this->readPassword($input, $output);
165
-
166
-		/** @var Connection $fromDB */
167
-		$fromDB = Server::get(Connection::class);
168
-		$toDB = $this->getToDBConnection($input, $output);
169
-
170
-		if ($input->getOption('clear-schema')) {
171
-			$this->clearSchema($toDB, $input, $output);
172
-		}
173
-
174
-		$this->createSchema($fromDB, $toDB, $input, $output);
175
-
176
-		$toTables = $this->getTables($toDB);
177
-		$fromTables = $this->getTables($fromDB);
178
-
179
-		// warn/fail if there are more tables in 'from' database
180
-		$extraFromTables = array_diff($fromTables, $toTables);
181
-		if (!empty($extraFromTables)) {
182
-			$output->writeln('<comment>The following tables will not be converted:</comment>');
183
-			$output->writeln($extraFromTables);
184
-			if (!$input->getOption('all-apps')) {
185
-				$output->writeln('<comment>Please note that tables belonging to disabled (but not removed) apps</comment>');
186
-				$output->writeln('<comment>can be included by specifying the --all-apps option.</comment>');
187
-			}
188
-
189
-			$continueConversion = !$input->isInteractive(); // assume yes for --no-interaction and no otherwise.
190
-			$question = new ConfirmationQuestion('Continue with the conversion (y/n)? [n] ', $continueConversion);
191
-
192
-			/** @var QuestionHelper $helper */
193
-			$helper = $this->getHelper('question');
194
-
195
-			if (!$helper->ask($input, $output, $question)) {
196
-				return 1;
197
-			}
198
-		}
199
-		$intersectingTables = array_intersect($toTables, $fromTables);
200
-		$this->convertDB($fromDB, $toDB, $intersectingTables, $input, $output);
201
-		return 0;
202
-	}
203
-
204
-	protected function createSchema(Connection $fromDB, Connection $toDB, InputInterface $input, OutputInterface $output) {
205
-		$output->writeln('<info>Creating schema in new database</info>');
206
-
207
-		$fromMS = new MigrationService('core', $fromDB);
208
-		$currentMigration = $fromMS->getMigration('current');
209
-		if ($currentMigration !== '0') {
210
-			$toMS = new MigrationService('core', $toDB);
211
-			$toMS->migrate($currentMigration);
212
-		}
213
-
214
-		$apps = $input->getOption('all-apps')
215
-			? $this->appManager->getAllAppsInAppsFolders()
216
-			: $this->appManager->getEnabledApps();
217
-		foreach ($apps as $app) {
218
-			$output->writeln('<info> - ' . $app . '</info>');
219
-			// Make sure autoloading works...
220
-			$this->appManager->loadApp($app);
221
-			$fromMS = new MigrationService($app, $fromDB);
222
-			$currentMigration = $fromMS->getMigration('current');
223
-			if ($currentMigration !== '0') {
224
-				$toMS = new MigrationService($app, $toDB);
225
-				$toMS->migrate($currentMigration, true);
226
-			}
227
-		}
228
-	}
229
-
230
-	protected function getToDBConnection(InputInterface $input, OutputInterface $output) {
231
-		$type = $input->getArgument('type');
232
-		$connectionParams = $this->connectionFactory->createConnectionParams(type: $type);
233
-		$connectionParams = array_merge($connectionParams, [
234
-			'host' => $input->getArgument('hostname'),
235
-			'user' => $input->getArgument('username'),
236
-			'password' => $input->getOption('password'),
237
-			'dbname' => $input->getArgument('database'),
238
-		]);
239
-
240
-		// parse port
241
-		if ($input->getOption('port')) {
242
-			$connectionParams['port'] = $input->getOption('port');
243
-		}
244
-
245
-		// parse hostname for unix socket
246
-		if (preg_match('/^(.+)(:(\d+|[^:]+))?$/', $input->getArgument('hostname'), $matches)) {
247
-			$connectionParams['host'] = $matches[1];
248
-			if (isset($matches[3])) {
249
-				if (is_numeric($matches[3])) {
250
-					$connectionParams['port'] = $matches[3];
251
-				} else {
252
-					$connectionParams['unix_socket'] = $matches[3];
253
-				}
254
-			}
255
-		}
256
-
257
-		return $this->connectionFactory->getConnection($type, $connectionParams);
258
-	}
259
-
260
-	protected function clearSchema(Connection $db, InputInterface $input, OutputInterface $output) {
261
-		$toTables = $this->getTables($db);
262
-		if (!empty($toTables)) {
263
-			$output->writeln('<info>Clearing schema in new database</info>');
264
-		}
265
-		foreach ($toTables as $table) {
266
-			$db->createSchemaManager()->dropTable($table);
267
-		}
268
-	}
269
-
270
-	protected function getTables(Connection $db) {
271
-		$db->getConfiguration()->setSchemaAssetsFilter(function ($asset) {
272
-			/** @var string|AbstractAsset $asset */
273
-			$filterExpression = '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
274
-			if ($asset instanceof AbstractAsset) {
275
-				return preg_match($filterExpression, $asset->getName()) !== false;
276
-			}
277
-			return preg_match($filterExpression, $asset) !== false;
278
-		});
279
-		return $db->createSchemaManager()->listTableNames();
280
-	}
281
-
282
-	/**
283
-	 * @param Connection $fromDB
284
-	 * @param Connection $toDB
285
-	 * @param Table $table
286
-	 * @param InputInterface $input
287
-	 * @param OutputInterface $output
288
-	 */
289
-	protected function copyTable(Connection $fromDB, Connection $toDB, Table $table, InputInterface $input, OutputInterface $output) {
290
-		if ($table->getName() === $toDB->getPrefix() . 'migrations') {
291
-			$output->writeln('<comment>Skipping migrations table because it was already filled by running the migrations</comment>');
292
-			return;
293
-		}
294
-
295
-		$chunkSize = (int)$input->getOption('chunk-size');
296
-
297
-		$query = $fromDB->getQueryBuilder();
298
-		$query->automaticTablePrefix(false);
299
-		$query->select($query->func()->count('*', 'num_entries'))
300
-			->from($table->getName());
301
-		$result = $query->executeQuery();
302
-		$count = $result->fetchOne();
303
-		$result->closeCursor();
304
-
305
-		$numChunks = ceil($count / $chunkSize);
306
-		if ($numChunks > 1) {
307
-			$output->writeln('chunked query, ' . $numChunks . ' chunks');
308
-		}
309
-
310
-		$progress = new ProgressBar($output, $count);
311
-		$progress->setFormat('very_verbose');
312
-		$progress->start();
313
-		$redraw = $count > $chunkSize ? 100 : ($count > 100 ? 5 : 1);
314
-		$progress->setRedrawFrequency($redraw);
315
-
316
-		$query = $fromDB->getQueryBuilder();
317
-		$query->automaticTablePrefix(false);
318
-		$query->select('*')
319
-			->from($table->getName())
320
-			->setMaxResults($chunkSize);
321
-
322
-		try {
323
-			$orderColumns = $table->getPrimaryKeyColumns();
324
-		} catch (Exception $e) {
325
-			$orderColumns = $table->getColumns();
326
-		}
327
-
328
-		foreach ($orderColumns as $column) {
329
-			$query->addOrderBy($column->getName());
330
-		}
331
-
332
-		$insertQuery = $toDB->getQueryBuilder();
333
-		$insertQuery->automaticTablePrefix(false);
334
-		$insertQuery->insert($table->getName());
335
-		$parametersCreated = false;
336
-
337
-		for ($chunk = 0; $chunk < $numChunks; $chunk++) {
338
-			$query->setFirstResult($chunk * $chunkSize);
339
-
340
-			$result = $query->executeQuery();
341
-
342
-			try {
343
-				$toDB->beginTransaction();
344
-
345
-				foreach ($result->iterateAssociative() as $row) {
346
-					$progress->advance();
347
-					if (!$parametersCreated) {
348
-						foreach ($row as $key => $value) {
349
-							$insertQuery->setValue($key, $insertQuery->createParameter($key));
350
-						}
351
-						$parametersCreated = true;
352
-					}
353
-
354
-					foreach ($row as $key => $value) {
355
-						$type = $this->getColumnType($table, $key);
356
-						if ($type !== false) {
357
-							$insertQuery->setParameter($key, $value, $type);
358
-						} else {
359
-							$insertQuery->setParameter($key, $value);
360
-						}
361
-					}
362
-					$insertQuery->executeStatement();
363
-				}
364
-				$result->closeCursor();
365
-
366
-				$toDB->commit();
367
-			} catch (\Throwable $e) {
368
-				$toDB->rollBack();
369
-				throw $e;
370
-			}
371
-		}
372
-
373
-		$progress->finish();
374
-		$output->writeln('');
375
-	}
376
-
377
-	protected function getColumnType(Table $table, $columnName) {
378
-		$tableName = $table->getName();
379
-		if (isset($this->columnTypes[$tableName][$columnName])) {
380
-			return $this->columnTypes[$tableName][$columnName];
381
-		}
382
-
383
-		$type = Type::lookupName($table->getColumn($columnName)->getType());
384
-
385
-		switch ($type) {
386
-			case Types::BLOB:
387
-			case Types::TEXT:
388
-				$this->columnTypes[$tableName][$columnName] = IQueryBuilder::PARAM_LOB;
389
-				break;
390
-			case Types::BOOLEAN:
391
-				$this->columnTypes[$tableName][$columnName] = IQueryBuilder::PARAM_BOOL;
392
-				break;
393
-			default:
394
-				$this->columnTypes[$tableName][$columnName] = false;
395
-		}
396
-
397
-		return $this->columnTypes[$tableName][$columnName];
398
-	}
399
-
400
-	protected function convertDB(Connection $fromDB, Connection $toDB, array $tables, InputInterface $input, OutputInterface $output) {
401
-		$this->config->setSystemValue('maintenance', true);
402
-		$schema = $fromDB->createSchema();
403
-
404
-		try {
405
-			// copy table rows
406
-			foreach ($tables as $table) {
407
-				$output->writeln('<info> - ' . $table . '</info>');
408
-				$this->copyTable($fromDB, $toDB, $schema->getTable($table), $input, $output);
409
-			}
410
-			if ($input->getArgument('type') === 'pgsql') {
411
-				$tools = new PgSqlTools($this->config);
412
-				$tools->resynchronizeDatabaseSequences($toDB);
413
-			}
414
-			// save new database config
415
-			$this->saveDBInfo($input);
416
-		} catch (\Exception $e) {
417
-			$this->config->setSystemValue('maintenance', false);
418
-			throw $e;
419
-		}
420
-		$this->config->setSystemValue('maintenance', false);
421
-	}
422
-
423
-	protected function saveDBInfo(InputInterface $input) {
424
-		$type = $input->getArgument('type');
425
-		$username = $input->getArgument('username');
426
-		$dbHost = $input->getArgument('hostname');
427
-		$dbName = $input->getArgument('database');
428
-		$password = $input->getOption('password');
429
-		if ($input->getOption('port')) {
430
-			$dbHost .= ':' . $input->getOption('port');
431
-		}
432
-
433
-		$this->config->setSystemValues([
434
-			'dbtype' => $type,
435
-			'dbname' => $dbName,
436
-			'dbhost' => $dbHost,
437
-			'dbuser' => $username,
438
-			'dbpassword' => $password,
439
-		]);
440
-	}
441
-
442
-	/**
443
-	 * Return possible values for the named option
444
-	 *
445
-	 * @param string $optionName
446
-	 * @param CompletionContext $context
447
-	 * @return string[]
448
-	 */
449
-	public function completeOptionValues($optionName, CompletionContext $context) {
450
-		return [];
451
-	}
452
-
453
-	/**
454
-	 * Return possible values for the named argument
455
-	 *
456
-	 * @param string $argumentName
457
-	 * @param CompletionContext $context
458
-	 * @return string[]
459
-	 */
460
-	public function completeArgumentValues($argumentName, CompletionContext $context) {
461
-		if ($argumentName === 'type') {
462
-			return ['mysql', 'oci', 'pgsql'];
463
-		}
464
-		return [];
465
-	}
38
+    protected array $columnTypes = [];
39
+
40
+    public function __construct(
41
+        protected IConfig $config,
42
+        protected ConnectionFactory $connectionFactory,
43
+        protected IAppManager $appManager,
44
+    ) {
45
+        parent::__construct();
46
+    }
47
+
48
+    protected function configure() {
49
+        $this
50
+            ->setName('db:convert-type')
51
+            ->setDescription('Convert the Nextcloud database to the newly configured one')
52
+            ->addArgument(
53
+                'type',
54
+                InputArgument::REQUIRED,
55
+                'the type of the database to convert to'
56
+            )
57
+            ->addArgument(
58
+                'username',
59
+                InputArgument::REQUIRED,
60
+                'the username of the database to convert to'
61
+            )
62
+            ->addArgument(
63
+                'hostname',
64
+                InputArgument::REQUIRED,
65
+                'the hostname of the database to convert to'
66
+            )
67
+            ->addArgument(
68
+                'database',
69
+                InputArgument::REQUIRED,
70
+                'the name of the database to convert to'
71
+            )
72
+            ->addOption(
73
+                'port',
74
+                null,
75
+                InputOption::VALUE_REQUIRED,
76
+                'the port of the database to convert to'
77
+            )
78
+            ->addOption(
79
+                'password',
80
+                null,
81
+                InputOption::VALUE_REQUIRED,
82
+                'the password of the database to convert to. Will be asked when not specified. Can also be passed via stdin.'
83
+            )
84
+            ->addOption(
85
+                'clear-schema',
86
+                null,
87
+                InputOption::VALUE_NONE,
88
+                'remove all tables from the destination database'
89
+            )
90
+            ->addOption(
91
+                'all-apps',
92
+                null,
93
+                InputOption::VALUE_NONE,
94
+                'whether to create schema for all apps instead of only installed apps'
95
+            )
96
+            ->addOption(
97
+                'chunk-size',
98
+                null,
99
+                InputOption::VALUE_REQUIRED,
100
+                'the maximum number of database rows to handle in a single query, bigger tables will be handled in chunks of this size. Lower this if the process runs out of memory during conversion.',
101
+                '1000'
102
+            )
103
+        ;
104
+    }
105
+
106
+    protected function validateInput(InputInterface $input, OutputInterface $output) {
107
+        $type = $this->connectionFactory->normalizeType($input->getArgument('type'));
108
+        if ($type === 'sqlite3') {
109
+            throw new \InvalidArgumentException(
110
+                'Converting to SQLite (sqlite3) is currently not supported.'
111
+            );
112
+        }
113
+        if ($type === $this->config->getSystemValue('dbtype', '')) {
114
+            throw new \InvalidArgumentException(sprintf(
115
+                'Can not convert from %1$s to %1$s.',
116
+                $type
117
+            ));
118
+        }
119
+        if ($type === 'oci' && $input->getOption('clear-schema')) {
120
+            // Doctrine unconditionally tries (at least in version 2.3)
121
+            // to drop sequence triggers when dropping a table, even though
122
+            // such triggers may not exist. This results in errors like
123
+            // "ORA-04080: trigger 'OC_STORAGES_AI_PK' does not exist".
124
+            throw new \InvalidArgumentException(
125
+                'The --clear-schema option is not supported when converting to Oracle (oci).'
126
+            );
127
+        }
128
+    }
129
+
130
+    protected function readPassword(InputInterface $input, OutputInterface $output) {
131
+        // Explicitly specified password
132
+        if ($input->getOption('password')) {
133
+            return;
134
+        }
135
+
136
+        // Read from stdin. stream_set_blocking is used to prevent blocking
137
+        // when nothing is passed via stdin.
138
+        stream_set_blocking(STDIN, 0);
139
+        $password = file_get_contents('php://stdin');
140
+        stream_set_blocking(STDIN, 1);
141
+        if (trim($password) !== '') {
142
+            $input->setOption('password', $password);
143
+            return;
144
+        }
145
+
146
+        // Read password by interacting
147
+        if ($input->isInteractive()) {
148
+            /** @var QuestionHelper $helper */
149
+            $helper = $this->getHelper('question');
150
+            $question = new Question('What is the database password (press <enter> for none)? ');
151
+            $question->setHidden(true);
152
+            $question->setHiddenFallback(false);
153
+            $password = $helper->ask($input, $output, $question);
154
+            if ($password === null) {
155
+                $password = ''; // possibly unnecessary
156
+            }
157
+            $input->setOption('password', $password);
158
+            return;
159
+        }
160
+    }
161
+
162
+    protected function execute(InputInterface $input, OutputInterface $output): int {
163
+        $this->validateInput($input, $output);
164
+        $this->readPassword($input, $output);
165
+
166
+        /** @var Connection $fromDB */
167
+        $fromDB = Server::get(Connection::class);
168
+        $toDB = $this->getToDBConnection($input, $output);
169
+
170
+        if ($input->getOption('clear-schema')) {
171
+            $this->clearSchema($toDB, $input, $output);
172
+        }
173
+
174
+        $this->createSchema($fromDB, $toDB, $input, $output);
175
+
176
+        $toTables = $this->getTables($toDB);
177
+        $fromTables = $this->getTables($fromDB);
178
+
179
+        // warn/fail if there are more tables in 'from' database
180
+        $extraFromTables = array_diff($fromTables, $toTables);
181
+        if (!empty($extraFromTables)) {
182
+            $output->writeln('<comment>The following tables will not be converted:</comment>');
183
+            $output->writeln($extraFromTables);
184
+            if (!$input->getOption('all-apps')) {
185
+                $output->writeln('<comment>Please note that tables belonging to disabled (but not removed) apps</comment>');
186
+                $output->writeln('<comment>can be included by specifying the --all-apps option.</comment>');
187
+            }
188
+
189
+            $continueConversion = !$input->isInteractive(); // assume yes for --no-interaction and no otherwise.
190
+            $question = new ConfirmationQuestion('Continue with the conversion (y/n)? [n] ', $continueConversion);
191
+
192
+            /** @var QuestionHelper $helper */
193
+            $helper = $this->getHelper('question');
194
+
195
+            if (!$helper->ask($input, $output, $question)) {
196
+                return 1;
197
+            }
198
+        }
199
+        $intersectingTables = array_intersect($toTables, $fromTables);
200
+        $this->convertDB($fromDB, $toDB, $intersectingTables, $input, $output);
201
+        return 0;
202
+    }
203
+
204
+    protected function createSchema(Connection $fromDB, Connection $toDB, InputInterface $input, OutputInterface $output) {
205
+        $output->writeln('<info>Creating schema in new database</info>');
206
+
207
+        $fromMS = new MigrationService('core', $fromDB);
208
+        $currentMigration = $fromMS->getMigration('current');
209
+        if ($currentMigration !== '0') {
210
+            $toMS = new MigrationService('core', $toDB);
211
+            $toMS->migrate($currentMigration);
212
+        }
213
+
214
+        $apps = $input->getOption('all-apps')
215
+            ? $this->appManager->getAllAppsInAppsFolders()
216
+            : $this->appManager->getEnabledApps();
217
+        foreach ($apps as $app) {
218
+            $output->writeln('<info> - ' . $app . '</info>');
219
+            // Make sure autoloading works...
220
+            $this->appManager->loadApp($app);
221
+            $fromMS = new MigrationService($app, $fromDB);
222
+            $currentMigration = $fromMS->getMigration('current');
223
+            if ($currentMigration !== '0') {
224
+                $toMS = new MigrationService($app, $toDB);
225
+                $toMS->migrate($currentMigration, true);
226
+            }
227
+        }
228
+    }
229
+
230
+    protected function getToDBConnection(InputInterface $input, OutputInterface $output) {
231
+        $type = $input->getArgument('type');
232
+        $connectionParams = $this->connectionFactory->createConnectionParams(type: $type);
233
+        $connectionParams = array_merge($connectionParams, [
234
+            'host' => $input->getArgument('hostname'),
235
+            'user' => $input->getArgument('username'),
236
+            'password' => $input->getOption('password'),
237
+            'dbname' => $input->getArgument('database'),
238
+        ]);
239
+
240
+        // parse port
241
+        if ($input->getOption('port')) {
242
+            $connectionParams['port'] = $input->getOption('port');
243
+        }
244
+
245
+        // parse hostname for unix socket
246
+        if (preg_match('/^(.+)(:(\d+|[^:]+))?$/', $input->getArgument('hostname'), $matches)) {
247
+            $connectionParams['host'] = $matches[1];
248
+            if (isset($matches[3])) {
249
+                if (is_numeric($matches[3])) {
250
+                    $connectionParams['port'] = $matches[3];
251
+                } else {
252
+                    $connectionParams['unix_socket'] = $matches[3];
253
+                }
254
+            }
255
+        }
256
+
257
+        return $this->connectionFactory->getConnection($type, $connectionParams);
258
+    }
259
+
260
+    protected function clearSchema(Connection $db, InputInterface $input, OutputInterface $output) {
261
+        $toTables = $this->getTables($db);
262
+        if (!empty($toTables)) {
263
+            $output->writeln('<info>Clearing schema in new database</info>');
264
+        }
265
+        foreach ($toTables as $table) {
266
+            $db->createSchemaManager()->dropTable($table);
267
+        }
268
+    }
269
+
270
+    protected function getTables(Connection $db) {
271
+        $db->getConfiguration()->setSchemaAssetsFilter(function ($asset) {
272
+            /** @var string|AbstractAsset $asset */
273
+            $filterExpression = '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
274
+            if ($asset instanceof AbstractAsset) {
275
+                return preg_match($filterExpression, $asset->getName()) !== false;
276
+            }
277
+            return preg_match($filterExpression, $asset) !== false;
278
+        });
279
+        return $db->createSchemaManager()->listTableNames();
280
+    }
281
+
282
+    /**
283
+     * @param Connection $fromDB
284
+     * @param Connection $toDB
285
+     * @param Table $table
286
+     * @param InputInterface $input
287
+     * @param OutputInterface $output
288
+     */
289
+    protected function copyTable(Connection $fromDB, Connection $toDB, Table $table, InputInterface $input, OutputInterface $output) {
290
+        if ($table->getName() === $toDB->getPrefix() . 'migrations') {
291
+            $output->writeln('<comment>Skipping migrations table because it was already filled by running the migrations</comment>');
292
+            return;
293
+        }
294
+
295
+        $chunkSize = (int)$input->getOption('chunk-size');
296
+
297
+        $query = $fromDB->getQueryBuilder();
298
+        $query->automaticTablePrefix(false);
299
+        $query->select($query->func()->count('*', 'num_entries'))
300
+            ->from($table->getName());
301
+        $result = $query->executeQuery();
302
+        $count = $result->fetchOne();
303
+        $result->closeCursor();
304
+
305
+        $numChunks = ceil($count / $chunkSize);
306
+        if ($numChunks > 1) {
307
+            $output->writeln('chunked query, ' . $numChunks . ' chunks');
308
+        }
309
+
310
+        $progress = new ProgressBar($output, $count);
311
+        $progress->setFormat('very_verbose');
312
+        $progress->start();
313
+        $redraw = $count > $chunkSize ? 100 : ($count > 100 ? 5 : 1);
314
+        $progress->setRedrawFrequency($redraw);
315
+
316
+        $query = $fromDB->getQueryBuilder();
317
+        $query->automaticTablePrefix(false);
318
+        $query->select('*')
319
+            ->from($table->getName())
320
+            ->setMaxResults($chunkSize);
321
+
322
+        try {
323
+            $orderColumns = $table->getPrimaryKeyColumns();
324
+        } catch (Exception $e) {
325
+            $orderColumns = $table->getColumns();
326
+        }
327
+
328
+        foreach ($orderColumns as $column) {
329
+            $query->addOrderBy($column->getName());
330
+        }
331
+
332
+        $insertQuery = $toDB->getQueryBuilder();
333
+        $insertQuery->automaticTablePrefix(false);
334
+        $insertQuery->insert($table->getName());
335
+        $parametersCreated = false;
336
+
337
+        for ($chunk = 0; $chunk < $numChunks; $chunk++) {
338
+            $query->setFirstResult($chunk * $chunkSize);
339
+
340
+            $result = $query->executeQuery();
341
+
342
+            try {
343
+                $toDB->beginTransaction();
344
+
345
+                foreach ($result->iterateAssociative() as $row) {
346
+                    $progress->advance();
347
+                    if (!$parametersCreated) {
348
+                        foreach ($row as $key => $value) {
349
+                            $insertQuery->setValue($key, $insertQuery->createParameter($key));
350
+                        }
351
+                        $parametersCreated = true;
352
+                    }
353
+
354
+                    foreach ($row as $key => $value) {
355
+                        $type = $this->getColumnType($table, $key);
356
+                        if ($type !== false) {
357
+                            $insertQuery->setParameter($key, $value, $type);
358
+                        } else {
359
+                            $insertQuery->setParameter($key, $value);
360
+                        }
361
+                    }
362
+                    $insertQuery->executeStatement();
363
+                }
364
+                $result->closeCursor();
365
+
366
+                $toDB->commit();
367
+            } catch (\Throwable $e) {
368
+                $toDB->rollBack();
369
+                throw $e;
370
+            }
371
+        }
372
+
373
+        $progress->finish();
374
+        $output->writeln('');
375
+    }
376
+
377
+    protected function getColumnType(Table $table, $columnName) {
378
+        $tableName = $table->getName();
379
+        if (isset($this->columnTypes[$tableName][$columnName])) {
380
+            return $this->columnTypes[$tableName][$columnName];
381
+        }
382
+
383
+        $type = Type::lookupName($table->getColumn($columnName)->getType());
384
+
385
+        switch ($type) {
386
+            case Types::BLOB:
387
+            case Types::TEXT:
388
+                $this->columnTypes[$tableName][$columnName] = IQueryBuilder::PARAM_LOB;
389
+                break;
390
+            case Types::BOOLEAN:
391
+                $this->columnTypes[$tableName][$columnName] = IQueryBuilder::PARAM_BOOL;
392
+                break;
393
+            default:
394
+                $this->columnTypes[$tableName][$columnName] = false;
395
+        }
396
+
397
+        return $this->columnTypes[$tableName][$columnName];
398
+    }
399
+
400
+    protected function convertDB(Connection $fromDB, Connection $toDB, array $tables, InputInterface $input, OutputInterface $output) {
401
+        $this->config->setSystemValue('maintenance', true);
402
+        $schema = $fromDB->createSchema();
403
+
404
+        try {
405
+            // copy table rows
406
+            foreach ($tables as $table) {
407
+                $output->writeln('<info> - ' . $table . '</info>');
408
+                $this->copyTable($fromDB, $toDB, $schema->getTable($table), $input, $output);
409
+            }
410
+            if ($input->getArgument('type') === 'pgsql') {
411
+                $tools = new PgSqlTools($this->config);
412
+                $tools->resynchronizeDatabaseSequences($toDB);
413
+            }
414
+            // save new database config
415
+            $this->saveDBInfo($input);
416
+        } catch (\Exception $e) {
417
+            $this->config->setSystemValue('maintenance', false);
418
+            throw $e;
419
+        }
420
+        $this->config->setSystemValue('maintenance', false);
421
+    }
422
+
423
+    protected function saveDBInfo(InputInterface $input) {
424
+        $type = $input->getArgument('type');
425
+        $username = $input->getArgument('username');
426
+        $dbHost = $input->getArgument('hostname');
427
+        $dbName = $input->getArgument('database');
428
+        $password = $input->getOption('password');
429
+        if ($input->getOption('port')) {
430
+            $dbHost .= ':' . $input->getOption('port');
431
+        }
432
+
433
+        $this->config->setSystemValues([
434
+            'dbtype' => $type,
435
+            'dbname' => $dbName,
436
+            'dbhost' => $dbHost,
437
+            'dbuser' => $username,
438
+            'dbpassword' => $password,
439
+        ]);
440
+    }
441
+
442
+    /**
443
+     * Return possible values for the named option
444
+     *
445
+     * @param string $optionName
446
+     * @param CompletionContext $context
447
+     * @return string[]
448
+     */
449
+    public function completeOptionValues($optionName, CompletionContext $context) {
450
+        return [];
451
+    }
452
+
453
+    /**
454
+     * Return possible values for the named argument
455
+     *
456
+     * @param string $argumentName
457
+     * @param CompletionContext $context
458
+     * @return string[]
459
+     */
460
+    public function completeArgumentValues($argumentName, CompletionContext $context) {
461
+        if ($argumentName === 'type') {
462
+            return ['mysql', 'oci', 'pgsql'];
463
+        }
464
+        return [];
465
+    }
466 466
 }
Please login to merge, or discard this patch.
core/BackgroundJobs/MovePreviewJob.php 2 patches
Indentation   +212 added lines, -212 removed lines patch added patch discarded remove patch
@@ -31,216 +31,216 @@
 block discarded – undo
31 31
 use Psr\Log\LoggerInterface;
32 32
 
33 33
 class MovePreviewJob extends TimedJob {
34
-	private IAppData $appData;
35
-	private string $previewRootPath;
36
-
37
-	public function __construct(
38
-		ITimeFactory $time,
39
-		private readonly IAppConfig $appConfig,
40
-		private readonly IConfig $config,
41
-		private readonly PreviewMapper $previewMapper,
42
-		private readonly StorageFactory $storageFactory,
43
-		private readonly IDBConnection $connection,
44
-		private readonly IRootFolder $rootFolder,
45
-		private readonly IMimeTypeDetector $mimeTypeDetector,
46
-		private readonly IMimeTypeLoader $mimeTypeLoader,
47
-		private readonly LoggerInterface $logger,
48
-		private readonly IGenerator $generator,
49
-		IAppDataFactory $appDataFactory,
50
-	) {
51
-		parent::__construct($time);
52
-
53
-		$this->appData = $appDataFactory->get('preview');
54
-		$this->setTimeSensitivity(self::TIME_INSENSITIVE);
55
-		$this->setInterval(24 * 60 * 60);
56
-		$this->previewRootPath = 'appdata_' . $this->config->getSystemValueString('instanceid') . '/preview/';
57
-	}
58
-
59
-	#[Override]
60
-	protected function run(mixed $argument): void {
61
-		if ($this->appConfig->getValueBool('core', 'previewMovedDone')) {
62
-			return;
63
-		}
64
-
65
-		$startTime = time();
66
-		while (true) {
67
-			$qb = $this->connection->getQueryBuilder();
68
-			$qb->select('path')
69
-				->from('filecache')
70
-				// Hierarchical preview folder structure
71
-				->where($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%/%/%/%/%/%/%/%')))
72
-				// Legacy flat preview folder structure
73
-				->orWhere($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%.%')))
74
-				->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
75
-				->setMaxResults(100);
76
-
77
-			$result = $qb->executeQuery();
78
-			$foundPreviews = $this->processQueryResult($result);
79
-
80
-			if (!$foundPreviews) {
81
-				break;
82
-			}
83
-
84
-			// Stop if execution time is more than one hour.
85
-			if (time() - $startTime > 3600) {
86
-				return;
87
-			}
88
-		}
89
-
90
-		$this->appConfig->setValueBool('core', 'previewMovedDone', true);
91
-	}
92
-
93
-	private function processQueryResult(IResult $result): bool {
94
-		$foundPreview = false;
95
-		$fileIds = [];
96
-		$flatFileIds = [];
97
-		while ($row = $result->fetchAssociative()) {
98
-			$pathSplit = explode('/', $row['path']);
99
-			assert(count($pathSplit) >= 2);
100
-			$fileId = (int)$pathSplit[count($pathSplit) - 2];
101
-			if (count($pathSplit) === 11) {
102
-				// Hierarchical structure
103
-				if (!in_array($fileId, $fileIds)) {
104
-					$fileIds[] = $fileId;
105
-				}
106
-			} else {
107
-				// Flat structure
108
-				if (!in_array($fileId, $flatFileIds)) {
109
-					$flatFileIds[] = $fileId;
110
-				}
111
-			}
112
-			$foundPreview = true;
113
-		}
114
-
115
-		foreach ($fileIds as $fileId) {
116
-			$this->processPreviews($fileId, flatPath: false);
117
-		}
118
-
119
-		foreach ($flatFileIds as $fileId) {
120
-			$this->processPreviews($fileId, flatPath: true);
121
-		}
122
-		return $foundPreview;
123
-	}
124
-
125
-	/**
126
-	 * @param array<string|int, string[]> $previewFolders
127
-	 */
128
-	private function processPreviews(int $fileId, bool $flatPath): void {
129
-		$internalPath = $this->getInternalFolder((string)$fileId, $flatPath);
130
-		$folder = $this->appData->getFolder($internalPath);
131
-
132
-		/**
133
-		 * @var list<array{file: SimpleFile, preview: Preview}> $previewFiles
134
-		 */
135
-		$previewFiles = [];
136
-
137
-		foreach ($folder->getDirectoryListing() as $previewFile) {
138
-			$path = $fileId . '/' . $previewFile->getName();
139
-			/** @var SimpleFile $previewFile */
140
-			$preview = Preview::fromPath($path, $this->mimeTypeDetector);
141
-			$preview->setId($this->generator->nextId());
142
-			if (!$preview) {
143
-				$this->logger->error('Unable to import old preview at path.');
144
-				continue;
145
-			}
146
-			$preview->setSize($previewFile->getSize());
147
-			$preview->setMtime($previewFile->getMtime());
148
-			$preview->setOldFileId($previewFile->getId());
149
-			$preview->setEncrypted(false);
150
-
151
-			$previewFiles[] = [
152
-				'file' => $previewFile,
153
-				'preview' => $preview,
154
-			];
155
-		}
156
-
157
-		$qb = $this->connection->getQueryBuilder();
158
-		$qb->select('storage', 'etag', 'mimetype')
159
-			->from('filecache')
160
-			->where($qb->expr()->eq('fileid', $qb->createNamedParameter($fileId)))
161
-			->setMaxResults(1);
162
-
163
-		$result = $qb->executeQuery();
164
-		$result = $result->fetchAllAssociative();
165
-
166
-		if (count($result) > 0) {
167
-			foreach ($previewFiles as $previewFile) {
168
-				/** @var Preview $preview */
169
-				$preview = $previewFile['preview'];
170
-				/** @var SimpleFile $file */
171
-				$file = $previewFile['file'];
172
-				$preview->setStorageId($result[0]['storage']);
173
-				$preview->setEtag($result[0]['etag']);
174
-				$preview->setSourceMimeType($this->mimeTypeLoader->getMimetypeById((int)$result[0]['mimetype']));
175
-				try {
176
-					$preview = $this->previewMapper->insert($preview);
177
-				} catch (Exception) {
178
-					// We already have this preview in the preview table, skip
179
-					$qb->delete('filecache')
180
-						->where($qb->expr()->eq('fileid', $qb->createNamedParameter($file->getId())))
181
-						->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
182
-						->executeStatement();
183
-					continue;
184
-				}
185
-
186
-				try {
187
-					$this->storageFactory->migratePreview($preview, $file);
188
-					$qb = $this->connection->getQueryBuilder();
189
-					$qb->delete('filecache')
190
-						->where($qb->expr()->eq('fileid', $qb->createNamedParameter($file->getId())))
191
-						->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
192
-						->executeStatement();
193
-					// Do not call $file->delete() as this will also delete the file from the file system
194
-				} catch (\Exception $e) {
195
-					$this->previewMapper->delete($preview);
196
-					throw $e;
197
-				}
198
-			}
199
-		} else {
200
-			// No matching fileId, delete preview
201
-			try {
202
-				$this->connection->beginTransaction();
203
-				foreach ($previewFiles as $previewFile) {
204
-					/** @var SimpleFile $file */
205
-					$file = $previewFile['file'];
206
-					$file->delete();
207
-				}
208
-				$this->connection->commit();
209
-			} catch (Exception) {
210
-				$this->connection->rollback();
211
-			}
212
-		}
213
-
214
-		$this->deleteFolder($internalPath);
215
-	}
216
-
217
-	public static function getInternalFolder(string $name, bool $flatPath): string {
218
-		if ($flatPath) {
219
-			return $name;
220
-		}
221
-		return implode('/', str_split(substr(md5($name), 0, 7))) . '/' . $name;
222
-	}
223
-
224
-	private function deleteFolder(string $path): void {
225
-		$current = $path;
226
-
227
-		while (true) {
228
-			$appDataPath = $this->previewRootPath . $current;
229
-			$qb = $this->connection->getQueryBuilder();
230
-			$qb->delete('filecache')
231
-				->where($qb->expr()->eq('path_hash', $qb->createNamedParameter(md5($appDataPath))))
232
-				->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
233
-				->executeStatement();
234
-
235
-			$current = dirname($current);
236
-			if ($current === '/' || $current === '.' || $current === '') {
237
-				break;
238
-			}
239
-
240
-			$folder = $this->appData->getFolder($current);
241
-			if (count($folder->getDirectoryListing()) !== 0) {
242
-				break;
243
-			}
244
-		}
245
-	}
34
+    private IAppData $appData;
35
+    private string $previewRootPath;
36
+
37
+    public function __construct(
38
+        ITimeFactory $time,
39
+        private readonly IAppConfig $appConfig,
40
+        private readonly IConfig $config,
41
+        private readonly PreviewMapper $previewMapper,
42
+        private readonly StorageFactory $storageFactory,
43
+        private readonly IDBConnection $connection,
44
+        private readonly IRootFolder $rootFolder,
45
+        private readonly IMimeTypeDetector $mimeTypeDetector,
46
+        private readonly IMimeTypeLoader $mimeTypeLoader,
47
+        private readonly LoggerInterface $logger,
48
+        private readonly IGenerator $generator,
49
+        IAppDataFactory $appDataFactory,
50
+    ) {
51
+        parent::__construct($time);
52
+
53
+        $this->appData = $appDataFactory->get('preview');
54
+        $this->setTimeSensitivity(self::TIME_INSENSITIVE);
55
+        $this->setInterval(24 * 60 * 60);
56
+        $this->previewRootPath = 'appdata_' . $this->config->getSystemValueString('instanceid') . '/preview/';
57
+    }
58
+
59
+    #[Override]
60
+    protected function run(mixed $argument): void {
61
+        if ($this->appConfig->getValueBool('core', 'previewMovedDone')) {
62
+            return;
63
+        }
64
+
65
+        $startTime = time();
66
+        while (true) {
67
+            $qb = $this->connection->getQueryBuilder();
68
+            $qb->select('path')
69
+                ->from('filecache')
70
+                // Hierarchical preview folder structure
71
+                ->where($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%/%/%/%/%/%/%/%')))
72
+                // Legacy flat preview folder structure
73
+                ->orWhere($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%.%')))
74
+                ->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
75
+                ->setMaxResults(100);
76
+
77
+            $result = $qb->executeQuery();
78
+            $foundPreviews = $this->processQueryResult($result);
79
+
80
+            if (!$foundPreviews) {
81
+                break;
82
+            }
83
+
84
+            // Stop if execution time is more than one hour.
85
+            if (time() - $startTime > 3600) {
86
+                return;
87
+            }
88
+        }
89
+
90
+        $this->appConfig->setValueBool('core', 'previewMovedDone', true);
91
+    }
92
+
93
+    private function processQueryResult(IResult $result): bool {
94
+        $foundPreview = false;
95
+        $fileIds = [];
96
+        $flatFileIds = [];
97
+        while ($row = $result->fetchAssociative()) {
98
+            $pathSplit = explode('/', $row['path']);
99
+            assert(count($pathSplit) >= 2);
100
+            $fileId = (int)$pathSplit[count($pathSplit) - 2];
101
+            if (count($pathSplit) === 11) {
102
+                // Hierarchical structure
103
+                if (!in_array($fileId, $fileIds)) {
104
+                    $fileIds[] = $fileId;
105
+                }
106
+            } else {
107
+                // Flat structure
108
+                if (!in_array($fileId, $flatFileIds)) {
109
+                    $flatFileIds[] = $fileId;
110
+                }
111
+            }
112
+            $foundPreview = true;
113
+        }
114
+
115
+        foreach ($fileIds as $fileId) {
116
+            $this->processPreviews($fileId, flatPath: false);
117
+        }
118
+
119
+        foreach ($flatFileIds as $fileId) {
120
+            $this->processPreviews($fileId, flatPath: true);
121
+        }
122
+        return $foundPreview;
123
+    }
124
+
125
+    /**
126
+     * @param array<string|int, string[]> $previewFolders
127
+     */
128
+    private function processPreviews(int $fileId, bool $flatPath): void {
129
+        $internalPath = $this->getInternalFolder((string)$fileId, $flatPath);
130
+        $folder = $this->appData->getFolder($internalPath);
131
+
132
+        /**
133
+         * @var list<array{file: SimpleFile, preview: Preview}> $previewFiles
134
+         */
135
+        $previewFiles = [];
136
+
137
+        foreach ($folder->getDirectoryListing() as $previewFile) {
138
+            $path = $fileId . '/' . $previewFile->getName();
139
+            /** @var SimpleFile $previewFile */
140
+            $preview = Preview::fromPath($path, $this->mimeTypeDetector);
141
+            $preview->setId($this->generator->nextId());
142
+            if (!$preview) {
143
+                $this->logger->error('Unable to import old preview at path.');
144
+                continue;
145
+            }
146
+            $preview->setSize($previewFile->getSize());
147
+            $preview->setMtime($previewFile->getMtime());
148
+            $preview->setOldFileId($previewFile->getId());
149
+            $preview->setEncrypted(false);
150
+
151
+            $previewFiles[] = [
152
+                'file' => $previewFile,
153
+                'preview' => $preview,
154
+            ];
155
+        }
156
+
157
+        $qb = $this->connection->getQueryBuilder();
158
+        $qb->select('storage', 'etag', 'mimetype')
159
+            ->from('filecache')
160
+            ->where($qb->expr()->eq('fileid', $qb->createNamedParameter($fileId)))
161
+            ->setMaxResults(1);
162
+
163
+        $result = $qb->executeQuery();
164
+        $result = $result->fetchAllAssociative();
165
+
166
+        if (count($result) > 0) {
167
+            foreach ($previewFiles as $previewFile) {
168
+                /** @var Preview $preview */
169
+                $preview = $previewFile['preview'];
170
+                /** @var SimpleFile $file */
171
+                $file = $previewFile['file'];
172
+                $preview->setStorageId($result[0]['storage']);
173
+                $preview->setEtag($result[0]['etag']);
174
+                $preview->setSourceMimeType($this->mimeTypeLoader->getMimetypeById((int)$result[0]['mimetype']));
175
+                try {
176
+                    $preview = $this->previewMapper->insert($preview);
177
+                } catch (Exception) {
178
+                    // We already have this preview in the preview table, skip
179
+                    $qb->delete('filecache')
180
+                        ->where($qb->expr()->eq('fileid', $qb->createNamedParameter($file->getId())))
181
+                        ->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
182
+                        ->executeStatement();
183
+                    continue;
184
+                }
185
+
186
+                try {
187
+                    $this->storageFactory->migratePreview($preview, $file);
188
+                    $qb = $this->connection->getQueryBuilder();
189
+                    $qb->delete('filecache')
190
+                        ->where($qb->expr()->eq('fileid', $qb->createNamedParameter($file->getId())))
191
+                        ->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
192
+                        ->executeStatement();
193
+                    // Do not call $file->delete() as this will also delete the file from the file system
194
+                } catch (\Exception $e) {
195
+                    $this->previewMapper->delete($preview);
196
+                    throw $e;
197
+                }
198
+            }
199
+        } else {
200
+            // No matching fileId, delete preview
201
+            try {
202
+                $this->connection->beginTransaction();
203
+                foreach ($previewFiles as $previewFile) {
204
+                    /** @var SimpleFile $file */
205
+                    $file = $previewFile['file'];
206
+                    $file->delete();
207
+                }
208
+                $this->connection->commit();
209
+            } catch (Exception) {
210
+                $this->connection->rollback();
211
+            }
212
+        }
213
+
214
+        $this->deleteFolder($internalPath);
215
+    }
216
+
217
+    public static function getInternalFolder(string $name, bool $flatPath): string {
218
+        if ($flatPath) {
219
+            return $name;
220
+        }
221
+        return implode('/', str_split(substr(md5($name), 0, 7))) . '/' . $name;
222
+    }
223
+
224
+    private function deleteFolder(string $path): void {
225
+        $current = $path;
226
+
227
+        while (true) {
228
+            $appDataPath = $this->previewRootPath . $current;
229
+            $qb = $this->connection->getQueryBuilder();
230
+            $qb->delete('filecache')
231
+                ->where($qb->expr()->eq('path_hash', $qb->createNamedParameter(md5($appDataPath))))
232
+                ->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
233
+                ->executeStatement();
234
+
235
+            $current = dirname($current);
236
+            if ($current === '/' || $current === '.' || $current === '') {
237
+                break;
238
+            }
239
+
240
+            $folder = $this->appData->getFolder($current);
241
+            if (count($folder->getDirectoryListing()) !== 0) {
242
+                break;
243
+            }
244
+        }
245
+    }
246 246
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -53,7 +53,7 @@  discard block
 block discarded – undo
53 53
 		$this->appData = $appDataFactory->get('preview');
54 54
 		$this->setTimeSensitivity(self::TIME_INSENSITIVE);
55 55
 		$this->setInterval(24 * 60 * 60);
56
-		$this->previewRootPath = 'appdata_' . $this->config->getSystemValueString('instanceid') . '/preview/';
56
+		$this->previewRootPath = 'appdata_'.$this->config->getSystemValueString('instanceid').'/preview/';
57 57
 	}
58 58
 
59 59
 	#[Override]
@@ -68,9 +68,9 @@  discard block
 block discarded – undo
68 68
 			$qb->select('path')
69 69
 				->from('filecache')
70 70
 				// Hierarchical preview folder structure
71
-				->where($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%/%/%/%/%/%/%/%')))
71
+				->where($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath.'%/%/%/%/%/%/%/%/%')))
72 72
 				// Legacy flat preview folder structure
73
-				->orWhere($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%.%')))
73
+				->orWhere($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath.'%/%.%')))
74 74
 				->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
75 75
 				->setMaxResults(100);
76 76
 
@@ -97,7 +97,7 @@  discard block
 block discarded – undo
97 97
 		while ($row = $result->fetchAssociative()) {
98 98
 			$pathSplit = explode('/', $row['path']);
99 99
 			assert(count($pathSplit) >= 2);
100
-			$fileId = (int)$pathSplit[count($pathSplit) - 2];
100
+			$fileId = (int) $pathSplit[count($pathSplit) - 2];
101 101
 			if (count($pathSplit) === 11) {
102 102
 				// Hierarchical structure
103 103
 				if (!in_array($fileId, $fileIds)) {
@@ -126,7 +126,7 @@  discard block
 block discarded – undo
126 126
 	 * @param array<string|int, string[]> $previewFolders
127 127
 	 */
128 128
 	private function processPreviews(int $fileId, bool $flatPath): void {
129
-		$internalPath = $this->getInternalFolder((string)$fileId, $flatPath);
129
+		$internalPath = $this->getInternalFolder((string) $fileId, $flatPath);
130 130
 		$folder = $this->appData->getFolder($internalPath);
131 131
 
132 132
 		/**
@@ -135,7 +135,7 @@  discard block
 block discarded – undo
135 135
 		$previewFiles = [];
136 136
 
137 137
 		foreach ($folder->getDirectoryListing() as $previewFile) {
138
-			$path = $fileId . '/' . $previewFile->getName();
138
+			$path = $fileId.'/'.$previewFile->getName();
139 139
 			/** @var SimpleFile $previewFile */
140 140
 			$preview = Preview::fromPath($path, $this->mimeTypeDetector);
141 141
 			$preview->setId($this->generator->nextId());
@@ -171,7 +171,7 @@  discard block
 block discarded – undo
171 171
 				$file = $previewFile['file'];
172 172
 				$preview->setStorageId($result[0]['storage']);
173 173
 				$preview->setEtag($result[0]['etag']);
174
-				$preview->setSourceMimeType($this->mimeTypeLoader->getMimetypeById((int)$result[0]['mimetype']));
174
+				$preview->setSourceMimeType($this->mimeTypeLoader->getMimetypeById((int) $result[0]['mimetype']));
175 175
 				try {
176 176
 					$preview = $this->previewMapper->insert($preview);
177 177
 				} catch (Exception) {
@@ -218,14 +218,14 @@  discard block
 block discarded – undo
218 218
 		if ($flatPath) {
219 219
 			return $name;
220 220
 		}
221
-		return implode('/', str_split(substr(md5($name), 0, 7))) . '/' . $name;
221
+		return implode('/', str_split(substr(md5($name), 0, 7))).'/'.$name;
222 222
 	}
223 223
 
224 224
 	private function deleteFolder(string $path): void {
225 225
 		$current = $path;
226 226
 
227 227
 		while (true) {
228
-			$appDataPath = $this->previewRootPath . $current;
228
+			$appDataPath = $this->previewRootPath.$current;
229 229
 			$qb = $this->connection->getQueryBuilder();
230 230
 			$qb->delete('filecache')
231 231
 				->where($qb->expr()->eq('path_hash', $qb->createNamedParameter(md5($appDataPath))))
Please login to merge, or discard this patch.
core/Migrations/Version32000Date20250731062008.php 2 patches
Indentation   +120 added lines, -120 removed lines patch added patch discarded remove patch
@@ -25,124 +25,124 @@
 block discarded – undo
25 25
 #[DataCleansing(table: 'vcategory', description: 'Cleanup of duplicate vcategory records')]
26 26
 #[DataCleansing(table: 'vcategory_to_object', description: 'Update object references')]
27 27
 class Version32000Date20250731062008 extends SimpleMigrationStep {
28
-	public function __construct(
29
-		private IDBConnection $connection,
30
-	) {
31
-	}
32
-
33
-	/**
34
-	 * @param IOutput $output
35
-	 * @param Closure(): ISchemaWrapper $schemaClosure
36
-	 * @param array $options
37
-	 */
38
-	#[Override]
39
-	public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
40
-		// Clean up duplicate categories before adding unique constraint
41
-		$this->cleanupDuplicateCategories($output);
42
-	}
43
-
44
-	/**
45
-	 * Clean up duplicate categories
46
-	 */
47
-	private function cleanupDuplicateCategories(IOutput $output): void {
48
-		$output->info('Starting cleanup of duplicate vcategory records...');
49
-
50
-		// Find all categories, ordered to identify duplicates
51
-		$qb = $this->connection->getQueryBuilder();
52
-		$qb->select('id', 'uid', 'type', 'category')
53
-			->from('vcategory')
54
-			->orderBy('uid')
55
-			->addOrderBy('type')
56
-			->addOrderBy('category')
57
-			->addOrderBy('id');
58
-
59
-		$result = $qb->executeQuery();
60
-
61
-		$seen = [];
62
-		$duplicateCount = 0;
63
-
64
-		while ($category = $result->fetchAssociative()) {
65
-			$key = $category['uid'] . '|' . $category['type'] . '|' . $category['category'];
66
-			$categoryId = (int)$category['id'];
67
-
68
-			if (!isset($seen[$key])) {
69
-				// First occurrence - keep this one
70
-				$seen[$key] = $categoryId;
71
-				continue;
72
-			}
73
-
74
-			// Duplicate found
75
-			$keepId = $seen[$key];
76
-			$duplicateCount++;
77
-
78
-			$output->info("Found duplicate: keeping ID $keepId, removing ID $categoryId");
79
-
80
-			$this->cleanupDuplicateAssignments($output, $categoryId, $keepId);
81
-
82
-			// Update object references
83
-			$updateQb = $this->connection->getQueryBuilder();
84
-			$updateQb->update('vcategory_to_object')
85
-				->set('categoryid', $updateQb->createNamedParameter($keepId))
86
-				->where($updateQb->expr()->eq('categoryid', $updateQb->createNamedParameter($categoryId)));
87
-
88
-			$affectedRows = $updateQb->executeStatement();
89
-			if ($affectedRows > 0) {
90
-				$output->info(" - Updated $affectedRows object references from category $categoryId to $keepId");
91
-			}
92
-
93
-			// Remove duplicate category record
94
-			$deleteQb = $this->connection->getQueryBuilder();
95
-			$deleteQb->delete('vcategory')
96
-				->where($deleteQb->expr()->eq('id', $deleteQb->createNamedParameter($categoryId)));
97
-
98
-			$deleteQb->executeStatement();
99
-			$output->info(" - Deleted duplicate category record ID $categoryId");
100
-
101
-		}
102
-
103
-		$result->closeCursor();
104
-
105
-		if ($duplicateCount === 0) {
106
-			$output->info('No duplicate categories found');
107
-		} else {
108
-			$output->info("Duplicate cleanup completed - processed $duplicateCount duplicates");
109
-		}
110
-	}
111
-
112
-	/**
113
-	 * Clean up duplicate assignments
114
-	 * That will delete rows with $categoryId when there is the same row with $keepId
115
-	 */
116
-	private function cleanupDuplicateAssignments(IOutput $output, int $categoryId, int $keepId): void {
117
-		$selectQb = $this->connection->getQueryBuilder();
118
-		$selectQb->select('o1.*')
119
-			->from('vcategory_to_object', 'o1')
120
-			->join(
121
-				'o1', 'vcategory_to_object', 'o2',
122
-				$selectQb->expr()->andX(
123
-					$selectQb->expr()->eq('o1.type', 'o2.type'),
124
-					$selectQb->expr()->eq('o1.objid', 'o2.objid'),
125
-				)
126
-			)
127
-			->where($selectQb->expr()->eq('o1.categoryid', $selectQb->createNamedParameter($categoryId)))
128
-			->andWhere($selectQb->expr()->eq('o2.categoryid', $selectQb->createNamedParameter($keepId)));
129
-
130
-		$deleteQb = $this->connection->getQueryBuilder();
131
-		$deleteQb->delete('vcategory_to_object')
132
-			->where($deleteQb->expr()->eq('objid', $deleteQb->createParameter('objid')))
133
-			->andWhere($deleteQb->expr()->eq('categoryid', $deleteQb->createParameter('categoryid')))
134
-			->andWhere($deleteQb->expr()->eq('type', $deleteQb->createParameter('type')));
135
-
136
-		$duplicatedAssignments = $selectQb->executeQuery();
137
-		$count = 0;
138
-		while ($row = $duplicatedAssignments->fetchAssociative()) {
139
-			$deleteQb
140
-				->setParameters($row)
141
-				->executeStatement();
142
-			$count++;
143
-		}
144
-		if ($count > 0) {
145
-			$output->info(" - Deleted $count duplicate category assignments for $categoryId and $keepId");
146
-		}
147
-	}
28
+    public function __construct(
29
+        private IDBConnection $connection,
30
+    ) {
31
+    }
32
+
33
+    /**
34
+     * @param IOutput $output
35
+     * @param Closure(): ISchemaWrapper $schemaClosure
36
+     * @param array $options
37
+     */
38
+    #[Override]
39
+    public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
40
+        // Clean up duplicate categories before adding unique constraint
41
+        $this->cleanupDuplicateCategories($output);
42
+    }
43
+
44
+    /**
45
+     * Clean up duplicate categories
46
+     */
47
+    private function cleanupDuplicateCategories(IOutput $output): void {
48
+        $output->info('Starting cleanup of duplicate vcategory records...');
49
+
50
+        // Find all categories, ordered to identify duplicates
51
+        $qb = $this->connection->getQueryBuilder();
52
+        $qb->select('id', 'uid', 'type', 'category')
53
+            ->from('vcategory')
54
+            ->orderBy('uid')
55
+            ->addOrderBy('type')
56
+            ->addOrderBy('category')
57
+            ->addOrderBy('id');
58
+
59
+        $result = $qb->executeQuery();
60
+
61
+        $seen = [];
62
+        $duplicateCount = 0;
63
+
64
+        while ($category = $result->fetchAssociative()) {
65
+            $key = $category['uid'] . '|' . $category['type'] . '|' . $category['category'];
66
+            $categoryId = (int)$category['id'];
67
+
68
+            if (!isset($seen[$key])) {
69
+                // First occurrence - keep this one
70
+                $seen[$key] = $categoryId;
71
+                continue;
72
+            }
73
+
74
+            // Duplicate found
75
+            $keepId = $seen[$key];
76
+            $duplicateCount++;
77
+
78
+            $output->info("Found duplicate: keeping ID $keepId, removing ID $categoryId");
79
+
80
+            $this->cleanupDuplicateAssignments($output, $categoryId, $keepId);
81
+
82
+            // Update object references
83
+            $updateQb = $this->connection->getQueryBuilder();
84
+            $updateQb->update('vcategory_to_object')
85
+                ->set('categoryid', $updateQb->createNamedParameter($keepId))
86
+                ->where($updateQb->expr()->eq('categoryid', $updateQb->createNamedParameter($categoryId)));
87
+
88
+            $affectedRows = $updateQb->executeStatement();
89
+            if ($affectedRows > 0) {
90
+                $output->info(" - Updated $affectedRows object references from category $categoryId to $keepId");
91
+            }
92
+
93
+            // Remove duplicate category record
94
+            $deleteQb = $this->connection->getQueryBuilder();
95
+            $deleteQb->delete('vcategory')
96
+                ->where($deleteQb->expr()->eq('id', $deleteQb->createNamedParameter($categoryId)));
97
+
98
+            $deleteQb->executeStatement();
99
+            $output->info(" - Deleted duplicate category record ID $categoryId");
100
+
101
+        }
102
+
103
+        $result->closeCursor();
104
+
105
+        if ($duplicateCount === 0) {
106
+            $output->info('No duplicate categories found');
107
+        } else {
108
+            $output->info("Duplicate cleanup completed - processed $duplicateCount duplicates");
109
+        }
110
+    }
111
+
112
+    /**
113
+     * Clean up duplicate assignments
114
+     * That will delete rows with $categoryId when there is the same row with $keepId
115
+     */
116
+    private function cleanupDuplicateAssignments(IOutput $output, int $categoryId, int $keepId): void {
117
+        $selectQb = $this->connection->getQueryBuilder();
118
+        $selectQb->select('o1.*')
119
+            ->from('vcategory_to_object', 'o1')
120
+            ->join(
121
+                'o1', 'vcategory_to_object', 'o2',
122
+                $selectQb->expr()->andX(
123
+                    $selectQb->expr()->eq('o1.type', 'o2.type'),
124
+                    $selectQb->expr()->eq('o1.objid', 'o2.objid'),
125
+                )
126
+            )
127
+            ->where($selectQb->expr()->eq('o1.categoryid', $selectQb->createNamedParameter($categoryId)))
128
+            ->andWhere($selectQb->expr()->eq('o2.categoryid', $selectQb->createNamedParameter($keepId)));
129
+
130
+        $deleteQb = $this->connection->getQueryBuilder();
131
+        $deleteQb->delete('vcategory_to_object')
132
+            ->where($deleteQb->expr()->eq('objid', $deleteQb->createParameter('objid')))
133
+            ->andWhere($deleteQb->expr()->eq('categoryid', $deleteQb->createParameter('categoryid')))
134
+            ->andWhere($deleteQb->expr()->eq('type', $deleteQb->createParameter('type')));
135
+
136
+        $duplicatedAssignments = $selectQb->executeQuery();
137
+        $count = 0;
138
+        while ($row = $duplicatedAssignments->fetchAssociative()) {
139
+            $deleteQb
140
+                ->setParameters($row)
141
+                ->executeStatement();
142
+            $count++;
143
+        }
144
+        if ($count > 0) {
145
+            $output->info(" - Deleted $count duplicate category assignments for $categoryId and $keepId");
146
+        }
147
+    }
148 148
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -62,8 +62,8 @@
 block discarded – undo
62 62
 		$duplicateCount = 0;
63 63
 
64 64
 		while ($category = $result->fetchAssociative()) {
65
-			$key = $category['uid'] . '|' . $category['type'] . '|' . $category['category'];
66
-			$categoryId = (int)$category['id'];
65
+			$key = $category['uid'].'|'.$category['type'].'|'.$category['category'];
66
+			$categoryId = (int) $category['id'];
67 67
 
68 68
 			if (!isset($seen[$key])) {
69 69
 				// First occurrence - keep this one
Please login to merge, or discard this patch.
core/Migrations/Version28000Date20240828142927.php 1 patch
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -24,60 +24,60 @@
 block discarded – undo
24 24
 #[ModifyColumn(table: 'jobs', name: 'argument_hash', type: ColumnType::STRING, description: 'Increase the column size from 32 to 64')]
25 25
 #[ModifyColumn(table: 'jobs', name: 'argument_hash', type: ColumnType::STRING, description: 'Rehash the argument_hash column using sha256')]
26 26
 class Version28000Date20240828142927 extends SimpleMigrationStep {
27
-	public function __construct(
28
-		protected IDBConnection $connection,
29
-	) {
30
-	}
27
+    public function __construct(
28
+        protected IDBConnection $connection,
29
+    ) {
30
+    }
31 31
 
32
-	public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
33
-		/** @var ISchemaWrapper $schema */
34
-		$schema = $schemaClosure();
32
+    public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
33
+        /** @var ISchemaWrapper $schema */
34
+        $schema = $schemaClosure();
35 35
 
36
-		// Increase the column size from 32 to 64
37
-		$table = $schema->getTable('jobs');
38
-		$table->modifyColumn('argument_hash', [
39
-			'notnull' => false,
40
-			'length' => 64,
41
-		]);
36
+        // Increase the column size from 32 to 64
37
+        $table = $schema->getTable('jobs');
38
+        $table->modifyColumn('argument_hash', [
39
+            'notnull' => false,
40
+            'length' => 64,
41
+        ]);
42 42
 
43
-		return $schema;
44
-	}
43
+        return $schema;
44
+    }
45 45
 
46
-	public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
47
-		$chunkSize = 1000;
48
-		$offset = 0;
49
-		$nullHash = hash('sha256', 'null');
46
+    public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
47
+        $chunkSize = 1000;
48
+        $offset = 0;
49
+        $nullHash = hash('sha256', 'null');
50 50
 
51
-		$selectQuery = $this->connection->getQueryBuilder()
52
-			->select('*')
53
-			->from('jobs')
54
-			->setMaxResults($chunkSize);
51
+        $selectQuery = $this->connection->getQueryBuilder()
52
+            ->select('*')
53
+            ->from('jobs')
54
+            ->setMaxResults($chunkSize);
55 55
 
56
-		$insertQuery = $this->connection->getQueryBuilder();
57
-		$insertQuery->update('jobs')
58
-			->set('argument_hash', $insertQuery->createParameter('argument_hash'))
59
-			->where($insertQuery->expr()->eq('id', $insertQuery->createParameter('id')));
56
+        $insertQuery = $this->connection->getQueryBuilder();
57
+        $insertQuery->update('jobs')
58
+            ->set('argument_hash', $insertQuery->createParameter('argument_hash'))
59
+            ->where($insertQuery->expr()->eq('id', $insertQuery->createParameter('id')));
60 60
 
61
-		do {
62
-			$result = $selectQuery
63
-				->setFirstResult($offset)
64
-				->executeQuery();
61
+        do {
62
+            $result = $selectQuery
63
+                ->setFirstResult($offset)
64
+                ->executeQuery();
65 65
 
66
-			$jobs = $result->fetchAllAssociative();
67
-			$count = count($jobs);
66
+            $jobs = $result->fetchAllAssociative();
67
+            $count = count($jobs);
68 68
 
69
-			foreach ($jobs as $jobRow) {
70
-				if ($jobRow['argument'] === 'null') {
71
-					$hash = $nullHash;
72
-				} else {
73
-					$hash = hash('sha256', $jobRow['argument']);
74
-				}
75
-				$insertQuery->setParameter('id', (string)$jobRow['id'], IQueryBuilder::PARAM_INT);
76
-				$insertQuery->setParameter('argument_hash', $hash);
77
-				$insertQuery->executeStatement();
78
-			}
69
+            foreach ($jobs as $jobRow) {
70
+                if ($jobRow['argument'] === 'null') {
71
+                    $hash = $nullHash;
72
+                } else {
73
+                    $hash = hash('sha256', $jobRow['argument']);
74
+                }
75
+                $insertQuery->setParameter('id', (string)$jobRow['id'], IQueryBuilder::PARAM_INT);
76
+                $insertQuery->setParameter('argument_hash', $hash);
77
+                $insertQuery->executeStatement();
78
+            }
79 79
 
80
-			$offset += $chunkSize;
81
-		} while ($count === $chunkSize);
82
-	}
80
+            $offset += $chunkSize;
81
+        } while ($count === $chunkSize);
82
+    }
83 83
 }
Please login to merge, or discard this patch.
core/Migrations/Version13000Date20170718121200.php 2 patches
Indentation   +977 added lines, -977 removed lines patch added patch discarded remove patch
@@ -13,1017 +13,1017 @@
 block discarded – undo
13 13
 use OCP\Migration\SimpleMigrationStep;
14 14
 
15 15
 class Version13000Date20170718121200 extends SimpleMigrationStep {
16
-	public function __construct(
17
-		private IDBConnection $connection,
18
-	) {
19
-	}
16
+    public function __construct(
17
+        private IDBConnection $connection,
18
+    ) {
19
+    }
20 20
 
21
-	public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
22
-		/** @var ISchemaWrapper $schema */
23
-		$schema = $schemaClosure();
21
+    public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
22
+        /** @var ISchemaWrapper $schema */
23
+        $schema = $schemaClosure();
24 24
 
25
-		if (!$schema->hasTable('properties')) {
26
-			return;
27
-		}
28
-		// in case we have a properties table from oc we drop it since we will only migrate
29
-		// the dav_properties values in the postSchemaChange step
30
-		$table = $schema->getTable('properties');
31
-		if ($table->hasColumn('fileid')) {
32
-			$qb = $this->connection->getQueryBuilder();
33
-			$qb->delete('properties');
34
-			$qb->executeStatement();
35
-		}
36
-	}
25
+        if (!$schema->hasTable('properties')) {
26
+            return;
27
+        }
28
+        // in case we have a properties table from oc we drop it since we will only migrate
29
+        // the dav_properties values in the postSchemaChange step
30
+        $table = $schema->getTable('properties');
31
+        if ($table->hasColumn('fileid')) {
32
+            $qb = $this->connection->getQueryBuilder();
33
+            $qb->delete('properties');
34
+            $qb->executeStatement();
35
+        }
36
+    }
37 37
 
38 38
 
39
-	/**
40
-	 * @param IOutput $output
41
-	 * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
42
-	 * @param array $options
43
-	 * @return null|ISchemaWrapper
44
-	 * @since 13.0.0
45
-	 */
46
-	public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
47
-		/** @var ISchemaWrapper $schema */
48
-		$schema = $schemaClosure();
39
+    /**
40
+     * @param IOutput $output
41
+     * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
42
+     * @param array $options
43
+     * @return null|ISchemaWrapper
44
+     * @since 13.0.0
45
+     */
46
+    public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
47
+        /** @var ISchemaWrapper $schema */
48
+        $schema = $schemaClosure();
49 49
 
50
-		if (!$schema->hasTable('appconfig')) {
51
-			$table = $schema->createTable('appconfig');
52
-			$table->addColumn('appid', 'string', [
53
-				'notnull' => true,
54
-				'length' => 32,
55
-				'default' => '',
56
-			]);
57
-			$table->addColumn('configkey', 'string', [
58
-				'notnull' => true,
59
-				'length' => 64,
60
-				'default' => '',
61
-			]);
62
-			$table->addColumn('configvalue', 'text', [
63
-				'notnull' => false,
64
-			]);
65
-			$table->setPrimaryKey(['appid', 'configkey']);
66
-			$table->addIndex(['configkey'], 'appconfig_config_key_index');
67
-			$table->addIndex(['appid'], 'appconfig_appid_key');
68
-		}
50
+        if (!$schema->hasTable('appconfig')) {
51
+            $table = $schema->createTable('appconfig');
52
+            $table->addColumn('appid', 'string', [
53
+                'notnull' => true,
54
+                'length' => 32,
55
+                'default' => '',
56
+            ]);
57
+            $table->addColumn('configkey', 'string', [
58
+                'notnull' => true,
59
+                'length' => 64,
60
+                'default' => '',
61
+            ]);
62
+            $table->addColumn('configvalue', 'text', [
63
+                'notnull' => false,
64
+            ]);
65
+            $table->setPrimaryKey(['appid', 'configkey']);
66
+            $table->addIndex(['configkey'], 'appconfig_config_key_index');
67
+            $table->addIndex(['appid'], 'appconfig_appid_key');
68
+        }
69 69
 
70
-		if (!$schema->hasTable('storages')) {
71
-			$table = $schema->createTable('storages');
72
-			$table->addColumn('id', 'string', [
73
-				'notnull' => false,
74
-				'length' => 64,
75
-			]);
76
-			$table->addColumn('numeric_id', Types::BIGINT, [
77
-				'autoincrement' => true,
78
-				'notnull' => true,
79
-				'length' => 20,
80
-			]);
81
-			$table->addColumn('available', 'integer', [
82
-				'notnull' => true,
83
-				'default' => 1,
84
-			]);
85
-			$table->addColumn('last_checked', 'integer', [
86
-				'notnull' => false,
87
-			]);
88
-			$table->setPrimaryKey(['numeric_id']);
89
-			$table->addUniqueIndex(['id'], 'storages_id_index');
90
-		}
70
+        if (!$schema->hasTable('storages')) {
71
+            $table = $schema->createTable('storages');
72
+            $table->addColumn('id', 'string', [
73
+                'notnull' => false,
74
+                'length' => 64,
75
+            ]);
76
+            $table->addColumn('numeric_id', Types::BIGINT, [
77
+                'autoincrement' => true,
78
+                'notnull' => true,
79
+                'length' => 20,
80
+            ]);
81
+            $table->addColumn('available', 'integer', [
82
+                'notnull' => true,
83
+                'default' => 1,
84
+            ]);
85
+            $table->addColumn('last_checked', 'integer', [
86
+                'notnull' => false,
87
+            ]);
88
+            $table->setPrimaryKey(['numeric_id']);
89
+            $table->addUniqueIndex(['id'], 'storages_id_index');
90
+        }
91 91
 
92
-		if (!$schema->hasTable('mounts')) {
93
-			$table = $schema->createTable('mounts');
94
-			$table->addColumn('id', 'integer', [
95
-				'autoincrement' => true,
96
-				'notnull' => true,
97
-				'length' => 4,
98
-			]);
99
-			$table->addColumn('storage_id', Types::BIGINT, [
100
-				'notnull' => true,
101
-				'length' => 20,
102
-			]);
103
-			$table->addColumn('root_id', Types::BIGINT, [
104
-				'notnull' => true,
105
-				'length' => 20,
106
-			]);
107
-			$table->addColumn('user_id', 'string', [
108
-				'notnull' => true,
109
-				'length' => 64,
110
-			]);
111
-			$table->addColumn('mount_point', 'string', [
112
-				'notnull' => true,
113
-				'length' => 4000,
114
-			]);
115
-			$table->addColumn('mount_id', Types::BIGINT, [
116
-				'notnull' => false,
117
-				'length' => 20,
118
-			]);
119
-			$table->setPrimaryKey(['id']);
120
-			$table->addIndex(['user_id'], 'mounts_user_index');
121
-			$table->addIndex(['storage_id'], 'mounts_storage_index');
122
-			$table->addIndex(['root_id'], 'mounts_root_index');
123
-			$table->addIndex(['mount_id'], 'mounts_mount_id_index');
124
-			$table->addIndex(['user_id', 'root_id', 'mount_point'], 'mounts_user_root_path_index', [], ['lengths' => [null, null, 128]]);
125
-		} else {
126
-			$table = $schema->getTable('mounts');
127
-			if (!$table->hasColumn('mount_id')) {
128
-				$table->addColumn('mount_id', Types::BIGINT, [
129
-					'notnull' => false,
130
-					'length' => 20,
131
-				]);
132
-			}
133
-			if (!$table->hasIndex('mounts_mount_id_index')) {
134
-				$table->addIndex(['mount_id'], 'mounts_mount_id_index');
135
-			}
136
-		}
92
+        if (!$schema->hasTable('mounts')) {
93
+            $table = $schema->createTable('mounts');
94
+            $table->addColumn('id', 'integer', [
95
+                'autoincrement' => true,
96
+                'notnull' => true,
97
+                'length' => 4,
98
+            ]);
99
+            $table->addColumn('storage_id', Types::BIGINT, [
100
+                'notnull' => true,
101
+                'length' => 20,
102
+            ]);
103
+            $table->addColumn('root_id', Types::BIGINT, [
104
+                'notnull' => true,
105
+                'length' => 20,
106
+            ]);
107
+            $table->addColumn('user_id', 'string', [
108
+                'notnull' => true,
109
+                'length' => 64,
110
+            ]);
111
+            $table->addColumn('mount_point', 'string', [
112
+                'notnull' => true,
113
+                'length' => 4000,
114
+            ]);
115
+            $table->addColumn('mount_id', Types::BIGINT, [
116
+                'notnull' => false,
117
+                'length' => 20,
118
+            ]);
119
+            $table->setPrimaryKey(['id']);
120
+            $table->addIndex(['user_id'], 'mounts_user_index');
121
+            $table->addIndex(['storage_id'], 'mounts_storage_index');
122
+            $table->addIndex(['root_id'], 'mounts_root_index');
123
+            $table->addIndex(['mount_id'], 'mounts_mount_id_index');
124
+            $table->addIndex(['user_id', 'root_id', 'mount_point'], 'mounts_user_root_path_index', [], ['lengths' => [null, null, 128]]);
125
+        } else {
126
+            $table = $schema->getTable('mounts');
127
+            if (!$table->hasColumn('mount_id')) {
128
+                $table->addColumn('mount_id', Types::BIGINT, [
129
+                    'notnull' => false,
130
+                    'length' => 20,
131
+                ]);
132
+            }
133
+            if (!$table->hasIndex('mounts_mount_id_index')) {
134
+                $table->addIndex(['mount_id'], 'mounts_mount_id_index');
135
+            }
136
+        }
137 137
 
138
-		if (!$schema->hasTable('mimetypes')) {
139
-			$table = $schema->createTable('mimetypes');
140
-			$table->addColumn('id', Types::BIGINT, [
141
-				'autoincrement' => true,
142
-				'notnull' => true,
143
-				'length' => 20,
144
-			]);
145
-			$table->addColumn('mimetype', 'string', [
146
-				'notnull' => true,
147
-				'length' => 255,
148
-				'default' => '',
149
-			]);
150
-			$table->setPrimaryKey(['id']);
151
-			$table->addUniqueIndex(['mimetype'], 'mimetype_id_index');
152
-		}
138
+        if (!$schema->hasTable('mimetypes')) {
139
+            $table = $schema->createTable('mimetypes');
140
+            $table->addColumn('id', Types::BIGINT, [
141
+                'autoincrement' => true,
142
+                'notnull' => true,
143
+                'length' => 20,
144
+            ]);
145
+            $table->addColumn('mimetype', 'string', [
146
+                'notnull' => true,
147
+                'length' => 255,
148
+                'default' => '',
149
+            ]);
150
+            $table->setPrimaryKey(['id']);
151
+            $table->addUniqueIndex(['mimetype'], 'mimetype_id_index');
152
+        }
153 153
 
154
-		if (!$schema->hasTable('filecache')) {
155
-			$table = $schema->createTable('filecache');
156
-			$table->addColumn('fileid', Types::BIGINT, [
157
-				'autoincrement' => true,
158
-				'notnull' => true,
159
-				'length' => 20,
160
-			]);
161
-			$table->addColumn('storage', Types::BIGINT, [
162
-				'notnull' => true,
163
-				'length' => 20,
164
-				'default' => 0,
165
-			]);
166
-			$table->addColumn('path', 'string', [
167
-				'notnull' => false,
168
-				'length' => 4000,
169
-			]);
170
-			$table->addColumn('path_hash', 'string', [
171
-				'notnull' => true,
172
-				'length' => 32,
173
-				'default' => '',
174
-			]);
175
-			$table->addColumn('parent', Types::BIGINT, [
176
-				'notnull' => true,
177
-				'length' => 20,
178
-				'default' => 0,
179
-			]);
180
-			$table->addColumn('name', 'string', [
181
-				'notnull' => false,
182
-				'length' => 250,
183
-			]);
184
-			$table->addColumn('mimetype', Types::BIGINT, [
185
-				'notnull' => true,
186
-				'length' => 20,
187
-				'default' => 0,
188
-			]);
189
-			$table->addColumn('mimepart', Types::BIGINT, [
190
-				'notnull' => true,
191
-				'length' => 20,
192
-				'default' => 0,
193
-			]);
194
-			$table->addColumn('size', 'bigint', [
195
-				'notnull' => true,
196
-				'length' => 8,
197
-				'default' => 0,
198
-			]);
199
-			$table->addColumn('mtime', Types::BIGINT, [
200
-				'notnull' => true,
201
-				'length' => 20,
202
-				'default' => 0,
203
-			]);
204
-			$table->addColumn('storage_mtime', Types::BIGINT, [
205
-				'notnull' => true,
206
-				'length' => 20,
207
-				'default' => 0,
208
-			]);
209
-			$table->addColumn('encrypted', 'integer', [
210
-				'notnull' => true,
211
-				'length' => 4,
212
-				'default' => 0,
213
-			]);
214
-			$table->addColumn('unencrypted_size', 'bigint', [
215
-				'notnull' => true,
216
-				'length' => 8,
217
-				'default' => 0,
218
-			]);
219
-			$table->addColumn('etag', 'string', [
220
-				'notnull' => false,
221
-				'length' => 40,
222
-			]);
223
-			$table->addColumn('permissions', 'integer', [
224
-				'notnull' => false,
225
-				'length' => 4,
226
-				'default' => 0,
227
-			]);
228
-			$table->addColumn('checksum', 'string', [
229
-				'notnull' => false,
230
-				'length' => 255,
231
-			]);
232
-			$table->setPrimaryKey(['fileid']);
233
-			$table->addUniqueIndex(['storage', 'path_hash'], 'fs_storage_path_hash');
234
-			$table->addIndex(['parent', 'name'], 'fs_parent_name_hash');
235
-			$table->addIndex(['storage', 'mimetype'], 'fs_storage_mimetype');
236
-			$table->addIndex(['storage', 'mimepart'], 'fs_storage_mimepart');
237
-			$table->addIndex(['storage', 'size', 'fileid'], 'fs_storage_size');
238
-			$table->addIndex(['fileid', 'storage', 'size'], 'fs_id_storage_size');
239
-			$table->addIndex(['parent'], 'fs_parent');
240
-			$table->addIndex(['name'], 'fs_name_hash');
241
-			$table->addIndex(['mtime'], 'fs_mtime');
242
-			$table->addIndex(['size'], 'fs_size');
243
-			if ($this->connection->getDatabaseProvider() !== IDBConnection::PLATFORM_POSTGRES) {
244
-				$table->addIndex(['storage', 'path'], 'fs_storage_path_prefix', [], ['lengths' => [null, 64]]);
245
-			}
246
-		}
154
+        if (!$schema->hasTable('filecache')) {
155
+            $table = $schema->createTable('filecache');
156
+            $table->addColumn('fileid', Types::BIGINT, [
157
+                'autoincrement' => true,
158
+                'notnull' => true,
159
+                'length' => 20,
160
+            ]);
161
+            $table->addColumn('storage', Types::BIGINT, [
162
+                'notnull' => true,
163
+                'length' => 20,
164
+                'default' => 0,
165
+            ]);
166
+            $table->addColumn('path', 'string', [
167
+                'notnull' => false,
168
+                'length' => 4000,
169
+            ]);
170
+            $table->addColumn('path_hash', 'string', [
171
+                'notnull' => true,
172
+                'length' => 32,
173
+                'default' => '',
174
+            ]);
175
+            $table->addColumn('parent', Types::BIGINT, [
176
+                'notnull' => true,
177
+                'length' => 20,
178
+                'default' => 0,
179
+            ]);
180
+            $table->addColumn('name', 'string', [
181
+                'notnull' => false,
182
+                'length' => 250,
183
+            ]);
184
+            $table->addColumn('mimetype', Types::BIGINT, [
185
+                'notnull' => true,
186
+                'length' => 20,
187
+                'default' => 0,
188
+            ]);
189
+            $table->addColumn('mimepart', Types::BIGINT, [
190
+                'notnull' => true,
191
+                'length' => 20,
192
+                'default' => 0,
193
+            ]);
194
+            $table->addColumn('size', 'bigint', [
195
+                'notnull' => true,
196
+                'length' => 8,
197
+                'default' => 0,
198
+            ]);
199
+            $table->addColumn('mtime', Types::BIGINT, [
200
+                'notnull' => true,
201
+                'length' => 20,
202
+                'default' => 0,
203
+            ]);
204
+            $table->addColumn('storage_mtime', Types::BIGINT, [
205
+                'notnull' => true,
206
+                'length' => 20,
207
+                'default' => 0,
208
+            ]);
209
+            $table->addColumn('encrypted', 'integer', [
210
+                'notnull' => true,
211
+                'length' => 4,
212
+                'default' => 0,
213
+            ]);
214
+            $table->addColumn('unencrypted_size', 'bigint', [
215
+                'notnull' => true,
216
+                'length' => 8,
217
+                'default' => 0,
218
+            ]);
219
+            $table->addColumn('etag', 'string', [
220
+                'notnull' => false,
221
+                'length' => 40,
222
+            ]);
223
+            $table->addColumn('permissions', 'integer', [
224
+                'notnull' => false,
225
+                'length' => 4,
226
+                'default' => 0,
227
+            ]);
228
+            $table->addColumn('checksum', 'string', [
229
+                'notnull' => false,
230
+                'length' => 255,
231
+            ]);
232
+            $table->setPrimaryKey(['fileid']);
233
+            $table->addUniqueIndex(['storage', 'path_hash'], 'fs_storage_path_hash');
234
+            $table->addIndex(['parent', 'name'], 'fs_parent_name_hash');
235
+            $table->addIndex(['storage', 'mimetype'], 'fs_storage_mimetype');
236
+            $table->addIndex(['storage', 'mimepart'], 'fs_storage_mimepart');
237
+            $table->addIndex(['storage', 'size', 'fileid'], 'fs_storage_size');
238
+            $table->addIndex(['fileid', 'storage', 'size'], 'fs_id_storage_size');
239
+            $table->addIndex(['parent'], 'fs_parent');
240
+            $table->addIndex(['name'], 'fs_name_hash');
241
+            $table->addIndex(['mtime'], 'fs_mtime');
242
+            $table->addIndex(['size'], 'fs_size');
243
+            if ($this->connection->getDatabaseProvider() !== IDBConnection::PLATFORM_POSTGRES) {
244
+                $table->addIndex(['storage', 'path'], 'fs_storage_path_prefix', [], ['lengths' => [null, 64]]);
245
+            }
246
+        }
247 247
 
248
-		if (!$schema->hasTable('group_user')) {
249
-			$table = $schema->createTable('group_user');
250
-			$table->addColumn('gid', 'string', [
251
-				'notnull' => true,
252
-				'length' => 64,
253
-				'default' => '',
254
-			]);
255
-			$table->addColumn('uid', 'string', [
256
-				'notnull' => true,
257
-				'length' => 64,
258
-				'default' => '',
259
-			]);
260
-			$table->setPrimaryKey(['gid', 'uid']);
261
-			$table->addIndex(['uid'], 'gu_uid_index');
262
-		}
248
+        if (!$schema->hasTable('group_user')) {
249
+            $table = $schema->createTable('group_user');
250
+            $table->addColumn('gid', 'string', [
251
+                'notnull' => true,
252
+                'length' => 64,
253
+                'default' => '',
254
+            ]);
255
+            $table->addColumn('uid', 'string', [
256
+                'notnull' => true,
257
+                'length' => 64,
258
+                'default' => '',
259
+            ]);
260
+            $table->setPrimaryKey(['gid', 'uid']);
261
+            $table->addIndex(['uid'], 'gu_uid_index');
262
+        }
263 263
 
264
-		if (!$schema->hasTable('group_admin')) {
265
-			$table = $schema->createTable('group_admin');
266
-			$table->addColumn('gid', 'string', [
267
-				'notnull' => true,
268
-				'length' => 64,
269
-				'default' => '',
270
-			]);
271
-			$table->addColumn('uid', 'string', [
272
-				'notnull' => true,
273
-				'length' => 64,
274
-				'default' => '',
275
-			]);
276
-			$table->setPrimaryKey(['gid', 'uid']);
277
-			$table->addIndex(['uid'], 'group_admin_uid');
278
-		}
264
+        if (!$schema->hasTable('group_admin')) {
265
+            $table = $schema->createTable('group_admin');
266
+            $table->addColumn('gid', 'string', [
267
+                'notnull' => true,
268
+                'length' => 64,
269
+                'default' => '',
270
+            ]);
271
+            $table->addColumn('uid', 'string', [
272
+                'notnull' => true,
273
+                'length' => 64,
274
+                'default' => '',
275
+            ]);
276
+            $table->setPrimaryKey(['gid', 'uid']);
277
+            $table->addIndex(['uid'], 'group_admin_uid');
278
+        }
279 279
 
280
-		if (!$schema->hasTable('groups')) {
281
-			$table = $schema->createTable('groups');
282
-			$table->addColumn('gid', 'string', [
283
-				'notnull' => true,
284
-				'length' => 64,
285
-				'default' => '',
286
-			]);
287
-			$table->setPrimaryKey(['gid']);
288
-		}
280
+        if (!$schema->hasTable('groups')) {
281
+            $table = $schema->createTable('groups');
282
+            $table->addColumn('gid', 'string', [
283
+                'notnull' => true,
284
+                'length' => 64,
285
+                'default' => '',
286
+            ]);
287
+            $table->setPrimaryKey(['gid']);
288
+        }
289 289
 
290
-		if (!$schema->hasTable('preferences')) {
291
-			$table = $schema->createTable('preferences');
292
-			$table->addColumn('userid', 'string', [
293
-				'notnull' => true,
294
-				'length' => 64,
295
-				'default' => '',
296
-			]);
297
-			$table->addColumn('appid', 'string', [
298
-				'notnull' => true,
299
-				'length' => 32,
300
-				'default' => '',
301
-			]);
302
-			$table->addColumn('configkey', 'string', [
303
-				'notnull' => true,
304
-				'length' => 64,
305
-				'default' => '',
306
-			]);
307
-			$table->addColumn('configvalue', 'text', [
308
-				'notnull' => false,
309
-			]);
310
-			$table->setPrimaryKey(['userid', 'appid', 'configkey']);
311
-			$table->addIndex(['appid', 'configkey'], 'preferences_app_key');
312
-		}
290
+        if (!$schema->hasTable('preferences')) {
291
+            $table = $schema->createTable('preferences');
292
+            $table->addColumn('userid', 'string', [
293
+                'notnull' => true,
294
+                'length' => 64,
295
+                'default' => '',
296
+            ]);
297
+            $table->addColumn('appid', 'string', [
298
+                'notnull' => true,
299
+                'length' => 32,
300
+                'default' => '',
301
+            ]);
302
+            $table->addColumn('configkey', 'string', [
303
+                'notnull' => true,
304
+                'length' => 64,
305
+                'default' => '',
306
+            ]);
307
+            $table->addColumn('configvalue', 'text', [
308
+                'notnull' => false,
309
+            ]);
310
+            $table->setPrimaryKey(['userid', 'appid', 'configkey']);
311
+            $table->addIndex(['appid', 'configkey'], 'preferences_app_key');
312
+        }
313 313
 
314
-		if (!$schema->hasTable('properties')) {
315
-			$table = $schema->createTable('properties');
316
-			$table->addColumn('id', 'integer', [
317
-				'autoincrement' => true,
318
-				'notnull' => true,
319
-				'length' => 4,
320
-			]);
321
-			$table->addColumn('userid', 'string', [
322
-				'notnull' => true,
323
-				'length' => 64,
324
-				'default' => '',
325
-			]);
326
-			$table->addColumn('propertypath', 'string', [
327
-				'notnull' => true,
328
-				'length' => 255,
329
-				'default' => '',
330
-			]);
331
-			$table->addColumn('propertyname', 'string', [
332
-				'notnull' => true,
333
-				'length' => 255,
334
-				'default' => '',
335
-			]);
336
-			$table->addColumn('propertyvalue', 'text', [
337
-				'notnull' => true,
338
-			]);
339
-			$table->setPrimaryKey(['id']);
340
-			// Dropped in Version29000Date20240131122720 because property_index is redundant with properties_path_index
341
-			// $table->addIndex(['userid'], 'property_index');
342
-			$table->addIndex(['userid', 'propertypath'], 'properties_path_index');
343
-			$table->addIndex(['propertypath'], 'properties_pathonly_index');
344
-		} else {
345
-			$table = $schema->getTable('properties');
346
-			if ($table->hasColumn('propertytype')) {
347
-				$table->dropColumn('propertytype');
348
-			}
349
-			if ($table->hasColumn('fileid')) {
350
-				$table->dropColumn('fileid');
351
-			}
352
-			if (!$table->hasColumn('propertypath')) {
353
-				$table->addColumn('propertypath', 'string', [
354
-					'notnull' => true,
355
-					'length' => 255,
356
-				]);
357
-			}
358
-			if (!$table->hasColumn('userid')) {
359
-				$table->addColumn('userid', 'string', [
360
-					'notnull' => false,
361
-					'length' => 64,
362
-					'default' => '',
363
-				]);
364
-			}
365
-		}
314
+        if (!$schema->hasTable('properties')) {
315
+            $table = $schema->createTable('properties');
316
+            $table->addColumn('id', 'integer', [
317
+                'autoincrement' => true,
318
+                'notnull' => true,
319
+                'length' => 4,
320
+            ]);
321
+            $table->addColumn('userid', 'string', [
322
+                'notnull' => true,
323
+                'length' => 64,
324
+                'default' => '',
325
+            ]);
326
+            $table->addColumn('propertypath', 'string', [
327
+                'notnull' => true,
328
+                'length' => 255,
329
+                'default' => '',
330
+            ]);
331
+            $table->addColumn('propertyname', 'string', [
332
+                'notnull' => true,
333
+                'length' => 255,
334
+                'default' => '',
335
+            ]);
336
+            $table->addColumn('propertyvalue', 'text', [
337
+                'notnull' => true,
338
+            ]);
339
+            $table->setPrimaryKey(['id']);
340
+            // Dropped in Version29000Date20240131122720 because property_index is redundant with properties_path_index
341
+            // $table->addIndex(['userid'], 'property_index');
342
+            $table->addIndex(['userid', 'propertypath'], 'properties_path_index');
343
+            $table->addIndex(['propertypath'], 'properties_pathonly_index');
344
+        } else {
345
+            $table = $schema->getTable('properties');
346
+            if ($table->hasColumn('propertytype')) {
347
+                $table->dropColumn('propertytype');
348
+            }
349
+            if ($table->hasColumn('fileid')) {
350
+                $table->dropColumn('fileid');
351
+            }
352
+            if (!$table->hasColumn('propertypath')) {
353
+                $table->addColumn('propertypath', 'string', [
354
+                    'notnull' => true,
355
+                    'length' => 255,
356
+                ]);
357
+            }
358
+            if (!$table->hasColumn('userid')) {
359
+                $table->addColumn('userid', 'string', [
360
+                    'notnull' => false,
361
+                    'length' => 64,
362
+                    'default' => '',
363
+                ]);
364
+            }
365
+        }
366 366
 
367
-		if (!$schema->hasTable('share')) {
368
-			$table = $schema->createTable('share');
369
-			$table->addColumn('id', 'integer', [
370
-				'autoincrement' => true,
371
-				'notnull' => true,
372
-				'length' => 4,
373
-			]);
374
-			$table->addColumn('share_type', 'smallint', [
375
-				'notnull' => true,
376
-				'length' => 1,
377
-				'default' => 0,
378
-			]);
379
-			$table->addColumn('share_with', 'string', [
380
-				'notnull' => false,
381
-				'length' => 255,
382
-			]);
383
-			$table->addColumn('password', 'string', [
384
-				'notnull' => false,
385
-				'length' => 255,
386
-			]);
387
-			$table->addColumn('uid_owner', 'string', [
388
-				'notnull' => true,
389
-				'length' => 64,
390
-				'default' => '',
391
-			]);
392
-			$table->addColumn('uid_initiator', 'string', [
393
-				'notnull' => false,
394
-				'length' => 64,
395
-			]);
396
-			$table->addColumn('parent', 'integer', [
397
-				'notnull' => false,
398
-				'length' => 4,
399
-			]);
400
-			$table->addColumn('item_type', 'string', [
401
-				'notnull' => true,
402
-				'length' => 64,
403
-				'default' => '',
404
-			]);
405
-			$table->addColumn('item_source', 'string', [
406
-				'notnull' => false,
407
-				'length' => 255,
408
-			]);
409
-			$table->addColumn('item_target', 'string', [
410
-				'notnull' => false,
411
-				'length' => 255,
412
-			]);
413
-			$table->addColumn('file_source', 'integer', [
414
-				'notnull' => false,
415
-				'length' => 4,
416
-			]);
417
-			$table->addColumn('file_target', 'string', [
418
-				'notnull' => false,
419
-				'length' => 512,
420
-			]);
421
-			$table->addColumn('permissions', 'smallint', [
422
-				'notnull' => true,
423
-				'length' => 1,
424
-				'default' => 0,
425
-			]);
426
-			$table->addColumn('stime', 'bigint', [
427
-				'notnull' => true,
428
-				'length' => 8,
429
-				'default' => 0,
430
-			]);
431
-			$table->addColumn('accepted', 'smallint', [
432
-				'notnull' => true,
433
-				'length' => 1,
434
-				'default' => 0,
435
-			]);
436
-			$table->addColumn('expiration', 'datetime', [
437
-				'notnull' => false,
438
-			]);
439
-			$table->addColumn('token', 'string', [
440
-				'notnull' => false,
441
-				'length' => 32,
442
-			]);
443
-			$table->addColumn('mail_send', 'smallint', [
444
-				'notnull' => true,
445
-				'length' => 1,
446
-				'default' => 0,
447
-			]);
448
-			$table->addColumn('share_name', 'string', [
449
-				'notnull' => false,
450
-				'length' => 64,
451
-			]);
452
-			$table->setPrimaryKey(['id']);
453
-			$table->addIndex(['item_type', 'share_type'], 'item_share_type_index');
454
-			$table->addIndex(['file_source'], 'file_source_index');
455
-			$table->addIndex(['token'], 'token_index');
456
-			$table->addIndex(['share_with'], 'share_with_index');
457
-			$table->addIndex(['parent'], 'parent_index');
458
-			$table->addIndex(['uid_owner'], 'owner_index');
459
-			$table->addIndex(['uid_initiator'], 'initiator_index');
460
-		} else {
461
-			$table = $schema->getTable('share');
462
-			if (!$table->hasColumn('password')) {
463
-				$table->addColumn('password', 'string', [
464
-					'notnull' => false,
465
-					'length' => 255,
466
-				]);
467
-			}
468
-		}
367
+        if (!$schema->hasTable('share')) {
368
+            $table = $schema->createTable('share');
369
+            $table->addColumn('id', 'integer', [
370
+                'autoincrement' => true,
371
+                'notnull' => true,
372
+                'length' => 4,
373
+            ]);
374
+            $table->addColumn('share_type', 'smallint', [
375
+                'notnull' => true,
376
+                'length' => 1,
377
+                'default' => 0,
378
+            ]);
379
+            $table->addColumn('share_with', 'string', [
380
+                'notnull' => false,
381
+                'length' => 255,
382
+            ]);
383
+            $table->addColumn('password', 'string', [
384
+                'notnull' => false,
385
+                'length' => 255,
386
+            ]);
387
+            $table->addColumn('uid_owner', 'string', [
388
+                'notnull' => true,
389
+                'length' => 64,
390
+                'default' => '',
391
+            ]);
392
+            $table->addColumn('uid_initiator', 'string', [
393
+                'notnull' => false,
394
+                'length' => 64,
395
+            ]);
396
+            $table->addColumn('parent', 'integer', [
397
+                'notnull' => false,
398
+                'length' => 4,
399
+            ]);
400
+            $table->addColumn('item_type', 'string', [
401
+                'notnull' => true,
402
+                'length' => 64,
403
+                'default' => '',
404
+            ]);
405
+            $table->addColumn('item_source', 'string', [
406
+                'notnull' => false,
407
+                'length' => 255,
408
+            ]);
409
+            $table->addColumn('item_target', 'string', [
410
+                'notnull' => false,
411
+                'length' => 255,
412
+            ]);
413
+            $table->addColumn('file_source', 'integer', [
414
+                'notnull' => false,
415
+                'length' => 4,
416
+            ]);
417
+            $table->addColumn('file_target', 'string', [
418
+                'notnull' => false,
419
+                'length' => 512,
420
+            ]);
421
+            $table->addColumn('permissions', 'smallint', [
422
+                'notnull' => true,
423
+                'length' => 1,
424
+                'default' => 0,
425
+            ]);
426
+            $table->addColumn('stime', 'bigint', [
427
+                'notnull' => true,
428
+                'length' => 8,
429
+                'default' => 0,
430
+            ]);
431
+            $table->addColumn('accepted', 'smallint', [
432
+                'notnull' => true,
433
+                'length' => 1,
434
+                'default' => 0,
435
+            ]);
436
+            $table->addColumn('expiration', 'datetime', [
437
+                'notnull' => false,
438
+            ]);
439
+            $table->addColumn('token', 'string', [
440
+                'notnull' => false,
441
+                'length' => 32,
442
+            ]);
443
+            $table->addColumn('mail_send', 'smallint', [
444
+                'notnull' => true,
445
+                'length' => 1,
446
+                'default' => 0,
447
+            ]);
448
+            $table->addColumn('share_name', 'string', [
449
+                'notnull' => false,
450
+                'length' => 64,
451
+            ]);
452
+            $table->setPrimaryKey(['id']);
453
+            $table->addIndex(['item_type', 'share_type'], 'item_share_type_index');
454
+            $table->addIndex(['file_source'], 'file_source_index');
455
+            $table->addIndex(['token'], 'token_index');
456
+            $table->addIndex(['share_with'], 'share_with_index');
457
+            $table->addIndex(['parent'], 'parent_index');
458
+            $table->addIndex(['uid_owner'], 'owner_index');
459
+            $table->addIndex(['uid_initiator'], 'initiator_index');
460
+        } else {
461
+            $table = $schema->getTable('share');
462
+            if (!$table->hasColumn('password')) {
463
+                $table->addColumn('password', 'string', [
464
+                    'notnull' => false,
465
+                    'length' => 255,
466
+                ]);
467
+            }
468
+        }
469 469
 
470
-		if (!$schema->hasTable('jobs')) {
471
-			$table = $schema->createTable('jobs');
472
-			$table->addColumn('id', 'integer', [
473
-				'autoincrement' => true,
474
-				'notnull' => true,
475
-				'length' => 4,
476
-				'unsigned' => true,
477
-			]);
478
-			$table->addColumn('class', 'string', [
479
-				'notnull' => true,
480
-				'length' => 255,
481
-				'default' => '',
482
-			]);
483
-			$table->addColumn('argument', 'string', [
484
-				'notnull' => true,
485
-				'length' => 4000,
486
-				'default' => '',
487
-			]);
488
-			$table->addColumn('last_run', 'integer', [
489
-				'notnull' => false,
490
-				'default' => 0,
491
-			]);
492
-			$table->addColumn('last_checked', 'integer', [
493
-				'notnull' => false,
494
-				'default' => 0,
495
-			]);
496
-			$table->addColumn('reserved_at', 'integer', [
497
-				'notnull' => false,
498
-				'default' => 0,
499
-			]);
500
-			$table->addColumn('execution_duration', 'integer', [
501
-				'notnull' => true,
502
-				'default' => 0,
503
-			]);
504
-			$table->setPrimaryKey(['id']);
505
-			$table->addIndex(['class'], 'job_class_index');
506
-			$table->addIndex(['last_checked', 'reserved_at'], 'job_lastcheck_reserved');
507
-		}
470
+        if (!$schema->hasTable('jobs')) {
471
+            $table = $schema->createTable('jobs');
472
+            $table->addColumn('id', 'integer', [
473
+                'autoincrement' => true,
474
+                'notnull' => true,
475
+                'length' => 4,
476
+                'unsigned' => true,
477
+            ]);
478
+            $table->addColumn('class', 'string', [
479
+                'notnull' => true,
480
+                'length' => 255,
481
+                'default' => '',
482
+            ]);
483
+            $table->addColumn('argument', 'string', [
484
+                'notnull' => true,
485
+                'length' => 4000,
486
+                'default' => '',
487
+            ]);
488
+            $table->addColumn('last_run', 'integer', [
489
+                'notnull' => false,
490
+                'default' => 0,
491
+            ]);
492
+            $table->addColumn('last_checked', 'integer', [
493
+                'notnull' => false,
494
+                'default' => 0,
495
+            ]);
496
+            $table->addColumn('reserved_at', 'integer', [
497
+                'notnull' => false,
498
+                'default' => 0,
499
+            ]);
500
+            $table->addColumn('execution_duration', 'integer', [
501
+                'notnull' => true,
502
+                'default' => 0,
503
+            ]);
504
+            $table->setPrimaryKey(['id']);
505
+            $table->addIndex(['class'], 'job_class_index');
506
+            $table->addIndex(['last_checked', 'reserved_at'], 'job_lastcheck_reserved');
507
+        }
508 508
 
509
-		if (!$schema->hasTable('users')) {
510
-			$table = $schema->createTable('users');
511
-			$table->addColumn('uid', 'string', [
512
-				'notnull' => true,
513
-				'length' => 64,
514
-				'default' => '',
515
-			]);
516
-			$table->addColumn('displayname', 'string', [
517
-				'notnull' => false,
518
-				'length' => 64,
519
-			]);
520
-			$table->addColumn('password', 'string', [
521
-				'notnull' => true,
522
-				'length' => 255,
523
-				'default' => '',
524
-			]);
525
-			$table->setPrimaryKey(['uid']);
526
-		}
509
+        if (!$schema->hasTable('users')) {
510
+            $table = $schema->createTable('users');
511
+            $table->addColumn('uid', 'string', [
512
+                'notnull' => true,
513
+                'length' => 64,
514
+                'default' => '',
515
+            ]);
516
+            $table->addColumn('displayname', 'string', [
517
+                'notnull' => false,
518
+                'length' => 64,
519
+            ]);
520
+            $table->addColumn('password', 'string', [
521
+                'notnull' => true,
522
+                'length' => 255,
523
+                'default' => '',
524
+            ]);
525
+            $table->setPrimaryKey(['uid']);
526
+        }
527 527
 
528
-		if (!$schema->hasTable('authtoken')) {
529
-			$table = $schema->createTable('authtoken');
530
-			$table->addColumn('id', 'integer', [
531
-				'autoincrement' => true,
532
-				'notnull' => true,
533
-				'length' => 4,
534
-				'unsigned' => true,
535
-			]);
536
-			$table->addColumn('uid', 'string', [
537
-				'notnull' => true,
538
-				'length' => 64,
539
-				'default' => '',
540
-			]);
541
-			$table->addColumn('login_name', 'string', [
542
-				'notnull' => true,
543
-				'length' => 64,
544
-				'default' => '',
545
-			]);
546
-			$table->addColumn('password', 'text', [
547
-				'notnull' => false,
548
-			]);
549
-			$table->addColumn('name', 'text', [
550
-				'notnull' => true,
551
-				'default' => '',
552
-			]);
553
-			$table->addColumn('token', 'string', [
554
-				'notnull' => true,
555
-				'length' => 200,
556
-				'default' => '',
557
-			]);
558
-			$table->addColumn('type', 'smallint', [
559
-				'notnull' => false,
560
-				'length' => 2,
561
-				'default' => 0,
562
-				'unsigned' => true,
563
-			]);
564
-			$table->addColumn('remember', 'smallint', [
565
-				'notnull' => false,
566
-				'length' => 1,
567
-				'default' => 0,
568
-				'unsigned' => true,
569
-			]);
570
-			$table->addColumn('last_activity', 'integer', [
571
-				'notnull' => false,
572
-				'length' => 4,
573
-				'default' => 0,
574
-				'unsigned' => true,
575
-			]);
576
-			$table->addColumn('last_check', 'integer', [
577
-				'notnull' => false,
578
-				'length' => 4,
579
-				'default' => 0,
580
-				'unsigned' => true,
581
-			]);
582
-			$table->addColumn('scope', 'text', [
583
-				'notnull' => false,
584
-			]);
585
-			$table->setPrimaryKey(['id']);
586
-			$table->addUniqueIndex(['token'], 'authtoken_token_index');
587
-			$table->addIndex(['last_activity'], 'authtoken_last_activity_idx');
588
-		} else {
589
-			$table = $schema->getTable('authtoken');
590
-			$table->addColumn('scope', 'text', [
591
-				'notnull' => false,
592
-			]);
593
-		}
528
+        if (!$schema->hasTable('authtoken')) {
529
+            $table = $schema->createTable('authtoken');
530
+            $table->addColumn('id', 'integer', [
531
+                'autoincrement' => true,
532
+                'notnull' => true,
533
+                'length' => 4,
534
+                'unsigned' => true,
535
+            ]);
536
+            $table->addColumn('uid', 'string', [
537
+                'notnull' => true,
538
+                'length' => 64,
539
+                'default' => '',
540
+            ]);
541
+            $table->addColumn('login_name', 'string', [
542
+                'notnull' => true,
543
+                'length' => 64,
544
+                'default' => '',
545
+            ]);
546
+            $table->addColumn('password', 'text', [
547
+                'notnull' => false,
548
+            ]);
549
+            $table->addColumn('name', 'text', [
550
+                'notnull' => true,
551
+                'default' => '',
552
+            ]);
553
+            $table->addColumn('token', 'string', [
554
+                'notnull' => true,
555
+                'length' => 200,
556
+                'default' => '',
557
+            ]);
558
+            $table->addColumn('type', 'smallint', [
559
+                'notnull' => false,
560
+                'length' => 2,
561
+                'default' => 0,
562
+                'unsigned' => true,
563
+            ]);
564
+            $table->addColumn('remember', 'smallint', [
565
+                'notnull' => false,
566
+                'length' => 1,
567
+                'default' => 0,
568
+                'unsigned' => true,
569
+            ]);
570
+            $table->addColumn('last_activity', 'integer', [
571
+                'notnull' => false,
572
+                'length' => 4,
573
+                'default' => 0,
574
+                'unsigned' => true,
575
+            ]);
576
+            $table->addColumn('last_check', 'integer', [
577
+                'notnull' => false,
578
+                'length' => 4,
579
+                'default' => 0,
580
+                'unsigned' => true,
581
+            ]);
582
+            $table->addColumn('scope', 'text', [
583
+                'notnull' => false,
584
+            ]);
585
+            $table->setPrimaryKey(['id']);
586
+            $table->addUniqueIndex(['token'], 'authtoken_token_index');
587
+            $table->addIndex(['last_activity'], 'authtoken_last_activity_idx');
588
+        } else {
589
+            $table = $schema->getTable('authtoken');
590
+            $table->addColumn('scope', 'text', [
591
+                'notnull' => false,
592
+            ]);
593
+        }
594 594
 
595
-		if (!$schema->hasTable('bruteforce_attempts')) {
596
-			$table = $schema->createTable('bruteforce_attempts');
597
-			$table->addColumn('id', 'integer', [
598
-				'autoincrement' => true,
599
-				'notnull' => true,
600
-				'length' => 4,
601
-				'unsigned' => true,
602
-			]);
603
-			$table->addColumn('action', 'string', [
604
-				'notnull' => true,
605
-				'length' => 64,
606
-				'default' => '',
607
-			]);
608
-			$table->addColumn('occurred', 'integer', [
609
-				'notnull' => true,
610
-				'length' => 4,
611
-				'default' => 0,
612
-				'unsigned' => true,
613
-			]);
614
-			$table->addColumn('ip', 'string', [
615
-				'notnull' => true,
616
-				'length' => 255,
617
-				'default' => '',
618
-			]);
619
-			$table->addColumn('subnet', 'string', [
620
-				'notnull' => true,
621
-				'length' => 255,
622
-				'default' => '',
623
-			]);
624
-			$table->addColumn('metadata', 'string', [
625
-				'notnull' => true,
626
-				'length' => 255,
627
-				'default' => '',
628
-			]);
629
-			$table->setPrimaryKey(['id']);
630
-			$table->addIndex(['ip'], 'bruteforce_attempts_ip');
631
-			$table->addIndex(['subnet'], 'bruteforce_attempts_subnet');
632
-		}
595
+        if (!$schema->hasTable('bruteforce_attempts')) {
596
+            $table = $schema->createTable('bruteforce_attempts');
597
+            $table->addColumn('id', 'integer', [
598
+                'autoincrement' => true,
599
+                'notnull' => true,
600
+                'length' => 4,
601
+                'unsigned' => true,
602
+            ]);
603
+            $table->addColumn('action', 'string', [
604
+                'notnull' => true,
605
+                'length' => 64,
606
+                'default' => '',
607
+            ]);
608
+            $table->addColumn('occurred', 'integer', [
609
+                'notnull' => true,
610
+                'length' => 4,
611
+                'default' => 0,
612
+                'unsigned' => true,
613
+            ]);
614
+            $table->addColumn('ip', 'string', [
615
+                'notnull' => true,
616
+                'length' => 255,
617
+                'default' => '',
618
+            ]);
619
+            $table->addColumn('subnet', 'string', [
620
+                'notnull' => true,
621
+                'length' => 255,
622
+                'default' => '',
623
+            ]);
624
+            $table->addColumn('metadata', 'string', [
625
+                'notnull' => true,
626
+                'length' => 255,
627
+                'default' => '',
628
+            ]);
629
+            $table->setPrimaryKey(['id']);
630
+            $table->addIndex(['ip'], 'bruteforce_attempts_ip');
631
+            $table->addIndex(['subnet'], 'bruteforce_attempts_subnet');
632
+        }
633 633
 
634
-		if (!$schema->hasTable('vcategory')) {
635
-			$table = $schema->createTable('vcategory');
636
-			$table->addColumn('id', 'integer', [
637
-				'autoincrement' => true,
638
-				'notnull' => true,
639
-				'length' => 4,
640
-				'unsigned' => true,
641
-			]);
642
-			$table->addColumn('uid', 'string', [
643
-				'notnull' => true,
644
-				'length' => 64,
645
-				'default' => '',
646
-			]);
647
-			$table->addColumn('type', 'string', [
648
-				'notnull' => true,
649
-				'length' => 64,
650
-				'default' => '',
651
-			]);
652
-			$table->addColumn('category', 'string', [
653
-				'notnull' => true,
654
-				'length' => 255,
655
-				'default' => '',
656
-			]);
657
-			$table->setPrimaryKey(['id']);
658
-			$table->addIndex(['uid'], 'uid_index');
659
-			$table->addIndex(['type'], 'type_index');
660
-			$table->addIndex(['category'], 'category_index');
661
-			$table->addUniqueIndex(['uid', 'type', 'category'], 'unique_category_per_user');
662
-		}
634
+        if (!$schema->hasTable('vcategory')) {
635
+            $table = $schema->createTable('vcategory');
636
+            $table->addColumn('id', 'integer', [
637
+                'autoincrement' => true,
638
+                'notnull' => true,
639
+                'length' => 4,
640
+                'unsigned' => true,
641
+            ]);
642
+            $table->addColumn('uid', 'string', [
643
+                'notnull' => true,
644
+                'length' => 64,
645
+                'default' => '',
646
+            ]);
647
+            $table->addColumn('type', 'string', [
648
+                'notnull' => true,
649
+                'length' => 64,
650
+                'default' => '',
651
+            ]);
652
+            $table->addColumn('category', 'string', [
653
+                'notnull' => true,
654
+                'length' => 255,
655
+                'default' => '',
656
+            ]);
657
+            $table->setPrimaryKey(['id']);
658
+            $table->addIndex(['uid'], 'uid_index');
659
+            $table->addIndex(['type'], 'type_index');
660
+            $table->addIndex(['category'], 'category_index');
661
+            $table->addUniqueIndex(['uid', 'type', 'category'], 'unique_category_per_user');
662
+        }
663 663
 
664
-		if (!$schema->hasTable('vcategory_to_object')) {
665
-			$table = $schema->createTable('vcategory_to_object');
666
-			$table->addColumn('objid', 'integer', [
667
-				'notnull' => true,
668
-				'length' => 4,
669
-				'default' => 0,
670
-				'unsigned' => true,
671
-			]);
672
-			$table->addColumn('categoryid', 'integer', [
673
-				'notnull' => true,
674
-				'length' => 4,
675
-				'default' => 0,
676
-				'unsigned' => true,
677
-			]);
678
-			$table->addColumn('type', 'string', [
679
-				'notnull' => true,
680
-				'length' => 64,
681
-				'default' => '',
682
-			]);
683
-			$table->setPrimaryKey(['categoryid', 'objid', 'type']);
684
-			$table->addIndex(['objid', 'type'], 'vcategory_objectd_index');
685
-		}
664
+        if (!$schema->hasTable('vcategory_to_object')) {
665
+            $table = $schema->createTable('vcategory_to_object');
666
+            $table->addColumn('objid', 'integer', [
667
+                'notnull' => true,
668
+                'length' => 4,
669
+                'default' => 0,
670
+                'unsigned' => true,
671
+            ]);
672
+            $table->addColumn('categoryid', 'integer', [
673
+                'notnull' => true,
674
+                'length' => 4,
675
+                'default' => 0,
676
+                'unsigned' => true,
677
+            ]);
678
+            $table->addColumn('type', 'string', [
679
+                'notnull' => true,
680
+                'length' => 64,
681
+                'default' => '',
682
+            ]);
683
+            $table->setPrimaryKey(['categoryid', 'objid', 'type']);
684
+            $table->addIndex(['objid', 'type'], 'vcategory_objectd_index');
685
+        }
686 686
 
687
-		if (!$schema->hasTable('systemtag')) {
688
-			$table = $schema->createTable('systemtag');
689
-			$table->addColumn('id', 'integer', [
690
-				'autoincrement' => true,
691
-				'notnull' => true,
692
-				'length' => 4,
693
-				'unsigned' => true,
694
-			]);
695
-			$table->addColumn('name', 'string', [
696
-				'notnull' => true,
697
-				'length' => 64,
698
-				'default' => '',
699
-			]);
700
-			$table->addColumn('visibility', 'smallint', [
701
-				'notnull' => true,
702
-				'length' => 1,
703
-				'default' => 1,
704
-			]);
705
-			$table->addColumn('editable', 'smallint', [
706
-				'notnull' => true,
707
-				'length' => 1,
708
-				'default' => 1,
709
-			]);
710
-			$table->setPrimaryKey(['id']);
711
-			$table->addUniqueIndex(['name', 'visibility', 'editable'], 'tag_ident');
712
-		}
687
+        if (!$schema->hasTable('systemtag')) {
688
+            $table = $schema->createTable('systemtag');
689
+            $table->addColumn('id', 'integer', [
690
+                'autoincrement' => true,
691
+                'notnull' => true,
692
+                'length' => 4,
693
+                'unsigned' => true,
694
+            ]);
695
+            $table->addColumn('name', 'string', [
696
+                'notnull' => true,
697
+                'length' => 64,
698
+                'default' => '',
699
+            ]);
700
+            $table->addColumn('visibility', 'smallint', [
701
+                'notnull' => true,
702
+                'length' => 1,
703
+                'default' => 1,
704
+            ]);
705
+            $table->addColumn('editable', 'smallint', [
706
+                'notnull' => true,
707
+                'length' => 1,
708
+                'default' => 1,
709
+            ]);
710
+            $table->setPrimaryKey(['id']);
711
+            $table->addUniqueIndex(['name', 'visibility', 'editable'], 'tag_ident');
712
+        }
713 713
 
714
-		if (!$schema->hasTable('systemtag_object_mapping')) {
715
-			$table = $schema->createTable('systemtag_object_mapping');
716
-			$table->addColumn('objectid', 'string', [
717
-				'notnull' => true,
718
-				'length' => 64,
719
-				'default' => '',
720
-			]);
721
-			$table->addColumn('objecttype', 'string', [
722
-				'notnull' => true,
723
-				'length' => 64,
724
-				'default' => '',
725
-			]);
726
-			$table->addColumn('systemtagid', 'integer', [
727
-				'notnull' => true,
728
-				'length' => 4,
729
-				'default' => 0,
730
-				'unsigned' => true,
731
-			]);
732
-			$table->setPrimaryKey(['objecttype', 'objectid', 'systemtagid'], 'som_pk');
733
-			//			$table->addUniqueIndex(['objecttype', 'objectid', 'systemtagid'], 'mapping');
734
-			$table->addIndex(['systemtagid', 'objecttype'], 'systag_by_tagid');
735
-			// systag_by_objectid was added later and might be missing in older deployments
736
-			$table->addIndex(['objectid'], 'systag_by_objectid');
737
-			// systag_objecttype was added later and might be missing in older deployments
738
-			$table->addIndex(['objecttype'], 'systag_objecttype');
739
-		}
714
+        if (!$schema->hasTable('systemtag_object_mapping')) {
715
+            $table = $schema->createTable('systemtag_object_mapping');
716
+            $table->addColumn('objectid', 'string', [
717
+                'notnull' => true,
718
+                'length' => 64,
719
+                'default' => '',
720
+            ]);
721
+            $table->addColumn('objecttype', 'string', [
722
+                'notnull' => true,
723
+                'length' => 64,
724
+                'default' => '',
725
+            ]);
726
+            $table->addColumn('systemtagid', 'integer', [
727
+                'notnull' => true,
728
+                'length' => 4,
729
+                'default' => 0,
730
+                'unsigned' => true,
731
+            ]);
732
+            $table->setPrimaryKey(['objecttype', 'objectid', 'systemtagid'], 'som_pk');
733
+            //			$table->addUniqueIndex(['objecttype', 'objectid', 'systemtagid'], 'mapping');
734
+            $table->addIndex(['systemtagid', 'objecttype'], 'systag_by_tagid');
735
+            // systag_by_objectid was added later and might be missing in older deployments
736
+            $table->addIndex(['objectid'], 'systag_by_objectid');
737
+            // systag_objecttype was added later and might be missing in older deployments
738
+            $table->addIndex(['objecttype'], 'systag_objecttype');
739
+        }
740 740
 
741
-		if (!$schema->hasTable('systemtag_group')) {
742
-			$table = $schema->createTable('systemtag_group');
743
-			$table->addColumn('systemtagid', 'integer', [
744
-				'notnull' => true,
745
-				'length' => 4,
746
-				'default' => 0,
747
-				'unsigned' => true,
748
-			]);
749
-			$table->addColumn('gid', 'string', [
750
-				'notnull' => true,
751
-			]);
752
-			$table->setPrimaryKey(['gid', 'systemtagid']);
753
-		}
741
+        if (!$schema->hasTable('systemtag_group')) {
742
+            $table = $schema->createTable('systemtag_group');
743
+            $table->addColumn('systemtagid', 'integer', [
744
+                'notnull' => true,
745
+                'length' => 4,
746
+                'default' => 0,
747
+                'unsigned' => true,
748
+            ]);
749
+            $table->addColumn('gid', 'string', [
750
+                'notnull' => true,
751
+            ]);
752
+            $table->setPrimaryKey(['gid', 'systemtagid']);
753
+        }
754 754
 
755
-		if (!$schema->hasTable('file_locks')) {
756
-			$table = $schema->createTable('file_locks');
757
-			$table->addColumn('id', 'integer', [
758
-				'autoincrement' => true,
759
-				'notnull' => true,
760
-				'length' => 4,
761
-				'unsigned' => true,
762
-			]);
763
-			$table->addColumn('lock', 'integer', [
764
-				'notnull' => true,
765
-				'length' => 4,
766
-				'default' => 0,
767
-			]);
768
-			$table->addColumn('key', 'string', [
769
-				'notnull' => true,
770
-				'length' => 64,
771
-			]);
772
-			$table->addColumn('ttl', 'integer', [
773
-				'notnull' => true,
774
-				'length' => 4,
775
-				'default' => -1,
776
-			]);
777
-			$table->setPrimaryKey(['id']);
778
-			$table->addUniqueIndex(['key'], 'lock_key_index');
779
-			$table->addIndex(['ttl'], 'lock_ttl_index');
780
-		}
755
+        if (!$schema->hasTable('file_locks')) {
756
+            $table = $schema->createTable('file_locks');
757
+            $table->addColumn('id', 'integer', [
758
+                'autoincrement' => true,
759
+                'notnull' => true,
760
+                'length' => 4,
761
+                'unsigned' => true,
762
+            ]);
763
+            $table->addColumn('lock', 'integer', [
764
+                'notnull' => true,
765
+                'length' => 4,
766
+                'default' => 0,
767
+            ]);
768
+            $table->addColumn('key', 'string', [
769
+                'notnull' => true,
770
+                'length' => 64,
771
+            ]);
772
+            $table->addColumn('ttl', 'integer', [
773
+                'notnull' => true,
774
+                'length' => 4,
775
+                'default' => -1,
776
+            ]);
777
+            $table->setPrimaryKey(['id']);
778
+            $table->addUniqueIndex(['key'], 'lock_key_index');
779
+            $table->addIndex(['ttl'], 'lock_ttl_index');
780
+        }
781 781
 
782
-		if (!$schema->hasTable('comments')) {
783
-			$table = $schema->createTable('comments');
784
-			$table->addColumn('id', 'integer', [
785
-				'autoincrement' => true,
786
-				'notnull' => true,
787
-				'length' => 4,
788
-				'unsigned' => true,
789
-			]);
790
-			$table->addColumn('parent_id', 'integer', [
791
-				'notnull' => true,
792
-				'length' => 4,
793
-				'default' => 0,
794
-				'unsigned' => true,
795
-			]);
796
-			$table->addColumn('topmost_parent_id', 'integer', [
797
-				'notnull' => true,
798
-				'length' => 4,
799
-				'default' => 0,
800
-				'unsigned' => true,
801
-			]);
802
-			$table->addColumn('children_count', 'integer', [
803
-				'notnull' => true,
804
-				'length' => 4,
805
-				'default' => 0,
806
-				'unsigned' => true,
807
-			]);
808
-			$table->addColumn('actor_type', 'string', [
809
-				'notnull' => true,
810
-				'length' => 64,
811
-				'default' => '',
812
-			]);
813
-			$table->addColumn('actor_id', 'string', [
814
-				'notnull' => true,
815
-				'length' => 64,
816
-				'default' => '',
817
-			]);
818
-			$table->addColumn('message', 'text', [
819
-				'notnull' => false,
820
-			]);
821
-			$table->addColumn('verb', 'string', [
822
-				'notnull' => false,
823
-				'length' => 64,
824
-			]);
825
-			$table->addColumn('creation_timestamp', 'datetime', [
826
-				'notnull' => false,
827
-			]);
828
-			$table->addColumn('latest_child_timestamp', 'datetime', [
829
-				'notnull' => false,
830
-			]);
831
-			$table->addColumn('object_type', 'string', [
832
-				'notnull' => true,
833
-				'length' => 64,
834
-				'default' => '',
835
-			]);
836
-			$table->addColumn('object_id', 'string', [
837
-				'notnull' => true,
838
-				'length' => 64,
839
-				'default' => '',
840
-			]);
841
-			$table->addColumn('reference_id', 'string', [
842
-				'notnull' => false,
843
-				'length' => 64,
844
-			]);
845
-			$table->setPrimaryKey(['id']);
846
-			$table->addIndex(['parent_id'], 'comments_parent_id_index');
847
-			$table->addIndex(['topmost_parent_id'], 'comments_topmost_parent_id_idx');
848
-			$table->addIndex(['object_type', 'object_id', 'creation_timestamp'], 'comments_object_index');
849
-			$table->addIndex(['actor_type', 'actor_id'], 'comments_actor_index');
850
-		}
782
+        if (!$schema->hasTable('comments')) {
783
+            $table = $schema->createTable('comments');
784
+            $table->addColumn('id', 'integer', [
785
+                'autoincrement' => true,
786
+                'notnull' => true,
787
+                'length' => 4,
788
+                'unsigned' => true,
789
+            ]);
790
+            $table->addColumn('parent_id', 'integer', [
791
+                'notnull' => true,
792
+                'length' => 4,
793
+                'default' => 0,
794
+                'unsigned' => true,
795
+            ]);
796
+            $table->addColumn('topmost_parent_id', 'integer', [
797
+                'notnull' => true,
798
+                'length' => 4,
799
+                'default' => 0,
800
+                'unsigned' => true,
801
+            ]);
802
+            $table->addColumn('children_count', 'integer', [
803
+                'notnull' => true,
804
+                'length' => 4,
805
+                'default' => 0,
806
+                'unsigned' => true,
807
+            ]);
808
+            $table->addColumn('actor_type', 'string', [
809
+                'notnull' => true,
810
+                'length' => 64,
811
+                'default' => '',
812
+            ]);
813
+            $table->addColumn('actor_id', 'string', [
814
+                'notnull' => true,
815
+                'length' => 64,
816
+                'default' => '',
817
+            ]);
818
+            $table->addColumn('message', 'text', [
819
+                'notnull' => false,
820
+            ]);
821
+            $table->addColumn('verb', 'string', [
822
+                'notnull' => false,
823
+                'length' => 64,
824
+            ]);
825
+            $table->addColumn('creation_timestamp', 'datetime', [
826
+                'notnull' => false,
827
+            ]);
828
+            $table->addColumn('latest_child_timestamp', 'datetime', [
829
+                'notnull' => false,
830
+            ]);
831
+            $table->addColumn('object_type', 'string', [
832
+                'notnull' => true,
833
+                'length' => 64,
834
+                'default' => '',
835
+            ]);
836
+            $table->addColumn('object_id', 'string', [
837
+                'notnull' => true,
838
+                'length' => 64,
839
+                'default' => '',
840
+            ]);
841
+            $table->addColumn('reference_id', 'string', [
842
+                'notnull' => false,
843
+                'length' => 64,
844
+            ]);
845
+            $table->setPrimaryKey(['id']);
846
+            $table->addIndex(['parent_id'], 'comments_parent_id_index');
847
+            $table->addIndex(['topmost_parent_id'], 'comments_topmost_parent_id_idx');
848
+            $table->addIndex(['object_type', 'object_id', 'creation_timestamp'], 'comments_object_index');
849
+            $table->addIndex(['actor_type', 'actor_id'], 'comments_actor_index');
850
+        }
851 851
 
852
-		if (!$schema->hasTable('comments_read_markers')) {
853
-			$table = $schema->createTable('comments_read_markers');
854
-			$table->addColumn('user_id', 'string', [
855
-				'notnull' => true,
856
-				'length' => 64,
857
-				'default' => '',
858
-			]);
859
-			$table->addColumn('marker_datetime', 'datetime', [
860
-				'notnull' => false,
861
-			]);
862
-			$table->addColumn('object_type', 'string', [
863
-				'notnull' => true,
864
-				'length' => 64,
865
-				'default' => '',
866
-			]);
867
-			$table->addColumn('object_id', 'string', [
868
-				'notnull' => true,
869
-				'length' => 64,
870
-				'default' => '',
871
-			]);
872
-			$table->addIndex(['object_type', 'object_id'], 'comments_marker_object_index');
873
-			$table->setPrimaryKey(['user_id', 'object_type', 'object_id'], 'crm_pk');
874
-			//			$table->addUniqueIndex(['user_id', 'object_type', 'object_id'], 'comments_marker_index');
875
-		}
852
+        if (!$schema->hasTable('comments_read_markers')) {
853
+            $table = $schema->createTable('comments_read_markers');
854
+            $table->addColumn('user_id', 'string', [
855
+                'notnull' => true,
856
+                'length' => 64,
857
+                'default' => '',
858
+            ]);
859
+            $table->addColumn('marker_datetime', 'datetime', [
860
+                'notnull' => false,
861
+            ]);
862
+            $table->addColumn('object_type', 'string', [
863
+                'notnull' => true,
864
+                'length' => 64,
865
+                'default' => '',
866
+            ]);
867
+            $table->addColumn('object_id', 'string', [
868
+                'notnull' => true,
869
+                'length' => 64,
870
+                'default' => '',
871
+            ]);
872
+            $table->addIndex(['object_type', 'object_id'], 'comments_marker_object_index');
873
+            $table->setPrimaryKey(['user_id', 'object_type', 'object_id'], 'crm_pk');
874
+            //			$table->addUniqueIndex(['user_id', 'object_type', 'object_id'], 'comments_marker_index');
875
+        }
876 876
 
877
-		//		if (!$schema->hasTable('credentials')) {
878
-		//			$table = $schema->createTable('credentials');
879
-		//			$table->addColumn('user', 'string', [
880
-		//				'notnull' => false,
881
-		//				'length' => 64,
882
-		//			]);
883
-		//			$table->addColumn('identifier', 'string', [
884
-		//				'notnull' => true,
885
-		//				'length' => 64,
886
-		//			]);
887
-		//			$table->addColumn('credentials', 'text', [
888
-		//				'notnull' => false,
889
-		//			]);
890
-		//			$table->setPrimaryKey(['user', 'identifier']);
891
-		//			$table->addIndex(['user'], 'credentials_user');
892
-		//		}
877
+        //		if (!$schema->hasTable('credentials')) {
878
+        //			$table = $schema->createTable('credentials');
879
+        //			$table->addColumn('user', 'string', [
880
+        //				'notnull' => false,
881
+        //				'length' => 64,
882
+        //			]);
883
+        //			$table->addColumn('identifier', 'string', [
884
+        //				'notnull' => true,
885
+        //				'length' => 64,
886
+        //			]);
887
+        //			$table->addColumn('credentials', 'text', [
888
+        //				'notnull' => false,
889
+        //			]);
890
+        //			$table->setPrimaryKey(['user', 'identifier']);
891
+        //			$table->addIndex(['user'], 'credentials_user');
892
+        //		}
893 893
 
894
-		if (!$schema->hasTable('admin_sections')) {
895
-			$table = $schema->createTable('admin_sections');
896
-			$table->addColumn('id', 'string', [
897
-				'notnull' => true,
898
-				'length' => 64,
899
-			]);
900
-			$table->addColumn('class', 'string', [
901
-				'notnull' => true,
902
-				'length' => 255,
903
-				'default' => '',
904
-			]);
905
-			$table->addColumn('priority', 'smallint', [
906
-				'notnull' => true,
907
-				'length' => 1,
908
-				'default' => 0,
909
-			]);
910
-			$table->setPrimaryKey(['id']);
911
-			$table->addUniqueIndex(['class'], 'admin_sections_class');
912
-		}
894
+        if (!$schema->hasTable('admin_sections')) {
895
+            $table = $schema->createTable('admin_sections');
896
+            $table->addColumn('id', 'string', [
897
+                'notnull' => true,
898
+                'length' => 64,
899
+            ]);
900
+            $table->addColumn('class', 'string', [
901
+                'notnull' => true,
902
+                'length' => 255,
903
+                'default' => '',
904
+            ]);
905
+            $table->addColumn('priority', 'smallint', [
906
+                'notnull' => true,
907
+                'length' => 1,
908
+                'default' => 0,
909
+            ]);
910
+            $table->setPrimaryKey(['id']);
911
+            $table->addUniqueIndex(['class'], 'admin_sections_class');
912
+        }
913 913
 
914
-		if (!$schema->hasTable('admin_settings')) {
915
-			$table = $schema->createTable('admin_settings');
916
-			$table->addColumn('id', 'integer', [
917
-				'autoincrement' => true,
918
-				'notnull' => true,
919
-				'length' => 4,
920
-			]);
921
-			$table->addColumn('class', 'string', [
922
-				'notnull' => true,
923
-				'length' => 255,
924
-				'default' => '',
925
-			]);
926
-			$table->addColumn('section', 'string', [
927
-				'notnull' => false,
928
-				'length' => 64,
929
-			]);
930
-			$table->addColumn('priority', 'smallint', [
931
-				'notnull' => true,
932
-				'length' => 1,
933
-				'default' => 0,
934
-			]);
935
-			$table->setPrimaryKey(['id']);
936
-			$table->addUniqueIndex(['class'], 'admin_settings_class');
937
-			$table->addIndex(['section'], 'admin_settings_section');
938
-		}
914
+        if (!$schema->hasTable('admin_settings')) {
915
+            $table = $schema->createTable('admin_settings');
916
+            $table->addColumn('id', 'integer', [
917
+                'autoincrement' => true,
918
+                'notnull' => true,
919
+                'length' => 4,
920
+            ]);
921
+            $table->addColumn('class', 'string', [
922
+                'notnull' => true,
923
+                'length' => 255,
924
+                'default' => '',
925
+            ]);
926
+            $table->addColumn('section', 'string', [
927
+                'notnull' => false,
928
+                'length' => 64,
929
+            ]);
930
+            $table->addColumn('priority', 'smallint', [
931
+                'notnull' => true,
932
+                'length' => 1,
933
+                'default' => 0,
934
+            ]);
935
+            $table->setPrimaryKey(['id']);
936
+            $table->addUniqueIndex(['class'], 'admin_settings_class');
937
+            $table->addIndex(['section'], 'admin_settings_section');
938
+        }
939 939
 
940
-		if (!$schema->hasTable('personal_sections')) {
941
-			$table = $schema->createTable('personal_sections');
942
-			$table->addColumn('id', 'string', [
943
-				'notnull' => true,
944
-				'length' => 64,
945
-			]);
946
-			$table->addColumn('class', 'string', [
947
-				'notnull' => true,
948
-				'length' => 255,
949
-				'default' => '',
950
-			]);
951
-			$table->addColumn('priority', 'smallint', [
952
-				'notnull' => true,
953
-				'length' => 1,
954
-				'default' => 0,
955
-			]);
956
-			$table->setPrimaryKey(['id']);
957
-			$table->addUniqueIndex(['class'], 'personal_sections_class');
958
-		}
940
+        if (!$schema->hasTable('personal_sections')) {
941
+            $table = $schema->createTable('personal_sections');
942
+            $table->addColumn('id', 'string', [
943
+                'notnull' => true,
944
+                'length' => 64,
945
+            ]);
946
+            $table->addColumn('class', 'string', [
947
+                'notnull' => true,
948
+                'length' => 255,
949
+                'default' => '',
950
+            ]);
951
+            $table->addColumn('priority', 'smallint', [
952
+                'notnull' => true,
953
+                'length' => 1,
954
+                'default' => 0,
955
+            ]);
956
+            $table->setPrimaryKey(['id']);
957
+            $table->addUniqueIndex(['class'], 'personal_sections_class');
958
+        }
959 959
 
960
-		if (!$schema->hasTable('personal_settings')) {
961
-			$table = $schema->createTable('personal_settings');
962
-			$table->addColumn('id', 'integer', [
963
-				'autoincrement' => true,
964
-				'notnull' => true,
965
-				'length' => 4,
966
-			]);
967
-			$table->addColumn('class', 'string', [
968
-				'notnull' => true,
969
-				'length' => 255,
970
-				'default' => '',
971
-			]);
972
-			$table->addColumn('section', 'string', [
973
-				'notnull' => false,
974
-				'length' => 64,
975
-			]);
976
-			$table->addColumn('priority', 'smallint', [
977
-				'notnull' => true,
978
-				'length' => 1,
979
-				'default' => 0,
980
-			]);
981
-			$table->setPrimaryKey(['id']);
982
-			$table->addUniqueIndex(['class'], 'personal_settings_class');
983
-			$table->addIndex(['section'], 'personal_settings_section');
984
-		}
960
+        if (!$schema->hasTable('personal_settings')) {
961
+            $table = $schema->createTable('personal_settings');
962
+            $table->addColumn('id', 'integer', [
963
+                'autoincrement' => true,
964
+                'notnull' => true,
965
+                'length' => 4,
966
+            ]);
967
+            $table->addColumn('class', 'string', [
968
+                'notnull' => true,
969
+                'length' => 255,
970
+                'default' => '',
971
+            ]);
972
+            $table->addColumn('section', 'string', [
973
+                'notnull' => false,
974
+                'length' => 64,
975
+            ]);
976
+            $table->addColumn('priority', 'smallint', [
977
+                'notnull' => true,
978
+                'length' => 1,
979
+                'default' => 0,
980
+            ]);
981
+            $table->setPrimaryKey(['id']);
982
+            $table->addUniqueIndex(['class'], 'personal_settings_class');
983
+            $table->addIndex(['section'], 'personal_settings_section');
984
+        }
985 985
 
986
-		if (!$schema->hasTable('accounts')) {
987
-			$table = $schema->createTable('accounts');
988
-			$table->addColumn('uid', 'string', [
989
-				'notnull' => true,
990
-				'length' => 64,
991
-				'default' => '',
992
-			]);
993
-			$table->addColumn('data', 'text', [
994
-				'notnull' => true,
995
-				'default' => '',
996
-			]);
997
-			$table->setPrimaryKey(['uid']);
998
-		}
999
-		return $schema;
1000
-	}
986
+        if (!$schema->hasTable('accounts')) {
987
+            $table = $schema->createTable('accounts');
988
+            $table->addColumn('uid', 'string', [
989
+                'notnull' => true,
990
+                'length' => 64,
991
+                'default' => '',
992
+            ]);
993
+            $table->addColumn('data', 'text', [
994
+                'notnull' => true,
995
+                'default' => '',
996
+            ]);
997
+            $table->setPrimaryKey(['uid']);
998
+        }
999
+        return $schema;
1000
+    }
1001 1001
 
1002
-	public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
1003
-		/** @var ISchemaWrapper $schema */
1004
-		$schema = $schemaClosure();
1005
-		if (!$schema->hasTable('dav_properties')) {
1006
-			return;
1007
-		}
1008
-		$query = $this->connection->getQueryBuilder();
1009
-		$query->select('*')
1010
-			->from('dav_properties');
1011
-		$result = $query->executeQuery();
1002
+    public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
1003
+        /** @var ISchemaWrapper $schema */
1004
+        $schema = $schemaClosure();
1005
+        if (!$schema->hasTable('dav_properties')) {
1006
+            return;
1007
+        }
1008
+        $query = $this->connection->getQueryBuilder();
1009
+        $query->select('*')
1010
+            ->from('dav_properties');
1011
+        $result = $query->executeQuery();
1012 1012
 
1013
-		$insert = $this->connection->getQueryBuilder();
1014
-		$insert->insert('properties')
1015
-			->setValue('propertypath', $insert->createParameter('propertypath'))
1016
-			->setValue('propertyname', $insert->createParameter('propertyname'))
1017
-			->setValue('propertyvalue', $insert->createParameter('propertyvalue'))
1018
-			->setValue('userid', $insert->createParameter('userid'));
1013
+        $insert = $this->connection->getQueryBuilder();
1014
+        $insert->insert('properties')
1015
+            ->setValue('propertypath', $insert->createParameter('propertypath'))
1016
+            ->setValue('propertyname', $insert->createParameter('propertyname'))
1017
+            ->setValue('propertyvalue', $insert->createParameter('propertyvalue'))
1018
+            ->setValue('userid', $insert->createParameter('userid'));
1019 1019
 
1020
-		while ($row = $result->fetchAssociative()) {
1021
-			preg_match('/(calendar)\/([A-z0-9-@_]+)\//', $row['propertypath'], $match);
1022
-			$insert->setParameter('propertypath', (string)$row['propertypath'])
1023
-				->setParameter('propertyname', (string)$row['propertyname'])
1024
-				->setParameter('propertyvalue', (string)$row['propertyvalue'])
1025
-				->setParameter('userid', ($match[2] ?? ''));
1026
-			$insert->executeStatement();
1027
-		}
1028
-	}
1020
+        while ($row = $result->fetchAssociative()) {
1021
+            preg_match('/(calendar)\/([A-z0-9-@_]+)\//', $row['propertypath'], $match);
1022
+            $insert->setParameter('propertypath', (string)$row['propertypath'])
1023
+                ->setParameter('propertyname', (string)$row['propertyname'])
1024
+                ->setParameter('propertyvalue', (string)$row['propertyvalue'])
1025
+                ->setParameter('userid', ($match[2] ?? ''));
1026
+            $insert->executeStatement();
1027
+        }
1028
+    }
1029 1029
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -1019,9 +1019,9 @@
 block discarded – undo
1019 1019
 
1020 1020
 		while ($row = $result->fetchAssociative()) {
1021 1021
 			preg_match('/(calendar)\/([A-z0-9-@_]+)\//', $row['propertypath'], $match);
1022
-			$insert->setParameter('propertypath', (string)$row['propertypath'])
1023
-				->setParameter('propertyname', (string)$row['propertyname'])
1024
-				->setParameter('propertyvalue', (string)$row['propertyvalue'])
1022
+			$insert->setParameter('propertypath', (string) $row['propertypath'])
1023
+				->setParameter('propertyname', (string) $row['propertyname'])
1024
+				->setParameter('propertyvalue', (string) $row['propertyvalue'])
1025 1025
 				->setParameter('userid', ($match[2] ?? ''));
1026 1026
 			$insert->executeStatement();
1027 1027
 		}
Please login to merge, or discard this patch.
core/Migrations/Version20000Date20201109081918.php 2 patches
Indentation   +63 added lines, -63 removed lines patch added patch discarded remove patch
@@ -16,74 +16,74 @@
 block discarded – undo
16 16
 use OCP\Migration\SimpleMigrationStep;
17 17
 
18 18
 class Version20000Date20201109081918 extends SimpleMigrationStep {
19
-	public function __construct(
20
-		protected IDBConnection $connection,
21
-	) {
22
-	}
19
+    public function __construct(
20
+        protected IDBConnection $connection,
21
+    ) {
22
+    }
23 23
 
24
-	/**
25
-	 * @param IOutput $output
26
-	 * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
27
-	 * @param array $options
28
-	 * @return null|ISchemaWrapper
29
-	 */
30
-	public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
31
-		/** @var ISchemaWrapper $schema */
32
-		$schema = $schemaClosure();
24
+    /**
25
+     * @param IOutput $output
26
+     * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
27
+     * @param array $options
28
+     * @return null|ISchemaWrapper
29
+     */
30
+    public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
31
+        /** @var ISchemaWrapper $schema */
32
+        $schema = $schemaClosure();
33 33
 
34
-		if (!$schema->hasTable('storages_credentials')) {
35
-			$table = $schema->createTable('storages_credentials');
36
-			$table->addColumn('id', Types::BIGINT, [
37
-				'autoincrement' => true,
38
-				'notnull' => true,
39
-				'length' => 64,
40
-			]);
41
-			$table->addColumn('user', Types::STRING, [
42
-				'notnull' => false,
43
-				'length' => 64,
44
-			]);
45
-			$table->addColumn('identifier', Types::STRING, [
46
-				'notnull' => true,
47
-				'length' => 64,
48
-			]);
49
-			$table->addColumn('credentials', Types::TEXT, [
50
-				'notnull' => false,
51
-			]);
52
-			$table->setPrimaryKey(['id']);
53
-			$table->addUniqueIndex(['user', 'identifier'], 'stocred_ui');
54
-			$table->addIndex(['user'], 'stocred_user');
55
-		}
34
+        if (!$schema->hasTable('storages_credentials')) {
35
+            $table = $schema->createTable('storages_credentials');
36
+            $table->addColumn('id', Types::BIGINT, [
37
+                'autoincrement' => true,
38
+                'notnull' => true,
39
+                'length' => 64,
40
+            ]);
41
+            $table->addColumn('user', Types::STRING, [
42
+                'notnull' => false,
43
+                'length' => 64,
44
+            ]);
45
+            $table->addColumn('identifier', Types::STRING, [
46
+                'notnull' => true,
47
+                'length' => 64,
48
+            ]);
49
+            $table->addColumn('credentials', Types::TEXT, [
50
+                'notnull' => false,
51
+            ]);
52
+            $table->setPrimaryKey(['id']);
53
+            $table->addUniqueIndex(['user', 'identifier'], 'stocred_ui');
54
+            $table->addIndex(['user'], 'stocred_user');
55
+        }
56 56
 
57
-		return $schema;
58
-	}
57
+        return $schema;
58
+    }
59 59
 
60
-	/**
61
-	 * {@inheritDoc}
62
-	 *
63
-	 * @since 13.0.0
64
-	 */
65
-	public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options): void {
66
-		if (!$this->connection->tableExists('credentials')) {
67
-			return;
68
-		}
60
+    /**
61
+     * {@inheritDoc}
62
+     *
63
+     * @since 13.0.0
64
+     */
65
+    public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options): void {
66
+        if (!$this->connection->tableExists('credentials')) {
67
+            return;
68
+        }
69 69
 
70
-		$query = $this->connection->getQueryBuilder();
71
-		$query->select('*')
72
-			->from('credentials');
70
+        $query = $this->connection->getQueryBuilder();
71
+        $query->select('*')
72
+            ->from('credentials');
73 73
 
74
-		$insert = $this->connection->getQueryBuilder();
75
-		$insert->insert('storages_credentials')
76
-			->setValue('user', $insert->createParameter('user'))
77
-			->setValue('identifier', $insert->createParameter('identifier'))
78
-			->setValue('credentials', $insert->createParameter('credentials'));
74
+        $insert = $this->connection->getQueryBuilder();
75
+        $insert->insert('storages_credentials')
76
+            ->setValue('user', $insert->createParameter('user'))
77
+            ->setValue('identifier', $insert->createParameter('identifier'))
78
+            ->setValue('credentials', $insert->createParameter('credentials'));
79 79
 
80
-		$result = $query->executeQuery();
81
-		while ($row = $result->fetchAssociative()) {
82
-			$insert->setParameter('user', (string)$row['user'])
83
-				->setParameter('identifier', (string)$row['identifier'])
84
-				->setParameter('credentials', (string)$row['credentials']);
85
-			$insert->executeStatement();
86
-		}
87
-		$result->closeCursor();
88
-	}
80
+        $result = $query->executeQuery();
81
+        while ($row = $result->fetchAssociative()) {
82
+            $insert->setParameter('user', (string)$row['user'])
83
+                ->setParameter('identifier', (string)$row['identifier'])
84
+                ->setParameter('credentials', (string)$row['credentials']);
85
+            $insert->executeStatement();
86
+        }
87
+        $result->closeCursor();
88
+    }
89 89
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -79,9 +79,9 @@
 block discarded – undo
79 79
 
80 80
 		$result = $query->executeQuery();
81 81
 		while ($row = $result->fetchAssociative()) {
82
-			$insert->setParameter('user', (string)$row['user'])
83
-				->setParameter('identifier', (string)$row['identifier'])
84
-				->setParameter('credentials', (string)$row['credentials']);
82
+			$insert->setParameter('user', (string) $row['user'])
83
+				->setParameter('identifier', (string) $row['identifier'])
84
+				->setParameter('credentials', (string) $row['credentials']);
85 85
 			$insert->executeStatement();
86 86
 		}
87 87
 		$result->closeCursor();
Please login to merge, or discard this patch.
core/Migrations/Version33000Date20251023120529.php 1 patch
Indentation   +53 added lines, -53 removed lines patch added patch discarded remove patch
@@ -21,66 +21,66 @@
 block discarded – undo
21 21
  */
22 22
 #[AddIndex(table: 'preview_locations', type: IndexType::UNIQUE)]
23 23
 class Version33000Date20251023120529 extends SimpleMigrationStep {
24
-	public function __construct(
25
-		private readonly IDBConnection $connection,
26
-	) {
27
-	}
24
+    public function __construct(
25
+        private readonly IDBConnection $connection,
26
+    ) {
27
+    }
28 28
 
29
-	/**
30
-	 * @param Closure(): ISchemaWrapper $schemaClosure The `\Closure` returns a `ISchemaWrapper`
31
-	 */
32
-	public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
33
-		/** @var ISchemaWrapper $schema */
34
-		$schema = $schemaClosure();
29
+    /**
30
+     * @param Closure(): ISchemaWrapper $schemaClosure The `\Closure` returns a `ISchemaWrapper`
31
+     */
32
+    public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
33
+        /** @var ISchemaWrapper $schema */
34
+        $schema = $schemaClosure();
35 35
 
36
-		if ($schema->hasTable('preview_locations')) {
37
-			$table = $schema->getTable('preview_locations');
38
-			$table->addUniqueIndex(['bucket_name', 'object_store_name'], 'unique_bucket_store');
39
-		}
36
+        if ($schema->hasTable('preview_locations')) {
37
+            $table = $schema->getTable('preview_locations');
38
+            $table->addUniqueIndex(['bucket_name', 'object_store_name'], 'unique_bucket_store');
39
+        }
40 40
 
41
-		return $schema;
42
-	}
41
+        return $schema;
42
+    }
43 43
 
44
-	public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
45
-		// This shouldn't run on a production instance, only daily
46
-		$qb = $this->connection->getQueryBuilder();
47
-		$qb->select('*')
48
-			->from('preview_locations');
49
-		$result = $qb->executeQuery();
44
+    public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
45
+        // This shouldn't run on a production instance, only daily
46
+        $qb = $this->connection->getQueryBuilder();
47
+        $qb->select('*')
48
+            ->from('preview_locations');
49
+        $result = $qb->executeQuery();
50 50
 
51
-		$set = [];
51
+        $set = [];
52 52
 
53
-		while ($row = $result->fetchAssociative()) {
54
-			// Iterate over all the rows with duplicated rows
55
-			$id = $row['id'];
53
+        while ($row = $result->fetchAssociative()) {
54
+            // Iterate over all the rows with duplicated rows
55
+            $id = $row['id'];
56 56
 
57
-			if (isset($set[$row['bucket_name'] . '_' . $row['object_store_name']])) {
58
-				// duplicate
59
-				$authoritativeId = $set[$row['bucket_name'] . '_' . $row['object_store_name']];
60
-				$qb = $this->connection->getQueryBuilder();
61
-				$qb->select('id')
62
-					->from('preview_locations')
63
-					->where($qb->expr()->eq('bucket_name', $qb->createNamedParameter($row['bucket_name'])))
64
-					->andWhere($qb->expr()->eq('object_store_name', $qb->createNamedParameter($row['object_store_name'])))
65
-					->andWhere($qb->expr()->neq('id', $qb->createNamedParameter($authoritativeId)));
57
+            if (isset($set[$row['bucket_name'] . '_' . $row['object_store_name']])) {
58
+                // duplicate
59
+                $authoritativeId = $set[$row['bucket_name'] . '_' . $row['object_store_name']];
60
+                $qb = $this->connection->getQueryBuilder();
61
+                $qb->select('id')
62
+                    ->from('preview_locations')
63
+                    ->where($qb->expr()->eq('bucket_name', $qb->createNamedParameter($row['bucket_name'])))
64
+                    ->andWhere($qb->expr()->eq('object_store_name', $qb->createNamedParameter($row['object_store_name'])))
65
+                    ->andWhere($qb->expr()->neq('id', $qb->createNamedParameter($authoritativeId)));
66 66
 
67
-				$result = $qb->executeQuery();
68
-				while ($row = $result->fetchAssociative()) {
69
-					// Update previews entries to the now de-duplicated id
70
-					$qb = $this->connection->getQueryBuilder();
71
-					$qb->update('previews')
72
-						->set('location_id', $qb->createNamedParameter($id))
73
-						->where($qb->expr()->eq('id', $qb->createNamedParameter($row['id'])));
74
-					$qb->executeStatement();
67
+                $result = $qb->executeQuery();
68
+                while ($row = $result->fetchAssociative()) {
69
+                    // Update previews entries to the now de-duplicated id
70
+                    $qb = $this->connection->getQueryBuilder();
71
+                    $qb->update('previews')
72
+                        ->set('location_id', $qb->createNamedParameter($id))
73
+                        ->where($qb->expr()->eq('id', $qb->createNamedParameter($row['id'])));
74
+                    $qb->executeStatement();
75 75
 
76
-					$qb = $this->connection->getQueryBuilder();
77
-					$qb->delete('preview_locations')
78
-						->where($qb->expr()->eq('id', $qb->createNamedParameter($row['id'])));
79
-					$qb->executeStatement();
80
-				}
81
-				break;
82
-			}
83
-			$set[$row['bucket_name'] . '_' . $row['object_store_name']] = $row['id'];
84
-		}
85
-	}
76
+                    $qb = $this->connection->getQueryBuilder();
77
+                    $qb->delete('preview_locations')
78
+                        ->where($qb->expr()->eq('id', $qb->createNamedParameter($row['id'])));
79
+                    $qb->executeStatement();
80
+                }
81
+                break;
82
+            }
83
+            $set[$row['bucket_name'] . '_' . $row['object_store_name']] = $row['id'];
84
+        }
85
+    }
86 86
 }
Please login to merge, or discard this patch.