Completed
Push — master ( 764b58...af5acc )
by
unknown
24:58 queued 15s
created
tests/lib/Files/Cache/FileAccessTest.php 1 patch
Indentation   +417 added lines, -417 removed lines patch added patch discarded remove patch
@@ -18,421 +18,421 @@
 block discarded – undo
18 18
  * @group DB
19 19
  */
20 20
 class FileAccessTest extends TestCase {
21
-	private IDBConnection $dbConnection;
22
-	private FileAccess $fileAccess;
23
-
24
-	protected function setUp(): void {
25
-		parent::setUp();
26
-
27
-		// Setup the actual database connection (assume the database is configured properly in PHPUnit setup)
28
-		$this->dbConnection = \OCP\Server::get(IDBConnection::class);
29
-
30
-		// Ensure FileAccess is instantiated with the real connection
31
-		$this->fileAccess = new FileAccess(
32
-			$this->dbConnection,
33
-			\OCP\Server::get(\OC\SystemConfig::class),
34
-			\OCP\Server::get(LoggerInterface::class),
35
-			\OCP\Server::get(\OC\FilesMetadata\FilesMetadataManager::class),
36
-			\OCP\Server::get(\OCP\Files\IMimeTypeLoader::class)
37
-		);
38
-
39
-		// Clear and prepare `filecache` table for tests
40
-		$queryBuilder = $this->dbConnection->getQueryBuilder()->runAcrossAllShards();
41
-		$queryBuilder->delete('filecache')->executeStatement();
42
-
43
-		// Clean up potential leftovers from other tests
44
-		$queryBuilder = $this->dbConnection->getQueryBuilder();
45
-		$queryBuilder->delete('mounts')->executeStatement();
46
-
47
-
48
-		$this->setUpTestDatabaseForGetDistinctMounts();
49
-		$this->setUpTestDatabaseForGetByAncestorInStorage();
50
-	}
51
-
52
-	private function setUpTestDatabaseForGetDistinctMounts(): void {
53
-		$queryBuilder = $this->dbConnection->getQueryBuilder();
54
-
55
-		// Insert test data
56
-		$queryBuilder->insert('mounts')
57
-			->values([
58
-				'storage_id' => $queryBuilder->createNamedParameter(1, IQueryBuilder::PARAM_INT),
59
-				'root_id' => $queryBuilder->createNamedParameter(10, IQueryBuilder::PARAM_INT),
60
-				'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
61
-				'mount_point' => $queryBuilder->createNamedParameter('/files'),
62
-				'user_id' => $queryBuilder->createNamedParameter('test'),
63
-			])
64
-			->executeStatement();
65
-
66
-		$queryBuilder->insert('mounts')
67
-			->values([
68
-				'storage_id' => $queryBuilder->createNamedParameter(3, IQueryBuilder::PARAM_INT),
69
-				'root_id' => $queryBuilder->createNamedParameter(30, IQueryBuilder::PARAM_INT),
70
-				'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
71
-				'mount_point' => $queryBuilder->createNamedParameter('/documents'),
72
-				'user_id' => $queryBuilder->createNamedParameter('test'),
73
-			])
74
-			->executeStatement();
75
-
76
-		$queryBuilder->insert('mounts')
77
-			->values([
78
-				'storage_id' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
79
-				'root_id' => $queryBuilder->createNamedParameter(31, IQueryBuilder::PARAM_INT),
80
-				'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass2'),
81
-				'mount_point' => $queryBuilder->createNamedParameter('/foobar'),
82
-				'user_id' => $queryBuilder->createNamedParameter('test'),
83
-			])
84
-			->executeStatement();
85
-	}
86
-
87
-	/**
88
-	 * Test that getDistinctMounts returns all mounts without filters
89
-	 */
90
-	public function testGetDistinctMountsWithoutFilters(): void {
91
-		$result = iterator_to_array($this->fileAccess->getDistinctMounts([], false));
92
-
93
-		$this->assertCount(3, $result);
94
-
95
-		$this->assertEquals([
96
-			'storage_id' => 1,
97
-			'root_id' => 10,
98
-			'overridden_root' => 10,
99
-		], $result[0]);
100
-
101
-		$this->assertEquals([
102
-			'storage_id' => 3,
103
-			'root_id' => 30,
104
-			'overridden_root' => 30,
105
-		], $result[1]);
106
-
107
-		$this->assertEquals([
108
-			'storage_id' => 4,
109
-			'root_id' => 31,
110
-			'overridden_root' => 31,
111
-		], $result[2]);
112
-	}
113
-
114
-	/**
115
-	 * Test that getDistinctMounts applies filtering by mount providers
116
-	 */
117
-	public function testGetDistinctMountsWithMountProviderFilter(): void {
118
-		$result = iterator_to_array($this->fileAccess->getDistinctMounts(['TestProviderClass1'], false));
119
-
120
-		$this->assertCount(2, $result);
121
-
122
-		$this->assertEquals([
123
-			'storage_id' => 1,
124
-			'root_id' => 10,
125
-			'overridden_root' => 10,
126
-		], $result[0]);
127
-
128
-		$this->assertEquals([
129
-			'storage_id' => 3,
130
-			'root_id' => 30,
131
-			'overridden_root' => 30,
132
-		], $result[1]);
133
-	}
134
-
135
-	/**
136
-	 * Test that getDistinctMounts rewrites home directory paths
137
-	 */
138
-	public function testGetDistinctMountsWithRewriteHomeDirectories(): void {
139
-		// Add additional test data for a home directory mount
140
-		$queryBuilder = $this->dbConnection->getQueryBuilder();
141
-		$queryBuilder->insert('mounts')
142
-			->values([
143
-				'storage_id' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
144
-				'root_id' => $queryBuilder->createNamedParameter(40, IQueryBuilder::PARAM_INT),
145
-				'mount_provider_class' => $queryBuilder->createNamedParameter(\OC\Files\Mount\LocalHomeMountProvider::class),
146
-				'mount_point' => $queryBuilder->createNamedParameter('/home/user'),
147
-				'user_id' => $queryBuilder->createNamedParameter('test'),
148
-			])
149
-			->executeStatement();
150
-
151
-		// Add a mount that is mounted in the home directory
152
-		$queryBuilder = $this->dbConnection->getQueryBuilder();
153
-		$queryBuilder->insert('mounts')
154
-			->values([
155
-				'storage_id' => $queryBuilder->createNamedParameter(5, IQueryBuilder::PARAM_INT),
156
-				'root_id' => $queryBuilder->createNamedParameter(41, IQueryBuilder::PARAM_INT),
157
-				'mount_provider_class' => $queryBuilder->createNamedParameter('TestMountProvider3'),
158
-				'mount_point' => $queryBuilder->createNamedParameter('/test/files/foobar'),
159
-				'user_id' => $queryBuilder->createNamedParameter('test'),
160
-			])
161
-			->executeStatement();
162
-
163
-		// Simulate adding a "files" directory to the filecache table
164
-		$queryBuilder = $this->dbConnection->getQueryBuilder()->runAcrossAllShards();
165
-		$queryBuilder->delete('filecache')->executeStatement();
166
-		$queryBuilder = $this->dbConnection->getQueryBuilder();
167
-		$queryBuilder->insert('filecache')
168
-			->values([
169
-				'fileid' => $queryBuilder->createNamedParameter(99, IQueryBuilder::PARAM_INT),
170
-				'storage' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
171
-				'parent' => $queryBuilder->createNamedParameter(40),
172
-				'name' => $queryBuilder->createNamedParameter('files'),
173
-				'path' => $queryBuilder->createNamedParameter('files'),
174
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files')),
175
-			])
176
-			->executeStatement();
177
-
178
-		$result = iterator_to_array($this->fileAccess->getDistinctMounts());
179
-
180
-		$this->assertCount(2, $result);
181
-
182
-		$this->assertEquals([
183
-			'storage_id' => 4,
184
-			'root_id' => 40,
185
-			'overridden_root' => 99,
186
-		], $result[0]);
187
-
188
-		$this->assertEquals([
189
-			'storage_id' => 5,
190
-			'root_id' => 41,
191
-			'overridden_root' => 41,
192
-		], $result[1]);
193
-	}
194
-
195
-	private function setUpTestDatabaseForGetByAncestorInStorage(): void {
196
-		// prepare `filecache` table for tests
197
-		$queryBuilder = $this->dbConnection->getQueryBuilder();
198
-
199
-		$queryBuilder->insert('filecache')
200
-			->values([
201
-				'fileid' => 1,
202
-				'parent' => 0,
203
-				'path' => $queryBuilder->createNamedParameter('files'),
204
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files')),
205
-				'storage' => $queryBuilder->createNamedParameter(1),
206
-				'name' => $queryBuilder->createNamedParameter('files'),
207
-				'mimetype' => 1,
208
-				'encrypted' => 0,
209
-			])
210
-			->executeStatement();
211
-
212
-		$queryBuilder->insert('filecache')
213
-			->values([
214
-				'fileid' => 2,
215
-				'parent' => 1,
216
-				'path' => $queryBuilder->createNamedParameter('files/documents'),
217
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files/documents')),
218
-				'storage' => $queryBuilder->createNamedParameter(1),
219
-				'name' => $queryBuilder->createNamedParameter('documents'),
220
-				'mimetype' => 2,
221
-				'encrypted' => 1,
222
-			])
223
-			->executeStatement();
224
-
225
-		$queryBuilder->insert('filecache')
226
-			->values([
227
-				'fileid' => 3,
228
-				'parent' => 1,
229
-				'path' => $queryBuilder->createNamedParameter('files/photos'),
230
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files/photos')),
231
-				'storage' => $queryBuilder->createNamedParameter(1),
232
-				'name' => $queryBuilder->createNamedParameter('photos'),
233
-				'mimetype' => 3,
234
-				'encrypted' => 1,
235
-			])
236
-			->executeStatement();
237
-
238
-		$queryBuilder->insert('filecache')
239
-			->values([
240
-				'fileid' => 4,
241
-				'parent' => 3,
242
-				'path' => $queryBuilder->createNamedParameter('files/photos/endtoendencrypted'),
243
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files/photos/endtoendencrypted')),
244
-				'storage' => $queryBuilder->createNamedParameter(1),
245
-				'name' => $queryBuilder->createNamedParameter('endtoendencrypted'),
246
-				'mimetype' => 4,
247
-				'encrypted' => 0,
248
-			])
249
-			->executeStatement();
250
-
251
-		$queryBuilder->insert('filecache')
252
-			->values([
253
-				'fileid' => 5,
254
-				'parent' => 1,
255
-				'path' => $queryBuilder->createNamedParameter('files/serversideencrypted'),
256
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files/serversideencrypted')),
257
-				'storage' => $queryBuilder->createNamedParameter(1),
258
-				'name' => $queryBuilder->createNamedParameter('serversideencrypted'),
259
-				'mimetype' => 4,
260
-				'encrypted' => 1,
261
-			])
262
-			->executeStatement();
263
-
264
-		$queryBuilder->insert('filecache')
265
-			->values([
266
-				'fileid' => 6,
267
-				'parent' => 0,
268
-				'path' => $queryBuilder->createNamedParameter('files/storage2'),
269
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files/storage2')),
270
-				'storage' => $queryBuilder->createNamedParameter(2),
271
-				'name' => $queryBuilder->createNamedParameter('storage2'),
272
-				'mimetype' => 5,
273
-				'encrypted' => 0,
274
-			])
275
-			->executeStatement();
276
-
277
-		$queryBuilder->insert('filecache')
278
-			->values([
279
-				'fileid' => 7,
280
-				'parent' => 6,
281
-				'path' => $queryBuilder->createNamedParameter('files/storage2/file'),
282
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files/storage2/file')),
283
-				'storage' => $queryBuilder->createNamedParameter(2),
284
-				'name' => $queryBuilder->createNamedParameter('file'),
285
-				'mimetype' => 6,
286
-				'encrypted' => 0,
287
-			])
288
-			->executeStatement();
289
-	}
290
-
291
-	/**
292
-	 * Test fetching files by ancestor in storage.
293
-	 */
294
-	public function testGetByAncestorInStorage(): void {
295
-		$generator = $this->fileAccess->getByAncestorInStorage(
296
-			1, // storageId
297
-			1, // rootId
298
-			0, // lastFileId
299
-			10, // maxResults
300
-			[], // mimeTypes
301
-			true, // include end-to-end encrypted files
302
-			true, // include server-side encrypted files
303
-		);
304
-
305
-		$result = iterator_to_array($generator);
306
-
307
-		$this->assertCount(4, $result);
308
-
309
-		$paths = array_map(fn (CacheEntry $entry) => $entry->getPath(), $result);
310
-		$this->assertEquals([
311
-			'files/documents',
312
-			'files/photos',
313
-			'files/photos/endtoendencrypted',
314
-			'files/serversideencrypted',
315
-		], $paths);
316
-	}
317
-
318
-	/**
319
-	 * Test filtering by mime types.
320
-	 */
321
-	public function testGetByAncestorInStorageWithMimeTypes(): void {
322
-		$generator = $this->fileAccess->getByAncestorInStorage(
323
-			1,
324
-			1,
325
-			0,
326
-			10,
327
-			[2], // Only include documents (mimetype=2)
328
-			true,
329
-			true,
330
-		);
331
-
332
-		$result = iterator_to_array($generator);
333
-
334
-		$this->assertCount(1, $result);
335
-		$this->assertEquals('files/documents', $result[0]->getPath());
336
-	}
337
-
338
-	/**
339
-	 * Test excluding end-to-end encrypted files.
340
-	 */
341
-	public function testGetByAncestorInStorageWithoutEndToEndEncrypted(): void {
342
-		$generator = $this->fileAccess->getByAncestorInStorage(
343
-			1,
344
-			1,
345
-			0,
346
-			10,
347
-			[],
348
-			false, // exclude end-to-end encrypted files
349
-			true,
350
-		);
351
-
352
-		$result = iterator_to_array($generator);
353
-
354
-		$this->assertCount(3, $result);
355
-		$paths = array_map(fn (CacheEntry $entry) => $entry->getPath(), $result);
356
-		$this->assertEquals(['files/documents', 'files/photos', 'files/serversideencrypted'], $paths);
357
-	}
358
-
359
-	/**
360
-	 * Test excluding server-side encrypted files.
361
-	 */
362
-	public function testGetByAncestorInStorageWithoutServerSideEncrypted(): void {
363
-		$generator = $this->fileAccess->getByAncestorInStorage(
364
-			1,
365
-			1,
366
-			0,
367
-			10,
368
-			[],
369
-			true,
370
-			false, // exclude server-side encrypted files
371
-		);
372
-
373
-		$result = iterator_to_array($generator);
374
-
375
-		$this->assertCount(1, $result);
376
-		$this->assertEquals('files/photos/endtoendencrypted', $result[0]->getPath());
377
-	}
378
-
379
-	/**
380
-	 * Test max result limits.
381
-	 */
382
-	public function testGetByAncestorInStorageWithMaxResults(): void {
383
-		$generator = $this->fileAccess->getByAncestorInStorage(
384
-			1,
385
-			1,
386
-			0,
387
-			1, // Limit to 1 result
388
-			[],
389
-			true,
390
-			true,
391
-		);
392
-
393
-		$result = iterator_to_array($generator);
394
-
395
-		$this->assertCount(1, $result);
396
-		$this->assertEquals('files/documents', $result[0]->getPath());
397
-	}
398
-
399
-	/**
400
-	 * Test rootId filter
401
-	 */
402
-	public function testGetByAncestorInStorageWithRootIdFilter(): void {
403
-		$generator = $this->fileAccess->getByAncestorInStorage(
404
-			1,
405
-			3, // Filter by rootId
406
-			0,
407
-			10,
408
-			[],
409
-			true,
410
-			true,
411
-		);
412
-
413
-		$result = iterator_to_array($generator);
414
-
415
-		$this->assertCount(1, $result);
416
-		$this->assertEquals('files/photos/endtoendencrypted', $result[0]->getPath());
417
-	}
418
-
419
-	/**
420
-	 * Test rootId filter
421
-	 */
422
-	public function testGetByAncestorInStorageWithStorageFilter(): void {
423
-		$generator = $this->fileAccess->getByAncestorInStorage(
424
-			2, // Filter by storage
425
-			6, // and by rootId
426
-			0,
427
-			10,
428
-			[],
429
-			true,
430
-			true,
431
-		);
432
-
433
-		$result = iterator_to_array($generator);
434
-
435
-		$this->assertCount(1, $result);
436
-		$this->assertEquals('files/storage2/file', $result[0]->getPath());
437
-	}
21
+    private IDBConnection $dbConnection;
22
+    private FileAccess $fileAccess;
23
+
24
+    protected function setUp(): void {
25
+        parent::setUp();
26
+
27
+        // Setup the actual database connection (assume the database is configured properly in PHPUnit setup)
28
+        $this->dbConnection = \OCP\Server::get(IDBConnection::class);
29
+
30
+        // Ensure FileAccess is instantiated with the real connection
31
+        $this->fileAccess = new FileAccess(
32
+            $this->dbConnection,
33
+            \OCP\Server::get(\OC\SystemConfig::class),
34
+            \OCP\Server::get(LoggerInterface::class),
35
+            \OCP\Server::get(\OC\FilesMetadata\FilesMetadataManager::class),
36
+            \OCP\Server::get(\OCP\Files\IMimeTypeLoader::class)
37
+        );
38
+
39
+        // Clear and prepare `filecache` table for tests
40
+        $queryBuilder = $this->dbConnection->getQueryBuilder()->runAcrossAllShards();
41
+        $queryBuilder->delete('filecache')->executeStatement();
42
+
43
+        // Clean up potential leftovers from other tests
44
+        $queryBuilder = $this->dbConnection->getQueryBuilder();
45
+        $queryBuilder->delete('mounts')->executeStatement();
46
+
47
+
48
+        $this->setUpTestDatabaseForGetDistinctMounts();
49
+        $this->setUpTestDatabaseForGetByAncestorInStorage();
50
+    }
51
+
52
+    private function setUpTestDatabaseForGetDistinctMounts(): void {
53
+        $queryBuilder = $this->dbConnection->getQueryBuilder();
54
+
55
+        // Insert test data
56
+        $queryBuilder->insert('mounts')
57
+            ->values([
58
+                'storage_id' => $queryBuilder->createNamedParameter(1, IQueryBuilder::PARAM_INT),
59
+                'root_id' => $queryBuilder->createNamedParameter(10, IQueryBuilder::PARAM_INT),
60
+                'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
61
+                'mount_point' => $queryBuilder->createNamedParameter('/files'),
62
+                'user_id' => $queryBuilder->createNamedParameter('test'),
63
+            ])
64
+            ->executeStatement();
65
+
66
+        $queryBuilder->insert('mounts')
67
+            ->values([
68
+                'storage_id' => $queryBuilder->createNamedParameter(3, IQueryBuilder::PARAM_INT),
69
+                'root_id' => $queryBuilder->createNamedParameter(30, IQueryBuilder::PARAM_INT),
70
+                'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
71
+                'mount_point' => $queryBuilder->createNamedParameter('/documents'),
72
+                'user_id' => $queryBuilder->createNamedParameter('test'),
73
+            ])
74
+            ->executeStatement();
75
+
76
+        $queryBuilder->insert('mounts')
77
+            ->values([
78
+                'storage_id' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
79
+                'root_id' => $queryBuilder->createNamedParameter(31, IQueryBuilder::PARAM_INT),
80
+                'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass2'),
81
+                'mount_point' => $queryBuilder->createNamedParameter('/foobar'),
82
+                'user_id' => $queryBuilder->createNamedParameter('test'),
83
+            ])
84
+            ->executeStatement();
85
+    }
86
+
87
+    /**
88
+     * Test that getDistinctMounts returns all mounts without filters
89
+     */
90
+    public function testGetDistinctMountsWithoutFilters(): void {
91
+        $result = iterator_to_array($this->fileAccess->getDistinctMounts([], false));
92
+
93
+        $this->assertCount(3, $result);
94
+
95
+        $this->assertEquals([
96
+            'storage_id' => 1,
97
+            'root_id' => 10,
98
+            'overridden_root' => 10,
99
+        ], $result[0]);
100
+
101
+        $this->assertEquals([
102
+            'storage_id' => 3,
103
+            'root_id' => 30,
104
+            'overridden_root' => 30,
105
+        ], $result[1]);
106
+
107
+        $this->assertEquals([
108
+            'storage_id' => 4,
109
+            'root_id' => 31,
110
+            'overridden_root' => 31,
111
+        ], $result[2]);
112
+    }
113
+
114
+    /**
115
+     * Test that getDistinctMounts applies filtering by mount providers
116
+     */
117
+    public function testGetDistinctMountsWithMountProviderFilter(): void {
118
+        $result = iterator_to_array($this->fileAccess->getDistinctMounts(['TestProviderClass1'], false));
119
+
120
+        $this->assertCount(2, $result);
121
+
122
+        $this->assertEquals([
123
+            'storage_id' => 1,
124
+            'root_id' => 10,
125
+            'overridden_root' => 10,
126
+        ], $result[0]);
127
+
128
+        $this->assertEquals([
129
+            'storage_id' => 3,
130
+            'root_id' => 30,
131
+            'overridden_root' => 30,
132
+        ], $result[1]);
133
+    }
134
+
135
+    /**
136
+     * Test that getDistinctMounts rewrites home directory paths
137
+     */
138
+    public function testGetDistinctMountsWithRewriteHomeDirectories(): void {
139
+        // Add additional test data for a home directory mount
140
+        $queryBuilder = $this->dbConnection->getQueryBuilder();
141
+        $queryBuilder->insert('mounts')
142
+            ->values([
143
+                'storage_id' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
144
+                'root_id' => $queryBuilder->createNamedParameter(40, IQueryBuilder::PARAM_INT),
145
+                'mount_provider_class' => $queryBuilder->createNamedParameter(\OC\Files\Mount\LocalHomeMountProvider::class),
146
+                'mount_point' => $queryBuilder->createNamedParameter('/home/user'),
147
+                'user_id' => $queryBuilder->createNamedParameter('test'),
148
+            ])
149
+            ->executeStatement();
150
+
151
+        // Add a mount that is mounted in the home directory
152
+        $queryBuilder = $this->dbConnection->getQueryBuilder();
153
+        $queryBuilder->insert('mounts')
154
+            ->values([
155
+                'storage_id' => $queryBuilder->createNamedParameter(5, IQueryBuilder::PARAM_INT),
156
+                'root_id' => $queryBuilder->createNamedParameter(41, IQueryBuilder::PARAM_INT),
157
+                'mount_provider_class' => $queryBuilder->createNamedParameter('TestMountProvider3'),
158
+                'mount_point' => $queryBuilder->createNamedParameter('/test/files/foobar'),
159
+                'user_id' => $queryBuilder->createNamedParameter('test'),
160
+            ])
161
+            ->executeStatement();
162
+
163
+        // Simulate adding a "files" directory to the filecache table
164
+        $queryBuilder = $this->dbConnection->getQueryBuilder()->runAcrossAllShards();
165
+        $queryBuilder->delete('filecache')->executeStatement();
166
+        $queryBuilder = $this->dbConnection->getQueryBuilder();
167
+        $queryBuilder->insert('filecache')
168
+            ->values([
169
+                'fileid' => $queryBuilder->createNamedParameter(99, IQueryBuilder::PARAM_INT),
170
+                'storage' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
171
+                'parent' => $queryBuilder->createNamedParameter(40),
172
+                'name' => $queryBuilder->createNamedParameter('files'),
173
+                'path' => $queryBuilder->createNamedParameter('files'),
174
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files')),
175
+            ])
176
+            ->executeStatement();
177
+
178
+        $result = iterator_to_array($this->fileAccess->getDistinctMounts());
179
+
180
+        $this->assertCount(2, $result);
181
+
182
+        $this->assertEquals([
183
+            'storage_id' => 4,
184
+            'root_id' => 40,
185
+            'overridden_root' => 99,
186
+        ], $result[0]);
187
+
188
+        $this->assertEquals([
189
+            'storage_id' => 5,
190
+            'root_id' => 41,
191
+            'overridden_root' => 41,
192
+        ], $result[1]);
193
+    }
194
+
195
+    private function setUpTestDatabaseForGetByAncestorInStorage(): void {
196
+        // prepare `filecache` table for tests
197
+        $queryBuilder = $this->dbConnection->getQueryBuilder();
198
+
199
+        $queryBuilder->insert('filecache')
200
+            ->values([
201
+                'fileid' => 1,
202
+                'parent' => 0,
203
+                'path' => $queryBuilder->createNamedParameter('files'),
204
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files')),
205
+                'storage' => $queryBuilder->createNamedParameter(1),
206
+                'name' => $queryBuilder->createNamedParameter('files'),
207
+                'mimetype' => 1,
208
+                'encrypted' => 0,
209
+            ])
210
+            ->executeStatement();
211
+
212
+        $queryBuilder->insert('filecache')
213
+            ->values([
214
+                'fileid' => 2,
215
+                'parent' => 1,
216
+                'path' => $queryBuilder->createNamedParameter('files/documents'),
217
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files/documents')),
218
+                'storage' => $queryBuilder->createNamedParameter(1),
219
+                'name' => $queryBuilder->createNamedParameter('documents'),
220
+                'mimetype' => 2,
221
+                'encrypted' => 1,
222
+            ])
223
+            ->executeStatement();
224
+
225
+        $queryBuilder->insert('filecache')
226
+            ->values([
227
+                'fileid' => 3,
228
+                'parent' => 1,
229
+                'path' => $queryBuilder->createNamedParameter('files/photos'),
230
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files/photos')),
231
+                'storage' => $queryBuilder->createNamedParameter(1),
232
+                'name' => $queryBuilder->createNamedParameter('photos'),
233
+                'mimetype' => 3,
234
+                'encrypted' => 1,
235
+            ])
236
+            ->executeStatement();
237
+
238
+        $queryBuilder->insert('filecache')
239
+            ->values([
240
+                'fileid' => 4,
241
+                'parent' => 3,
242
+                'path' => $queryBuilder->createNamedParameter('files/photos/endtoendencrypted'),
243
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files/photos/endtoendencrypted')),
244
+                'storage' => $queryBuilder->createNamedParameter(1),
245
+                'name' => $queryBuilder->createNamedParameter('endtoendencrypted'),
246
+                'mimetype' => 4,
247
+                'encrypted' => 0,
248
+            ])
249
+            ->executeStatement();
250
+
251
+        $queryBuilder->insert('filecache')
252
+            ->values([
253
+                'fileid' => 5,
254
+                'parent' => 1,
255
+                'path' => $queryBuilder->createNamedParameter('files/serversideencrypted'),
256
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files/serversideencrypted')),
257
+                'storage' => $queryBuilder->createNamedParameter(1),
258
+                'name' => $queryBuilder->createNamedParameter('serversideencrypted'),
259
+                'mimetype' => 4,
260
+                'encrypted' => 1,
261
+            ])
262
+            ->executeStatement();
263
+
264
+        $queryBuilder->insert('filecache')
265
+            ->values([
266
+                'fileid' => 6,
267
+                'parent' => 0,
268
+                'path' => $queryBuilder->createNamedParameter('files/storage2'),
269
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files/storage2')),
270
+                'storage' => $queryBuilder->createNamedParameter(2),
271
+                'name' => $queryBuilder->createNamedParameter('storage2'),
272
+                'mimetype' => 5,
273
+                'encrypted' => 0,
274
+            ])
275
+            ->executeStatement();
276
+
277
+        $queryBuilder->insert('filecache')
278
+            ->values([
279
+                'fileid' => 7,
280
+                'parent' => 6,
281
+                'path' => $queryBuilder->createNamedParameter('files/storage2/file'),
282
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files/storage2/file')),
283
+                'storage' => $queryBuilder->createNamedParameter(2),
284
+                'name' => $queryBuilder->createNamedParameter('file'),
285
+                'mimetype' => 6,
286
+                'encrypted' => 0,
287
+            ])
288
+            ->executeStatement();
289
+    }
290
+
291
+    /**
292
+     * Test fetching files by ancestor in storage.
293
+     */
294
+    public function testGetByAncestorInStorage(): void {
295
+        $generator = $this->fileAccess->getByAncestorInStorage(
296
+            1, // storageId
297
+            1, // rootId
298
+            0, // lastFileId
299
+            10, // maxResults
300
+            [], // mimeTypes
301
+            true, // include end-to-end encrypted files
302
+            true, // include server-side encrypted files
303
+        );
304
+
305
+        $result = iterator_to_array($generator);
306
+
307
+        $this->assertCount(4, $result);
308
+
309
+        $paths = array_map(fn (CacheEntry $entry) => $entry->getPath(), $result);
310
+        $this->assertEquals([
311
+            'files/documents',
312
+            'files/photos',
313
+            'files/photos/endtoendencrypted',
314
+            'files/serversideencrypted',
315
+        ], $paths);
316
+    }
317
+
318
+    /**
319
+     * Test filtering by mime types.
320
+     */
321
+    public function testGetByAncestorInStorageWithMimeTypes(): void {
322
+        $generator = $this->fileAccess->getByAncestorInStorage(
323
+            1,
324
+            1,
325
+            0,
326
+            10,
327
+            [2], // Only include documents (mimetype=2)
328
+            true,
329
+            true,
330
+        );
331
+
332
+        $result = iterator_to_array($generator);
333
+
334
+        $this->assertCount(1, $result);
335
+        $this->assertEquals('files/documents', $result[0]->getPath());
336
+    }
337
+
338
+    /**
339
+     * Test excluding end-to-end encrypted files.
340
+     */
341
+    public function testGetByAncestorInStorageWithoutEndToEndEncrypted(): void {
342
+        $generator = $this->fileAccess->getByAncestorInStorage(
343
+            1,
344
+            1,
345
+            0,
346
+            10,
347
+            [],
348
+            false, // exclude end-to-end encrypted files
349
+            true,
350
+        );
351
+
352
+        $result = iterator_to_array($generator);
353
+
354
+        $this->assertCount(3, $result);
355
+        $paths = array_map(fn (CacheEntry $entry) => $entry->getPath(), $result);
356
+        $this->assertEquals(['files/documents', 'files/photos', 'files/serversideencrypted'], $paths);
357
+    }
358
+
359
+    /**
360
+     * Test excluding server-side encrypted files.
361
+     */
362
+    public function testGetByAncestorInStorageWithoutServerSideEncrypted(): void {
363
+        $generator = $this->fileAccess->getByAncestorInStorage(
364
+            1,
365
+            1,
366
+            0,
367
+            10,
368
+            [],
369
+            true,
370
+            false, // exclude server-side encrypted files
371
+        );
372
+
373
+        $result = iterator_to_array($generator);
374
+
375
+        $this->assertCount(1, $result);
376
+        $this->assertEquals('files/photos/endtoendencrypted', $result[0]->getPath());
377
+    }
378
+
379
+    /**
380
+     * Test max result limits.
381
+     */
382
+    public function testGetByAncestorInStorageWithMaxResults(): void {
383
+        $generator = $this->fileAccess->getByAncestorInStorage(
384
+            1,
385
+            1,
386
+            0,
387
+            1, // Limit to 1 result
388
+            [],
389
+            true,
390
+            true,
391
+        );
392
+
393
+        $result = iterator_to_array($generator);
394
+
395
+        $this->assertCount(1, $result);
396
+        $this->assertEquals('files/documents', $result[0]->getPath());
397
+    }
398
+
399
+    /**
400
+     * Test rootId filter
401
+     */
402
+    public function testGetByAncestorInStorageWithRootIdFilter(): void {
403
+        $generator = $this->fileAccess->getByAncestorInStorage(
404
+            1,
405
+            3, // Filter by rootId
406
+            0,
407
+            10,
408
+            [],
409
+            true,
410
+            true,
411
+        );
412
+
413
+        $result = iterator_to_array($generator);
414
+
415
+        $this->assertCount(1, $result);
416
+        $this->assertEquals('files/photos/endtoendencrypted', $result[0]->getPath());
417
+    }
418
+
419
+    /**
420
+     * Test rootId filter
421
+     */
422
+    public function testGetByAncestorInStorageWithStorageFilter(): void {
423
+        $generator = $this->fileAccess->getByAncestorInStorage(
424
+            2, // Filter by storage
425
+            6, // and by rootId
426
+            0,
427
+            10,
428
+            [],
429
+            true,
430
+            true,
431
+        );
432
+
433
+        $result = iterator_to_array($generator);
434
+
435
+        $this->assertCount(1, $result);
436
+        $this->assertEquals('files/storage2/file', $result[0]->getPath());
437
+    }
438 438
 }
