Skip to content

Commit 3d113da

Browse files
committed
fix(UserMountCache): Add back unique index for oc_mounts and use normal insert
Signed-off-by: provokateurin <[email protected]>
1 parent 2a96020 commit 3d113da

File tree

8 files changed

+97
-37
lines changed

8 files changed

+97
-37
lines changed

core/Listener/AddMissingIndicesListener.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,6 @@ public function handle(Event $event): void {
186186
'mounts_class_index',
187187
['mount_provider_class']
188188
);
189-
$event->addMissingIndex(
190-
'mounts',
191-
'mounts_user_root_path_index',
192-
['user_id', 'root_id', 'mount_point'],
193-
['lengths' => [null, null, 128]]
194-
);
195189

196190
$event->addMissingIndex(
197191
'systemtag_object_mapping',
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OC\Core\Migrations;
11+
12+
use Closure;
13+
use OCP\DB\ISchemaWrapper;
14+
use OCP\DB\Types;
15+
use OCP\IDBConnection;
16+
use OCP\Migration\IOutput;
17+
use OCP\Migration\SimpleMigrationStep;
18+
use Override;
19+
20+
class Version33000Date20251209123503 extends SimpleMigrationStep {
21+
public function __construct(
22+
private readonly IDBConnection $connection,
23+
) {
24+
}
25+
26+
#[Override]
27+
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
28+
$this->connection->truncateTable('mounts', false);
29+
}
30+
31+
#[Override]
32+
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
33+
/** @var ISchemaWrapper $schema */
34+
$schema = $schemaClosure();
35+
36+
$table = $schema->getTable('mounts');
37+
if (!$table->hasColumn('mount_point_hash')) {
38+
$table->addColumn('mount_point_hash', Types::STRING, [
39+
'notnull' => true,
40+
'length' => 32, // xxh128
41+
]);
42+
$table->dropIndex('mounts_user_root_path_index');
43+
$table->addUniqueIndex(['user_id', 'root_id', 'mount_point_hash'], 'mounts_user_root_path_index');
44+
return $schema;
45+
}
46+
47+
return null;
48+
}
49+
}

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,7 @@
15291529
'OC\\Core\\Migrations\\Version32000Date20250620081925' => $baseDir . '/core/Migrations/Version32000Date20250620081925.php',
15301530
'OC\\Core\\Migrations\\Version32000Date20250731062008' => $baseDir . '/core/Migrations/Version32000Date20250731062008.php',
15311531
'OC\\Core\\Migrations\\Version32000Date20250806110519' => $baseDir . '/core/Migrations/Version32000Date20250806110519.php',
1532+
'OC\\Core\\Migrations\\Version33000Date20251209123503' => $baseDir . '/core/Migrations/Version33000Date20251209123503.php',
15321533
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
15331534
'OC\\Core\\ResponseDefinitions' => $baseDir . '/core/ResponseDefinitions.php',
15341535
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',

lib/composer/composer/autoload_static.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,32 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
1111
);
1212

1313
public static $prefixLengthsPsr4 = array (
14-
'O' =>
14+
'O' =>
1515
array (
1616
'OC\\Core\\' => 8,
1717
'OC\\' => 3,
1818
'OCP\\' => 4,
1919
),
20-
'N' =>
20+
'N' =>
2121
array (
2222
'NCU\\' => 4,
2323
),
2424
);
2525

