From 3354d5a56972803a3f55fb4840124b7f7114d6b6 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Thu, 24 Oct 2019 22:28:26 -0700 Subject: [PATCH] Delete articles and statuses from feeds no longer subscribed-to. At startup. Fix #899. --- Frameworks/Account/Account.swift | 1 + .../ArticlesDatabase/ArticlesDatabase.swift | 9 ++++++ .../ArticlesDatabase/ArticlesTable.swift | 29 +++++++++++++++++++ .../ArticlesDatabase/StatusesTable.swift | 6 ++++ 4 files changed, 45 insertions(+) diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index b4b1ff707..4c9258c83 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -254,6 +254,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, pullObjectsFromDisk() DispatchQueue.main.async { + self.database.cleanupDatabaseAtStartup(subscribedToFeedIDs: self.flattenedFeeds().feedIDs()) self.fetchAllUnreadCounts() } diff --git a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift index 5aa003d30..e14dac4de 100644 --- a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift +++ b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift @@ -152,6 +152,15 @@ public final class ArticlesDatabase { public func emptyCaches() { articlesTable.emptyCaches() } + + // MARK: - Cleanup + + // These are to be used only at startup. These are to prevent the database from growing forever. + + /// Calls the various clean-up functions. + public func cleanupDatabaseAtStartup(subscribedToFeedIDs: Set) { + articlesTable.deleteArticlesNotInSubscribedToFeedIDs(subscribedToFeedIDs) + } } // MARK: - Private diff --git a/Frameworks/ArticlesDatabase/ArticlesTable.swift b/Frameworks/ArticlesDatabase/ArticlesTable.swift index 418e53a1a..a0b6edbd2 100644 --- a/Frameworks/ArticlesDatabase/ArticlesTable.swift +++ b/Frameworks/ArticlesDatabase/ArticlesTable.swift @@ -423,6 +423,31 @@ final class ArticlesTable: DatabaseTable { self.databaseArticlesCache = [String: DatabaseArticle]() } } + + // MARK: - Cleanup + + /// Delete articles from feeds that are no longer in the current set of subscribed-to feeds. + /// This deletes from the articles and articleStatuses tables, + /// and, via a trigger, it also deletes from the search index. + func deleteArticlesNotInSubscribedToFeedIDs(_ feedIDs: Set) { + if feedIDs.isEmpty { + return + } + queue.run { (database) in + let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(feedIDs.count))! + let sql = "select articleID from articles where feedID not in \(placeholders);" + let parameters = Array(feedIDs) as [Any] + guard let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) else { + return + } + let articleIDs = resultSet.mapToSet{ $0.string(forColumn: DatabaseKey.articleID) } + if articleIDs.isEmpty { + return + } + self.removeArticles(articleIDs, database) + self.statusesTable.removeStatuses(articleIDs, database) + } + } } // MARK: - Private @@ -730,6 +755,10 @@ private extension ArticlesTable { // Drop Articles that we can ignore. return Set(articles.filter{ !statusIndicatesArticleIsIgnorable($0.status) }) } + + func removeArticles(_ articleIDs: Set, _ database: FMDatabase) { + deleteRowsWhere(key: DatabaseKey.articleID, equalsAnyValue: Array(articleIDs), in: database) + } } private extension Set where Element == ParsedItem { diff --git a/Frameworks/ArticlesDatabase/StatusesTable.swift b/Frameworks/ArticlesDatabase/StatusesTable.swift index fa5a741ab..65615c1b0 100644 --- a/Frameworks/ArticlesDatabase/StatusesTable.swift +++ b/Frameworks/ArticlesDatabase/StatusesTable.swift @@ -134,6 +134,12 @@ final class StatusesTable: DatabaseTable { return d } + + // MARK: - Cleanup + + func removeStatuses(_ articleIDs: Set, _ database: FMDatabase) { + deleteRowsWhere(key: DatabaseKey.articleID, equalsAnyValue: Array(articleIDs), in: database) + } } // MARK: - Private