Please login to merge, or discard this patch.
lib/private/Files/Cache/FileAccess.php 2 patches
Indentation   +198 added lines, -198 removed lines patch added patch discarded remove patch
@@ -21,202 +21,202 @@
 block discarded – undo
21 21
  * Low level access to the file cache
22 22
  */
23 23
 class FileAccess implements IFileAccess {
24
-	public function __construct(
25
-		private IDBConnection $connection,
26
-		private SystemConfig $systemConfig,
27
-		private LoggerInterface $logger,
28
-		private FilesMetadataManager $metadataManager,
29
-		private IMimeTypeLoader $mimeTypeLoader,
30
-	) {
31
-	}
32
-
33
-	private function getQuery(): CacheQueryBuilder {
34
-		return new CacheQueryBuilder(
35
-			$this->connection->getQueryBuilder(),
36
-			$this->metadataManager,
37
-		);
38
-	}
39
-
40
-	public function getByFileIdInStorage(int $fileId, int $storageId): ?CacheEntry {
41
-		$items = array_values($this->getByFileIdsInStorage([$fileId], $storageId));
42
-		return $items[0] ?? null;
43
-	}
44
-
45
-	public function getByPathInStorage(string $path, int $storageId): ?CacheEntry {
46
-		$query = $this->getQuery()->selectFileCache();
47
-		$query->andWhere($query->expr()->eq('filecache.path_hash', $query->createNamedParameter(md5($path))));
48
-		$query->andWhere($query->expr()->eq('filecache.storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
49
-
50
-		$row = $query->executeQuery()->fetch();
51
-		return $row ? Cache::cacheEntryFromData($row, $this->mimeTypeLoader) : null;
52
-	}
53
-
54
-	public function getByFileId(int $fileId): ?CacheEntry {
55
-		$items = array_values($this->getByFileIds([$fileId]));
56
-		return $items[0] ?? null;
57
-	}
58
-
59
-	/**
60
-	 * @param array[] $rows
61
-	 * @return array<int, CacheEntry>
62
-	 */
63
-	private function rowsToEntries(array $rows): array {
64
-		$result = [];
65
-		foreach ($rows as $row) {
66
-			$entry = Cache::cacheEntryFromData($row, $this->mimeTypeLoader);
67
-			$result[$entry->getId()] = $entry;
68
-		}
69
-		return $result;
70
-	}
71
-
72
-	/**
73
-	 * @param int[] $fileIds
74
-	 * @return array<int, CacheEntry>
75
-	 */
76
-	public function getByFileIds(array $fileIds): array {
77
-		$query = $this->getQuery()->selectFileCache();
78
-		$query->andWhere($query->expr()->in('filecache.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)));
79
-
80
-		$rows = $query->executeQuery()->fetchAll();
81
-		return $this->rowsToEntries($rows);
82
-	}
83
-
84
-	/**
85
-	 * @param int[] $fileIds
86
-	 * @param int $storageId
87
-	 * @return array<int, CacheEntry>
88
-	 */
89
-	public function getByFileIdsInStorage(array $fileIds, int $storageId): array {
90
-		$fileIds = array_values($fileIds);
91
-		$query = $this->getQuery()->selectFileCache();
92
-		$query->andWhere($query->expr()->in('filecache.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)));
93
-		$query->andWhere($query->expr()->eq('filecache.storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
94
-
95
-		$rows = $query->executeQuery()->fetchAll();
96
-		return $this->rowsToEntries($rows);
97
-	}
98
-
99
-	public function getByAncestorInStorage(int $storageId, int $folderId, int $fileIdCursor = 0, int $maxResults = 100, array $mimeTypeIds = [], bool $endToEndEncrypted = true, bool $serverSideEncrypted = true): \Generator {
100
-		$qb = $this->getQuery();
101
-		$qb->select('path')
102
-			->from('filecache')
103
-			->where($qb->expr()->eq('fileid', $qb->createNamedParameter($folderId, IQueryBuilder::PARAM_INT)));
104
-		$result = $qb->executeQuery();
105
-		/** @var array{path:string}|false $root */
106
-		$root = $result->fetch();
107
-		$result->closeCursor();
108
-
109
-		if ($root === false) {
110
-			throw new Exception('Could not fetch storage root');
111
-		}
112
-
113
-		$qb = $this->getQuery();
114
-
115
-		$path = $root['path'] === '' ? '' : $root['path'] . '/';
116
-
117
-		$qb->selectDistinct('*')
118
-			->from('filecache', 'f')
119
-			->where($qb->expr()->like('f.path', $qb->createNamedParameter($this->connection->escapeLikeParameter($path) . '%')))
120
-			->andWhere($qb->expr()->eq('f.storage', $qb->createNamedParameter($storageId)))
121
-			->andWhere($qb->expr()->gt('f.fileid', $qb->createNamedParameter($fileIdCursor, IQueryBuilder::PARAM_INT)));
122
-
123
-		if (!$endToEndEncrypted) {
124
-			// End to end encrypted files are descendants of a folder with encrypted=1
125
-			// Use a subquery to check the `encrypted` status of the parent folder
126
-			$subQuery = $this->getQuery()->select('p.encrypted')
127
-				->from('filecache', 'p')
128
-				->andWhere($qb->expr()->eq('p.fileid', 'f.parent'))
129
-				->getSQL();
130
-
131
-			$qb->andWhere(
132
-				$qb->expr()->eq($qb->createFunction(sprintf('(%s)', $subQuery)), $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT))
133
-			);
134
-		}
135
-
136
-		if (!$serverSideEncrypted) {
137
-			// Server side encrypted files have encrypted=1 directly
138
-			$qb->andWhere($qb->expr()->eq('f.encrypted', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
139
-		}
140
-
141
-		if (count($mimeTypeIds) > 0) {
142
-			$qb->andWhere($qb->expr()->in('f.mimetype', $qb->createNamedParameter($mimeTypeIds, IQueryBuilder::PARAM_INT_ARRAY)));
143
-		}
144
-
145
-		if ($maxResults !== 0) {
146
-			$qb->setMaxResults($maxResults);
147
-		}
148
-		$qb->orderBy('f.fileid', 'ASC');
149
-		$files = $qb->executeQuery();
150
-
151
-		while (
152
-			/** @var array */
153
-			$row = $files->fetch()
154
-		) {
155
-			yield Cache::cacheEntryFromData($row, $this->mimeTypeLoader);
156
-		}
157
-
158
-		$files->closeCursor();
159
-	}
160
-
161
-	public function getDistinctMounts(array $mountProviders = [], bool $onlyUserFilesMounts = true): \Generator {
162
-		$qb = $this->connection->getQueryBuilder();
163
-		$qb->selectDistinct(['root_id', 'storage_id', 'mount_provider_class'])
164
-			->from('mounts');
165
-		if ($onlyUserFilesMounts) {
166
-			$qb->andWhere(
167
-				$qb->expr()->orX(
168
-					$qb->expr()->like('mount_point', $qb->createNamedParameter('/%/files/%')),
169
-					$qb->expr()->in('mount_provider_class', $qb->createNamedParameter([
170
-						\OC\Files\Mount\LocalHomeMountProvider::class,
171
-						\OC\Files\Mount\ObjectHomeMountProvider::class,
172
-					], IQueryBuilder::PARAM_STR_ARRAY))
173
-				)
174
-			);
175
-		}
176
-		if (count($mountProviders) > 0) {
177
-			$qb->andWhere($qb->expr()->in('mount_provider_class', $qb->createNamedParameter($mountProviders, IQueryBuilder::PARAM_STR_ARRAY)));
178
-		}
179
-		$qb->orderBy('root_id', 'ASC');
180
-		$result = $qb->executeQuery();
181
-
182
-		while (
183
-			/** @var array{storage_id:int, root_id:int,mount_provider_class:string} $row */
184
-			$row = $result->fetch()
185
-		) {
186
-			$storageId = (int)$row['storage_id'];
187
-			$rootId = (int)$row['root_id'];
188
-			$overrideRoot = $rootId;
189
-			// LocalHomeMountProvider is the default provider for user home directories
190
-			// ObjectHomeMountProvider is the home directory provider for when S3 primary storage is used
191
-			if ($onlyUserFilesMounts && in_array($row['mount_provider_class'], [
192
-				\OC\Files\Mount\LocalHomeMountProvider::class,
193
-				\OC\Files\Mount\ObjectHomeMountProvider::class,
194
-			], true)) {
195
-				// Only crawl files, not cache or trashbin
196
-				$qb = $this->getQuery();
197
-				try {
198
-					$qb->select('fileid')
199
-						->from('filecache')
200
-						->where($qb->expr()->eq('storage', $qb->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
201
-						->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($rootId, IQueryBuilder::PARAM_INT)))
202
-						->andWhere($qb->expr()->eq('path', $qb->createNamedParameter('files')));
203
-					/** @var array|false $root */
204
-					$root = $qb->executeQuery()->fetch();
205
-					if ($root !== false) {
206
-						$overrideRoot = (int)$root['fileid'];
207
-					}
208
-				} catch (Exception $e) {
209
-					$this->logger->error('Could not fetch home storage files root for storage ' . $storageId, ['exception' => $e]);
210
-					continue;
211
-				}
212
-			}
213
-			// Reference to root_id is still necessary even if we have the overridden_root_id, because storage_id and root_id uniquely identify a mount
214
-			yield [
215
-				'storage_id' => $storageId,
216
-				'root_id' => $rootId,
217
-				'overridden_root' => $overrideRoot,
218
-			];
219
-		}
220
-		$result->closeCursor();
221
-	}
24
+    public function __construct(
25
+        private IDBConnection $connection,
26
+        private SystemConfig $systemConfig,
27
+        private LoggerInterface $logger,
28
+        private FilesMetadataManager $metadataManager,
29
+        private IMimeTypeLoader $mimeTypeLoader,
30
+    ) {
31
+    }
32
+
33
+    private function getQuery(): CacheQueryBuilder {
34
+        return new CacheQueryBuilder(
35
+            $this->connection->getQueryBuilder(),
36
+            $this->metadataManager,
37
+        );
38
+    }
39
+
40
+    public function getByFileIdInStorage(int $fileId, int $storageId): ?CacheEntry {
41
+        $items = array_values($this->getByFileIdsInStorage([$fileId], $storageId));
42
+        return $items[0] ?? null;
43
+    }
44
+
45
+    public function getByPathInStorage(string $path, int $storageId): ?CacheEntry {
46
+        $query = $this->getQuery()->selectFileCache();
47
+        $query->andWhere($query->expr()->eq('filecache.path_hash', $query->createNamedParameter(md5($path))));
48
+        $query->andWhere($query->expr()->eq('filecache.storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
49
+
50
+        $row = $query->executeQuery()->fetch();
51
+        return $row ? Cache::cacheEntryFromData($row, $this->mimeTypeLoader) : null;
52
+    }
53
+
54
+    public function getByFileId(int $fileId): ?CacheEntry {
55
+        $items = array_values($this->getByFileIds([$fileId]));
56
+        return $items[0] ?? null;
57
+    }
58
+
59
+    /**
60
+     * @param array[] $rows
61
+     * @return array<int, CacheEntry>
62
+     */
63
+    private function rowsToEntries(array $rows): array {
64
+        $result = [];
65
+        foreach ($rows as $row) {
66
+            $entry = Cache::cacheEntryFromData($row, $this->mimeTypeLoader);
67
+            $result[$entry->getId()] = $entry;
68
+        }
69
+        return $result;
70
+    }
71
+
72
+    /**
73
+     * @param int[] $fileIds
74
+     * @return array<int, CacheEntry>
75
+     */
76
+    public function getByFileIds(array $fileIds): array {
77
+        $query = $this->getQuery()->selectFileCache();
78
+        $query->andWhere($query->expr()->in('filecache.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)));
79
+
80
+        $rows = $query->executeQuery()->fetchAll();
81
+        return $this->rowsToEntries($rows);
82
+    }
83
+
84
+    /**
85
+     * @param int[] $fileIds
86
+     * @param int $storageId
87
+     * @return array<int, CacheEntry>
88
+     */
89
+    public function getByFileIdsInStorage(array $fileIds, int $storageId): array {
90
+        $fileIds = array_values($fileIds);
91
+        $query = $this->getQuery()->selectFileCache();
92
+        $query->andWhere($query->expr()->in('filecache.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)));
93
+        $query->andWhere($query->expr()->eq('filecache.storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
94
+
95
+        $rows = $query->executeQuery()->fetchAll();
96
+        return $this->rowsToEntries($rows);
97
+    }
98
+
99
+    public function getByAncestorInStorage(int $storageId, int $folderId, int $fileIdCursor = 0, int $maxResults = 100, array $mimeTypeIds = [], bool $endToEndEncrypted = true, bool $serverSideEncrypted = true): \Generator {
100
+        $qb = $this->getQuery();
101
+        $qb->select('path')
102
+            ->from('filecache')
103
+            ->where($qb->expr()->eq('fileid', $qb->createNamedParameter($folderId, IQueryBuilder::PARAM_INT)));
104
+        $result = $qb->executeQuery();
105
+        /** @var array{path:string}|false $root */
106
+        $root = $result->fetch();
107
+        $result->closeCursor();
108
+
109
+        if ($root === false) {
110
+            throw new Exception('Could not fetch storage root');
111
+        }
112
+
113
+        $qb = $this->getQuery();
114
+
115
+        $path = $root['path'] === '' ? '' : $root['path'] . '/';
116
+
117
+        $qb->selectDistinct('*')
118
+            ->from('filecache', 'f')
119
+            ->where($qb->expr()->like('f.path', $qb->createNamedParameter($this->connection->escapeLikeParameter($path) . '%')))
120
+            ->andWhere($qb->expr()->eq('f.storage', $qb->createNamedParameter($storageId)))
121
+            ->andWhere($qb->expr()->gt('f.fileid', $qb->createNamedParameter($fileIdCursor, IQueryBuilder::PARAM_INT)));
122
+
123
+        if (!$endToEndEncrypted) {
124
+            // End to end encrypted files are descendants of a folder with encrypted=1
125
+            // Use a subquery to check the `encrypted` status of the parent folder
126
+            $subQuery = $this->getQuery()->select('p.encrypted')
127
+                ->from('filecache', 'p')
128
+                ->andWhere($qb->expr()->eq('p.fileid', 'f.parent'))
129
+                ->getSQL();
130
+
131
+            $qb->andWhere(
132
+                $qb->expr()->eq($qb->createFunction(sprintf('(%s)', $subQuery)), $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT))
133
+            );
134
+        }
135
+
136
+        if (!$serverSideEncrypted) {
137
+            // Server side encrypted files have encrypted=1 directly
138
+            $qb->andWhere($qb->expr()->eq('f.encrypted', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
139
+        }
140
+
141
+        if (count($mimeTypeIds) > 0) {
142
+            $qb->andWhere($qb->expr()->in('f.mimetype', $qb->createNamedParameter($mimeTypeIds, IQueryBuilder::PARAM_INT_ARRAY)));
143
+        }
144
+
145
+        if ($maxResults !== 0) {
146
+            $qb->setMaxResults($maxResults);
147
+        }
148
+        $qb->orderBy('f.fileid', 'ASC');
149
+        $files = $qb->executeQuery();
150
+
151
+        while (
152
+            /** @var array */
153
+            $row = $files->fetch()
154
+        ) {
155
+            yield Cache::cacheEntryFromData($row, $this->mimeTypeLoader);
156
+        }
157
+
158
+        $files->closeCursor();
159
+    }
160
+
161
+    public function getDistinctMounts(array $mountProviders = [], bool $onlyUserFilesMounts = true): \Generator {
162
+        $qb = $this->connection->getQueryBuilder();
163
+        $qb->selectDistinct(['root_id', 'storage_id', 'mount_provider_class'])
164
+            ->from('mounts');
165
+        if ($onlyUserFilesMounts) {
166
+            $qb->andWhere(
167
+                $qb->expr()->orX(
168
+                    $qb->expr()->like('mount_point', $qb->createNamedParameter('/%/files/%')),
169
+                    $qb->expr()->in('mount_provider_class', $qb->createNamedParameter([
170
+                        \OC\Files\Mount\LocalHomeMountProvider::class,
171
+                        \OC\Files\Mount\ObjectHomeMountProvider::class,
172
+                    ], IQueryBuilder::PARAM_STR_ARRAY))
173
+                )
174
+            );
175
+        }
176
+        if (count($mountProviders) > 0) {
177
+            $qb->andWhere($qb->expr()->in('mount_provider_class', $qb->createNamedParameter($mountProviders, IQueryBuilder::PARAM_STR_ARRAY)));
178
+        }
179
+        $qb->orderBy('root_id', 'ASC');
180
+        $result = $qb->executeQuery();
181
+
182
+        while (
183
+            /** @var array{storage_id:int, root_id:int,mount_provider_class:string} $row */
184
+            $row = $result->fetch()
185
+        ) {
186
+            $storageId = (int)$row['storage_id'];
187
+            $rootId = (int)$row['root_id'];
188
+            $overrideRoot = $rootId;
189
+            // LocalHomeMountProvider is the default provider for user home directories
190
+            // ObjectHomeMountProvider is the home directory provider for when S3 primary storage is used
191
+            if ($onlyUserFilesMounts && in_array($row['mount_provider_class'], [
192
+                \OC\Files\Mount\LocalHomeMountProvider::class,
193
+                \OC\Files\Mount\ObjectHomeMountProvider::class,
194
+            ], true)) {
195
+                // Only crawl files, not cache or trashbin
196
+                $qb = $this->getQuery();
197
+                try {
198
+                    $qb->select('fileid')
199
+                        ->from('filecache')
200
+                        ->where($qb->expr()->eq('storage', $qb->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
201
+                        ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($rootId, IQueryBuilder::PARAM_INT)))
202
+                        ->andWhere($qb->expr()->eq('path', $qb->createNamedParameter('files')));
203
+                    /** @var array|false $root */
204
+                    $root = $qb->executeQuery()->fetch();
205
+                    if ($root !== false) {
206
+                        $overrideRoot = (int)$root['fileid'];
207
+                    }
208
+                } catch (Exception $e) {
209
+                    $this->logger->error('Could not fetch home storage files root for storage ' . $storageId, ['exception' => $e]);
210
+                    continue;
211
+                }
212
+            }
213
+            // Reference to root_id is still necessary even if we have the overridden_root_id, because storage_id and root_id uniquely identify a mount
214
+            yield [
215
+                'storage_id' => $storageId,
216
+                'root_id' => $rootId,
217
+                'overridden_root' => $overrideRoot,
218
+            ];
219
+        }
220
+        $result->closeCursor();
221
+    }
222 222
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -112,11 +112,11 @@  discard block
 block discarded – undo
112 112
 
113 113
 		$qb = $this->getQuery();
114 114
 
115
-		$path = $root['path'] === '' ? '' : $root['path'] . '/';
115
+		$path = $root['path'] === '' ? '' : $root['path'].'/';
116 116
 
117 117
 		$qb->selectDistinct('*')
118 118
 			->from('filecache', 'f')
119
-			->where($qb->expr()->like('f.path', $qb->createNamedParameter($this->connection->escapeLikeParameter($path) . '%')))
119
+			->where($qb->expr()->like('f.path', $qb->createNamedParameter($this->connection->escapeLikeParameter($path).'%')))
120 120
 			->andWhere($qb->expr()->eq('f.storage', $qb->createNamedParameter($storageId)))
121 121
 			->andWhere($qb->expr()->gt('f.fileid', $qb->createNamedParameter($fileIdCursor, IQueryBuilder::PARAM_INT)));
122 122
 
@@ -183,8 +183,8 @@  discard block
 block discarded – undo
183 183
 			/** @var array{storage_id:int, root_id:int,mount_provider_class:string} $row */
184 184
 			$row = $result->fetch()
185 185
 		) {
186
-			$storageId = (int)$row['storage_id'];
187
-			$rootId = (int)$row['root_id'];
186
+			$storageId = (int) $row['storage_id'];
187
+			$rootId = (int) $row['root_id'];
188 188
 			$overrideRoot = $rootId;
189 189
 			// LocalHomeMountProvider is the default provider for user home directories
190 190
 			// ObjectHomeMountProvider is the home directory provider for when S3 primary storage is used
@@ -203,10 +203,10 @@  discard block
 block discarded – undo
203 203
 					/** @var array|false $root */
204 204
 					$root = $qb->executeQuery()->fetch();
205 205
 					if ($root !== false) {
206
-						$overrideRoot = (int)$root['fileid'];
206
+						$overrideRoot = (int) $root['fileid'];
207 207
 					}
208 208
 				} catch (Exception $e) {
209
-					$this->logger->error('Could not fetch home storage files root for storage ' . $storageId, ['exception' => $e]);
209
+					$this->logger->error('Could not fetch home storage files root for storage '.$storageId, ['exception' => $e]);
210 210
 					continue;
211 211
 				}
212 212
 			}
Please login to merge, or discard this patch.
lib/public/Files/Cache/IFileAccess.php 1 patch
Indentation   +88 added lines, -88 removed lines patch added patch discarded remove patch
@@ -17,98 +17,98 @@
 block discarded – undo
17 17
  * @since 29.0.0
18 18
  */
19 19
 interface IFileAccess {
20
-	/**
21
-	 * Get a filecache data by file id from a specific storage.
22
-	 *
23
-	 * This is preferred over `getByFileId` when the storage id is known as it
24
-	 * can be more efficient in some setups.
25
-	 *
26
-	 * @param int $fileId
27
-	 * @param int $storageId
28
-	 * @return ICacheEntry|null
29
-	 *
30
-	 * @since 29.0.0
31
-	 */
32
-	public function getByFileIdInStorage(int $fileId, int $storageId): ?ICacheEntry;
20
+    /**
21
+     * Get a filecache data by file id from a specific storage.
22
+     *
23
+     * This is preferred over `getByFileId` when the storage id is known as it
24
+     * can be more efficient in some setups.
25
+     *
26
+     * @param int $fileId
27
+     * @param int $storageId
28
+     * @return ICacheEntry|null
29
+     *
30
+     * @since 29.0.0
31
+     */
32
+    public function getByFileIdInStorage(int $fileId, int $storageId): ?ICacheEntry;
33 33
 
34
-	/**
35
-	 * Get a filecache data by path and storage id.
36
-	 *
37
-	 * @param string $path
38
-	 * @param int $storageId
39
-	 * @return ICacheEntry|null
40
-	 *
41
-	 * @since 29.0.0
42
-	 */
43
-	public function getByPathInStorage(string $path, int $storageId): ?ICacheEntry;
34
+    /**
35
+     * Get a filecache data by path and storage id.
36
+     *
37
+     * @param string $path
38
+     * @param int $storageId
39
+     * @return ICacheEntry|null
40
+     *
41
+     * @since 29.0.0
42
+     */
43
+    public function getByPathInStorage(string $path, int $storageId): ?ICacheEntry;
44 44
 
45
-	/**
46
-	 * Get a filecache data by file id.
47
-	 *
48
-	 * If the storage id is known then `getByFileIdInStorage` is preferred as it can be more efficient in some setups.
49
-	 *
50
-	 * @param int $fileId
51
-	 * @return ICacheEntry|null
52
-	 *
53
-	 * @since 29.0.0
54
-	 */
55
-	public function getByFileId(int $fileId): ?ICacheEntry;
45
+    /**
46
+     * Get a filecache data by file id.
47
+     *
48
+     * If the storage id is known then `getByFileIdInStorage` is preferred as it can be more efficient in some setups.
49
+     *
50
+     * @param int $fileId
51
+     * @return ICacheEntry|null
52
+     *
53
+     * @since 29.0.0
54
+     */
55
+    public function getByFileId(int $fileId): ?ICacheEntry;
56 56
 
57
-	/**
58
-	 * Get filecache data by file ids.
59
-	 *
60
-	 * If the storage id is known then `getByFileIdsInStorage` is preferred as it can be more efficient in some setups.
61
-	 *
62
-	 * @param int[] $fileIds
63
-	 * @return array<int, ICacheEntry>
64
-	 *
65
-	 * @since 29.0.0
66
-	 */
67
-	public function getByFileIds(array $fileIds): array;
57
+    /**
58
+     * Get filecache data by file ids.
59
+     *
60
+     * If the storage id is known then `getByFileIdsInStorage` is preferred as it can be more efficient in some setups.
61
+     *
62
+     * @param int[] $fileIds
63
+     * @return array<int, ICacheEntry>
64
+     *
65
+     * @since 29.0.0
66
+     */
67
+    public function getByFileIds(array $fileIds): array;
68 68
 
69
-	/**
70
-	 * Get filecache data by file ids from a specific storage.
71
-	 *
72
-	 * This is prefered over `getByFileIds` when the storage id is known as it
73
-	 * can be more efficient in some setups.
74
-	 *
75
-	 * @param int[] $fileIds
76
-	 * @param int $storageId
77
-	 * @return array<int, ICacheEntry>
78
-	 *
79
-	 * @since 29.0.0
80
-	 */
81
-	public function getByFileIdsInStorage(array $fileIds, int $storageId): array;
69
+    /**
70
+     * Get filecache data by file ids from a specific storage.
71
+     *
72
+     * This is prefered over `getByFileIds` when the storage id is known as it
73
+     * can be more efficient in some setups.
74
+     *
75
+     * @param int[] $fileIds
76
+     * @param int $storageId
77
+     * @return array<int, ICacheEntry>
78
+     *
79
+     * @since 29.0.0
80
+     */
81
+    public function getByFileIdsInStorage(array $fileIds, int $storageId): array;
82 82
 
83
-	/**
84
-	 * Retrieves files stored in a specific storage that have a specified ancestor in the file hierarchy.
85
-	 * Allows filtering by mime types, encryption status, and limits the number of results.
86
-	 *
87
-	 * @param int $storageId The ID of the storage to search within.
88
-	 * @param int $folderId The file ID of the ancestor to base the search on.
89
-	 * @param int $fileIdCursor The last processed file ID. Only files with a higher ID will be included. Defaults to 0.
90
-	 * @param int $maxResults The maximum number of results to retrieve. If set to 0, all matching files will be retrieved.
91
-	 * @param list<int> $mimeTypeIds An array of mime types to filter the results. If empty, no mime type filtering will be applied.
92
-	 * @param bool $endToEndEncrypted Whether to include EndToEndEncrypted files
93
-	 * @param bool $serverSideEncrypted Whether to include ServerSideEncrypted files
94
-	 * @return \Generator<ICacheEntry> A generator yielding matching files as cache entries.
95
-	 * @throws \OCP\DB\Exception
96
-	 *
97
-	 * @since 32.0.0
98
-	 */
99
-	public function getByAncestorInStorage(int $storageId, int $folderId, int $fileIdCursor = 0, int $maxResults = 100, array $mimeTypeIds = [], bool $endToEndEncrypted = true, bool $serverSideEncrypted = true): \Generator;
83
+    /**
84
+     * Retrieves files stored in a specific storage that have a specified ancestor in the file hierarchy.
85
+     * Allows filtering by mime types, encryption status, and limits the number of results.
86
+     *
87
+     * @param int $storageId The ID of the storage to search within.
88
+     * @param int $folderId The file ID of the ancestor to base the search on.
89
+     * @param int $fileIdCursor The last processed file ID. Only files with a higher ID will be included. Defaults to 0.
90
+     * @param int $maxResults The maximum number of results to retrieve. If set to 0, all matching files will be retrieved.
91
+     * @param list<int> $mimeTypeIds An array of mime types to filter the results. If empty, no mime type filtering will be applied.
92
+     * @param bool $endToEndEncrypted Whether to include EndToEndEncrypted files
93
+     * @param bool $serverSideEncrypted Whether to include ServerSideEncrypted files
94
+     * @return \Generator<ICacheEntry> A generator yielding matching files as cache entries.
95
+     * @throws \OCP\DB\Exception
96
+     *
97
+     * @since 32.0.0
98
+     */
99
+    public function getByAncestorInStorage(int $storageId, int $folderId, int $fileIdCursor = 0, int $maxResults = 100, array $mimeTypeIds = [], bool $endToEndEncrypted = true, bool $serverSideEncrypted = true): \Generator;
100 100
 
101
-	/**
102
-	 * Retrieves a list of all distinct mounts.
103
-	 * Allows filtering by specific mount providers.
104
-	 * Optionally rewrites home directory root paths to avoid cache and trashbin.
105
-	 *
106
-	 * @param list<string> $mountProviders An array of mount provider class names to filter. If empty, all providers will be included.
107
-	 * @param bool $onlyUserFilesMounts Whether to rewrite the root IDs for home directories to only include user files and to only consider mounts with mount points in the user files.
108
-	 * @return \Generator<array{storage_id: int, root_id: int, overridden_root: int}> A generator yielding mount configurations as an array containing 'storage_id', 'root_id', and 'override_root'.
109
-	 * @throws \OCP\DB\Exception
110
-	 *
111
-	 * @since 32.0.0
112
-	 */
113
-	public function getDistinctMounts(array $mountProviders = [], bool $onlyUserFilesMounts = true): \Generator;
101
+    /**
102
+     * Retrieves a list of all distinct mounts.
103
+     * Allows filtering by specific mount providers.
104
+     * Optionally rewrites home directory root paths to avoid cache and trashbin.
105
+     *
106
+     * @param list<string> $mountProviders An array of mount provider class names to filter. If empty, all providers will be included.
107
+     * @param bool $onlyUserFilesMounts Whether to rewrite the root IDs for home directories to only include user files and to only consider mounts with mount points in the user files.
108
+     * @return \Generator<array{storage_id: int, root_id: int, overridden_root: int}> A generator yielding mount configurations as an array containing 'storage_id', 'root_id', and 'override_root'.
109
+     * @throws \OCP\DB\Exception
110
+     *
111
+     * @since 32.0.0
112
+     */
113
+    public function getDistinctMounts(array $mountProviders = [], bool $onlyUserFilesMounts = true): \Generator;
114 114
 }
Please login to merge, or discard this patch.