From f17cf83e16310dab229bd1112729b51f1f7bdc5b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 20 Mar 2025 16:33:32 +0100 Subject: feat: add command to list orphan objects Signed-off-by: Robin Appelman --- apps/files/lib/Command/Object/Orphans.php | 73 +++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 apps/files/lib/Command/Object/Orphans.php (limited to 'apps/files/lib/Command/Object/Orphans.php') diff --git a/apps/files/lib/Command/Object/Orphans.php b/apps/files/lib/Command/Object/Orphans.php new file mode 100644 index 00000000000..22538cf7b91 --- /dev/null +++ b/apps/files/lib/Command/Object/Orphans.php @@ -0,0 +1,73 @@ +query = $connection->getQueryBuilder(); + $this->query->select('fileid') + ->from('filecache') + ->where($this->query->expr()->eq('fileid', $this->query->createParameter('file_id'))); + } + + protected function configure(): void { + parent::configure(); + $this + ->setName('files:object:orphans') + ->setDescription('List all objects in the object store that don\'t have a matching entry in the database') + ->addOption('bucket', 'b', InputOption::VALUE_REQUIRED, "Bucket to list the objects from, only required in cases where it can't be determined from the config"); + } + + public function execute(InputInterface $input, OutputInterface $output): int { + $objectStore = $this->objectUtils->getObjectStore($input->getOption('bucket'), $output); + if (!$objectStore) { + return self::FAILURE; + } + + if (!$objectStore instanceof IObjectStoreMetaData) { + $output->writeln('Configured object store does currently not support listing objects'); + return self::FAILURE; + } + $prefixLength = strlen('urn:oid:'); + + $objects = $objectStore->listObjects('urn:oid:'); + $objects->rewind(); + $orphans = new \CallbackFilterIterator($objects, function (array $object) use ($prefixLength) { + $fileId = (int)substr($object['urn'], $prefixLength); + return !$this->fileIdInDb($fileId); + }); + $orphans = new \ArrayIterator(iterator_to_array($orphans)); + $this->objectUtils->writeIteratorToOutput($input, $output, $orphans, self::CHUNK_SIZE); + + return self::SUCCESS; + } + + private function fileIdInDb(int $fileId): bool { + $this->query->setParameter('file_id', $fileId, IQueryBuilder::PARAM_INT); + $result = $this->query->executeQuery(); + return $result->fetchOne() !== false; + } +} -- cgit v1.2.3