connection->getQueryBuilder(); $query->update($table) ->set('orphan', $query->createNamedParameter($value, IQueryBuilder::PARAM_BOOL)) ; if ($fileIds) { $query->where($query->expr()->in('fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY))); } return $query->executeStatement(); }; // Mark all files as orphaned. $update('memories'); // Mark all live photos as orphaned. if ($livephoto) { $update('memories_livephoto'); } // Unorphan all files on abort if we can if ($value) { Util::registerInterruptHandler('orphanAll', function () { // If we are in a transaction, abort it. if ($this->connection->inTransaction()) { $this->connection->rollBack(); } // Unorphan all files. $this->orphanAll(false); }); } } /** * Orphan and run an update on all files. * * @param array $fields list of fields to select * @param int $txnSize number of rows to process in a single transaction * @param \Closure(array): void $callback will be passed each row */ public function orphanAndRun(array $fields, int $txnSize, \Closure $callback): void { // Orphan all files. This means if we are interrupted, // it will lead to a re-index of the whole library! $this->orphanAll(true, null, false); while (\count($orphans = $this->getSomeOrphans($txnSize, $fields))) { $this->connection->beginTransaction(); foreach ($orphans as $row) { $callback($row); } // Mark all files as not orphaned. $fileIds = array_map(static fn ($row): int => (int) $row['fileid'], $orphans); $this->orphanAll(false, $fileIds, false); $this->connection->commit(); } } /** * Get a list of orphaned files. * * @param int $count max number of rows to return * @param string[] $fields list of fields to select */ private function getSomeOrphans(int $count, array $fields): array { $query = $this->connection->getQueryBuilder(); return $query->select(...$fields) ->from('memories') ->where($query->expr()->eq('orphan', $query->expr()->literal(1))) ->setMaxResults($count) ->executeQuery() ->fetchAll() ; } }