Add migration to rewrite pending commands to use folder IDs
This commit is contained in:
parent
5c10cfc6e6
commit
c5e5b7b4f1
11 changed files with 216 additions and 25 deletions
|
@ -12,7 +12,7 @@ import timber.log.Timber;
|
|||
|
||||
|
||||
class StoreSchemaDefinition implements SchemaDefinition {
|
||||
static final int DB_VERSION = 72;
|
||||
static final int DB_VERSION = 73;
|
||||
|
||||
private final MigrationsHelper migrationsHelper;
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package com.fsck.k9.storage.migrations;
|
||||
|
||||
class LegacyPendingAppend extends LegacyPendingCommand {
|
||||
public String folder;
|
||||
public String uid;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package com.fsck.k9.storage.migrations;
|
||||
|
||||
abstract class LegacyPendingCommand {
|
||||
}
|
|
@ -5,7 +5,7 @@ import java.util.List;
|
|||
import static com.fsck.k9.controller.Preconditions.requireValidUids;
|
||||
import static com.fsck.k9.helper.Preconditions.checkNotNull;
|
||||
|
||||
class LegacyPendingDelete {
|
||||
class LegacyPendingDelete extends LegacyPendingCommand {
|
||||
public final String folder;
|
||||
public final List<String> uids;
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package com.fsck.k9.storage.migrations;
|
||||
|
||||
class LegacyPendingExpunge extends LegacyPendingCommand {
|
||||
public String folder;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.fsck.k9.storage.migrations;
|
||||
|
||||
class LegacyPendingMarkAllAsRead extends LegacyPendingCommand {
|
||||
public String folder;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.fsck.k9.storage.migrations;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
class LegacyPendingMoveAndMarkAsRead extends LegacyPendingCommand {
|
||||
public String srcFolder;
|
||||
public String destFolder;
|
||||
public Map<String, String> newUidMap;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.fsck.k9.storage.migrations;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
class LegacyPendingMoveOrCopy extends LegacyPendingCommand {
|
||||
public String srcFolder;
|
||||
public String destFolder;
|
||||
public boolean isCopy;
|
||||
public Map<String, String> newUidMap;
|
||||
}
|
|
@ -4,27 +4,9 @@ import com.fsck.k9.mail.Flag;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import static com.fsck.k9.controller.Preconditions.requireValidUids;
|
||||
import static com.fsck.k9.helper.Preconditions.checkNotNull;
|
||||
|
||||
class LegacyPendingSetFlag {
|
||||
public final String folder;
|
||||
public final boolean newState;
|
||||
public final Flag flag;
|
||||
public final List<String> uids;
|
||||
|
||||
|
||||
public static LegacyPendingSetFlag create(String folder, boolean newState, Flag flag, List<String> uids) {
|
||||
checkNotNull(folder);
|
||||
checkNotNull(flag);
|
||||
requireValidUids(uids);
|
||||
return new LegacyPendingSetFlag(folder, newState, flag, uids);
|
||||
}
|
||||
|
||||
private LegacyPendingSetFlag(String folder, boolean newState, Flag flag, List<String> uids) {
|
||||
this.folder = folder;
|
||||
this.newState = newState;
|
||||
this.flag = flag;
|
||||
this.uids = uids;
|
||||
}
|
||||
class LegacyPendingSetFlag extends LegacyPendingCommand {
|
||||
public String folder;
|
||||
public boolean newState;
|
||||
public Flag flag;
|
||||
public List<String> uids;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
package com.fsck.k9.storage.migrations
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import com.fsck.k9.controller.MessagingControllerCommands.PendingAppend
|
||||
import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand
|
||||
import com.fsck.k9.controller.MessagingControllerCommands.PendingDelete
|
||||
import com.fsck.k9.controller.MessagingControllerCommands.PendingExpunge
|
||||
import com.fsck.k9.controller.MessagingControllerCommands.PendingMarkAllAsRead
|
||||
import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveAndMarkAsRead
|
||||
import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveOrCopy
|
||||
import com.fsck.k9.controller.MessagingControllerCommands.PendingSetFlag
|
||||
import com.fsck.k9.controller.PendingCommandSerializer
|
||||
import com.fsck.k9.helper.map
|
||||
import com.squareup.moshi.Moshi
|
||||
import timber.log.Timber
|
||||
|
||||
internal class MigrationTo73(private val db: SQLiteDatabase) {
|
||||
private val serializer = PendingCommandSerializer.getInstance()
|
||||
private val moshi = Moshi.Builder().build()
|
||||
private val legacyAdapters = listOf(
|
||||
"append" to moshi.adapter(LegacyPendingAppend::class.java),
|
||||
"mark_all_as_read" to moshi.adapter(LegacyPendingMarkAllAsRead::class.java),
|
||||
"set_flag" to moshi.adapter(LegacyPendingSetFlag::class.java),
|
||||
"delete" to moshi.adapter(LegacyPendingDelete::class.java),
|
||||
"expunge" to moshi.adapter(LegacyPendingExpunge::class.java),
|
||||
"move_or_copy" to moshi.adapter(LegacyPendingMoveOrCopy::class.java),
|
||||
"move_and_mark_as_read" to moshi.adapter(LegacyPendingMoveAndMarkAsRead::class.java)
|
||||
).toMap()
|
||||
|
||||
fun rewritePendingCommandsToUseFolderIds() {
|
||||
val pendingCommands = loadPendingCommands()
|
||||
rewritePendingCommands(pendingCommands)
|
||||
}
|
||||
|
||||
private fun loadPendingCommands(): Map<Long, LegacyPendingCommand> {
|
||||
return db.rawQuery(
|
||||
"SELECT id, command, data FROM pending_commands WHERE command != 'empty_trash'",
|
||||
null
|
||||
).use { cursor ->
|
||||
cursor.map {
|
||||
val commandId = cursor.getLong(0)
|
||||
val command = cursor.getString(1)
|
||||
val data = cursor.getString(2)
|
||||
|
||||
val pendingCommand = deserialize(command, data)
|
||||
commandId to pendingCommand
|
||||
}.toMap()
|
||||
}
|
||||
}
|
||||
|
||||
private fun rewritePendingCommands(pendingCommands: Map<Long, LegacyPendingCommand>) {
|
||||
for ((commandId, pendingCommand) in pendingCommands) {
|
||||
when (pendingCommand) {
|
||||
is LegacyPendingAppend -> rewritePendingAppend(commandId, pendingCommand)
|
||||
is LegacyPendingMarkAllAsRead -> rewritePendingMarkAllAsRead(commandId, pendingCommand)
|
||||
is LegacyPendingSetFlag -> rewritePendingSetFlag(commandId, pendingCommand)
|
||||
is LegacyPendingDelete -> rewritePendingDelete(commandId, pendingCommand)
|
||||
is LegacyPendingExpunge -> rewritePendingExpunge(commandId, pendingCommand)
|
||||
is LegacyPendingMoveOrCopy -> rewritePendingMoveOrCopy(commandId, pendingCommand)
|
||||
is LegacyPendingMoveAndMarkAsRead -> rewritePendingMoveAndMarkAsRead(commandId, pendingCommand)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun rewritePendingAppend(commandId: Long, legacyPendingCommand: LegacyPendingAppend) {
|
||||
rewriteOrRemovePendingCommand(commandId, legacyPendingCommand.folder) { (folderId) ->
|
||||
PendingAppend.create(folderId, legacyPendingCommand.uid)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rewritePendingMarkAllAsRead(commandId: Long, legacyPendingCommand: LegacyPendingMarkAllAsRead) {
|
||||
rewriteOrRemovePendingCommand(commandId, legacyPendingCommand.folder) { (folderId) ->
|
||||
PendingMarkAllAsRead.create(folderId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rewritePendingSetFlag(commandId: Long, legacyPendingCommand: LegacyPendingSetFlag) {
|
||||
rewriteOrRemovePendingCommand(commandId, legacyPendingCommand.folder) { (folderId) ->
|
||||
PendingSetFlag.create(
|
||||
folderId,
|
||||
legacyPendingCommand.newState,
|
||||
legacyPendingCommand.flag,
|
||||
legacyPendingCommand.uids
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rewritePendingDelete(commandId: Long, legacyPendingCommand: LegacyPendingDelete) {
|
||||
rewriteOrRemovePendingCommand(commandId, legacyPendingCommand.folder) { (folderId) ->
|
||||
PendingDelete.create(folderId, legacyPendingCommand.uids)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rewritePendingExpunge(commandId: Long, legacyPendingCommand: LegacyPendingExpunge) {
|
||||
rewriteOrRemovePendingCommand(commandId, legacyPendingCommand.folder) { (folderId) ->
|
||||
PendingExpunge.create(folderId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rewritePendingMoveOrCopy(commandId: Long, legacyPendingCommand: LegacyPendingMoveOrCopy) {
|
||||
rewriteOrRemovePendingCommand(
|
||||
commandId,
|
||||
legacyPendingCommand.srcFolder,
|
||||
legacyPendingCommand.destFolder
|
||||
) { (srcFolderId, destFolderId) ->
|
||||
PendingMoveOrCopy.create(
|
||||
srcFolderId,
|
||||
destFolderId,
|
||||
legacyPendingCommand.isCopy,
|
||||
legacyPendingCommand.newUidMap
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rewritePendingMoveAndMarkAsRead(commandId: Long, legacyPendingCommand: LegacyPendingMoveAndMarkAsRead) {
|
||||
rewriteOrRemovePendingCommand(
|
||||
commandId,
|
||||
legacyPendingCommand.srcFolder,
|
||||
legacyPendingCommand.destFolder
|
||||
) { (srcFolderId, destFolderId) ->
|
||||
PendingMoveAndMarkAsRead.create(srcFolderId, destFolderId, legacyPendingCommand.newUidMap)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rewriteOrRemovePendingCommand(
|
||||
commandId: Long,
|
||||
vararg folderServerIds: String,
|
||||
convertPendingCommand: (folderIds: List<Long>) -> PendingCommand
|
||||
) {
|
||||
val folderIds = folderServerIds.map {
|
||||
loadFolderId(it)
|
||||
}
|
||||
|
||||
if (folderIds.any { it == null }) {
|
||||
Timber.w("Couldn't find folder ID for pending command with database ID $commandId. Removing entry.")
|
||||
removePendingCommand(commandId)
|
||||
} else {
|
||||
val pendingCommand = convertPendingCommand(folderIds.filterNotNull())
|
||||
updatePendingCommand(commandId, pendingCommand)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePendingCommand(commandId: Long, pendingCommand: PendingCommand) {
|
||||
val contentValues = ContentValues().apply {
|
||||
put("data", serializer.serialize(pendingCommand))
|
||||
}
|
||||
db.update("pending_commands", contentValues, "id = ?", arrayOf(commandId.toString()))
|
||||
}
|
||||
|
||||
private fun removePendingCommand(commandId: Long) {
|
||||
db.delete("pending_commands", "id = ?", arrayOf(commandId.toString()))
|
||||
}
|
||||
|
||||
private fun loadFolderId(folderServerId: String): Long? {
|
||||
return db.rawQuery("SELECT id from folders WHERE server_id = ?", arrayOf(folderServerId)).use { cursor ->
|
||||
if (cursor.moveToFirst()) {
|
||||
cursor.getLong(0)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deserialize(commandName: String, data: String): LegacyPendingCommand {
|
||||
val adapter = legacyAdapters[commandName] ?: error("Unsupported pending command type!")
|
||||
return adapter.fromJson(data) ?: error("Error deserializing pending command")
|
||||
}
|
||||
}
|
|
@ -18,5 +18,6 @@ object Migrations {
|
|||
if (oldVersion < 70) MigrationTo70(db).removePushState()
|
||||
if (oldVersion < 71) MigrationTo71(db).cleanUpFolderClass()
|
||||
if (oldVersion < 72) MigrationTo72(db).createMessagePartsRootIndex()
|
||||
if (oldVersion < 73) MigrationTo73(db).rewritePendingCommandsToUseFolderIds()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue