From de0a37d356b0d74e1cc2cf9768a3e245abf03e33 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 30 Mar 2023 17:17:10 -0400 Subject: [PATCH] Fix possible dangling thread records. --- .../helpers/SignalDatabaseMigrations.kt | 7 ++- .../V181_ThreadTableForeignKeyCleanup.kt | 53 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V181_ThreadTableForeignKeyCleanup.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 4b2a967f277..369eb71e3d6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -36,6 +36,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V177_MessageSendLog import org.thoughtcrime.securesms.database.helpers.migration.V178_ReportingTokenColumnMigration import org.thoughtcrime.securesms.database.helpers.migration.V179_CleanupDanglingMessageSendLogMigration import org.thoughtcrime.securesms.database.helpers.migration.V180_RecipientNicknameMigration +import org.thoughtcrime.securesms.database.helpers.migration.V181_ThreadTableForeignKeyCleanup /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -44,7 +45,7 @@ object SignalDatabaseMigrations { val TAG: String = Log.tag(SignalDatabaseMigrations.javaClass) - const val DATABASE_VERSION = 180 + const val DATABASE_VERSION = 181 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { @@ -175,6 +176,10 @@ object SignalDatabaseMigrations { if (oldVersion < 180) { V180_RecipientNicknameMigration.migrate(context, db, oldVersion, newVersion) } + + if (oldVersion < 181) { + V181_ThreadTableForeignKeyCleanup.migrate(context, db, oldVersion, newVersion) + } } @JvmStatic diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V181_ThreadTableForeignKeyCleanup.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V181_ThreadTableForeignKeyCleanup.kt new file mode 100644 index 00000000000..88a3731a7c5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V181_ThreadTableForeignKeyCleanup.kt @@ -0,0 +1,53 @@ +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase +import org.signal.core.util.Stopwatch +import org.signal.core.util.logging.Log + +/** + * We saw evidence (via failed backup restores) that some people have recipients in their thread table that do not exist in the recipient table. + * This is likely the result of a bad past migration, since a foreign key is in place. Cleaning it up now. + */ +object V181_ThreadTableForeignKeyCleanup : SignalDatabaseMigration { + + val TAG = Log.tag(V181_ThreadTableForeignKeyCleanup::class.java) + + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + val stopwatch = Stopwatch("deletes") + + try { + val threadDeleteCount = db.delete("thread", "recipient_id NOT IN (SELECT _id FROM recipient)", null) + Log.i(TAG, "Deleted $threadDeleteCount threads.") + stopwatch.split("threads") + + if (threadDeleteCount == 0) { + Log.w(TAG, "No threads deleted. Finishing early.") + return + } + + val messageDeleteCount = db.delete("message", "thread_id NOT IN (SELECT _id FROM thread)", null) + Log.i(TAG, "Deleted $messageDeleteCount messages.") + stopwatch.split("messages") + + if (messageDeleteCount == 0) { + Log.w(TAG, "No messages deleted. Finishing early.") + return + } + + val storySendDeleteCount = db.delete("story_sends", "message_id NOT IN (SELECT _id FROM message)", null) + Log.i(TAG, "Deleted $storySendDeleteCount story_sends.") + stopwatch.split("story_sends") + + val reactionDeleteCount = db.delete("reaction", "message_id NOT IN (SELECT _id FROM message)", null) + Log.i(TAG, "Deleted $reactionDeleteCount reactions.") + stopwatch.split("reactions") + + val callDeleteCount = db.delete("call", "message_id NOT IN (SELECT _id FROM message)", null) + Log.i(TAG, "Deleted $callDeleteCount calls.") + stopwatch.split("calls") + } finally { + stopwatch.stop(TAG) + } + } +}