From 6cc697e47645bb3624b258d6db4c6707398cc28a Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Tue, 16 Sep 2025 10:46:45 -0100 Subject: [PATCH 1/3] fix(userconfig): set 'mail' as indexed Signed-off-by: Maxence Lange --- .../composer/composer/autoload_classmap.php | 1 + .../composer/composer/autoload_static.php | 1 + apps/settings/lib/AppInfo/Application.php | 3 + apps/settings/lib/ConfigLexicon.php | 38 ++++++++++ lib/private/App/AppManager.php | 1 + lib/private/Config/ConfigManager.php | 15 ++++ lib/private/Config/UserConfig.php | 75 ++++++++++++------- lib/private/Repair/ConfigKeyMigration.php | 1 + 8 files changed, 108 insertions(+), 27 deletions(-) create mode 100644 apps/settings/lib/ConfigLexicon.php diff --git a/apps/settings/composer/composer/autoload_classmap.php b/apps/settings/composer/composer/autoload_classmap.php index bb63026da77a3..b12f345e05bc9 100644 --- a/apps/settings/composer/composer/autoload_classmap.php +++ b/apps/settings/composer/composer/autoload_classmap.php @@ -19,6 +19,7 @@ 'OCA\\Settings\\Command\\AdminDelegation\\Add' => $baseDir . '/../lib/Command/AdminDelegation/Add.php', 'OCA\\Settings\\Command\\AdminDelegation\\Remove' => $baseDir . '/../lib/Command/AdminDelegation/Remove.php', 'OCA\\Settings\\Command\\AdminDelegation\\Show' => $baseDir . '/../lib/Command/AdminDelegation/Show.php', + 'OCA\\Settings\\ConfigLexicon' => $baseDir . '/../lib/ConfigLexicon.php', 'OCA\\Settings\\Controller\\AISettingsController' => $baseDir . '/../lib/Controller/AISettingsController.php', 'OCA\\Settings\\Controller\\AdminSettingsController' => $baseDir . '/../lib/Controller/AdminSettingsController.php', 'OCA\\Settings\\Controller\\AppSettingsController' => $baseDir . '/../lib/Controller/AppSettingsController.php', diff --git a/apps/settings/composer/composer/autoload_static.php b/apps/settings/composer/composer/autoload_static.php index cca48b409ad15..e6e2ed9f45cf4 100644 --- a/apps/settings/composer/composer/autoload_static.php +++ b/apps/settings/composer/composer/autoload_static.php @@ -34,6 +34,7 @@ class ComposerStaticInitSettings 'OCA\\Settings\\Command\\AdminDelegation\\Add' => __DIR__ . '/..' . '/../lib/Command/AdminDelegation/Add.php', 'OCA\\Settings\\Command\\AdminDelegation\\Remove' => __DIR__ . '/..' . '/../lib/Command/AdminDelegation/Remove.php', 'OCA\\Settings\\Command\\AdminDelegation\\Show' => __DIR__ . '/..' . '/../lib/Command/AdminDelegation/Show.php', + 'OCA\\Settings\\ConfigLexicon' => __DIR__ . '/..' . '/../lib/ConfigLexicon.php', 'OCA\\Settings\\Controller\\AISettingsController' => __DIR__ . '/..' . '/../lib/Controller/AISettingsController.php', 'OCA\\Settings\\Controller\\AdminSettingsController' => __DIR__ . '/..' . '/../lib/Controller/AdminSettingsController.php', 'OCA\\Settings\\Controller\\AppSettingsController' => __DIR__ . '/..' . '/../lib/Controller/AppSettingsController.php', diff --git a/apps/settings/lib/AppInfo/Application.php b/apps/settings/lib/AppInfo/Application.php index 6e59e56fe8218..de007a6978fe5 100644 --- a/apps/settings/lib/AppInfo/Application.php +++ b/apps/settings/lib/AppInfo/Application.php @@ -12,6 +12,7 @@ use OC\Authentication\Events\AppPasswordCreatedEvent; use OC\Authentication\Token\IProvider; use OC\Server; +use OCA\Settings\ConfigLexicon; use OCA\Settings\Hooks; use OCA\Settings\Listener\AppPasswordCreatedActivityListener; use OCA\Settings\Listener\GroupRemovedListener; @@ -112,6 +113,8 @@ public function register(IRegistrationContext $context): void { $context->registerSearchProvider(AppSearch::class); $context->registerSearchProvider(UserSearch::class); + $context->registerConfigLexicon(ConfigLexicon::class); + // Register listeners $context->registerEventListener(AppPasswordCreatedEvent::class, AppPasswordCreatedActivityListener::class); $context->registerEventListener(UserAddedEvent::class, UserAddedToGroupActivityListener::class); diff --git a/apps/settings/lib/ConfigLexicon.php b/apps/settings/lib/ConfigLexicon.php new file mode 100644 index 0000000000000..8d2035dbc7393 --- /dev/null +++ b/apps/settings/lib/ConfigLexicon.php @@ -0,0 +1,38 @@ +configManager->migrateConfigLexiconKeys($appId); + $this->configManager->updateLexiconEntries($appId); $this->dispatcher->dispatchTyped(new AppUpdateEvent($appId)); $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent( diff --git a/lib/private/Config/ConfigManager.php b/lib/private/Config/ConfigManager.php index 28397402249de..7c763e2ae37ac 100644 --- a/lib/private/Config/ConfigManager.php +++ b/lib/private/Config/ConfigManager.php @@ -82,6 +82,21 @@ public function migrateConfigLexiconKeys(?string $appId = null): void { $this->userConfig->ignoreLexiconAliases(false); } + /** + * Upgrade stored data in case of changes in the lexicon. + * Heavy process to be executed on core and app upgrade. + * + * - upgrade UserConfig entries if set as indexed + */ + public function updateLexiconEntries(string $appId): void { + $this->loadConfigServices(); + $lexicon = $this->userConfig->getConfigDetailsFromLexicon($appId); + foreach ($lexicon['entries'] as $entry) { + // upgrade based on index flag + $this->userConfig->updateGlobalIndexed($appId, $entry->getKey(), $entry->isFlagged(UserConfig::FLAG_INDEXED)); + } + } + /** * config services cannot be load at __construct() or install will fail */ diff --git a/lib/private/Config/UserConfig.php b/lib/private/Config/UserConfig.php index d0d19561e2dc3..bf86cfa493d26 100644 --- a/lib/private/Config/UserConfig.php +++ b/lib/private/Config/UserConfig.php @@ -477,40 +477,55 @@ private function searchUsersByTypedValue(string $app, string $key, string|array $this->assertParams('', $app, $key, allowEmptyUser: true); $this->matchAndApplyLexiconDefinition('', $app, $key); + $lexiconEntry = $this->getLexiconEntry($app, $key); + if ($lexiconEntry?->isFlagged(self::FLAG_INDEXED) === false) { + $this->logger->notice('UserConfig+Lexicon: using searchUsersByTypedValue on a config key not set as indexed'); + } + $qb = $this->connection->getQueryBuilder(); $qb->from('preferences'); $qb->select('userid'); $qb->where($qb->expr()->eq('appid', $qb->createNamedParameter($app))); $qb->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key))); - // search within 'indexed' OR 'configvalue' only if 'flags' is set as not indexed - // TODO: when implementing config lexicon remove the searches on 'configvalue' if value is set as indexed $configValueColumn = ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) ? $qb->expr()->castColumn('configvalue', IQueryBuilder::PARAM_STR) : 'configvalue'; if (is_array($value)) { - $where = $qb->expr()->orX( - $qb->expr()->in('indexed', $qb->createNamedParameter($value, IQueryBuilder::PARAM_STR_ARRAY)), - $qb->expr()->andX( - $qb->expr()->neq($qb->expr()->bitwiseAnd('flags', self::FLAG_INDEXED), $qb->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT)), - $qb->expr()->in($configValueColumn, $qb->createNamedParameter($value, IQueryBuilder::PARAM_STR_ARRAY)) - ) - ); - } else { - if ($caseInsensitive) { + $where = $qb->expr()->in('indexed', $qb->createNamedParameter($value, IQueryBuilder::PARAM_STR_ARRAY)); + // in case lexicon does not exist for this key - or is not set as indexed - we keep searching for non-index entries if 'flags' is set as not indexed + if ($lexiconEntry?->isFlagged(self::FLAG_INDEXED) !== true) { $where = $qb->expr()->orX( - $qb->expr()->eq($qb->func()->lower('indexed'), $qb->createNamedParameter(strtolower($value))), + $where, $qb->expr()->andX( $qb->expr()->neq($qb->expr()->bitwiseAnd('flags', self::FLAG_INDEXED), $qb->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT)), - $qb->expr()->eq($qb->func()->lower($configValueColumn), $qb->createNamedParameter(strtolower($value))) + $qb->expr()->in($configValueColumn, $qb->createNamedParameter($value, IQueryBuilder::PARAM_STR_ARRAY)) ) ); + } + } else { + if ($caseInsensitive) { + $where = $qb->expr()->eq($qb->func()->lower('indexed'), $qb->createNamedParameter(strtolower($value))); + // in case lexicon does not exist for this key - or is not set as indexed - we keep searching for non-index entries if 'flags' is set as not indexed + if ($lexiconEntry?->isFlagged(self::FLAG_INDEXED) !== true) { + $where = $qb->expr()->orX( + $where, + $qb->expr()->andX( + $qb->expr()->neq($qb->expr()->bitwiseAnd('flags', self::FLAG_INDEXED), $qb->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT)), + $qb->expr()->eq($qb->func()->lower($configValueColumn), $qb->createNamedParameter(strtolower($value))) + ) + ); + } } else { - $where = $qb->expr()->orX( - $qb->expr()->eq('indexed', $qb->createNamedParameter($value)), - $qb->expr()->andX( - $qb->expr()->neq($qb->expr()->bitwiseAnd('flags', self::FLAG_INDEXED), $qb->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT)), - $qb->expr()->eq($configValueColumn, $qb->createNamedParameter($value)) - ) - ); + $where = $qb->expr()->eq('indexed', $qb->createNamedParameter($value)); + // in case lexicon does not exist for this key - or is not set as indexed - we keep searching for non-index entries if 'flags' is set as not indexed + if ($lexiconEntry?->isFlagged(self::FLAG_INDEXED) !== true) { + $where = $qb->expr()->orX( + $where, + $qb->expr()->andX( + $qb->expr()->neq($qb->expr()->bitwiseAnd('flags', self::FLAG_INDEXED), $qb->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT)), + $qb->expr()->eq($configValueColumn, $qb->createNamedParameter($value)) + ) + ); + } } } @@ -1408,13 +1423,19 @@ public function updateGlobalIndexed(string $app, string $key, bool $indexed): vo $this->assertParams('', $app, $key, allowEmptyUser: true); $this->matchAndApplyLexiconDefinition('', $app, $key); - foreach (array_keys($this->getValuesByUsers($app, $key)) as $userId) { - try { - $this->updateIndexed($userId, $app, $key, $indexed); - } catch (UnknownKeyException) { - // should not happen and can be ignored - } - } + $bitPosition = log(self::FLAG_INDEXED) / log(2); // emulate base-2 logarithm (log2) + $bitOperation = ($indexed) ? '`flags` | (1 << ' . $bitPosition . ')' : '`flags` & ~(1 << ' . $bitPosition . ')'; + + $update = $this->connection->getQueryBuilder(); + $update->update('preferences') + ->set('flags', $update->createFunction($bitOperation)) + ->set('indexed', ($indexed) ? 'configvalue' : $update->createNamedParameter('')) + ->where( + $update->expr()->eq('appid', $update->createNamedParameter($app)), + $update->expr()->eq('configkey', $update->createNamedParameter($key)) + ); + + $update->executeStatement(); // we clear all cache $this->clearCacheAll(); diff --git a/lib/private/Repair/ConfigKeyMigration.php b/lib/private/Repair/ConfigKeyMigration.php index da4aa153dc5a2..dcca43d65dfc7 100644 --- a/lib/private/Repair/ConfigKeyMigration.php +++ b/lib/private/Repair/ConfigKeyMigration.php @@ -25,5 +25,6 @@ public function getName(): string { public function run(IOutput $output) { $this->configManager->migrateConfigLexiconKeys(); + $this->configManager->updateLexiconEntries('core'); } } From ce5964cd589d226de6772c7aee569d0bb351c878 Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Tue, 16 Sep 2025 12:52:27 -0100 Subject: [PATCH 2/3] fix(userconfig): using api bit functions Signed-off-by: Maxence Lange --- apps/settings/lib/ConfigLexicon.php | 6 +-- lib/private/Config/ConfigManager.php | 2 +- lib/private/Config/UserConfig.php | 22 ++++++-- tests/lib/Config/LexiconTest.php | 50 +++++++++++++++++++ tests/lib/Config/TestLexicon_UserIndexed.php | 33 ++++++++++++ .../Config/TestLexicon_UserIndexedRemove.php | 32 ++++++++++++ 6 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 tests/lib/Config/TestLexicon_UserIndexed.php create mode 100644 tests/lib/Config/TestLexicon_UserIndexedRemove.php diff --git a/apps/settings/lib/ConfigLexicon.php b/apps/settings/lib/ConfigLexicon.php index 8d2035dbc7393..dc898c78b4ab1 100644 --- a/apps/settings/lib/ConfigLexicon.php +++ b/apps/settings/lib/ConfigLexicon.php @@ -8,7 +8,7 @@ */ namespace OCA\Settings; -use OC\Config\UserConfig; +use OCP\Config\IUserConfig; use OCP\Config\Lexicon\Entry; use OCP\Config\Lexicon\ILexicon; use OCP\Config\Lexicon\Strictness; @@ -20,7 +20,7 @@ * Please Add & Manage your Config Keys in that file and keep the Lexicon up to date! */ class ConfigLexicon implements ILexicon { - public const USER_SETTINGS_MAIL = 'mail'; + public const USER_SETTINGS_EMAIL = 'email'; public function getStrictness(): Strictness { return Strictness::IGNORE; @@ -32,7 +32,7 @@ public function getAppConfigs(): array { public function getUserConfigs(): array { return [ - new Entry(key: self::USER_SETTINGS_MAIL, type: ValueType::STRING, defaultRaw: '', definition: 'account mail address', flags: UserConfig::FLAG_INDEXED), + new Entry(key: self::USER_SETTINGS_EMAIL, type: ValueType::STRING, defaultRaw: '', definition: 'account mail address', flags: IUserConfig::FLAG_INDEXED), ]; } } diff --git a/lib/private/Config/ConfigManager.php b/lib/private/Config/ConfigManager.php index 7c763e2ae37ac..f5ef024578cf9 100644 --- a/lib/private/Config/ConfigManager.php +++ b/lib/private/Config/ConfigManager.php @@ -93,7 +93,7 @@ public function updateLexiconEntries(string $appId): void { $lexicon = $this->userConfig->getConfigDetailsFromLexicon($appId); foreach ($lexicon['entries'] as $entry) { // upgrade based on index flag - $this->userConfig->updateGlobalIndexed($appId, $entry->getKey(), $entry->isFlagged(UserConfig::FLAG_INDEXED)); + $this->userConfig->updateGlobalIndexed($appId, $entry->getKey(), $entry->isFlagged(IUserConfig::FLAG_INDEXED)); } } diff --git a/lib/private/Config/UserConfig.php b/lib/private/Config/UserConfig.php index bf86cfa493d26..8db6258d52a27 100644 --- a/lib/private/Config/UserConfig.php +++ b/lib/private/Config/UserConfig.php @@ -479,7 +479,7 @@ private function searchUsersByTypedValue(string $app, string $key, string|array $lexiconEntry = $this->getLexiconEntry($app, $key); if ($lexiconEntry?->isFlagged(self::FLAG_INDEXED) === false) { - $this->logger->notice('UserConfig+Lexicon: using searchUsersByTypedValue on a config key not set as indexed'); + $this->logger->notice('UserConfig+Lexicon: using searchUsersByTypedValue on config key ' . $app . '/' . $key . ' which is not set as indexed'); } $qb = $this->connection->getQueryBuilder(); @@ -1423,18 +1423,30 @@ public function updateGlobalIndexed(string $app, string $key, bool $indexed): vo $this->assertParams('', $app, $key, allowEmptyUser: true); $this->matchAndApplyLexiconDefinition('', $app, $key); - $bitPosition = log(self::FLAG_INDEXED) / log(2); // emulate base-2 logarithm (log2) - $bitOperation = ($indexed) ? '`flags` | (1 << ' . $bitPosition . ')' : '`flags` & ~(1 << ' . $bitPosition . ')'; - $update = $this->connection->getQueryBuilder(); $update->update('preferences') - ->set('flags', $update->createFunction($bitOperation)) + // emptying field 'indexed' if key is not set as indexed anymore ->set('indexed', ($indexed) ? 'configvalue' : $update->createNamedParameter('')) ->where( $update->expr()->eq('appid', $update->createNamedParameter($app)), $update->expr()->eq('configkey', $update->createNamedParameter($key)) ); + // switching flags 'indexed' on and off is about adding/removing the bit value on the correct entries + if ($indexed) { + $update->set('flags', $update->func()->add('flags', $update->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT))); + $update->andWhere( + $update->expr()->neq($update->expr()->castColumn( + $update->expr()->bitwiseAnd('flags', self::FLAG_INDEXED), IQueryBuilder::PARAM_INT), $update->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT) + )); + } else { + $update->set('flags', $update->func()->subtract('flags', $update->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT))); + $update->andWhere( + $update->expr()->eq($update->expr()->castColumn( + $update->expr()->bitwiseAnd('flags', self::FLAG_INDEXED), IQueryBuilder::PARAM_INT), $update->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT) + )); + } + $update->executeStatement(); // we clear all cache diff --git a/tests/lib/Config/LexiconTest.php b/tests/lib/Config/LexiconTest.php index 3f14721dd6e3f..b422588f66203 100644 --- a/tests/lib/Config/LexiconTest.php +++ b/tests/lib/Config/LexiconTest.php @@ -60,11 +60,15 @@ protected function tearDown(): void { $this->appConfig->deleteApp(TestLexicon_N::APPID); $this->appConfig->deleteApp(TestLexicon_W::APPID); $this->appConfig->deleteApp(TestLexicon_E::APPID); + $this->appConfig->deleteApp(TestLexicon_UserIndexed::APPID); + $this->appConfig->deleteApp(TestLexicon_UserIndexedRemove::APPID); $this->userConfig->deleteApp(TestConfigLexicon_I::APPID); $this->userConfig->deleteApp(TestLexicon_N::APPID); $this->userConfig->deleteApp(TestLexicon_W::APPID); $this->userConfig->deleteApp(TestLexicon_E::APPID); + $this->userConfig->deleteApp(TestLexicon_UserIndexed::APPID); + $this->userConfig->deleteApp(TestLexicon_UserIndexedRemove::APPID); } public function testAppLexiconSetCorrect() { @@ -234,4 +238,50 @@ public function testUserConfigLexiconPresets() { $this->presetManager->setLexiconPreset(Preset::FAMILY); $this->assertSame('family', $this->userConfig->getValueString('user1', TestLexicon_E::APPID, 'key3')); } + + public function testLexiconIndexedUpdate() { + $this->userConfig->setValueString('user1', TestLexicon_UserIndexed::APPID, 'key1', 'abcd'); + $this->userConfig->setValueString('user2', TestLexicon_UserIndexed::APPID, 'key1', '1234', flags: 64); + $this->userConfig->setValueString('user3', TestLexicon_UserIndexed::APPID, 'key1', 'qwer', flags: IUserConfig::FLAG_INDEXED); + $this->userConfig->setValueString('user4', TestLexicon_UserIndexed::APPID, 'key1', 'uiop', flags: 64 | IUserConfig::FLAG_INDEXED); + + $bootstrapCoordinator = Server::get(Coordinator::class); + $bootstrapCoordinator->getRegistrationContext()?->registerConfigLexicon(TestLexicon_UserIndexed::APPID, TestLexicon_UserIndexed::class); + $this->userConfig->clearCacheAll(); + + $this->configManager->updateLexiconEntries(TestLexicon_UserIndexed::APPID); + + $this->assertTrue($this->userConfig->isIndexed('user1', TestLexicon_UserIndexed::APPID, 'key1')); + $this->assertTrue($this->userConfig->isIndexed('user2', TestLexicon_UserIndexed::APPID, 'key1')); + $this->assertTrue($this->userConfig->isIndexed('user3', TestLexicon_UserIndexed::APPID, 'key1')); + $this->assertTrue($this->userConfig->isIndexed('user4', TestLexicon_UserIndexed::APPID, 'key1')); + + $this->assertSame(2, $this->userConfig->getValueFlags('user1', TestLexicon_UserIndexed::APPID, 'key1')); + $this->assertSame(66, $this->userConfig->getValueFlags('user2', TestLexicon_UserIndexed::APPID, 'key1')); + $this->assertSame(2, $this->userConfig->getValueFlags('user3', TestLexicon_UserIndexed::APPID, 'key1')); + $this->assertSame(66, $this->userConfig->getValueFlags('user4', TestLexicon_UserIndexed::APPID, 'key1')); + } + + public function testLexiconIndexedUpdateRemove() { + $this->userConfig->setValueString('user1', TestLexicon_UserIndexedRemove::APPID, 'key1', 'abcd'); + $this->userConfig->setValueString('user2', TestLexicon_UserIndexedRemove::APPID, 'key1', '1234', flags: 64); + $this->userConfig->setValueString('user3', TestLexicon_UserIndexedRemove::APPID, 'key1', 'qwer', flags: IUserConfig::FLAG_INDEXED); + $this->userConfig->setValueString('user4', TestLexicon_UserIndexedRemove::APPID, 'key1', 'uiop', flags: 64 | IUserConfig::FLAG_INDEXED); + + $bootstrapCoordinator = Server::get(Coordinator::class); + $bootstrapCoordinator->getRegistrationContext()?->registerConfigLexicon(TestLexicon_UserIndexedRemove::APPID, TestLexicon_UserIndexedRemove::class); + $this->userConfig->clearCacheAll(); + + $this->configManager->updateLexiconEntries(TestLexicon_UserIndexedRemove::APPID); + + $this->assertFalse($this->userConfig->isIndexed('user1', TestLexicon_UserIndexedRemove::APPID, 'key1')); + $this->assertFalse($this->userConfig->isIndexed('user2', TestLexicon_UserIndexedRemove::APPID, 'key1')); + $this->assertFalse($this->userConfig->isIndexed('user3', TestLexicon_UserIndexedRemove::APPID, 'key1')); + $this->assertFalse($this->userConfig->isIndexed('user4', TestLexicon_UserIndexedRemove::APPID, 'key1')); + + $this->assertSame(0, $this->userConfig->getValueFlags('user1', TestLexicon_UserIndexedRemove::APPID, 'key1')); + $this->assertSame(64, $this->userConfig->getValueFlags('user2', TestLexicon_UserIndexedRemove::APPID, 'key1')); + $this->assertSame(0, $this->userConfig->getValueFlags('user3', TestLexicon_UserIndexedRemove::APPID, 'key1')); + $this->assertSame(64, $this->userConfig->getValueFlags('user4', TestLexicon_UserIndexedRemove::APPID, 'key1')); + } } diff --git a/tests/lib/Config/TestLexicon_UserIndexed.php b/tests/lib/Config/TestLexicon_UserIndexed.php new file mode 100644 index 0000000000000..07107db4ec4d7 --- /dev/null +++ b/tests/lib/Config/TestLexicon_UserIndexed.php @@ -0,0 +1,33 @@ + Date: Wed, 17 Sep 2025 12:33:04 -0100 Subject: [PATCH 3/3] fix(userconfig): crop configvalue at 64 before index Signed-off-by: Maxence Lange --- lib/private/Config/UserConfig.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/private/Config/UserConfig.php b/lib/private/Config/UserConfig.php index 8db6258d52a27..05018adc3f8fe 100644 --- a/lib/private/Config/UserConfig.php +++ b/lib/private/Config/UserConfig.php @@ -1425,8 +1425,6 @@ public function updateGlobalIndexed(string $app, string $key, bool $indexed): vo $update = $this->connection->getQueryBuilder(); $update->update('preferences') - // emptying field 'indexed' if key is not set as indexed anymore - ->set('indexed', ($indexed) ? 'configvalue' : $update->createNamedParameter('')) ->where( $update->expr()->eq('appid', $update->createNamedParameter($app)), $update->expr()->eq('configkey', $update->createNamedParameter($key)) @@ -1434,12 +1432,15 @@ public function updateGlobalIndexed(string $app, string $key, bool $indexed): vo // switching flags 'indexed' on and off is about adding/removing the bit value on the correct entries if ($indexed) { + $update->set('indexed', $update->func()->substring('configvalue', $update->createNamedParameter(1, IQueryBuilder::PARAM_INT), $update->createNamedParameter(64, IQueryBuilder::PARAM_INT))); $update->set('flags', $update->func()->add('flags', $update->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT))); $update->andWhere( $update->expr()->neq($update->expr()->castColumn( $update->expr()->bitwiseAnd('flags', self::FLAG_INDEXED), IQueryBuilder::PARAM_INT), $update->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT) )); } else { + // emptying field 'indexed' if key is not set as indexed anymore + $update->set('indexed', $update->createNamedParameter('')); $update->set('flags', $update->func()->subtract('flags', $update->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT))); $update->andWhere( $update->expr()->eq($update->expr()->castColumn(