diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt index 72309ccd7e..1c8461b51d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -4931,11 +4931,43 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat } override fun remapThread(fromId: Long, toId: Long) { - writableDatabase - .update(TABLE_NAME) - .values(THREAD_ID to toId) - .where("$THREAD_ID = ?", fromId) - .run() + try { + writableDatabase + .update(TABLE_NAME) + .values(THREAD_ID to toId) + .where("$THREAD_ID = ?", fromId) + .run() + } catch (e: SQLiteException) { + Log.w(TAG, "Failed to remap threadId, likely causes a unique constraint violation. Fixing.", e) + // Looks at all of the messages where the fromRecipient is either fromId or toId, then + // finds all messages where the threadId and dateSent match, but the fromRecipients do not. + // Deletes the more recent of the messages to prevent duplicates. + val deleteCount = writableDatabase + .delete(TABLE_NAME) + .where( + """ + $ID IN ( + SELECT $ID FROM $TABLE_NAME AS m1 + WHERE $THREAD_ID IN ($fromId, $toId) + AND EXISTS ( + SELECT 1 FROM $TABLE_NAME AS m2 + WHERE m1.$FROM_RECIPIENT_ID = m2.$FROM_RECIPIENT_ID + AND m1.$DATE_SENT = m2.$DATE_SENT + AN m1.$THREAD_ID != m2.$THREAD_ID + AND m1.$ID > m2.$ID + ) + ) + """ + ) + .run() + + Log.w(TAG, "Deleted $deleteCount duplicates. Retrying the remap.", e) + writableDatabase + .update(TABLE_NAME) + .values(THREAD_ID to toId) + .where("$THREAD_ID = ?", fromId) + .run() + } } /**