diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/private/repair.php | 4 | ||||
-rw-r--r-- | lib/repair/cleantags.php | 114 |
2 files changed, 117 insertions, 1 deletions
diff --git a/lib/private/repair.php b/lib/private/repair.php index c4f057b53ae..d9fd99707e8 100644 --- a/lib/private/repair.php +++ b/lib/private/repair.php @@ -11,6 +11,7 @@ namespace OC; use OC\Hooks\BasicEmitter; use OC\Hooks\Emitter; use OC\Repair\AssetCache; +use OC\Repair\CleanTags; use OC\Repair\Collation; use OC\Repair\FillETags; use OC\Repair\InnoDB; @@ -81,7 +82,8 @@ class Repair extends BasicEmitter { new RepairLegacyStorages(\OC::$server->getConfig(), \OC_DB::getConnection()), new RepairConfig(), new AssetCache(), - new FillETags(\OC_DB::getConnection()) + new FillETags(\OC_DB::getConnection()), + new CleanTags(\OC_DB::getConnection()), ); } diff --git a/lib/repair/cleantags.php b/lib/repair/cleantags.php new file mode 100644 index 00000000000..6aa325df0b6 --- /dev/null +++ b/lib/repair/cleantags.php @@ -0,0 +1,114 @@ +<?php +/** + * Copyright (c) 2015 Joas Schilling <nickvergessen@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Repair; + +use OC\DB\Connection; +use OC\Hooks\BasicEmitter; +use OC\RepairStep; + +/** + * Class RepairConfig + * + * @package OC\Repair + */ +class CleanTags extends BasicEmitter implements RepairStep { + + /** @var Connection */ + protected $connection; + + /** + * @param Connection $connection + */ + public function __construct(Connection $connection) { + $this->connection = $connection; + } + + /** + * @return string + */ + public function getName() { + return 'Clean tags and favorites'; + } + + /** + * Updates the configuration after running an update + */ + public function run() { + + // Delete tag entries for deleted files + $this->deleteOrphanEntries( + '%d tags for delete files have been removed.', + '*PREFIX*vcategory_to_object', 'objid', + '*PREFIX*filecache', 'fileid', 'fileid' + ); + + // Delete tag entries for deleted tags + $this->deleteOrphanEntries( + '%d tag entries for deleted tags have been removed.', + '*PREFIX*vcategory_to_object', 'categoryid', + '*PREFIX*vcategory', 'id', 'uid' + ); + + // Delete tags that have no entries + $this->deleteOrphanEntries( + '%d tags with no entries have been removed.', + '*PREFIX*vcategory', 'id', + '*PREFIX*vcategory_to_object', 'categoryid', 'type' + ); + } + + /** + * 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($repairInfo, $deleteTable, $deleteId, $sourceTable, $sourceId, $sourceNullColumn) { + $qb = $this->connection->createQueryBuilder(); + + $qb->select('d.' . $deleteId) + ->from($deleteTable, 'd') + ->leftJoin('d', $sourceTable, 's', 'd.' . $deleteId . ' = s.' . $sourceId) + ->where( + '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()->in($deleteId, ':ids')); + $qb->setParameter('ids', $items, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY); + $qb->execute(); + } + } + + if ($repairInfo) { + $this->emit('\OC\Repair', 'info', array(sprintf($repairInfo, sizeof($orphanItems)))); + } + } +} |