Restore @Transaction annotations

pull/2908/head
Alex Baker 4 months ago
parent 92f62450ae
commit b35090cd43

@ -4,13 +4,13 @@ import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update import androidx.room.Update
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import org.tasks.data.CaldavFilters import org.tasks.data.CaldavFilters
import org.tasks.data.CaldavTaskContainer import org.tasks.data.CaldavTaskContainer
import org.tasks.data.NO_ORDER import org.tasks.data.NO_ORDER
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.data.db.Database
import org.tasks.data.db.DbUtils.dbchunk import org.tasks.data.db.DbUtils.dbchunk
import org.tasks.data.db.SuspendDbUtils.chunkedMap import org.tasks.data.db.SuspendDbUtils.chunkedMap
import org.tasks.data.entity.CaldavAccount import org.tasks.data.entity.CaldavAccount
@ -22,13 +22,12 @@ import org.tasks.data.entity.CaldavAccount.Companion.TYPE_TASKS
import org.tasks.data.entity.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.entity.CaldavTask import org.tasks.data.entity.CaldavTask
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
const val APPLE_EPOCH = 978307200000L // 1/1/2001 GMT const val APPLE_EPOCH = 978307200000L // 1/1/2001 GMT
@Dao @Dao
abstract class CaldavDao(private val database: Database) { abstract class CaldavDao {
@Query("SELECT COUNT(*) FROM caldav_lists WHERE cdl_account = :account") @Query("SELECT COUNT(*) FROM caldav_lists WHERE cdl_account = :account")
abstract suspend fun listCount(account: String): Int abstract suspend fun listCount(account: String): Int
@ -99,24 +98,24 @@ ORDER BY CASE cda_account_type
@Update @Update
abstract suspend fun update(caldavCalendar: CaldavCalendar) abstract suspend fun update(caldavCalendar: CaldavCalendar)
suspend fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long = @Transaction
database.withTransaction { open suspend fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long {
if (task.order != null) { if (task.order != null) {
return@withTransaction insert(caldavTask) return insert(caldavTask)
}
if (addToTop) {
task.order = findFirstTask(caldavTask.calendar!!, task.parent)
?.takeIf { task.creationDate.toAppleEpoch() >= it }
?.minus(1)
} else {
task.order = findLastTask(caldavTask.calendar!!, task.parent)
?.takeIf { task.creationDate.toAppleEpoch() <= it }
?.plus(1)
}
val id = insert(caldavTask)
update(task)
id
} }
if (addToTop) {
task.order = findFirstTask(caldavTask.calendar!!, task.parent)
?.takeIf { task.creationDate.toAppleEpoch() >= it }
?.minus(1)
} else {
task.order = findLastTask(caldavTask.calendar!!, task.parent)
?.takeIf { task.creationDate.toAppleEpoch() <= it }
?.plus(1)
}
val id = insert(caldavTask)
update(task)
return id
}
@Query(""" @Query("""
SELECT MIN(IFNULL(`order`, (created - $APPLE_EPOCH) / 1000)) SELECT MIN(IFNULL(`order`, (created - $APPLE_EPOCH) / 1000))
@ -312,49 +311,47 @@ GROUP BY caldav_lists.cdl_uuid
+ "WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task WHERE cd_deleted = 0 AND cd_calendar = :calendar)") + "WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task WHERE cd_deleted = 0 AND cd_calendar = :calendar)")
abstract suspend fun updateParents(calendar: String) abstract suspend fun updateParents(calendar: String)
suspend fun move( @Transaction
open suspend fun move(
task: TaskContainer, task: TaskContainer,
previousParent: Long, previousParent: Long,
newParent: Long, newParent: Long,
newPosition: Long?, newPosition: Long?,
) { ) {
database.withTransaction { val previousPosition = task.caldavSortOrder
val previousPosition = task.caldavSortOrder if (newPosition != null) {
if (newPosition != null) { if (newParent == previousParent && newPosition < previousPosition) {
if (newParent == previousParent && newPosition < previousPosition) { shiftDown(task.caldav!!, newParent, newPosition, previousPosition)
shiftDown(task.caldav!!, newParent, newPosition, previousPosition) } else {
} else { val list =
val list = newParent.takeIf { it > 0 }?.let { getTask(it)?.calendar } ?: task.caldav!!
newParent.takeIf { it > 0 }?.let { getTask(it)?.calendar } ?: task.caldav!! shiftDown(list, newParent, newPosition)
shiftDown(list, newParent, newPosition)
}
} }
task.task.order = newPosition
setTaskOrder(task.id, newPosition)
} }
task.task.order = newPosition
setTaskOrder(task.id, newPosition)
} }
suspend fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) { @Transaction
database.withTransaction { open suspend fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) {
val updated = ArrayList<Task>() val updated = ArrayList<Task>()
val tasks = getTasksToShift(calendar, parent, from, to) val tasks = getTasksToShift(calendar, parent, from, to)
for (i in tasks.indices) { for (i in tasks.indices) {
val task = tasks[i] val task = tasks[i]
val current = from + i val current = from + i
if (task.sortOrder == current) { if (task.sortOrder == current) {
val task = task.task val task = task.task
task.order = current + 1 task.order = current + 1
updated.add(task) updated.add(task)
} else if (task.sortOrder > current) { } else if (task.sortOrder > current) {
break break
}
} }
updateTasks(updated)
updated
.map(Task::id)
.dbchunk()
.forEach { touchInternal(it) }
} }
updateTasks(updated)
updated
.map(Task::id)
.dbchunk()
.forEach { touchInternal(it) }
} }
@Query("UPDATE tasks SET modified = :modificationTime WHERE _id in (:ids)") @Query("UPDATE tasks SET modified = :modificationTime WHERE _id in (:ids)")

