From eb07cda055d594c226ee19367abedcd894e0cfe5 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 14 Feb 2020 17:32:06 -0700 Subject: [PATCH] separate indexable query for groups --- .../ViewModels/ThreadViewModel.swift | 2 +- SignalMessaging/utils/ThreadUtil.m | 6 +- .../Database/Records/InteractionFinder.swift | 26 ++++--- .../Database/Records/ThreadFinder.swift | 75 ++++++++++--------- 4 files changed, 62 insertions(+), 47 deletions(-) diff --git a/SignalMessaging/ViewModels/ThreadViewModel.swift b/SignalMessaging/ViewModels/ThreadViewModel.swift index b193af650e..5efcd1ff8c 100644 --- a/SignalMessaging/ViewModels/ThreadViewModel.swift +++ b/SignalMessaging/ViewModels/ThreadViewModel.swift @@ -44,7 +44,7 @@ public class ThreadViewModel: NSObject { self.unreadCount = InteractionFinder(threadUniqueId: thread.uniqueId).unreadCount(transaction: transaction) self.hasUnreadMessages = unreadCount > 0 - self.hasPendingMessageRequest = AnyThreadFinder.hasPendingMessageRequest(thread: thread, transaction: transaction) + self.hasPendingMessageRequest = GRDBThreadFinder.hasPendingMessageRequest(thread: thread, transaction: transaction.unwrapGrdbRead) } @objc diff --git a/SignalMessaging/utils/ThreadUtil.m b/SignalMessaging/utils/ThreadUtil.m index e18ee2b0f6..cf25662c51 100644 --- a/SignalMessaging/utils/ThreadUtil.m +++ b/SignalMessaging/utils/ThreadUtil.m @@ -406,7 +406,8 @@ NS_ASSUME_NONNULL_BEGIN __block BOOL hasPendingMessageRequest; [self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) { - hasPendingMessageRequest = [AnyThreadFinder hasPendingMessageRequestWithThread:thread transaction:transaction]; + hasPendingMessageRequest = [GRDBThreadFinder hasPendingMessageRequestWithThread:thread + transaction:transaction.unwrapGrdbRead]; }]; // If we're creating this thread or we have a pending message request, @@ -424,7 +425,8 @@ NS_ASSUME_NONNULL_BEGIN { OWSAssertDebug(thread); - BOOL hasPendingMessageRequest = [AnyThreadFinder hasPendingMessageRequestWithThread:thread transaction:transaction]; + BOOL hasPendingMessageRequest = [GRDBThreadFinder hasPendingMessageRequestWithThread:thread + transaction:transaction.unwrapGrdbWrite]; // If we're creating this thread or we have a pending message request, // any action we trigger should share our profile. diff --git a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift index ab8775a9fa..7b6b80ad02 100644 --- a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift +++ b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift @@ -347,11 +347,6 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } } - @objc - public func possiblyHasIncomingMessages(transaction: GRDBReadTransaction) -> Bool { - return grdbAdapter.possiblyHasIncomingMessages(transaction: transaction) - } - #if DEBUG @objc public func enumerateUnstartedExpiringMessages(transaction: SDSAnyReadTransaction, block: @escaping (TSMessage, UnsafeMutablePointer) -> Void) { @@ -1040,6 +1035,21 @@ struct GRDBInteractionFinderAdapter: InteractionFinderAdapter { return try! Bool.fetchOne(transaction.database, sql: sql, arguments: arguments) ?? false } + func hasGroupUpdateInfoMessage(transaction: GRDBReadTransaction) -> Bool { + let sql = """ + SELECT EXISTS( + SELECT 1 + FROM \(InteractionRecord.databaseTableName) + WHERE \(interactionColumn: .threadUniqueId) = ? + AND \(interactionColumn: .recordType) = \(SDSRecordType.infoMessage.rawValue) + AND \(interactionColumn: .messageType) = \(TSInfoMessageType.typeGroupUpdate.rawValue) + LIMIT 1 + ) + """ + let arguments: StatementArguments = [threadUniqueId] + return try! Bool.fetchOne(transaction.database, sql: sql, arguments: arguments)! + } + func possiblyHasIncomingMessages(transaction: GRDBReadTransaction) -> Bool { // All of these message types could have been triggered by anyone in // the conversation. So, if one of them exists we have to assume the conversation @@ -1067,15 +1077,11 @@ struct GRDBInteractionFinderAdapter: InteractionFinderAdapter { FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? AND \(interactionColumn: .recordType) IN (\(sqlInteractionTypes)) - OR ( - \(interactionColumn: .recordType) = \(SDSRecordType.infoMessage.rawValue) - AND \(interactionColumn: .messageType) = \(TSInfoMessageType.typeGroupUpdate.rawValue) - ) LIMIT 1 ) """ let arguments: StatementArguments = [threadUniqueId] - return try! Bool.fetchOne(transaction.database, sql: sql, arguments: arguments) ?? false + return try! Bool.fetchOne(transaction.database, sql: sql, arguments: arguments)! } #if DEBUG diff --git a/SignalServiceKit/src/Storage/Database/Records/ThreadFinder.swift b/SignalServiceKit/src/Storage/Database/Records/ThreadFinder.swift index f789d2fee5..046b2d160e 100644 --- a/SignalServiceKit/src/Storage/Database/Records/ThreadFinder.swift +++ b/SignalServiceKit/src/Storage/Database/Records/ThreadFinder.swift @@ -60,35 +60,6 @@ public class AnyThreadFinder: NSObject, ThreadFinder { return yapAdapter.sortIndex(thread: thread, transaction: yap) } } - - @objc - public class func hasPendingMessageRequest(thread: TSThread, transaction: ReadTransaction) -> Bool { - // If the feature isn't enabled, do nothing. - guard RemoteConfig.messageRequests else { return false } - - // If we're creating the thread, don't show the message request view - guard thread.shouldThreadBeVisible else { return false } - - // If the thread is already whitelisted, do nothing. The user has already - // accepted the request for this thread. - guard !SSKEnvironment.shared.profileManager.isThread( - inProfileWhitelist: thread, - transaction: transaction - ) else { return false } - - let interactionFinder = InteractionFinder(threadUniqueId: thread.uniqueId) - - let hasSentMessages = interactionFinder.existsOutgoingMessage(transaction: transaction) - guard !hasSentMessages || FeatureFlags.phoneNumberPrivacy else { return false } - - // This thread is likely only visible because of system messages like so-and-so - // is on signal or sync status. Some of the "possibly" incoming messages might - // actually have been triggered by us, but if we sent one of these then the thread - // should be in our profile white list and not make it to this check. - guard interactionFinder.possiblyHasIncomingMessages(transaction: transaction.unwrapGrdbRead) else { return false } - - return true - } } struct YAPDBThreadFinder: ThreadFinder { @@ -159,13 +130,14 @@ struct YAPDBThreadFinder: ThreadFinder { } } -struct GRDBThreadFinder: ThreadFinder { +@objc +public class GRDBThreadFinder: NSObject, ThreadFinder { - typealias ReadTransaction = GRDBReadTransaction + public typealias ReadTransaction = GRDBReadTransaction static let cn = ThreadRecord.columnName - func visibleThreadCount(isArchived: Bool, transaction: GRDBReadTransaction) throws -> UInt { + public func visibleThreadCount(isArchived: Bool, transaction: GRDBReadTransaction) throws -> UInt { let sql = """ SELECT COUNT(*) FROM \(ThreadRecord.databaseTableName) @@ -182,7 +154,8 @@ struct GRDBThreadFinder: ThreadFinder { return count } - func enumerateVisibleThreads(isArchived: Bool, transaction: GRDBReadTransaction, block: @escaping (TSThread) -> Void) throws { + @objc + public func enumerateVisibleThreads(isArchived: Bool, transaction: GRDBReadTransaction, block: @escaping (TSThread) -> Void) throws { let sql = """ SELECT * FROM \(ThreadRecord.databaseTableName) @@ -197,7 +170,7 @@ struct GRDBThreadFinder: ThreadFinder { } } - func sortIndex(thread: TSThread, transaction: GRDBReadTransaction) throws -> UInt? { + public func sortIndex(thread: TSThread, transaction: GRDBReadTransaction) throws -> UInt? { let sql = """ SELECT sortIndex FROM ( @@ -216,4 +189,38 @@ struct GRDBThreadFinder: ThreadFinder { let arguments: StatementArguments = [grdbId.intValue] return try UInt.fetchOne(transaction.database, sql: sql, arguments: arguments) } + + @objc + public class func hasPendingMessageRequest(thread: TSThread, transaction: GRDBReadTransaction) -> Bool { + // If the feature isn't enabled, do nothing. + guard RemoteConfig.messageRequests else { return false } + + // If we're creating the thread, don't show the message request view + guard thread.shouldThreadBeVisible else { return false } + + // If the thread is already whitelisted, do nothing. The user has already + // accepted the request for this thread. + guard !SSKEnvironment.shared.profileManager.isThread( + inProfileWhitelist: thread, + transaction: transaction.asAnyRead + ) else { return false } + + let interactionFinder = GRDBInteractionFinderAdapter(threadUniqueId: thread.uniqueId) + + let hasSentMessages = interactionFinder.existsOutgoingMessage(transaction: transaction) + guard !hasSentMessages || FeatureFlags.phoneNumberPrivacy else { return false } + + if thread.isGroupThread(), + interactionFinder.hasGroupUpdateInfoMessage(transaction: transaction) { + return true + } + + // This thread is likely only visible because of system messages like so-and-so + // is on signal or sync status. Some of the "possibly" incoming messages might + // actually have been triggered by us, but if we sent one of these then the thread + // should be in our profile white list and not make it to this check. + guard interactionFinder.possiblyHasIncomingMessages(transaction: transaction) else { return false } + + return true + } }