diff --git a/FirebaseMessaging/CHANGELOG.md b/FirebaseMessaging/CHANGELOG.md index f2756c5ce0a..9f2346bc2d3 100644 --- a/FirebaseMessaging/CHANGELOG.md +++ b/FirebaseMessaging/CHANGELOG.md @@ -1,3 +1,6 @@ +# 12.8.0 +- [fixed] Fix missing database crash on launch. (#14880) + # 12.1.0 - [fixed] Fix Xcode 26 crash from missing `NSUserActivityTypeBrowsingWeb` symbol. Note that this fix isn't in the 12.1.0 zip and Carthage diff --git a/FirebaseMessaging/Sources/FIRMessagingRmqManager.m b/FirebaseMessaging/Sources/FIRMessagingRmqManager.m index ae28e2c6a6e..ed01c0ad26f 100644 --- a/FirebaseMessaging/Sources/FIRMessagingRmqManager.m +++ b/FirebaseMessaging/Sources/FIRMessagingRmqManager.m @@ -489,6 +489,12 @@ - (void)removeDatabase { }); } +- (void)createTable { + [self createTableWithName:kTableOutgoingRmqMessages command:kCreateTableOutgoingRmqMessages]; + [self createTableWithName:kTableLastRmqId command:kCreateTableLastRmqId]; + [self createTableWithName:kTableS2DRmqIds command:kCreateTableS2DRmqIds]; +} + - (void)openDatabase { dispatch_async(_databaseOperationQueue, ^{ NSFileManager *fileManager = [NSFileManager defaultManager]; @@ -512,26 +518,44 @@ - (void)openDatabase { NSAssert(NO, errorMessage); return; } - [self createTableWithName:kTableOutgoingRmqMessages command:kCreateTableOutgoingRmqMessages]; - - [self createTableWithName:kTableLastRmqId command:kCreateTableLastRmqId]; - [self createTableWithName:kTableS2DRmqIds command:kCreateTableS2DRmqIds]; + [self createTable]; } else { - // Calling sqlite3_open should create the database, since the file doesn't exist. + // The file exists, try to open it. If it fails, it might be corrupt. int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; #ifdef SQLITE_OPEN_FILEPROTECTION_NONE flags |= SQLITE_OPEN_FILEPROTECTION_NONE; #endif int result = sqlite3_open_v2([path UTF8String], &self->_database, flags, NULL); + + // If opening the database failed, it might be corrupt. Try to recover by deleting and + // recreating it. if (result != SQLITE_OK) { - NSString *errorString = FIRMessagingStringFromSQLiteResult(result); - NSString *errorMessage = - [NSString stringWithFormat:@"Could not create RMQ database at path %@, error: %@", path, - errorString]; - FIRMessagingLoggerError(kFIRMessagingMessageCodeRmq2PersistentStoreErrorCreatingDatabase, - @"%@", errorMessage); - NSAssert(NO, errorMessage); - didOpenDatabase = NO; + FIRMessagingLoggerWarn(kFIRMessagingMessageCodeRmq2PersistentStoreErrorOpeningDatabase, + @"Could not open RMQ database at path: %@. " + "Will delete and try to recreate it.", + path); + NSError *removeError; + if (![[NSFileManager defaultManager] removeItemAtPath:path error:&removeError]) { + FIRMessagingLoggerWarn(kFIRMessagingMessageCodeRmq2PersistentStoreErrorOpeningDatabase, + @"Failed to delete corrupt database at %@: %@", path, removeError); + } + // After deleting, try to open it again. + result = sqlite3_open_v2([path UTF8String], &self->_database, flags, NULL); + // If it still fails after the recovery attempt, then assert and crash. + if (result != SQLITE_OK) { + NSString *errorString = FIRMessagingStringFromSQLiteResult(result); + NSString *errorMessage = [NSString + stringWithFormat:@"Could not open or create RMQ database at path %@, error: %@", path, + errorString]; + FIRMessagingLoggerError(kFIRMessagingMessageCodeRmq2PersistentStoreErrorOpeningDatabase, + @"%@", errorMessage); + NSAssert(NO, errorMessage); + didOpenDatabase = NO; // Still failed, so indicate database did not open. + } else { + // Successfully recreated after corruption, so treat as a new database for table creation. + didOpenDatabase = YES; // Indicate successful opening after recreation. + [self createTable]; + } } else { [self updateDBWithStringRmqID]; }