From 3cd238616add4d476042a4c6761c3d42ee770fa3 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Wed, 3 Dec 2025 17:07:15 -0800 Subject: [PATCH] [FCM] Recovery logic for a corrupt database --- .../Sources/FIRMessagingRmqManager.m | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/FirebaseMessaging/Sources/FIRMessagingRmqManager.m b/FirebaseMessaging/Sources/FIRMessagingRmqManager.m index 6a28d1d926d..903047933fd 100644 --- a/FirebaseMessaging/Sources/FIRMessagingRmqManager.m +++ b/FirebaseMessaging/Sources/FIRMessagingRmqManager.m @@ -501,7 +501,7 @@ - (void)openDatabase { #ifdef SQLITE_OPEN_FILEPROTECTION_NONE flags |= SQLITE_OPEN_FILEPROTECTION_NONE; #endif - int result = sqlite3_open_v2([path UTF8String], &self -> _database, flags, NULL); + int result = sqlite3_open_v2([path UTF8String], &self->_database, flags, NULL); if (result != SQLITE_OK) { NSString *errorString = FIRMessagingStringFromSQLiteResult(result); NSString *errorMessage = [NSString @@ -517,21 +517,41 @@ - (void)openDatabase { [self createTableWithName:kTableLastRmqId command:kCreateTableLastRmqId]; [self createTableWithName:kTableS2DRmqIds command:kCreateTableS2DRmqIds]; } 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); + 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); + [[NSFileManager defaultManager] removeItemAtPath:path error:nil]; + // 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 createTableWithName:kTableOutgoingRmqMessages + command:kCreateTableOutgoingRmqMessages]; + [self createTableWithName:kTableLastRmqId command:kCreateTableLastRmqId]; + [self createTableWithName:kTableS2DRmqIds command:kCreateTableS2DRmqIds]; + } } else { [self updateDBWithStringRmqID]; }