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 e65a486799..c1b01295e5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -315,6 +315,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat private const val INDEX_THREAD_COUNT = "message_thread_count_index" private const val INDEX_THREAD_UNREAD_COUNT = "message_thread_unread_count_index" private const val INDEX_STORY_TYPE = "message_story_type_index" + private const val INDEX_ARCHIVED_STORY = "message_story_archived_index" @JvmField val CREATE_INDEXS = arrayOf( @@ -342,7 +343,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat "CREATE INDEX IF NOT EXISTS message_pinned_until_index ON $TABLE_NAME ($PINNED_UNTIL)", "CREATE INDEX IF NOT EXISTS message_pinned_at_index ON $TABLE_NAME ($PINNED_AT)", "CREATE INDEX IF NOT EXISTS message_deleted_by_index ON $TABLE_NAME ($DELETED_BY)", - "CREATE INDEX IF NOT EXISTS message_story_archived_index ON $TABLE_NAME ($STORY_ARCHIVED, $STORY_TYPE, $DATE_SENT) WHERE $STORY_TYPE > 0 AND $STORY_ARCHIVED > 0", + "CREATE INDEX IF NOT EXISTS $INDEX_ARCHIVED_STORY ON $TABLE_NAME ($STORY_ARCHIVED, $STORY_TYPE, $DATE_SENT) WHERE $STORY_TYPE > 0 AND $STORY_ARCHIVED > 0", "CREATE INDEX IF NOT EXISTS message_starred_index ON $TABLE_NAME ($STARRED) WHERE $STARRED > 0", "CREATE INDEX IF NOT EXISTS message_collapsed_state_index ON $TABLE_NAME ($COLLAPSED_STATE)", "CREATE INDEX IF NOT EXISTS message_collapsed_head_id_index ON $TABLE_NAME ($COLLAPSED_HEAD_ID)" @@ -1386,10 +1387,6 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat database.endTransaction() } - fun ensureMigration() { - databaseHelper.signalWritableDatabase - } - fun isViewOnce(messageId: Long): Boolean { return readableDatabase .exists(TABLE_NAME) @@ -1423,24 +1420,17 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat whereArgs = buildArgs(threadId) } - return MmsReader(queryMessages(where, whereArgs)) + return MmsReader(queryMessages(where, whereArgs, index = INDEX_STORY_TYPE)) } fun getAllOutgoingStories(reverse: Boolean, limit: Int): Reader { val where = "$IS_STORY_CLAUSE AND ($outgoingTypeClause)" - return MmsReader(queryMessages(where, null, reverse, limit.toLong())) + return MmsReader(queryMessages(where, null, reverse, limit.toLong(), index = INDEX_STORY_TYPE)) } fun markAllIncomingStoriesRead(): List { val where = "$IS_STORY_CLAUSE AND NOT ($outgoingTypeClause) AND $READ = 0" - val markedMessageInfos = setMessagesRead(where, null) - notifyConversationListListeners() - return markedMessageInfos - } - - fun markAllCallEventsRead(): List { - val where = "$IS_CALL_TYPE_CLAUSE AND $READ = 0" - val markedMessageInfos = setMessagesRead(where, null) + val markedMessageInfos = setMessagesRead(where, null, index = INDEX_STORY_TYPE) notifyConversationListListeners() return markedMessageInfos } @@ -1449,28 +1439,18 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val where = "$IS_STORY_CLAUSE AND ($outgoingTypeClause) AND $NOTIFIED = 0 AND ($TYPE & ${MessageTypes.BASE_TYPE_MASK}) = ${MessageTypes.BASE_SENT_FAILED_TYPE}" writableDatabase - .update("$TABLE_NAME INDEXED BY $INDEX_THREAD_STORY_SCHEDULED_DATE_LATEST_REVISION_ID") + .update("$TABLE_NAME INDEXED BY $INDEX_STORY_TYPE") .values(NOTIFIED to 1) .where(where) .run() notifyConversationListListeners() } - fun markOnboardingStoryRead() { - val recipientId = SignalStore.releaseChannel.releaseChannelRecipientId ?: return - val where = "$IS_STORY_CLAUSE AND NOT ($outgoingTypeClause) AND $READ = 0 AND $FROM_RECIPIENT_ID = ?" - val markedMessageInfos = setMessagesRead(where, buildArgs(recipientId)) - - if (markedMessageInfos.isNotEmpty()) { - notifyConversationListListeners() - } - } - fun getAllStoriesFor(recipientId: RecipientId, limit: Int): Reader { val threadId = threads.getThreadIdIfExistsFor(recipientId) val where = "$IS_STORY_CLAUSE AND $THREAD_ID = ?" val whereArgs = buildArgs(threadId) - val cursor = queryMessages(where, whereArgs, false, limit.toLong()) + val cursor = queryMessages(where, whereArgs, false, limit.toLong(), index = INDEX_STORY_TYPE) return MmsReader(cursor) } @@ -1478,21 +1458,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val threadId = threads.getThreadIdIfExistsFor(recipientId) val query = "$IS_STORY_CLAUSE AND NOT ($outgoingTypeClause) AND $THREAD_ID = ? AND $VIEWED_COLUMN = ?" val args = buildArgs(threadId, 0) - return MmsReader(queryMessages(query, args, false, limit.toLong())) - } - - fun getUnreadMissedCallCount(): Long { - return readableDatabase - .select("COUNT(*)") - .from(TABLE_NAME) - .where( - "($TYPE = ? OR $TYPE = ?) AND $READ = ?", - MessageTypes.MISSED_AUDIO_CALL_TYPE, - MessageTypes.MISSED_VIDEO_CALL_TYPE, - 0 - ) - .run() - .readToSingleLong(0L) + return MmsReader(queryMessages(query, args, false, limit.toLong(), index = INDEX_STORY_TYPE)) } fun getParentStoryIdForGroupReply(messageId: Long): GroupReply? { @@ -1539,7 +1505,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat @VisibleForTesting fun getStoryViewState(threadId: Long): StoryViewState { val hasStories = readableDatabase - .exists(TABLE_NAME) + .exists("$TABLE_NAME INDEXED BY $INDEX_STORY_TYPE") .where("$IS_STORY_CLAUSE AND $THREAD_ID = ?", threadId) .run() @@ -1548,7 +1514,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat } val hasUnviewedStories = readableDatabase - .exists(TABLE_NAME) + .exists("$TABLE_NAME INDEXED BY $INDEX_STORY_TYPE") .where("$IS_STORY_CLAUSE AND $THREAD_ID = ? AND $VIEWED_COLUMN = ? AND NOT ($outgoingTypeClause)", threadId, 0) .run() @@ -1581,7 +1547,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat fun getUnreadStoryThreadRecipientIds(): List { val query = """ SELECT DISTINCT ${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID} - FROM $TABLE_NAME + FROM $TABLE_NAME INDEXED BY $INDEX_STORY_TYPE JOIN ${ThreadTable.TABLE_NAME} ON $TABLE_NAME.$THREAD_ID = ${ThreadTable.TABLE_NAME}.${ThreadTable.ID} WHERE $IS_STORY_CLAUSE AND @@ -1614,7 +1580,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat $TABLE_NAME.$DATE_SENT, $RECEIPT_TIMESTAMP, ($outgoingTypeClause) = 0 AND $VIEWED_COLUMN = 0 AS is_unread - FROM $TABLE_NAME + FROM $TABLE_NAME INDEXED BY $INDEX_STORY_TYPE JOIN ${ThreadTable.TABLE_NAME} ON $TABLE_NAME.$THREAD_ID = ${ThreadTable.TABLE_NAME}.${ThreadTable.ID} WHERE $STORY_TYPE > 0 AND @@ -1704,7 +1670,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val sharedArgs = buildArgs(timestamp, releaseChannelThreadId) val deleteStoryRepliesQuery = """ - DELETE FROM $TABLE_NAME + DELETE FROM $TABLE_NAME INDEXED BY $INDEX_STORY_TYPE WHERE $PARENT_STORY_ID > 0 AND $PARENT_STORY_ID IN ( @@ -1778,7 +1744,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val outgoingFilter = "($outgoingTypeClause)" return writableDatabase - .update(TABLE_NAME) + .update("$TABLE_NAME INDEXED BY $INDEX_STORY_TYPE") .values(STORY_ARCHIVED to 1) .where("$IS_STORY_CLAUSE AND $DATE_SENT < ? AND $THREAD_ID != ? AND $outgoingFilter", timestamp, releaseChannelThreadId) .run() @@ -1786,21 +1752,23 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat fun getArchiveScreenStoriesCount(includeActive: Boolean): Int { val storyClause = if (includeActive) "$STORY_TYPE > 0 AND $DELETED_BY IS NULL" else IS_ARCHIVED_STORY_CLAUSE + val index = if (includeActive) INDEX_STORY_TYPE else INDEX_ARCHIVED_STORY val where = "$storyClause AND ($outgoingTypeClause)" - return readableDatabase.select("COUNT(*)").from(TABLE_NAME).where(where).run().readToSingleInt() + return readableDatabase.select("COUNT(*)").from("$TABLE_NAME INDEXED BY $index").where(where).run().readToSingleInt() } fun getArchiveScreenStoriesPage(includeActive: Boolean, sortNewest: Boolean, offset: Int, limit: Int): Reader { val storyClause = if (includeActive) "$STORY_TYPE > 0 AND $DELETED_BY IS NULL" else IS_ARCHIVED_STORY_CLAUSE + val index = if (includeActive) INDEX_STORY_TYPE else INDEX_ARCHIVED_STORY val where = "$storyClause AND ($outgoingTypeClause)" val order = if (sortNewest) "$TABLE_NAME.$DATE_SENT DESC" else "$TABLE_NAME.$DATE_SENT ASC" - return MmsReader(queryMessages(where, null, orderBy = order, limit = limit.toLong(), offset = offset.toLong())) + return MmsReader(queryMessages(where, null, orderBy = order, limit = limit.toLong(), offset = offset.toLong(), index = index)) } fun getOldestArchivedStorySentTimestamp(): Long? { return readableDatabase .select(DATE_SENT) - .from(TABLE_NAME) + .from("$TABLE_NAME INDEXED BY $INDEX_ARCHIVED_STORY") .where(IS_ARCHIVED_STORY_CLAUSE) .limit(1) .orderBy("$DATE_SENT ASC") @@ -1814,7 +1782,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val args = buildArgs(timestamp) val deletedCount = db.select(ID) - .from(TABLE_NAME) + .from("$TABLE_NAME INDEXED BY $INDEX_ARCHIVED_STORY") .where(where, args) .run() .use { cursor -> @@ -1838,17 +1806,17 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat fun deleteStoriesForRecipient(recipientId: RecipientId): Int { return writableDatabase.withinTransaction { db -> val threadId = threads.getThreadIdFor(recipientId) ?: return@withinTransaction 0 - val storesInRecipientThread = "$IS_STORY_CLAUSE AND $THREAD_ID = ?" + val storiesInRecipientThread = "$IS_STORY_CLAUSE AND $THREAD_ID = ?" val sharedArgs = buildArgs(threadId) val deleteStoryRepliesQuery = """ - DELETE FROM $TABLE_NAME + DELETE FROM $TABLE_NAME INDEXED BY $INDEX_STORY_TYPE WHERE $PARENT_STORY_ID > 0 AND $PARENT_STORY_ID IN ( SELECT $ID FROM $TABLE_NAME - WHERE $storesInRecipientThread + WHERE $storiesInRecipientThread ) """ @@ -1862,7 +1830,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat ABS($PARENT_STORY_ID) IN ( SELECT $ID FROM $TABLE_NAME - WHERE $storesInRecipientThread + WHERE $storiesInRecipientThread ) """ @@ -1873,7 +1841,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat ABS($PARENT_STORY_ID) IN ( SELECT $ID FROM $TABLE_NAME - WHERE $storesInRecipientThread + WHERE $storiesInRecipientThread ) """ @@ -1887,8 +1855,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat AppDependencies.databaseObserver.notifyStoryObservers(recipientId) val deletedStoryCount = db.select(ID) - .from(TABLE_NAME) - .where(storesInRecipientThread, sharedArgs) + .from("$TABLE_NAME INDEXED BY $INDEX_STORY_TYPE") + .where(storiesInRecipientThread, sharedArgs) .run() .use { cursor -> while (cursor.moveToNext()) { @@ -2130,13 +2098,19 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat /** * Note: [reverse] and [orderBy] are mutually exclusive. If you want the order to be reversed, explicitly use 'ASC' or 'DESC' */ - private fun queryMessages(where: String, arguments: Array?, reverse: Boolean = false, limit: Long = 0, offset: Long = 0, orderBy: String = ""): Cursor { + private fun queryMessages(where: String, arguments: Array?, reverse: Boolean = false, limit: Long = 0, offset: Long = 0, orderBy: String = "", index: String? = null): Cursor { val database = databaseHelper.signalReadableDatabase + val indexString = if (index != null) { + " INDEXED BY $index" + } else { + "" + } + var rawQueryString = """ SELECT ${Util.join(MMS_PROJECTION, ",")} FROM - $TABLE_NAME + $TABLE_NAME$indexString WHERE $where """.toSingleLine() @@ -2624,11 +2598,11 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat return setMessagesRead("$STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND ($READ = 0 OR ($REACTIONS_UNREAD = 1 AND ($outgoingTypeClause)) OR ($VOTES_UNREAD = 1 AND ($outgoingTypeClause)))", null) } - private fun setMessagesRead(where: String, arguments: Array?): List { + private fun setMessagesRead(where: String, arguments: Array?, index: String = INDEX_THREAD_STORY_SCHEDULED_DATE_LATEST_REVISION_ID): List { val releaseChannelId = SignalStore.releaseChannel.releaseChannelRecipientId return writableDatabase.rawQuery( """ - UPDATE $TABLE_NAME INDEXED BY $INDEX_THREAD_STORY_SCHEDULED_DATE_LATEST_REVISION_ID + UPDATE $TABLE_NAME INDEXED BY $index SET $READ = 1, $REACTIONS_UNREAD = 0, $REACTIONS_LAST_SEEN = ${System.currentTimeMillis()}, $VOTES_UNREAD = 0, $VOTES_LAST_SEEN = ${System.currentTimeMillis()} WHERE $where RETURNING $ID, $FROM_RECIPIENT_ID, $DATE_SENT, $DATE_RECEIVED, $TYPE, $EXPIRES_IN, $EXPIRE_STARTED, $THREAD_ID, $STORY_TYPE