@ -3,16 +3,15 @@ package org.tasks.data.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import org.tasks.data.dao.CaldavDao.Companion.LOCAL import org.tasks.data.dao.CaldavDao.Companion.LOCAL
import org.tasks.data.db.Database
import org.tasks.data.db.SuspendDbUtils.chunkedMap import org.tasks.data.db.SuspendDbUtils.chunkedMap
import org.tasks.data.db.SuspendDbUtils.eachChunk import org.tasks.data.db.SuspendDbUtils.eachChunk
import org.tasks.data.entity.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.withTransaction
@Dao @Dao
abstract class DeletionDao(private val database: Database) { abstract class DeletionDao {
@Query("DELETE FROM tasks WHERE _id IN(:ids)") @Query("DELETE FROM tasks WHERE _id IN(:ids)")
internal abstract suspend fun deleteTasks(ids: List<Long>) internal abstract suspend fun deleteTasks(ids: List<Long>)
@ -59,13 +58,13 @@ WHERE recurring = 1
@Delete @Delete
internal abstract suspend fun deleteCaldavCalendar(caldavCalendar: CaldavCalendar) internal abstract suspend fun deleteCaldavCalendar(caldavCalendar: CaldavCalendar)
suspend fun delete(caldavCalendar: CaldavCalendar): List<Long> = @Transaction
database.withTransaction { open suspend fun delete(caldavCalendar: CaldavCalendar): List<Long> {
val tasks = getActiveCaldavTasks(caldavCalendar.uuid!!) val tasks = getActiveCaldavTasks(caldavCalendar.uuid!!)
delete(tasks) delete(tasks)
deleteCaldavCalendar(caldavCalendar) deleteCaldavCalendar(caldavCalendar)
tasks return tasks
} }
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :account") @Query("SELECT * FROM caldav_lists WHERE cdl_account = :account")
abstract suspend fun getCalendars(account: String): List<CaldavCalendar> abstract suspend fun getCalendars(account: String): List<CaldavCalendar>
@ -76,13 +75,13 @@ WHERE recurring = 1
@Query("DELETE FROM tasks WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task INNER JOIN caldav_lists ON cdl_uuid = cd_calendar WHERE cdl_account = '$LOCAL' AND deleted > 0 AND cd_deleted = 0)") @Query("DELETE FROM tasks WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task INNER JOIN caldav_lists ON cdl_uuid = cd_calendar WHERE cdl_account = '$LOCAL' AND deleted > 0 AND cd_deleted = 0)")
abstract suspend fun purgeDeleted() abstract suspend fun purgeDeleted()
suspend fun delete(caldavAccount: CaldavAccount): List<Long> = @Transaction
database.withTransaction { open suspend fun delete(caldavAccount: CaldavAccount): List<Long> {
val deleted = ArrayList<Long>() val deleted = ArrayList<Long>()
for (calendar in getCalendars(caldavAccount.uuid!!)) { for (calendar in getCalendars(caldavAccount.uuid!!)) {
deleted.addAll(delete(calendar)) deleted.addAll(delete(calendar))
}
deleteCaldavAccount(caldavAccount)
deleted
} }
deleteCaldavAccount(caldavAccount)
return deleted
}
} }

@ -4,32 +4,30 @@ import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update import androidx.room.Update
import org.tasks.data.db.Database
import org.tasks.data.entity.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.data.entity.CaldavAccount.Companion.TYPE_GOOGLE_TASKS
import org.tasks.data.entity.CaldavTask import org.tasks.data.entity.CaldavTask
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
@Dao @Dao
abstract class GoogleTaskDao(private val database: Database) { abstract class GoogleTaskDao {
@Insert @Insert
abstract suspend fun insert(task: CaldavTask): Long abstract suspend fun insert(task: CaldavTask): Long
@Insert @Insert
abstract suspend fun insert(tasks: Iterable<CaldavTask>) abstract suspend fun insert(tasks: Iterable<CaldavTask>)
suspend fun insertAndShift(task: Task, caldavTask: CaldavTask, top: Boolean) { @Transaction
database.withTransaction { open suspend fun insertAndShift(task: Task, caldavTask: CaldavTask, top: Boolean) {
if (top) { if (top) {
task.order = 0 task.order = 0
shiftDown(caldavTask.calendar!!, task.parent, 0) shiftDown(caldavTask.calendar!!, task.parent, 0)
} else { } else {
task.order = getBottom(caldavTask.calendar!!, task.parent) task.order = getBottom(caldavTask.calendar!!, task.parent)
}
insert(caldavTask)
update(task)
} }
insert(caldavTask)
update(task)
} }
@Query("UPDATE tasks SET `order` = `order` + 1 WHERE parent = :parent AND `order` >= :position AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)") @Query("UPDATE tasks SET `order` = `order` + 1 WHERE parent = :parent AND `order` >= :position AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)")
@ -44,25 +42,24 @@ abstract class GoogleTaskDao(private val database: Database) {
@Query("UPDATE tasks SET `order` = `order` - 1 WHERE parent = :parent AND `order` >= :position AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)") @Query("UPDATE tasks SET `order` = `order` - 1 WHERE parent = :parent AND `order` >= :position AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)")
internal abstract suspend fun shiftUp(listId: String, parent: Long, position: Long) internal abstract suspend fun shiftUp(listId: String, parent: Long, position: Long)
suspend fun move(task: Task, list: String, newParent: Long, newPosition: Long) { @Transaction
database.withTransaction { open suspend fun move(task: Task, list: String, newParent: Long, newPosition: Long) {
val previousParent = task.parent val previousParent = task.parent
val previousPosition = task.order!! val previousPosition = task.order!!
if (newParent == previousParent) { if (newParent == previousParent) {
if (previousPosition < newPosition) { if (previousPosition < newPosition) {
shiftUp(list, newParent, previousPosition, newPosition) shiftUp(list, newParent, previousPosition, newPosition)
} else {
shiftDown(list, newParent, previousPosition, newPosition)
}
} else { } else {
shiftUp(list, previousParent, previousPosition) shiftDown(list, newParent, previousPosition, newPosition)
shiftDown(list, newParent, newPosition)
} }
task.parent = newParent } else {
task.order = newPosition shiftUp(list, previousParent, previousPosition)
update(task) shiftDown(list, newParent, newPosition)
setMoved(task.id, list)
} }
task.parent = newParent
task.order = newPosition
update(task)
setMoved(task.id, list)
} }
@Query("UPDATE caldav_tasks SET gt_moved = 1 WHERE cd_task = :task and cd_calendar = :list") @Query("UPDATE caldav_tasks SET gt_moved = 1 WHERE cd_task = :task and cd_calendar = :list")
@ -168,27 +165,26 @@ WHERE cd_remote_id = :id
""") """)
abstract suspend fun updatePosition(id: String, parent: String?, position: String) abstract suspend fun updatePosition(id: String, parent: String?, position: String)
suspend fun reposition(caldavDao: CaldavDao, listId: String) { @Transaction
database.withTransaction { open suspend fun reposition(caldavDao: CaldavDao, listId: String) {
caldavDao.updateParents(listId) caldavDao.updateParents(listId)
val orderedTasks = getByRemoteOrder(listId) val orderedTasks = getByRemoteOrder(listId)
var subtasks = 0L var subtasks = 0L
var parent = 0L var parent = 0L
for (task in orderedTasks) { for (task in orderedTasks) {
if (task.parent > 0) { if (task.parent > 0) {
if (task.order != subtasks) { if (task.order != subtasks) {
task.order = subtasks task.order = subtasks
update(task) update(task)
}
subtasks++
} else {
subtasks = 0
if (task.order != parent) {
task.order = parent
update(task)
}
parent++
} }
subtasks++
} else {
subtasks = 0
if (task.order != parent) {
task.order = parent
update(task)
}
parent++
} }
} }
} }

@ -4,43 +4,39 @@ import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update import androidx.room.Update
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import org.tasks.data.PrincipalWithAccess import org.tasks.data.PrincipalWithAccess
import org.tasks.data.db.Database
import org.tasks.data.entity.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.entity.Principal import org.tasks.data.entity.Principal
import org.tasks.data.entity.PrincipalAccess import org.tasks.data.entity.PrincipalAccess
import org.tasks.data.withTransaction
@Dao @Dao
abstract class PrincipalDao(private val database: Database) { interface PrincipalDao {
@Insert @Insert
abstract suspend fun insert(principal: Principal): Long suspend fun insert(principal: Principal): Long
@Insert @Insert
abstract suspend fun insert(access: PrincipalAccess): Long suspend fun insert(access: PrincipalAccess): Long
@Update @Update
abstract suspend fun update(access: PrincipalAccess) suspend fun update(access: PrincipalAccess)
@Query(""" @Query("""
DELETE DELETE
FROM principal_access FROM principal_access
WHERE list = :list WHERE list = :list
AND id NOT IN (:access)""") AND id NOT IN (:access)""")
abstract suspend fun deleteRemoved(list: Long, access: List<Long>) suspend fun deleteRemoved(list: Long, access: List<Long>)
@Delete @Delete
abstract suspend fun delete(access: PrincipalAccess) suspend fun delete(access: PrincipalAccess)
suspend fun getAll(): List<PrincipalWithAccess> = database.withTransaction {
getAllInternal()
}
@Transaction
@Query("SELECT * FROM principal_access") @Query("SELECT * FROM principal_access")
internal abstract suspend fun getAllInternal(): List<PrincipalWithAccess> suspend fun getAll(): List<PrincipalWithAccess>
suspend fun getOrCreatePrincipal(account: CaldavAccount, href: String, displayName: String? = null) = suspend fun getOrCreatePrincipal(account: CaldavAccount, href: String, displayName: String? = null) =
findPrincipal(account.id, href) findPrincipal(account.id, href)
@ -69,12 +65,12 @@ WHERE list = :list
).apply { id = insert(this) } ).apply { id = insert(this) }
@Query("SELECT * FROM principals WHERE account = :account AND href = :href") @Query("SELECT * FROM principals WHERE account = :account AND href = :href")
abstract suspend fun findPrincipal(account: Long, href: String): Principal? suspend fun findPrincipal(account: Long, href: String): Principal?
@Query("SELECT * FROM principal_access WHERE list = :list and principal = :principal") @Query("SELECT * FROM principal_access WHERE list = :list and principal = :principal")
abstract suspend fun findAccess(list: Long, principal: Long): PrincipalAccess? suspend fun findAccess(list: Long, principal: Long): PrincipalAccess?
@Transaction
@Query("SELECT * FROM principal_access WHERE list = :id") @Query("SELECT * FROM principal_access WHERE list = :id")
abstract fun getPrincipals(id: Long): Flow<List<PrincipalWithAccess>> fun getPrincipals(id: Long): Flow<List<PrincipalWithAccess>>
} }

