Add shared YAP extension for SignalServiceAddress lookup

This commit is contained in:
Nora Trapp 2019-06-26 19:00:17 -07:00
parent 7d94af3287
commit 52545fa0c4
8 changed files with 245 additions and 256 deletions

2
Pods

@ -1 +1 @@
Subproject commit 7fcaa35aac69aa47443b920ef4c6553bd1f8d9d3
Subproject commit 3d393009e3fded14568792745f55eb8faccf44a0

View File

@ -0,0 +1,48 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
@objc
class AnyContactThreadFinder: NSObject {
let grdbAdapter = GRDBContactThreadFinder()
let yapdbAdapter = YAPDBSignalServiceAddressIndex()
}
extension AnyContactThreadFinder {
@objc(contactThreadForAddress:transaction:)
func contactThread(for address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> TSContactThread? {
switch transaction.readTransaction {
case .grdbRead(let transaction):
return grdbAdapter.contactThread(for: address, transaction: transaction)
case .yapRead(let transaction):
return yapdbAdapter.fetchOne(for: address, transaction: transaction)
}
}
}
@objc
class GRDBContactThreadFinder: NSObject {
func contactThread(for address: SignalServiceAddress, transaction: GRDBReadTransaction) -> TSContactThread? {
if let account = contactThreadForUUID(address.uuid, transaction: transaction) {
return account
} else if let account = contactThreadForPhoneNumber(address.phoneNumber, transaction: transaction) {
return account
} else {
return nil
}
}
private func contactThreadForUUID(_ uuid: UUID?, transaction: GRDBReadTransaction) -> TSContactThread? {
guard let uuidString = uuid?.uuidString else { return nil }
let sql = "SELECT * FROM \(ThreadRecord.databaseTableName) WHERE \(threadColumn: .contactUUID) = ?"
return TSContactThread.grdbFetchOne(sql: sql, arguments: [uuidString], transaction: transaction) as? TSContactThread
}
private func contactThreadForPhoneNumber(_ phoneNumber: String?, transaction: GRDBReadTransaction) -> TSContactThread? {
guard let phoneNumber = phoneNumber else { return nil }
let sql = "SELECT * FROM \(ThreadRecord.databaseTableName) WHERE \(threadColumn: .contactPhoneNumber) = ?"
return TSContactThread.grdbFetchOne(sql: sql, arguments: [phoneNumber], transaction: transaction) as? TSContactThread
}
}

View File

@ -4,36 +4,26 @@
import Foundation
protocol SignalAccountFinder {
associatedtype ReadTransaction
func signalAccount(for address: SignalServiceAddress, transaction: ReadTransaction) -> SignalAccount?
}
@objc
class AnySignalAccountFinder: NSObject {
typealias ReadTransaction = SDSAnyReadTransaction
let grdbAdapter = GRDBSignalAccountFinder()
let yapdbAdapter = YAPDBSignalAccountFinder()
let yapdbAdapter = YAPDBSignalServiceAddressIndex()
}
extension AnySignalAccountFinder: SignalAccountFinder {
extension AnySignalAccountFinder {
@objc(signalAccountForAddress:transaction:)
func signalAccount(for address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> SignalAccount? {
switch transaction.readTransaction {
case .grdbRead(let transaction):
return grdbAdapter.signalAccount(for: address, transaction: transaction)
case .yapRead(let transaction):
return yapdbAdapter.signalAccount(for: address, transaction: transaction)
return yapdbAdapter.fetchOne(for: address, transaction: transaction)
}
}
}
@objc
class GRDBSignalAccountFinder: NSObject, SignalAccountFinder {
typealias ReadTransaction = GRDBReadTransaction
class GRDBSignalAccountFinder: NSObject {
func signalAccount(for address: SignalServiceAddress, transaction: GRDBReadTransaction) -> SignalAccount? {
if let account = signalAccountForUUID(address.uuid, transaction: transaction) {
return account
@ -56,109 +46,3 @@ class GRDBSignalAccountFinder: NSObject, SignalAccountFinder {
return SignalAccount.grdbFetchOne(sql: sql, arguments: [phoneNumber], transaction: transaction)
}
}
@objc
class YAPDBSignalAccountFinder: NSObject, SignalAccountFinder {
typealias ReadTransaction = YapDatabaseReadTransaction
private static let uuidColumn = "recipient_uuid"
private static let phoneNumberColumn = "recipient_phone_number"
private static let phoneNumberIndex = "index_signal_accounts_on_recipientPhoneNumber"
private static let uuidIndex = "index_signal_accounts_on_recipientUUID"
func signalAccount(for address: SignalServiceAddress, transaction: YapDatabaseReadTransaction) -> SignalAccount? {
if let account = signalAccountForUUID(address.uuid, transaction: transaction) {
return account
} else if let account = signalAccountForPhoneNumber(address.phoneNumber, transaction: transaction) {
return account
} else {
return nil
}
}
private func signalAccountForUUID(_ uuid: UUID?, transaction: YapDatabaseReadTransaction) -> SignalAccount? {
guard let uuidString = uuid?.uuidString else { return nil }
guard let ext = transaction.ext(YAPDBSignalAccountFinder.uuidIndex) as? YapDatabaseSecondaryIndexTransaction else {
owsFailDebug("Unexpected transaction type for extension")
return nil
}
let queryFormat = String(format: "WHERE %@ = \"%@\"", YAPDBSignalAccountFinder.uuidColumn, uuidString)
let query = YapDatabaseQuery(string: queryFormat, parameters: [])
var matchedAccount: SignalAccount?
ext.enumerateKeysAndObjects(matching: query) { _, _, object, stop in
guard let account = object as? SignalAccount else {
owsFailDebug("Unexpected object type \(type(of: object))")
return
}
matchedAccount = account
stop.pointee = true
}
return matchedAccount
}
private func signalAccountForPhoneNumber(_ phoneNumber: String?, transaction: YapDatabaseReadTransaction) -> SignalAccount? {
guard let phoneNumber = phoneNumber else { return nil }
guard let ext = transaction.ext(YAPDBSignalAccountFinder.phoneNumberIndex) as? YapDatabaseSecondaryIndexTransaction else {
owsFailDebug("Unexpected transaction type for extension")
return nil
}
let queryFormat = String(format: "WHERE %@ = \"%@\"", YAPDBSignalAccountFinder.phoneNumberColumn, phoneNumber)
let query = YapDatabaseQuery(string: queryFormat, parameters: [])
var matchedAccount: SignalAccount?
ext.enumerateKeysAndObjects(matching: query) { _, _, object, stop in
guard let account = object as? SignalAccount else {
owsFailDebug("Unexpected object type \(type(of: object))")
return
}
matchedAccount = account
stop.pointee = true
}
return matchedAccount
}
@objc
static func asyncRegisterDatabaseExtensions(_ storage: OWSStorage) {
storage.asyncRegister(indexUUIDExtension(), withName: uuidIndex)
storage.asyncRegister(indexPhoneNumberExtension(), withName: phoneNumberIndex)
}
private static func indexUUIDExtension() -> YapDatabaseSecondaryIndex {
let setup = YapDatabaseSecondaryIndexSetup()
setup.addColumn(uuidColumn, with: .text)
let handler = YapDatabaseSecondaryIndexHandler.withObjectBlock { _, dict, _, _, object in
guard let account = object as? SignalAccount else {
return
}
dict[uuidColumn] = account.recipientUUID
}
return YapDatabaseSecondaryIndex(setup: setup, handler: handler, versionTag: "1")
}
private static func indexPhoneNumberExtension() -> YapDatabaseSecondaryIndex {
let setup = YapDatabaseSecondaryIndexSetup()
setup.addColumn(phoneNumberColumn, with: .text)
let handler = YapDatabaseSecondaryIndexHandler.withObjectBlock { _, dict, _, _, object in
guard let account = object as? SignalAccount else {
return
}
dict[phoneNumberColumn] = account.recipientPhoneNumber
}
return YapDatabaseSecondaryIndex(setup: setup, handler: handler, versionTag: "1")
}
}

View File

@ -4,41 +4,31 @@
import Foundation
protocol SignalRecipientFinder {
associatedtype ReadTransaction
func signalRecipient(for address: SignalServiceAddress, transaction: ReadTransaction) -> SignalRecipient?
}
@objc
public class AnySignalRecipientFinder: NSObject {
typealias ReadTransaction = SDSAnyReadTransaction
class AnySignalRecipientFinder: NSObject {
let grdbAdapter = GRDBSignalRecipientFinder()
let yapdbAdapter = YAPDBSignalRecipientFinder()
let yapdbAdapter = YAPDBSignalServiceAddressIndex()
}
extension AnySignalRecipientFinder: SignalRecipientFinder {
extension AnySignalRecipientFinder {
@objc(signalRecipientForAddress:transaction:)
func signalRecipient(for address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> SignalRecipient? {
switch transaction.readTransaction {
case .grdbRead(let transaction):
return grdbAdapter.signalRecipient(for: address, transaction: transaction)
case .yapRead(let transaction):
return yapdbAdapter.signalRecipient(for: address, transaction: transaction)
return yapdbAdapter.fetchOne(for: address, transaction: transaction)
}
}
}
@objc
class GRDBSignalRecipientFinder: NSObject, SignalRecipientFinder {
typealias ReadTransaction = GRDBReadTransaction
class GRDBSignalRecipientFinder: NSObject {
func signalRecipient(for address: SignalServiceAddress, transaction: GRDBReadTransaction) -> SignalRecipient? {
if let recipient = signalRecipientForUUID(address.uuid, transaction: transaction) {
return recipient
} else if let recipient = signalRecipientForPhoneNumber(address.phoneNumber, transaction: transaction) {
return recipient
if let account = signalRecipientForUUID(address.uuid, transaction: transaction) {
return account
} else if let account = signalRecipientForPhoneNumber(address.phoneNumber, transaction: transaction) {
return account
} else {
return nil
}
@ -56,109 +46,3 @@ class GRDBSignalRecipientFinder: NSObject, SignalRecipientFinder {
return SignalRecipient.grdbFetchOne(sql: sql, arguments: [phoneNumber], transaction: transaction)
}
}
@objc
class YAPDBSignalRecipientFinder: NSObject, SignalRecipientFinder {
typealias ReadTransaction = YapDatabaseReadTransaction
private static let uuidColumn = "recipient_uuid"
private static let phoneNumberColumn = "recipient_phone_number"
private static let phoneNumberIndex = "index_signal_recipients_on_recipientPhoneNumber"
private static let uuidIndex = "index_signal_recipients_on_recipientUUID"
func signalRecipient(for address: SignalServiceAddress, transaction: YapDatabaseReadTransaction) -> SignalRecipient? {
if let recipient = signalRecipientForUUID(address.uuid, transaction: transaction) {
return recipient
} else if let recipient = signalRecipientForPhoneNumber(address.phoneNumber, transaction: transaction) {
return recipient
} else {
return nil
}
}
private func signalRecipientForUUID(_ uuid: UUID?, transaction: YapDatabaseReadTransaction) -> SignalRecipient? {
guard let uuidString = uuid?.uuidString else { return nil }
guard let ext = transaction.ext(YAPDBSignalRecipientFinder.uuidIndex) as? YapDatabaseSecondaryIndexTransaction else {
owsFailDebug("Unexpected transaction type for extension")
return nil
}
let queryFormat = String(format: "WHERE %@ = \"%@\"", YAPDBSignalRecipientFinder.uuidColumn, uuidString)
let query = YapDatabaseQuery(string: queryFormat, parameters: [])
var matchedAccount: SignalRecipient?
ext.enumerateKeysAndObjects(matching: query) { _, _, object, stop in
guard let recipient = object as? SignalRecipient else {
owsFailDebug("Unexpected object type \(type(of: object))")
return
}
matchedAccount = recipient
stop.pointee = true
}
return matchedAccount
}
private func signalRecipientForPhoneNumber(_ phoneNumber: String?, transaction: YapDatabaseReadTransaction) -> SignalRecipient? {
guard let phoneNumber = phoneNumber else { return nil }
guard let ext = transaction.ext(YAPDBSignalRecipientFinder.phoneNumberIndex) as? YapDatabaseSecondaryIndexTransaction else {
owsFailDebug("Unexpected transaction type for extension")
return nil
}
let queryFormat = String(format: "WHERE %@ = \"%@\"", YAPDBSignalRecipientFinder.phoneNumberColumn, phoneNumber)
let query = YapDatabaseQuery(string: queryFormat, parameters: [])
var matchedAccount: SignalRecipient?
ext.enumerateKeysAndObjects(matching: query) { _, _, object, stop in
guard let recipient = object as? SignalRecipient else {
owsFailDebug("Unexpected object type \(type(of: object))")
return
}
matchedAccount = recipient
stop.pointee = true
}
return matchedAccount
}
@objc
static func asyncRegisterDatabaseExtensions(_ storage: OWSStorage) {
storage.asyncRegister(indexUUIDExtension(), withName: uuidIndex)
storage.asyncRegister(indexPhoneNumberExtension(), withName: phoneNumberIndex)
}
private static func indexUUIDExtension() -> YapDatabaseSecondaryIndex {
let setup = YapDatabaseSecondaryIndexSetup()
setup.addColumn(uuidColumn, with: .text)
let handler = YapDatabaseSecondaryIndexHandler.withObjectBlock { _, dict, _, _, object in
guard let recipient = object as? SignalRecipient else {
return
}
dict[uuidColumn] = recipient.recipientUUID
}
return YapDatabaseSecondaryIndex(setup: setup, handler: handler, versionTag: "1")
}
private static func indexPhoneNumberExtension() -> YapDatabaseSecondaryIndex {
let setup = YapDatabaseSecondaryIndexSetup()
setup.addColumn(phoneNumberColumn, with: .text)
let handler = YapDatabaseSecondaryIndexHandler.withObjectBlock { _, dict, _, _, object in
guard let recipient = object as? SignalRecipient else {
return
}
dict[phoneNumberColumn] = recipient.recipientPhoneNumber
}
return YapDatabaseSecondaryIndex(setup: setup, handler: handler, versionTag: "1")
}
}

View File

@ -32,6 +32,12 @@ NSUInteger const TSContactThreadSchemaVersion = 1;
return SDSDatabaseStorage.shared;
}
+ (AnyContactThreadFinder *)threadFinder
{
return [AnyContactThreadFinder new];
}
#pragma mark -
// --- CODE GENERATION MARKER
@ -114,10 +120,7 @@ isArchivedByLegacyTimestampForSorting:isArchivedByLegacyTimestampForSorting
{
OWSAssertDebug(contactAddress);
// TODO
TSContactThread *thread
= (TSContactThread *)[self anyFetchWithUniqueId:"c" + contactAddress.transitional_phoneNumber
transaction:transaction];
TSContactThread *thread = [self.threadFinder contactThreadForAddress:contactAddress transaction:transaction];
if (!thread) {
thread = [[TSContactThread alloc] initWithContactAddress:contactAddress];
@ -142,9 +145,7 @@ isArchivedByLegacyTimestampForSorting:isArchivedByLegacyTimestampForSorting
+ (nullable instancetype)getThreadWithContactAddress:(SignalServiceAddress *)contactAddress
transaction:(SDSAnyReadTransaction *)transaction
{
// TODO
return (TSContactThread *)[TSContactThread anyFetchWithUniqueId:"c" + contactAddress.transitional_phoneNumber
transaction:transaction];
return [self.threadFinder contactThreadForAddress:contactAddress transaction:transaction];
}
- (SignalServiceAddress *)contactAddress
@ -194,7 +195,9 @@ isArchivedByLegacyTimestampForSorting:isArchivedByLegacyTimestampForSorting
+ (nullable SignalServiceAddress *)contactAddressFromThreadId:(NSString *)threadId
transaction:(SDSAnyReadTransaction *)transaction
{
return nil;
TSContactThread *_Nullable contactThread = (TSContactThread *)[TSContactThread anyFetchWithUniqueId:threadId
transaction:transaction];
return contactThread.contactAddress;
}
+ (nullable NSString *)legacyContactPhoneNumberFromThreadId:(NSString *)threadId

View File

@ -0,0 +1,145 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
protocol YAPDBSignalServiceAddressIndexable {
static var indexablePhoneNumberColumnName: String { get }
static var indexableUUIDColumnName: String { get }
var indexableUUIDValue: String? { get }
var indexablePhoneNumberValue: String? { get }
}
extension SignalAccount: YAPDBSignalServiceAddressIndexable {
static var indexablePhoneNumberColumnName = "recipient_phone_number"
static var indexableUUIDColumnName = "recipient_uuid"
var indexableUUIDValue: String? { return recipientUUID }
var indexablePhoneNumberValue: String? { return recipientPhoneNumber }
}
extension SignalRecipient: YAPDBSignalServiceAddressIndexable {
static var indexablePhoneNumberColumnName = "recipient_phone_number"
static var indexableUUIDColumnName = "recipient_uuid"
var indexableUUIDValue: String? { return recipientUUID }
var indexablePhoneNumberValue: String? { return recipientPhoneNumber }
}
extension TSContactThread: YAPDBSignalServiceAddressIndexable {
static var indexablePhoneNumberColumnName = "contact_phone_number"
static var indexableUUIDColumnName = "contact_uuid"
var indexableUUIDValue: String? { return contactUUID }
var indexablePhoneNumberValue: String? { return contactPhoneNumber }
}
@objc
class YAPDBSignalServiceAddressIndex: NSObject {
private static let indexableTypes: [YAPDBSignalServiceAddressIndexable.Type] = [
SignalAccount.self,
SignalRecipient.self,
TSContactThread.self
]
private static let phoneNumberIndexName = "index_on_recipientPhoneNumber"
private static let uuidIndexName = "index_on_recipientUUID"
func fetchOne<T: YAPDBSignalServiceAddressIndexable>(for address: SignalServiceAddress, transaction: YapDatabaseReadTransaction) -> T? {
if let result: T = fetchOneForUUID(address.uuid, transaction: transaction) {
return result
} else if let result: T = fetchOneForPhoneNumber(address.phoneNumber, transaction: transaction) {
return result
} else {
return nil
}
}
private func fetchOneForUUID<T: YAPDBSignalServiceAddressIndexable>(_ uuid: UUID?, transaction: YapDatabaseReadTransaction) -> T? {
guard let uuidString = uuid?.uuidString else { return nil }
guard let ext = transaction.ext(YAPDBSignalServiceAddressIndex.uuidIndexName) as? YapDatabaseSecondaryIndexTransaction else {
owsFailDebug("Unexpected transaction type for extension")
return nil
}
let queryFormat = String(format: "WHERE %@ = \"%@\"", T.indexableUUIDColumnName, uuidString)
let query = YapDatabaseQuery(string: queryFormat, parameters: [])
var matchedAccount: T?
ext.enumerateKeysAndObjects(matching: query) { _, _, object, stop in
guard let account = object as? T else {
return
}
matchedAccount = account
stop.pointee = true
}
return matchedAccount
}
private func fetchOneForPhoneNumber<T: YAPDBSignalServiceAddressIndexable>(_ phoneNumber: String?, transaction: YapDatabaseReadTransaction) -> T? {
guard let phoneNumber = phoneNumber else { return nil }
guard let ext = transaction.ext(YAPDBSignalServiceAddressIndex.phoneNumberIndexName) as? YapDatabaseSecondaryIndexTransaction else {
owsFailDebug("Unexpected transaction type for extension")
return nil
}
let queryFormat = String(format: "WHERE %@ = \"%@\"", T.indexablePhoneNumberColumnName, phoneNumber)
let query = YapDatabaseQuery(string: queryFormat, parameters: [])
var matchedAccount: T?
ext.enumerateKeysAndObjects(matching: query) { _, _, object, stop in
guard let account = object as? T else {
return
}
matchedAccount = account
stop.pointee = true
}
return matchedAccount
}
@objc
static func asyncRegisterDatabaseExtensions(_ storage: OWSStorage) {
storage.asyncRegister(indexUUIDExtension(), withName: uuidIndexName)
storage.asyncRegister(indexPhoneNumberExtension(), withName: phoneNumberIndexName)
}
private static func indexUUIDExtension() -> YapDatabaseSecondaryIndex {
let setup = YapDatabaseSecondaryIndexSetup()
for columnName in Set(indexableTypes.map { $0.indexableUUIDColumnName }) {
setup.addColumn(columnName, with: .text)
}
let handler = YapDatabaseSecondaryIndexHandler.withObjectBlock { _, dict, _, _, object in
guard let indexableObject = object as? YAPDBSignalServiceAddressIndexable else {
return
}
dict[type(of: indexableObject).indexableUUIDColumnName] = indexableObject.indexableUUIDValue
}
return YapDatabaseSecondaryIndex(setup: setup, handler: handler, versionTag: "1")
}
private static func indexPhoneNumberExtension() -> YapDatabaseSecondaryIndex {
let setup = YapDatabaseSecondaryIndexSetup()
for columnName in Set(indexableTypes.map { $0.indexablePhoneNumberColumnName }) {
setup.addColumn(columnName, with: .text)
}
let handler = YapDatabaseSecondaryIndexHandler.withObjectBlock { _, dict, _, _, object in
guard let indexableObject = object as? YAPDBSignalServiceAddressIndexable else {
return
}
dict[type(of: indexableObject).indexablePhoneNumberColumnName] = indexableObject.indexablePhoneNumberValue
}
return YapDatabaseSecondaryIndex(setup: setup, handler: handler, versionTag: "1")
}
}

View File

@ -502,6 +502,32 @@ public class GRDBDatabaseStorageAdapter: NSObject {
on: SignalAccountRecord.databaseTableName,
columns: [SignalAccountRecord.columnName(.recipientUUID)]
)
// Signal Recipient Indices
try db.create(
index: "index_signal_recipients_on_recipientPhoneNumber",
on: SignalAccountRecord.databaseTableName,
columns: [SignalAccountRecord.columnName(.recipientPhoneNumber)]
)
try db.create(
index: "index_signal_recipients_on_recipientUUID",
on: SignalAccountRecord.databaseTableName,
columns: [SignalAccountRecord.columnName(.recipientUUID)]
)
// Thread Indices
try db.create(
index: "index_thread_on_contactPhoneNumber",
on: ThreadRecord.databaseTableName,
columns: [ThreadRecord.columnName(.contactPhoneNumber)]
)
try db.create(
index: "index_tsthread_on_contactUUID",
on: ThreadRecord.databaseTableName,
columns: [ThreadRecord.columnName(.contactUUID)]
)
}
return migrator
}()

View File

@ -230,8 +230,7 @@ void VerifyRegistrationsForPrimaryStorage(OWSStorage *storage, dispatch_block_t
[YAPDBMediaGalleryFinder asyncRegisterDatabaseExtensionsWithPrimaryStorage:self];
[TSDatabaseView asyncRegisterLazyRestoreAttachmentsDatabaseView:self];
[YAPDBJobRecordFinderSetup asyncRegisterDatabaseExtensionObjCWithStorage:self];
[YAPDBSignalAccountFinder asyncRegisterDatabaseExtensions:self];
[YAPDBSignalRecipientFinder asyncRegisterDatabaseExtensions:self];
[YAPDBSignalServiceAddressIndex asyncRegisterDatabaseExtensions:self];
[self.database
flushExtensionRequestsWithCompletionQueue:dispatch_get_global_queue(