2626
public static $prefixDirsPsr4 = array (
27-
'OC\\Core\\' =>
27+
'OC\\Core\\' =>
2828
array (
2929
0 => __DIR__ . '/../../..' . '/core',
3030
),
31-
'OC\\' =>
31+
'OC\\' =>
3232
array (
3333
0 => __DIR__ . '/../../..' . '/lib/private',
3434
),
35-
'OCP\\' =>
35+
'OCP\\' =>
3636
array (
3737
0 => __DIR__ . '/../../..' . '/lib/public',
3838
),
39-
'NCU\\' =>
39+
'NCU\\' =>
4040
array (
4141
0 => __DIR__ . '/../../..' . '/lib/unstable',
4242
),
@@ -1570,6 +1570,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
15701570
'OC\\Core\\Migrations\\Version32000Date20250620081925' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250620081925.php',
15711571
'OC\\Core\\Migrations\\Version32000Date20250731062008' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250731062008.php',
15721572
'OC\\Core\\Migrations\\Version32000Date20250806110519' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250806110519.php',
1573+
'OC\\Core\\Migrations\\Version33000Date20251209123503' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20251209123503.php',
15731574
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
15741575
'OC\\Core\\ResponseDefinitions' => __DIR__ . '/../../..' . '/core/ResponseDefinitions.php',
15751576
'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',

lib/private/Files/Config/UserMountCache.php

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
use OC\User\LazyUser;
1111
use OCP\Cache\CappedMemoryCache;
12+
use OCP\DB\Exception;
1213
use OCP\DB\QueryBuilder\IQueryBuilder;
1314
use OCP\Diagnostics\IEventLogger;
1415
use OCP\EventDispatcher\IEventDispatcher;
@@ -164,14 +165,25 @@ private function findChangedMounts(array $newMounts, array $cachedMounts): array
164165

165166
private function addToCache(ICachedMountInfo $mount) {
166167
if ($mount->getStorageId() !== -1) {
167-
$this->connection->insertIfNotExist('*PREFIX*mounts', [
168-
'storage_id' => $mount->getStorageId(),
169-
'root_id' => $mount->getRootId(),
170-
'user_id' => $mount->getUser()->getUID(),
171-
'mount_point' => $mount->getMountPoint(),
172-
'mount_id' => $mount->getMountId(),
173-
'mount_provider_class' => $mount->getMountProvider(),
174-
], ['root_id', 'user_id', 'mount_point']);
168+
$qb = $this->connection->getQueryBuilder();
169+
$qb
170+
->insert('mounts')
171+
->values([
172+
'storage_id' => $qb->createNamedParameter($mount->getStorageId(), IQueryBuilder::PARAM_INT),
173+
'root_id' => $qb->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT),
174+
'user_id' => $qb->createNamedParameter($mount->getUser()->getUID()),
175+
'mount_point' => $qb->createNamedParameter($mount->getMountPoint()),
176+
'mount_point_hash' => $qb->createNamedParameter(hash('xxh128', $mount->getMountPoint())),
177+
'mount_id' => $qb->createNamedParameter($mount->getMountId(), IQueryBuilder::PARAM_INT),
178+
'mount_provider_class' => $qb->createNamedParameter($mount->getMountProvider()),
179+
]);
180+
try {
181+
$qb->executeStatement();
182+
} catch (Exception $e) {
183+
if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
184+
throw $e;
185+
}
186+
}
175187
} else {
176188
// in some cases this is legitimate, like orphaned shares
177189
$this->logger->debug('Could not get storage info for mount at ' . $mount->getMountPoint());
@@ -184,6 +196,7 @@ private function updateCachedMount(ICachedMountInfo $mount) {
184196
$query = $builder->update('mounts')
185197
->set('storage_id', $builder->createNamedParameter($mount->getStorageId()))
186198
->set('mount_point', $builder->createNamedParameter($mount->getMountPoint()))
199+
->set('mount_point_hash', $builder->createNamedParameter(hash('xxh128', $mount->getMountPoint())))
187200
->set('mount_id', $builder->createNamedParameter($mount->getMountId(), IQueryBuilder::PARAM_INT))
188201
->set('mount_provider_class', $builder->createNamedParameter($mount->getMountProvider()))
189202
->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
@@ -198,7 +211,7 @@ private function removeFromCache(ICachedMountInfo $mount) {
198211
$query = $builder->delete('mounts')
199212
->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
200213
->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT)))
201-
->andWhere($builder->expr()->eq('mount_point', $builder->createNamedParameter($mount->getMountPoint())));
214+
->andWhere($builder->expr()->eq('mount_point_hash', $builder->createNamedParameter(hash('xxh128', $mount->getMountPoint()))));
202215
$query->executeStatement();
203216
}
204217

@@ -449,16 +462,8 @@ public function remoteStorageMounts($storageId) {
449462
public function getUsedSpaceForUsers(array $users) {
450463
$builder = $this->connection->getQueryBuilder();
451464

452-
$slash = $builder->createNamedParameter('/');
453-
454-
$mountPoint = $builder->func()->concat(
455-
$builder->func()->concat($slash, 'user_id'),
456-
$slash
457-
);
458-
459-
$userIds = array_map(function (IUser $user) {
460-
return $user->getUID();
461-
}, $users);
465+
$mountPointHashes = array_map(static fn (IUser $user) => hash('xxh128', '/' . $user->getUID() . '/'), $users);
466+
$userIds = array_map(static fn (IUser $user) => $user->getUID(), $users);
462467

463468
$query = $builder->select('m.user_id', 'f.size')
464469
->from('mounts', 'm')
@@ -467,7 +472,7 @@ public function getUsedSpaceForUsers(array $users) {
467472
$builder->expr()->eq('m.storage_id', 'f.storage'),
468473
$builder->expr()->eq('f.path_hash', $builder->createNamedParameter(md5('files')))
469474
))
470-
->where($builder->expr()->eq('m.mount_point', $mountPoint))
475+
->where($builder->expr()->in('m.mount_point_hash', $builder->createNamedParameter($mountPointHashes, IQueryBuilder::PARAM_STR_ARRAY)))
471476
->andWhere($builder->expr()->in('m.user_id', $builder->createNamedParameter($userIds, IQueryBuilder::PARAM_STR_ARRAY)));
472477

473478
$result = $query->executeQuery();

tests/lib/DB/QueryBuilder/Partitioned/PartitionedQueryBuilderTest.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ private function setupFileCache(): void {
8787
'storage_id' => $query->createNamedParameter(1001001, IQueryBuilder::PARAM_INT),
8888
'user_id' => $query->createNamedParameter('partitioned_test'),
8989
'mount_point' => $query->createNamedParameter('/mount/point'),
90+
'mount_point_hash' => $query->createNamedParameter(hash('xxh128', '/mount/point')),
9091
'mount_provider_class' => $query->createNamedParameter('test'),
9192
'root_id' => $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT),
9293
]);
@@ -135,7 +136,7 @@ public function testSimplePartitionedQuery(): void {
135136
$builder->addPartition(new PartitionSplit('filecache', ['filecache']));
136137

137138
// query borrowed from UserMountCache
138-
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class')
139+
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_point_hash', 'mount_id', 'f.path', 'mount_provider_class')
139140
->from('mounts', 'm')
140141
->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
141142
->where($builder->expr()->eq('storage_id', $builder->createNamedParameter(1001001, IQueryBuilder::PARAM_INT)));
@@ -148,6 +149,7 @@ public function testSimplePartitionedQuery(): void {
148149
$this->assertCount(1, $results);
149150
$this->assertEquals($results[0]['user_id'], 'partitioned_test');
150151
$this->assertEquals($results[0]['mount_point'], '/mount/point');
152+
$this->assertEquals($results[0]['mount_point_hash'], hash('xxh128', '/mount/point'));
151153
$this->assertEquals($results[0]['mount_provider_class'], 'test');
152154
$this->assertEquals($results[0]['path'], 'file1');
153155
}
@@ -156,7 +158,7 @@ public function testMultiTablePartitionedQuery(): void {
156158
$builder = $this->getQueryBuilder();
157159
$builder->addPartition(new PartitionSplit('filecache', ['filecache', 'filecache_extended']));
158160

159-
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class', 'fe.upload_time')
161+
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_point_hash', 'mount_id', 'f.path', 'mount_provider_class', 'fe.upload_time')
160162
->from('mounts', 'm')
161163
->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
162164
->innerJoin('f', 'filecache_extended', 'fe', $builder->expr()->eq('f.fileid', 'fe.fileid'))
@@ -170,6 +172,7 @@ public function testMultiTablePartitionedQuery(): void {
170172
$this->assertCount(1, $results);
171173
$this->assertEquals($results[0]['user_id'], 'partitioned_test');
172174
$this->assertEquals($results[0]['mount_point'], '/mount/point');
175+
$this->assertEquals($results[0]['mount_point_hash'], hash('xxh128', '/mount/point'));
173176
$this->assertEquals($results[0]['mount_provider_class'], 'test');
174177
$this->assertEquals($results[0]['path'], 'file1');
175178
$this->assertEquals($results[0]['upload_time'], 1234);
@@ -179,7 +182,7 @@ public function testPartitionedQueryFromSplit(): void {
179182
$builder = $this->getQueryBuilder();
180183
$builder->addPartition(new PartitionSplit('filecache', ['filecache']));
181184

182-
$query = $builder->select('storage', 'm.root_id', 'm.user_id', 'm.mount_point', 'm.mount_id', 'path', 'm.mount_provider_class')
185+
$query = $builder->select('storage', 'm.root_id', 'm.user_id', 'm.mount_point', 'm.mount_point_hash', 'm.mount_id', 'path', 'm.mount_provider_class')
183186
->from('filecache', 'f')
184187
->innerJoin('f', 'mounts', 'm', $builder->expr()->eq('m.root_id', 'f.fileid'));
185188
$query->where($builder->expr()->eq('storage', $builder->createNamedParameter(1001001, IQueryBuilder::PARAM_INT)));
@@ -192,6 +195,7 @@ public function testPartitionedQueryFromSplit(): void {
192195
$this->assertCount(1, $results);
193196
$this->assertEquals($results[0]['user_id'], 'partitioned_test');
194197
$this->assertEquals($results[0]['mount_point'], '/mount/point');
198+
$this->assertEquals($results[0]['mount_point_hash'], hash('xxh128', '/mount/point'));
195199
$this->assertEquals($results[0]['mount_provider_class'], 'test');
196200
$this->assertEquals($results[0]['path'], 'file1');
197201
}
@@ -201,7 +205,7 @@ public function testMultiJoinPartitionedQuery(): void {
201205
$builder->addPartition(new PartitionSplit('filecache', ['filecache']));
202206

203207
// query borrowed from UserMountCache
204-
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class')
208+
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_point_hash', 'mount_id', 'f.path', 'mount_provider_class')
205209
->selectAlias('s.id', 'storage_string_id')
206210
->from('mounts', 'm')
207211
->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
@@ -216,6 +220,7 @@ public function testMultiJoinPartitionedQuery(): void {
216220
$this->assertCount(1, $results);
217221
$this->assertEquals($results[0]['user_id'], 'partitioned_test');
218222
$this->assertEquals($results[0]['mount_point'], '/mount/point');
223+
$this->assertEquals($results[0]['mount_point_hash'], hash('xxh128', '/mount/point'));
219224
$this->assertEquals($results[0]['mount_provider_class'], 'test');
220225
$this->assertEquals($results[0]['path'], 'file1');
221226
$this->assertEquals($results[0]['storage_string_id'], 'test1');

tests/lib/Files/Cache/FileAccessTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ private function setUpTestDatabaseForGetDistinctMounts(): void {
6464
'root_id' => $queryBuilder->createNamedParameter(10, IQueryBuilder::PARAM_INT),
6565
'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
6666
'mount_point' => $queryBuilder->createNamedParameter('/files'),
67+
'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/files')),
6768
'user_id' => $queryBuilder->createNamedParameter('test'),
6869
])
6970
->executeStatement();
@@ -74,6 +75,7 @@ private function setUpTestDatabaseForGetDistinctMounts(): void {
7475
'root_id' => $queryBuilder->createNamedParameter(30, IQueryBuilder::PARAM_INT),
7576
'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
7677
'mount_point' => $queryBuilder->createNamedParameter('/documents'),
78+
'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/documents')),
7779
'user_id' => $queryBuilder->createNamedParameter('test'),
7880
])
7981
->executeStatement();
@@ -84,6 +86,7 @@ private function setUpTestDatabaseForGetDistinctMounts(): void {
8486
'root_id' => $queryBuilder->createNamedParameter(31, IQueryBuilder::PARAM_INT),
8587
'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass2'),
8688
'mount_point' => $queryBuilder->createNamedParameter('/foobar'),
89+
'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/foobar')),
8790
'user_id' => $queryBuilder->createNamedParameter('test'),
8891
])
8992
->executeStatement();
@@ -149,6 +152,7 @@ public function testGetDistinctMountsWithRewriteHomeDirectories(): void {
149152
'root_id' => $queryBuilder->createNamedParameter(40, IQueryBuilder::PARAM_INT),
150153
'mount_provider_class' => $queryBuilder->createNamedParameter(LocalHomeMountProvider::class),
151154
'mount_point' => $queryBuilder->createNamedParameter('/home/user'),
155+
'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/home/user')),
152156
'user_id' => $queryBuilder->createNamedParameter('test'),
153157
])
154158
->executeStatement();
@@ -161,6 +165,7 @@ public function testGetDistinctMountsWithRewriteHomeDirectories(): void {
161165
'root_id' => $queryBuilder->createNamedParameter(41, IQueryBuilder::PARAM_INT),
162166
'mount_provider_class' => $queryBuilder->createNamedParameter('TestMountProvider3'),
163167
'mount_point' => $queryBuilder->createNamedParameter('/test/files/foobar'),
168+
'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/test/files/foobar')),
164169
'user_id' => $queryBuilder->createNamedParameter('test'),
165170
])
166171
->executeStatement();

version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patch level
1010
// when updating major/minor version number.
1111

12-
$OC_Version = [32, 0, 3, 2];
12+
$OC_Version = [32, 0, 3, 3];
1313

1414
// The human-readable string
1515
$OC_VersionString = '32.0.3';

0 commit comments

Comments
 (0)