@ -4,14 +4,13 @@ import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import org.tasks.data.db.Database import androidx.room.Transaction
import org.tasks.data.entity.Tag import org.tasks.data.entity.Tag
import org.tasks.data.entity.TagData import org.tasks.data.entity.TagData
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
@Dao @Dao
abstract class TagDao(private val database: Database) { abstract class TagDao {
@Query("UPDATE tags SET name = :name WHERE tag_uid = :tagUid") @Query("UPDATE tags SET name = :name WHERE tag_uid = :tagUid")
abstract suspend fun rename(tagUid: String, name: String) abstract suspend fun rename(tagUid: String, name: String)
@ -36,16 +35,15 @@ abstract class TagDao(private val database: Database) {
@Delete @Delete
abstract suspend fun delete(tags: List<Tag>) abstract suspend fun delete(tags: List<Tag>)
@Transaction
open suspend fun applyTags(task: Task, tagDataDao: TagDataDao, current: List<TagData>) { open suspend fun applyTags(task: Task, tagDataDao: TagDataDao, current: List<TagData>) {
database.withTransaction { val taskId = task.id
val taskId = task.id val existing = HashSet(tagDataDao.getTagDataForTask(taskId))
val existing = HashSet(tagDataDao.getTagDataForTask(taskId)) val selected = HashSet<TagData>(current)
val selected = HashSet<TagData>(current) val added = selected subtract existing
val added = selected subtract existing val removed = existing subtract selected
val removed = existing subtract selected deleteTags(taskId, removed.map { td -> td.remoteId!! })
deleteTags(taskId, removed.map { td -> td.remoteId!! }) insert(task, added)
insert(task, added)
}
} }
suspend fun insert(task: Task, tags: Collection<TagData>) { suspend fun insert(task: Task, tags: Collection<TagData>) {

@ -4,20 +4,19 @@ import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update import androidx.room.Update
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import org.tasks.data.NO_ORDER import org.tasks.data.NO_ORDER
import org.tasks.data.TagFilters import org.tasks.data.TagFilters
import org.tasks.data.db.Database
import org.tasks.data.db.DbUtils import org.tasks.data.db.DbUtils
import org.tasks.data.entity.Tag import org.tasks.data.entity.Tag
import org.tasks.data.entity.TagData import org.tasks.data.entity.TagData
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
@Dao @Dao
abstract class TagDataDao(private val database: Database) { abstract class TagDataDao {
@Query("SELECT * FROM tagdata") @Query("SELECT * FROM tagdata")
abstract fun subscribeToTags(): Flow<List<TagData>> abstract fun subscribeToTags(): Flow<List<TagData>>
@ -80,11 +79,12 @@ abstract class TagDataDao(private val database: Database) {
+ " GROUP BY tasks._id") + " GROUP BY tasks._id")
internal abstract suspend fun getAllTags(tasks: List<Long>): List<String?> internal abstract suspend fun getAllTags(tasks: List<Long>): List<String?>
suspend fun applyTags( @Transaction
open suspend fun applyTags(
tasks: List<Task>, tasks: List<Task>,
partiallySelected: List<TagData>, partiallySelected: List<TagData>,
selected: List<TagData> selected: List<TagData>
): List<Long> = database.withTransaction { ): List<Long> {
val modified = HashSet<Long>() val modified = HashSet<Long>()
val keep = partiallySelected.plus(selected).map { it.remoteId!! } val keep = partiallySelected.plus(selected).map { it.remoteId!! }
for (sublist in tasks.chunked(DbUtils.MAX_SQLITE_ARGS - keep.size)) { for (sublist in tasks.chunked(DbUtils.MAX_SQLITE_ARGS - keep.size)) {
@ -108,14 +108,13 @@ abstract class TagDataDao(private val database: Database) {
) )
} }
} }
ArrayList(modified) return ArrayList(modified)
} }
suspend fun delete(tagData: TagData) { @Transaction
database.withTransaction { open suspend fun delete(tagData: TagData) {
deleteTags(tagData.remoteId!!) deleteTags(tagData.remoteId!!)
deleteTagData(tagData) deleteTagData(tagData)
}
} }
@Delete @Delete

Loading…
Cancel
Save