// // Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import "OWSDatabaseMigrationRunner.h" #import "OWSDatabaseMigration.h" #import #import NS_ASSUME_NONNULL_BEGIN @implementation OWSDatabaseMigrationRunner #pragma mark - Dependencies - (OWSPrimaryStorage *)primaryStorage { OWSAssertDebug(SSKEnvironment.shared.primaryStorage); return SSKEnvironment.shared.primaryStorage; } #pragma mark - // This should all migrations which do NOT qualify as safeBlockingMigrations: - (NSArray *)allMigrations { return @[ [SNOpenGroupServerIdLookupMigration new], [SNMessageRequestsMigration new], [SNContactsMigration new], [SNBlockingManagerRemovalMigration new] ]; } - (void)assumeAllExistingMigrationsRun { for (OWSDatabaseMigration *migration in self.allMigrations) { OWSLogInfo(@"Skipping migration on new install: %@", migration); [migration save]; } } - (void)runAllOutstandingWithCompletion:(OWSDatabaseMigrationCompletion)completion { [self removeUnknownMigrations]; [self runMigrations:[self.allMigrations mutableCopy] prevWasSuccessful: true prevNeedsConfigSync:false completion:completion]; } // Some users (especially internal users) will move back and forth between // app versions. Whenever they move "forward" in the version history, we // want them to re-run any new migrations. Therefore, when they move "backward" // in the version history, we cull any unknown migrations. - (void)removeUnknownMigrations { NSMutableSet *knownMigrationIds = [NSMutableSet new]; for (OWSDatabaseMigration *migration in self.allMigrations) { [knownMigrationIds addObject:migration.uniqueId]; } [OWSPrimaryStorage.sharedManager.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { NSArray *savedMigrationIds = [transaction allKeysInCollection:OWSDatabaseMigration.collection]; NSMutableSet *unknownMigrationIds = [NSMutableSet new]; [unknownMigrationIds addObjectsFromArray:savedMigrationIds]; [unknownMigrationIds minusSet:knownMigrationIds]; for (NSString *unknownMigrationId in unknownMigrationIds) { OWSLogInfo(@"Culling unknown migration: %@", unknownMigrationId); [transaction removeObjectForKey:unknownMigrationId inCollection:OWSDatabaseMigration.collection]; } }]; } // Run migrations serially to: // // * Ensure predictable ordering. // * Prevent them from interfering with each other (e.g. deadlock). - (void)runMigrations:(NSMutableArray *)migrations prevWasSuccessful:(BOOL)prevWasSuccessful prevNeedsConfigSync:(BOOL)prevNeedsConfigSync completion:(OWSDatabaseMigrationCompletion)completion { OWSAssertDebug(migrations); OWSAssertDebug(completion); // If there are no more migrations to run, complete. if (migrations.count < 1) { dispatch_async(dispatch_get_main_queue(), ^{ completion(prevWasSuccessful, prevNeedsConfigSync); }); return; } // Pop next migration from front of queue. OWSDatabaseMigration *migration = migrations.firstObject; [migrations removeObjectAtIndex:0]; // If migration has already been run, skip it. if ([OWSDatabaseMigration fetchObjectWithUniqueID:migration.uniqueId] != nil) { [self runMigrations:migrations prevWasSuccessful:prevWasSuccessful prevNeedsConfigSync:prevNeedsConfigSync completion:completion]; return; } OWSLogInfo(@"Running migration: %@", migration); [migration runUpWithCompletion:^(BOOL successful, BOOL needsConfigSync){ OWSLogInfo(@"Migration complete: %@", migration); [self runMigrations:migrations prevWasSuccessful:(prevWasSuccessful && successful) prevNeedsConfigSync:(prevNeedsConfigSync || needsConfigSync) completion:completion]; }]; } @end NS_ASSUME_NONNULL_END