From 123bf78ca8e25cbc91d937f5090423594bf7ae35 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 14 Jun 2016 12:30:22 +0200 Subject: [PATCH] Clean up tags of deleted users --- lib/private/Repair.php | 2 +- lib/private/Repair/CleanTags.php | 57 +++++++++++++++++++++++++++++- tests/lib/Repair/CleanTagsTest.php | 28 +++++++++++++-- 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/lib/private/Repair.php b/lib/private/Repair.php index bb2967d7e6..1fd204fcd8 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -127,7 +127,7 @@ class Repair implements IOutput{ new RepairLegacyStorages(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection()), new AssetCache(), new FillETags(\OC::$server->getDatabaseConnection()), - new CleanTags(\OC::$server->getDatabaseConnection()), + new CleanTags(\OC::$server->getDatabaseConnection(), \OC::$server->getUserManager()), new DropOldTables(\OC::$server->getDatabaseConnection()), new DropOldJobs(\OC::$server->getJobList()), new RemoveGetETagEntries(\OC::$server->getDatabaseConnection()), diff --git a/lib/private/Repair/CleanTags.php b/lib/private/Repair/CleanTags.php index 60ddeff08f..4241fa6da3 100644 --- a/lib/private/Repair/CleanTags.php +++ b/lib/private/Repair/CleanTags.php @@ -25,6 +25,7 @@ namespace OC\Repair; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +use OCP\IUserManager; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; @@ -38,11 +39,18 @@ class CleanTags implements IRepairStep { /** @var IDBConnection */ protected $connection; + /** @var IUserManager */ + protected $userManager; + + protected $deletedTags = 0; + /** * @param IDBConnection $connection + * @param IUserManager $userManager */ - public function __construct(IDBConnection $connection) { + public function __construct(IDBConnection $connection, IUserManager $userManager) { $this->connection = $connection; + $this->userManager = $userManager; } /** @@ -56,11 +64,58 @@ class CleanTags implements IRepairStep { * Updates the configuration after running an update */ public function run(IOutput $output) { + $this->deleteOrphanTags($output); $this->deleteOrphanFileEntries($output); $this->deleteOrphanTagEntries($output); $this->deleteOrphanCategoryEntries($output); } + /** + * Delete tags for deleted users + */ + protected function deleteOrphanTags(IOutput $output) { + $offset = 0; + while ($this->checkTags($offset)) { + $offset += 50; + } + + $output->info(sprintf('%d tags of deleted users have been removed.', $this->deletedTags)); + } + + protected function checkTags($offset) { + $query = $this->connection->getQueryBuilder(); + $query->select('uid') + ->from('vcategory') + ->groupBy('uid') + ->orderBy('uid') + ->setMaxResults(50) + ->setFirstResult($offset); + $result = $query->execute(); + + $users = []; + $hadResults = false; + while ($row = $result->fetch()) { + $hadResults = true; + if (!$this->userManager->userExists($row['uid'])) { + $users[] = $row['uid']; + } + } + $result->closeCursor(); + + if (!$hadResults) { + // No more tags, stop looping + return false; + } + + if (!empty($users)) { + $query = $this->connection->getQueryBuilder(); + $query->delete('vcategory') + ->where($query->expr()->in('uid', $query->createNamedParameter($users, IQueryBuilder::PARAM_STR_ARRAY))); + $this->deletedTags += $query->execute(); + } + return true; + } + /** * Delete tag entries for deleted files */ diff --git a/tests/lib/Repair/CleanTagsTest.php b/tests/lib/Repair/CleanTagsTest.php index 804fa4f66c..ac79907c52 100644 --- a/tests/lib/Repair/CleanTagsTest.php +++ b/tests/lib/Repair/CleanTagsTest.php @@ -25,6 +25,9 @@ class CleanTagsTest extends \Test\TestCase { /** @var \OCP\IDBConnection */ protected $connection; + /** @var \OCP\IUserManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $userManager; + /** @var int */ protected $createdFile; @@ -38,8 +41,12 @@ class CleanTagsTest extends \Test\TestCase { ->disableOriginalConstructor() ->getMock(); + $this->userManager = $this->getMockBuilder('\OCP\IUserManager') + ->disableOriginalConstructor() + ->getMock(); + $this->connection = \OC::$server->getDatabaseConnection(); - $this->repair = new \OC\Repair\CleanTags($this->connection); + $this->repair = new \OC\Repair\CleanTags($this->connection, $this->userManager); $this->cleanUpTables(); } @@ -86,6 +93,20 @@ class CleanTagsTest extends \Test\TestCase { self::invokePrivate($this->repair, 'deleteOrphanCategoryEntries', [$this->outputMock]); $this->assertEntryCount('vcategory_to_object', 2, 'Assert tag entries count after cleaning category entries'); $this->assertEntryCount('vcategory', 2, 'Assert tag categories count after cleaning category entries'); + + + $this->addTagCategory('TestRepairCleanTags', 'contacts', 'userExists'); // Retained + $this->assertEntryCount('vcategory', 3, 'Assert tag categories count before cleaning categories by users'); + + $this->userManager->expects($this->exactly(2)) + ->method('userExists') + ->willReturnMap([ + ['userExists', true], + ['TestRepairCleanTags', false], + ]); + + self::invokePrivate($this->repair, 'deleteOrphanTags', [$this->outputMock]); + $this->assertEntryCount('vcategory', 1, 'Assert tag categories count after cleaning categories by users'); } /** @@ -107,13 +128,14 @@ class CleanTagsTest extends \Test\TestCase { * * @param string $category * @param string $type + * @param string $user * @return int */ - protected function addTagCategory($category, $type) { + protected function addTagCategory($category, $type, $user = 'TestRepairCleanTags') { $qb = $this->connection->getQueryBuilder(); $qb->insert('vcategory') ->values([ - 'uid' => $qb->createNamedParameter('TestRepairCleanTags'), + 'uid' => $qb->createNamedParameter($user), 'category' => $qb->createNamedParameter($category), 'type' => $qb->createNamedParameter($type), ])