public function run(IOutput $output) { $qb = $this->connection->getQueryBuilder(); $qb->update('filecache')->set('etag', $qb->expr()->literal('xxx'))->where($qb->expr()->eq('etag', $qb->expr()->literal('')))->orWhere($qb->expr()->isNull('etag')); $result = $qb->execute(); $output->info("ETags have been fixed for {$result} files/folders."); }
/** * @inheritdoc */ public function run(IOutput $output) { $output->startProgress(); $output->advance(1, "Migrate states"); $this->convStateMapper->migrate(); $output->finishProgress(); }
/** * @param IOutput $output */ public function run(IOutput $output) { $output->startProgress(2); $this->fixUserRootPermissions(); $output->advance(); $this->fixAvatarPermissions(); $output->finishProgress(); }
public function run(\OCP\Migration\IOutput $out) { if ($this->warning) { $out->warning('Simulated warning'); } else { $out->info('Simulated info'); } }
/** * {@inheritdoc} */ public function run(IOutput $output) { $appsToUpdate = ['contacts' => ['old' => '166044', 'new' => '168708'], 'calendar' => ['old' => '166043', 'new' => '168707'], 'bookmarks' => ['old' => '166042', 'new' => '168710'], 'search_lucene' => ['old' => '166057', 'new' => '168709'], 'documents' => ['old' => '166045', 'new' => '168711']]; foreach ($appsToUpdate as $appName => $ids) { if ($this->fixOcsId($appName, $ids['old'], $ids['new'])) { $output->info("Fixed invalid {$appName} OCS id"); } } }
public function run(IOutput $output) { if (!\OC_Template::isAssetPipelineEnabled()) { $output->info('Asset pipeline disabled -> nothing to do'); return; } $assetDir = \OC::$server->getConfig()->getSystemValue('assetdirectory', \OC::$SERVERROOT) . '/assets'; \OC_Helper::rmdirr($assetDir, false); $output->info('Asset cache cleared.'); }
/** * @inheritdoc */ public function run(IOutput $output) { $output->startProgress(); $this->userManager->callForAllUsers(function ($user) use($output) { /** @var IUser $user */ $output->advance(1, $user->getDisplayName()); $this->birthdayService->syncUser($user->getUID()); }); $output->finishProgress(); }
/** * @param IOutput $output */ private function removeContactShares(IOutput $output) { $qb = $this->connection->getQueryBuilder(); $qb->delete('share')->where($qb->expr()->eq('item_type', $qb->createNamedParameter('contact'))); $qb->execute(); $output->advance(); $qb = $this->connection->getQueryBuilder(); $qb->delete('share')->where($qb->expr()->eq('item_type', $qb->createNamedParameter('addressbook'))); $qb->execute(); $output->advance(); }
/** * Run repair step. * Must throw exception on error. * * @throws \Exception in case of failure */ public function run(IOutput $output) { $tables = $this->oldDatabaseTables(); $output->startProgress(count($tables)); foreach ($this->oldDatabaseTables() as $tableName) { if ($this->connection->tableExists($tableName)) { $this->connection->dropTable($tableName); } $output->advance(1, "Drop old database table: {$tableName}"); } $output->finishProgress(); }
/** * Fix mime types */ public function run(IOutput $output) { if (!$this->connection->getDatabasePlatform() instanceof MySqlPlatform) { $output->info('Not a mysql database -> nothing to no'); return; } $tables = $this->getAllNonUTF8BinTables($this->connection); foreach ($tables as $table) { $output->info("Change collation for {$table} ..."); $query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;'); $query->execute(); } }
/** * Fix mime types */ public function run(IOutput $output) { $connection = \OC::$server->getDatabaseConnection(); if (!$connection->getDatabasePlatform() instanceof MySqlPlatform) { $output->info('Not a mysql database -> nothing to do'); return; } $tables = $this->getAllMyIsamTables($connection); if (is_array($tables)) { foreach ($tables as $table) { $connection->exec("ALTER TABLE {$table} ENGINE=InnoDB;"); $output->info("Fixed {$table}"); } } }
/** * @param IOutput $output */ private function removeRootShares(IOutput $output) { $function = function (IUser $user) use($output) { $userFolder = $this->rootFolder->getUserFolder($user->getUID()); $fileId = $userFolder->getId(); $qb = $this->connection->getQueryBuilder(); $qb->delete('share')->where($qb->expr()->eq('file_source', $qb->createNamedParameter($fileId)))->andWhere($qb->expr()->orX($qb->expr()->eq('item_type', $qb->expr()->literal('file')), $qb->expr()->eq('item_type', $qb->expr()->literal('folder')))); $qb->execute(); $output->advance(); }; $userCount = $this->countUsers(); $output->startProgress($userCount); $this->userManager->callForAllUsers($function); $output->finishProgress(); }
/** * Run repair step. * Must throw exception on error. * * @throws \Exception in case of failure */ public function run(IOutput $output) { $deletedEntries = 0; $query = $this->connection->getQueryBuilder(); $query->select('s1.id')->selectAlias('s1.share_with', 'user')->selectAlias('s2.share_with', 'group')->from('share', 's1')->where($query->expr()->isNotNull('s1.parent'))->andWhere($query->expr()->eq('s1.share_type', $query->expr()->literal(2)))->andWhere($query->expr()->isNotNull('s2.id'))->andWhere($query->expr()->eq('s2.share_type', $query->expr()->literal(Share::SHARE_TYPE_GROUP)))->leftJoin('s1', 'share', 's2', $query->expr()->eq('s1.parent', 's2.id')); $deleteQuery = $this->connection->getQueryBuilder(); $deleteQuery->delete('share')->where($query->expr()->eq('id', $deleteQuery->createParameter('share'))); $result = $query->execute(); while ($row = $result->fetch()) { if (!$this->isMember($row['group'], $row['user'])) { $deletedEntries += $deleteQuery->setParameter('share', (int) $row['id'])->execute(); } } $result->closeCursor(); if ($deletedEntries) { $output->info('Removed ' . $deletedEntries . ' shares where user is not a member of the group anymore'); } }
/** * Fix duplicate entries in oc_lucene_status * * search_lucene prior to v0.5.0 did not have a primary key on the lucene_status table. Newer versions do, which * causes the migration check to fail because it tries to insert duplicate rows into the new schema. * * FIXME Currently, apps don't have a way of repairing anything before the migration check: * @link https://github.com/owncloud/core/issues/10980 * * As a result this repair step needs to live in the core repo, although it belongs into search_lucene: * @link https://github.com/owncloud/core/issues/10205#issuecomment-54957557 * * It will completely remove any rows that make a file id have more than one status: * fileid | status fileid | status * --------+-------- will become --------+-------- * 2 | E 3 | E * 2 | I * 3 | E * * search_lucene will then reindex the fileids without a status when the next indexing job is executed */ public function run(IOutput $out) { $connection = \OC::$server->getDatabaseConnection(); if ($connection->tableExists('lucene_status')) { $out->info('removing duplicate entries from lucene_status'); $query = $connection->prepare(' DELETE FROM `*PREFIX*lucene_status` WHERE `fileid` IN ( SELECT `fileid` FROM ( SELECT `fileid` FROM `*PREFIX*lucene_status` GROUP BY `fileid` HAVING count(`fileid`) > 1 ) AS `mysqlerr1093hack` )'); $query->execute(); } else { $out->info('lucene_status table does not exist -> nothing to do'); } }
/** * Remove shares where the parent share does not exist anymore */ private function removeSharesNonExistingParent(IOutput $out) { $deletedEntries = 0; $query = $this->connection->getQueryBuilder(); $query->select('s1.parent')->from('share', 's1')->where($query->expr()->isNotNull('s1.parent'))->andWhere($query->expr()->isNull('s2.id'))->leftJoin('s1', 'share', 's2', $query->expr()->eq('s1.parent', 's2.id'))->groupBy('s1.parent')->setMaxResults(self::CHUNK_SIZE); $deleteQuery = $this->connection->getQueryBuilder(); $deleteQuery->delete('share')->where($deleteQuery->expr()->eq('parent', $deleteQuery->createParameter('parent'))); $deletedInLastChunk = self::CHUNK_SIZE; while ($deletedInLastChunk === self::CHUNK_SIZE) { $deletedInLastChunk = 0; $result = $query->execute(); while ($row = $result->fetch()) { $deletedInLastChunk++; $deletedEntries += $deleteQuery->setParameter('parent', (int) $row['parent'])->execute(); } $result->closeCursor(); } if ($deletedEntries) { $out->info('Removed ' . $deletedEntries . ' shares where the parent did not exist'); } }
/** * Converts legacy home storage ids in the format * "local::/data/dir/path/userid/" to the new format "home::userid" */ public function run(IOutput $out) { // only run once if ($this->config->getAppValue('core', 'repairlegacystoragesdone') === 'yes') { return; } $dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data/'); $dataDir = rtrim($dataDir, '/') . '/'; $dataDirId = 'local::' . $dataDir; $count = 0; $hasWarnings = false; $this->connection->beginTransaction(); // note: not doing a direct UPDATE with the REPLACE function // because regexp search/extract is needed and it is not guaranteed // to work on all database types $sql = 'SELECT `id`, `numeric_id` FROM `*PREFIX*storages`' . ' WHERE `id` LIKE ?' . ' ORDER BY `id`'; $result = $this->connection->executeQuery($sql, array($dataDirId . '%')); while ($row = $result->fetch()) { $currentId = $row['id']; // one entry is the datadir itself if ($currentId === $dataDirId) { continue; } try { if ($this->fixLegacyStorage($currentId, (int) $row['numeric_id'])) { $count++; } } catch (RepairException $e) { $hasWarnings = true; $out->warning('Could not repair legacy storage ' . $currentId . ' automatically.'); } } // check for md5 ids, not in the format "prefix::" $sql = 'SELECT COUNT(*) AS "c" FROM `*PREFIX*storages`' . ' WHERE `id` NOT LIKE \'%::%\''; $result = $this->connection->executeQuery($sql); $row = $result->fetch(); // find at least one to make sure it's worth // querying the user list if ((int) $row['c'] > 0) { $userManager = \OC::$server->getUserManager(); // use chunks to avoid caching too many users in memory $limit = 30; $offset = 0; do { // query the next page of users $results = $userManager->search('', $limit, $offset); $storageIds = array(); foreach ($results as $uid => $userObject) { $storageId = $dataDirId . $uid . '/'; if (strlen($storageId) <= 64) { // skip short storage ids as they were handled in the previous section continue; } $storageIds[$uid] = $storageId; } if (count($storageIds) > 0) { // update the storages of these users foreach ($storageIds as $uid => $storageId) { $numericId = Storage::getNumericStorageId($storageId); try { if (!is_null($numericId) && $this->fixLegacyStorage($storageId, (int) $numericId)) { $count++; } } catch (RepairException $e) { $hasWarnings = true; $out->warning('Could not repair legacy storage ' . $storageId . ' automatically.'); } } } $offset += $limit; } while (count($results) >= $limit); } $out->info('Updated ' . $count . ' legacy home storage ids'); $this->connection->commit(); if ($hasWarnings) { $out->warning('Some legacy storages could not be repaired. Please manually fix them then re-run ./occ maintenance:repair'); } else { // if all were done, no need to redo the repair during next upgrade $this->config->setAppValue('core', 'repairlegacystoragesdone', 'yes'); } }
/** * Removes all entries with the key "{DAV:}getetag" from the table properties */ public function run(IOutput $out) { $sql = 'DELETE FROM `*PREFIX*properties`' . ' WHERE `propertyname` = ?'; $deletedRows = $this->connection->executeUpdate($sql, ['{DAV:}getetag']); $out->info('Removed ' . $deletedRows . ' unneeded "{DAV:}getetag" entries from properties table.'); }
/** * Fix mime types */ public function run(IOutput $out) { $ocVersionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); // NOTE TO DEVELOPERS: when adding new mime types, please make sure to // add a version comparison to avoid doing it every time // only update mime types if necessary as it can be expensive if (version_compare($ocVersionFromBeforeUpdate, '8.2.0', '<')) { if ($this->fixOfficeMimeTypes()) { $out->info('Fixed office mime types'); } if ($this->fixApkMimeType()) { $out->info('Fixed APK mime type'); } if ($this->fixFontsMimeTypes()) { $out->info('Fixed fonts mime types'); } if ($this->fixPostscriptMimeType()) { $out->info('Fixed Postscript mime types'); } if ($this->introduceRawMimeType()) { $out->info('Fixed Raw mime types'); } if ($this->introduce3dImagesMimeType()) { $out->info('Fixed 3D images mime types'); } if ($this->introduceConfMimeType()) { $out->info('Fixed Conf/cnf mime types'); } if ($this->introduceYamlMimeType()) { $out->info('Fixed Yaml/Yml mime types'); } } // Mimetype updates from #19272 if (version_compare($ocVersionFromBeforeUpdate, '8.2.0.8', '<')) { if ($this->introduceJavaMimeType()) { $out->info('Fixed java/class mime types'); } if ($this->introduceHppMimeType()) { $out->info('Fixed hpp mime type'); } if ($this->introduceRssMimeType()) { $out->info('Fixed rss mime type'); } if ($this->introduceRtfMimeType()) { $out->info('Fixed rtf mime type'); } } if (version_compare($ocVersionFromBeforeUpdate, '9.0.0.10', '<')) { if ($this->introduceRichDocumentsMimeTypes()) { $out->info('Fixed richdocuments additional office mime types'); } } }
/** * Deletes all entries from $deleteTable that do not have a matching entry in $sourceTable * * A query joins $deleteTable.$deleteId = $sourceTable.$sourceId and checks * whether $sourceNullColumn is null. If it is null, the entry in $deleteTable * is being deleted. * * @param string $repairInfo * @param string $deleteTable * @param string $deleteId * @param string $sourceTable * @param string $sourceId * @param string $sourceNullColumn If this column is null in the source table, * the entry is deleted in the $deleteTable */ protected function deleteOrphanEntries(IOutput $output, $repairInfo, $deleteTable, $deleteId, $sourceTable, $sourceId, $sourceNullColumn) { $qb = $this->connection->getQueryBuilder(); $qb->select('d.' . $deleteId)->from($deleteTable, 'd')->leftJoin('d', $sourceTable, 's', $qb->expr()->eq('d.' . $deleteId, ' s.' . $sourceId))->where($qb->expr()->eq('d.type', $qb->expr()->literal('files')))->andWhere($qb->expr()->isNull('s.' . $sourceNullColumn)); $result = $qb->execute(); $orphanItems = array(); while ($row = $result->fetch()) { $orphanItems[] = (int) $row[$deleteId]; } if (!empty($orphanItems)) { $orphanItemsBatch = array_chunk($orphanItems, 200); foreach ($orphanItemsBatch as $items) { $qb->delete($deleteTable)->where($qb->expr()->eq('type', $qb->expr()->literal('files')))->andWhere($qb->expr()->in($deleteId, $qb->createParameter('ids'))); $qb->setParameter('ids', $items, IQueryBuilder::PARAM_INT_ARRAY); $qb->execute(); } } if ($repairInfo) { $output->info(sprintf($repairInfo, sizeof($orphanItems))); } }