From 850515b3632d2a2c1e2b93e555e6e54304fb50da Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 12 Sep 2024 15:40:33 -0400 Subject: [PATCH] Make FTS recovery more resiliant. --- .../securesms/database/SearchTable.kt | 29 ++++++++++++------- .../util/SignalUncaughtExceptionHandler.java | 13 ++++++++- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SearchTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/SearchTable.kt index 181a4e658e..25a831935a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SearchTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SearchTable.kt @@ -260,15 +260,24 @@ class SearchTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa try { Log.w(TAG, "[fullyResetTables] Dropping tables and triggers...") - // Due to issues we've had in the past, the delete sequence here is very particular. It mimics the "safe drop" process in the SQLite source code - // that prevents weird vtable constructor issues when dropping potentially-corrupt tables. https://sqlite.org/src/info/4db9258a78?ln=1549-1592 - if (SqlUtil.tableExists(db, FTS_TABLE_NAME)) { - db.execSQL("DELETE FROM ${FTS_TABLE_NAME}_data") - db.execSQL("DELETE FROM ${FTS_TABLE_NAME}_config") - db.execSQL("INSERT INTO ${FTS_TABLE_NAME}_data VALUES(10, X'0000000000')") - db.execSQL("INSERT INTO ${FTS_TABLE_NAME}_config VALUES('version', 4)") - db.execSQL("DROP TABLE $FTS_TABLE_NAME") + try { + db.execSQL("DROP TABLE IF EXISTS $FTS_TABLE_NAME") + } catch (e: SQLiteException) { + Log.w(TAG, "Failed to drop $FTS_TABLE_NAME! Attempting to do so a safer way.", e) + + // Due to issues we've had in the past, the delete sequence here is very particular. It mimics the "safe drop" process in the SQLite source code + // that prevents weird vtable constructor issues when dropping potentially-corrupt tables. https://sqlite.org/src/info/4db9258a78?ln=1549-1592 + db.withinTransaction { + if (SqlUtil.tableExists(db, FTS_TABLE_NAME)) { + db.execSQL("DELETE FROM ${FTS_TABLE_NAME}_data") + db.execSQL("DELETE FROM ${FTS_TABLE_NAME}_config") + db.execSQL("INSERT INTO ${FTS_TABLE_NAME}_data VALUES(10, X'0000000000')") + db.execSQL("INSERT INTO ${FTS_TABLE_NAME}_config VALUES('version', 4)") + db.execSQL("DROP TABLE $FTS_TABLE_NAME") + } + } } + db.execSQL("DROP TRIGGER IF EXISTS $TRIGGER_AFTER_INSERT") db.execSQL("DROP TRIGGER IF EXISTS $TRIGGER_AFTER_DELETE") db.execSQL("DROP TRIGGER IF EXISTS $TRIGGER_AFTER_UPDATE") @@ -283,11 +292,11 @@ class SearchTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa Log.w(TAG, "[fullyResetTables] Done. Index will be rebuilt asynchronously)") - if (useTransaction) { + if (useTransaction && db.inTransaction()) { db.setTransactionSuccessful() } } finally { - if (useTransaction) { + if (useTransaction && db.inTransaction()) { db.endTransaction() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SignalUncaughtExceptionHandler.java b/app/src/main/java/org/thoughtcrime/securesms/util/SignalUncaughtExceptionHandler.java index b01e3f2033..b697722527 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SignalUncaughtExceptionHandler.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SignalUncaughtExceptionHandler.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.util; import android.database.sqlite.SQLiteDatabaseCorruptException; +import android.database.sqlite.SQLiteException; import androidx.annotation.NonNull; @@ -40,7 +41,7 @@ public class SignalUncaughtExceptionHandler implements Thread.UncaughtExceptionH } if (e instanceof SQLiteDatabaseCorruptException) { - if (e.getMessage().indexOf("message_fts") >= 0) { + if (e.getMessage() != null && e.getMessage().contains("message_fts")) { Log.w(TAG, "FTS corrupted! Resetting FTS index."); SignalDatabase.messageSearch().fullyResetTables(); } else { @@ -48,6 +49,16 @@ public class SignalUncaughtExceptionHandler implements Thread.UncaughtExceptionH } } + if (e instanceof SQLiteException && e.getMessage() != null) { + if (e.getMessage().contains("invalid fts5 file format")) { + Log.w(TAG, "FTS in invalid state! Resetting FTS index."); + SignalDatabase.messageSearch().fullyResetTables(); + } else if (e.getMessage().contains("no such table: message_fts")) { + Log.w(TAG, "FTS table not found! Resetting FTS index."); + SignalDatabase.messageSearch().fullyResetTables(); + } + } + if (e instanceof OnErrorNotImplementedException && e.getCause() != null) { e = e.getCause(); }