mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-07-04 13:05:19 +01:00
Fix improper index usage on story queries.
This commit is contained in:
@@ -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<MarkedMessageInfo> {
|
||||
val where = "$IS_STORY_CLAUSE AND NOT ($outgoingTypeClause) AND $READ = 0"
|
||||
val markedMessageInfos = setMessagesRead(where, null)
|
||||
notifyConversationListListeners()
|
||||
return markedMessageInfos
|
||||
}
|
||||
|
||||
fun markAllCallEventsRead(): List<MarkedMessageInfo> {
|
||||
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<RecipientId> {
|
||||
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<String>?, reverse: Boolean = false, limit: Long = 0, offset: Long = 0, orderBy: String = ""): Cursor {
|
||||
private fun queryMessages(where: String, arguments: Array<String>?, 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<String>?): List<MarkedMessageInfo> {
|
||||
private fun setMessagesRead(where: String, arguments: Array<String>?, index: String = INDEX_THREAD_STORY_SCHEDULED_DATE_LATEST_REVISION_ID): List<MarkedMessageInfo> {
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user