Migrate to bundled sqlite

pull/2898/head
Alex Baker 1 month ago
parent 60211355e0
commit 19de0e08a5

@ -200,6 +200,7 @@ dependencies {
implementation(libs.androidx.lifecycle.runtime) implementation(libs.androidx.lifecycle.runtime)
implementation(libs.androidx.lifecycle.viewmodel) implementation(libs.androidx.lifecycle.viewmodel)
implementation(libs.androidx.room) implementation(libs.androidx.room)
implementation(libs.androidx.sqlite)
implementation(libs.androidx.appcompat) implementation(libs.androidx.appcompat)
implementation(libs.markwon) implementation(libs.markwon)
implementation(libs.markwon.editor) implementation(libs.markwon.editor)

@ -43,15 +43,12 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.room.withTransaction
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.bottomappbar.BottomAppBar import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.composethemeadapter.MdcTheme import com.google.android.material.composethemeadapter.MdcTheme
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import org.tasks.data.sql.Join
import org.tasks.data.sql.QueryTemplate
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.adapter.TaskAdapter import com.todoroo.astrid.adapter.TaskAdapter
import com.todoroo.astrid.adapter.TaskAdapterProvider import com.todoroo.astrid.adapter.TaskAdapterProvider
@ -93,15 +90,18 @@ import org.tasks.billing.PurchaseActivity
import org.tasks.caldav.BaseCaldavCalendarSettingsActivity import org.tasks.caldav.BaseCaldavCalendarSettingsActivity
import org.tasks.compose.SubscriptionNagBanner import org.tasks.compose.SubscriptionNagBanner
import org.tasks.compose.collectAsStateLifecycleAware import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.data.TaskContainer
import org.tasks.data.dao.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.TagDataDao
import org.tasks.data.db.Database import org.tasks.data.db.Database
import org.tasks.data.db.SuspendDbUtils.chunkedMap
import org.tasks.data.entity.Tag import org.tasks.data.entity.Tag
import org.tasks.data.dao.TagDataDao
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.TaskContainer
import org.tasks.data.listSettingsClass import org.tasks.data.listSettingsClass
import org.tasks.data.sql.Join
import org.tasks.data.sql.QueryTemplate
import org.tasks.data.withTransaction
import org.tasks.databinding.FragmentTaskListBinding import org.tasks.databinding.FragmentTaskListBinding
import org.tasks.data.db.SuspendDbUtils.chunkedMap
import org.tasks.dialogs.DateTimePicker.Companion.newDateTimePicker import org.tasks.dialogs.DateTimePicker.Companion.newDateTimePicker
import org.tasks.dialogs.DialogBuilder import org.tasks.dialogs.DialogBuilder
import org.tasks.dialogs.FilterPicker.Companion.newFilterPicker import org.tasks.dialogs.FilterPicker.Companion.newFilterPicker

@ -5,7 +5,6 @@ import android.content.Context
import android.media.AudioAttributes import android.media.AudioAttributes
import android.media.AudioAttributes.USAGE_NOTIFICATION_EVENT import android.media.AudioAttributes.USAGE_NOTIFICATION_EVENT
import android.media.RingtoneManager import android.media.RingtoneManager
import androidx.room.withTransaction
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.gcal.GCalHelper import com.todoroo.astrid.gcal.GCalHelper
import com.todoroo.astrid.repeats.RepeatTaskHelper import com.todoroo.astrid.repeats.RepeatTaskHelper
@ -14,6 +13,7 @@ import org.tasks.LocalBroadcastManager
import org.tasks.data.dao.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.db.Database import org.tasks.data.db.Database
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
import org.tasks.jobs.WorkManager import org.tasks.jobs.WorkManager
import org.tasks.notifications.NotificationManager import org.tasks.notifications.NotificationManager
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences

@ -1,7 +1,6 @@
package com.todoroo.astrid.service package com.todoroo.astrid.service
import android.content.Context import android.content.Context
import androidx.room.withTransaction
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -17,6 +16,8 @@ import org.tasks.data.db.SuspendDbUtils.chunkedMap
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.Task import org.tasks.data.entity.Task
import org.tasks.data.inTransaction
import org.tasks.data.withTransaction
import org.tasks.files.FileHelper import org.tasks.files.FileHelper
import org.tasks.location.GeofenceApi import org.tasks.location.GeofenceApi
import org.tasks.notifications.NotificationManager import org.tasks.notifications.NotificationManager

@ -1,15 +1,19 @@
package org.tasks.data package org.tasks.data
import android.database.Cursor import android.database.Cursor
import androidx.sqlite.db.SupportSQLiteQuery import android.database.MatrixCursor
import org.tasks.data.entity.Task import androidx.sqlite.SQLiteStatement
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.tasks.data.dao.ContentProviderDao import org.tasks.data.dao.Astrid2ContentProviderDao
import org.tasks.data.db.Database
import org.tasks.data.entity.TagData import org.tasks.data.entity.TagData
import org.tasks.data.entity.Task
import javax.inject.Inject import javax.inject.Inject
@Deprecated("use coroutines") class ContentProviderDaoBlocking @Inject constructor(
class ContentProviderDaoBlocking @Inject constructor(private val dao: ContentProviderDao) { private val dao: Astrid2ContentProviderDao,
private val database: Database,
) {
fun getTagNames(taskId: Long): List<String> = runBlocking { fun getTagNames(taskId: Long): List<String> = runBlocking {
dao.getTagNames(taskId) dao.getTagNames(taskId)
} }
@ -22,9 +26,24 @@ class ContentProviderDaoBlocking @Inject constructor(private val dao: ContentPro
dao.tagDataOrderedByName() dao.tagDataOrderedByName()
} }
fun getTasks(): Cursor = dao.getTasks() fun getTasks(): Cursor = runBlocking { rawQuery("SELECT * FROM tasks") }
fun getLists(): Cursor = runBlocking {
rawQuery("""
SELECT caldav_lists.*, caldav_accounts.cda_name
FROM caldav_lists
INNER JOIN caldav_accounts ON cdl_account = cda_uuid
""".trimIndent()
)
}
fun getLists(): Cursor = dao.getLists() fun rawQuery(query: String): Cursor = runBlocking { database.rawQuery(query) { it.toCursor() } }
}
fun rawQuery(query: SupportSQLiteQuery): Cursor = dao.rawQuery(query) private fun SQLiteStatement.toCursor(): Cursor {
val cursor = MatrixCursor(getColumnNames().toTypedArray())
while (step()) {
cursor.addRow((0 until getColumnCount()).map { getText(it) })
}
return cursor
} }

@ -1,13 +1,12 @@
package org.tasks.data package org.tasks.data
import androidx.sqlite.db.SimpleSQLiteQuery
import org.tasks.data.sql.Field
import org.tasks.data.sql.Query
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.PermaSql import com.todoroo.astrid.api.PermaSql
import org.tasks.data.entity.Task
import org.tasks.data.dao.TaskDao import org.tasks.data.dao.TaskDao
import org.tasks.data.db.SuspendDbUtils.eachChunk import org.tasks.data.db.SuspendDbUtils.eachChunk
import org.tasks.data.entity.Task
import org.tasks.data.sql.Field
import org.tasks.data.sql.Query
import org.tasks.preferences.QueryPreferences import org.tasks.preferences.QueryPreferences
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import timber.log.Timber import timber.log.Timber
@ -30,7 +29,7 @@ suspend fun TaskDao.fetchFiltered(queryTemplate: String): List<Task> {
val query = getQuery(queryTemplate, Task.FIELDS) val query = getQuery(queryTemplate, Task.FIELDS)
val start = if (BuildConfig.DEBUG) currentTimeMillis() else 0 val start = if (BuildConfig.DEBUG) currentTimeMillis() else 0
val tasks = fetchTasks(query) val tasks = fetchTasks(query)
Timber.v("%sms: %s", currentTimeMillis() - start, query.sql) Timber.v("%sms: %s", currentTimeMillis() - start, query)
return tasks.map(TaskContainer::task) return tasks.map(TaskContainer::task)
} }
@ -38,13 +37,12 @@ suspend fun TaskDao.count(filter: Filter): Int {
val query = getQuery(filter.sql!!, Field.COUNT) val query = getQuery(filter.sql!!, Field.COUNT)
val start = if (BuildConfig.DEBUG) currentTimeMillis() else 0 val start = if (BuildConfig.DEBUG) currentTimeMillis() else 0
val count = countRaw(query) val count = countRaw(query)
Timber.v("%sms: %s", currentTimeMillis() - start, query.sql) Timber.v("%sms: %s", currentTimeMillis() - start, query)
return count return count
} }
private fun getQuery(queryTemplate: String, vararg fields: Field): SimpleSQLiteQuery = private fun getQuery(queryTemplate: String, vararg fields: Field): String =
SimpleSQLiteQuery( Query.select(*fields)
Query.select(*fields) .withQueryTemplate(PermaSql.replacePlaceholdersForQuery(queryTemplate))
.withQueryTemplate(PermaSql.replacePlaceholdersForQuery(queryTemplate)) .from(Task.TABLE)
.from(Task.TABLE) .toString()
.toString())

@ -2,13 +2,13 @@ package org.tasks.db
import android.content.Context import android.content.Context
import android.database.sqlite.SQLiteException import android.database.sqlite.SQLiteException
import androidx.core.database.getStringOrNull
import androidx.room.migration.Migration import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.SQLiteConnection
import androidx.sqlite.execSQL
import androidx.sqlite.use
import org.tasks.R import org.tasks.R
import org.tasks.caldav.FileStorage import org.tasks.caldav.FileStorage
import org.tasks.data.NO_ORDER import org.tasks.data.NO_ORDER
import org.tasks.data.OpenTaskDao.Companion.getLong
import org.tasks.data.entity.Alarm.Companion.TYPE_RANDOM import org.tasks.data.entity.Alarm.Companion.TYPE_RANDOM
import org.tasks.data.entity.Alarm.Companion.TYPE_REL_END import org.tasks.data.entity.Alarm.Companion.TYPE_REL_END
import org.tasks.data.entity.Alarm.Companion.TYPE_REL_START import org.tasks.data.entity.Alarm.Companion.TYPE_REL_START
@ -21,8 +21,6 @@ import org.tasks.data.entity.Task
import org.tasks.data.entity.Task.Companion.NOTIFY_AFTER_DEADLINE import org.tasks.data.entity.Task.Companion.NOTIFY_AFTER_DEADLINE
import org.tasks.data.entity.Task.Companion.NOTIFY_AT_DEADLINE import org.tasks.data.entity.Task.Companion.NOTIFY_AT_DEADLINE
import org.tasks.data.entity.Task.Companion.NOTIFY_AT_START import org.tasks.data.entity.Task.Companion.NOTIFY_AT_START
import org.tasks.extensions.getLongOrNull
import org.tasks.extensions.getString
import org.tasks.preferences.DefaultFilterProvider import org.tasks.preferences.DefaultFilterProvider
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.repeats.RecurrenceUtils.newRecur import org.tasks.repeats.RecurrenceUtils.newRecur
@ -34,21 +32,21 @@ import java.util.concurrent.TimeUnit.HOURS
object Migrations { object Migrations {
private val MIGRATION_35_36: Migration = object : Migration(35, 36) { private val MIGRATION_35_36: Migration = object : Migration(35, 36) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `tagdata` ADD COLUMN `color` INTEGER DEFAULT -1") connection.execSQL("ALTER TABLE `tagdata` ADD COLUMN `color` INTEGER DEFAULT -1")
} }
} }
private val MIGRATION_36_37: Migration = object : Migration(36, 37) { private val MIGRATION_36_37: Migration = object : Migration(36, 37) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `store` ADD COLUMN `deleted` INTEGER DEFAULT 0") connection.execSQL("ALTER TABLE `store` ADD COLUMN `deleted` INTEGER DEFAULT 0")
} }
} }
private val MIGRATION_37_38: Migration = object : Migration(37, 38) { private val MIGRATION_37_38: Migration = object : Migration(37, 38) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
try { try {
db.execSQL("ALTER TABLE `store` ADD COLUMN `value4` TEXT DEFAULT -1") connection.execSQL("ALTER TABLE `store` ADD COLUMN `value4` TEXT DEFAULT -1")
} catch (e: SQLiteException) { } catch (e: SQLiteException) {
Timber.w(e) Timber.w(e)
} }
@ -56,263 +54,263 @@ object Migrations {
} }
private val MIGRATION_38_39: Migration = object : Migration(38, 39) { private val MIGRATION_38_39: Migration = object : Migration(38, 39) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `notification` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `type` INTEGER NOT NULL)") "CREATE TABLE IF NOT EXISTS `notification` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `type` INTEGER NOT NULL)")
db.execSQL( connection.execSQL(
"CREATE UNIQUE INDEX `index_notification_task` ON `notification` (`task`)") "CREATE UNIQUE INDEX `index_notification_task` ON `notification` (`task`)")
} }
} }
private val MIGRATION_46_47: Migration = object : Migration(46, 47) { private val MIGRATION_46_47: Migration = object : Migration(46, 47) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `alarms` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `time` INTEGER NOT NULL)") "CREATE TABLE IF NOT EXISTS `alarms` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `time` INTEGER NOT NULL)")
db.execSQL( connection.execSQL(
"INSERT INTO `alarms` (`task`, `time`) SELECT `task`, `value` FROM `metadata` WHERE `key` = 'alarm' AND `deleted` = 0") "INSERT INTO `alarms` (`task`, `time`) SELECT `task`, `value` FROM `metadata` WHERE `key` = 'alarm' AND `deleted` = 0")
db.execSQL("DELETE FROM `metadata` WHERE `key` = 'alarm'") connection.execSQL("DELETE FROM `metadata` WHERE `key` = 'alarm'")
} }
} }
private val MIGRATION_47_48: Migration = object : Migration(47, 48) { private val MIGRATION_47_48: Migration = object : Migration(47, 48) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `locations` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `radius` INTEGER NOT NULL)") "CREATE TABLE IF NOT EXISTS `locations` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `radius` INTEGER NOT NULL)")
db.execSQL("INSERT INTO `locations` (`task`, `name`, `latitude`, `longitude`, `radius`) " connection.execSQL("INSERT INTO `locations` (`task`, `name`, `latitude`, `longitude`, `radius`) "
+ "SELECT `task`, `value`, `value2`, `value3`, `value4` FROM `metadata` WHERE `key` = 'geofence' AND `deleted` = 0") + "SELECT `task`, `value`, `value2`, `value3`, `value4` FROM `metadata` WHERE `key` = 'geofence' AND `deleted` = 0")
db.execSQL("DELETE FROM `metadata` WHERE `key` = 'geofence'") connection.execSQL("DELETE FROM `metadata` WHERE `key` = 'geofence'")
} }
} }
private val MIGRATION_48_49: Migration = object : Migration(48, 49) { private val MIGRATION_48_49: Migration = object : Migration(48, 49) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `tags` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `tag_uid` TEXT, `task_uid` TEXT)") "CREATE TABLE IF NOT EXISTS `tags` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `tag_uid` TEXT, `task_uid` TEXT)")
db.execSQL("INSERT INTO `tags` (`task`, `name`, `tag_uid`, `task_uid`) " connection.execSQL("INSERT INTO `tags` (`task`, `name`, `tag_uid`, `task_uid`) "
+ "SELECT `task`, `value`, `value2`, `value3` FROM `metadata` WHERE `key` = 'tags-tag' AND `deleted` = 0") + "SELECT `task`, `value`, `value2`, `value3` FROM `metadata` WHERE `key` = 'tags-tag' AND `deleted` = 0")
db.execSQL("DELETE FROM `metadata` WHERE `key` = 'tags-tag'") connection.execSQL("DELETE FROM `metadata` WHERE `key` = 'tags-tag'")
} }
} }
private val MIGRATION_49_50: Migration = object : Migration(49, 50) { private val MIGRATION_49_50: Migration = object : Migration(49, 50) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `google_tasks` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `remote_id` TEXT, `list_id` TEXT, `parent` INTEGER NOT NULL, `indent` INTEGER NOT NULL, `order` INTEGER NOT NULL, `remote_order` INTEGER NOT NULL, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)") "CREATE TABLE IF NOT EXISTS `google_tasks` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `remote_id` TEXT, `list_id` TEXT, `parent` INTEGER NOT NULL, `indent` INTEGER NOT NULL, `order` INTEGER NOT NULL, `remote_order` INTEGER NOT NULL, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)")
db.execSQL("INSERT INTO `google_tasks` (`task`, `remote_id`, `list_id`, `parent`, `indent`, `order`, `remote_order`, `last_sync`, `deleted`) " connection.execSQL("INSERT INTO `google_tasks` (`task`, `remote_id`, `list_id`, `parent`, `indent`, `order`, `remote_order`, `last_sync`, `deleted`) "
+ "SELECT `task`, `value`, `value2`, IFNULL(`value3`, 0), IFNULL(`value4`, 0), IFNULL(`value5`, 0), IFNULL(`value6`, 0), IFNULL(`value7`, 0), IFNULL(`deleted`, 0) FROM `metadata` WHERE `key` = 'gtasks'") + "SELECT `task`, `value`, `value2`, IFNULL(`value3`, 0), IFNULL(`value4`, 0), IFNULL(`value5`, 0), IFNULL(`value6`, 0), IFNULL(`value7`, 0), IFNULL(`deleted`, 0) FROM `metadata` WHERE `key` = 'gtasks'")
db.execSQL("DROP TABLE IF EXISTS `metadata`") connection.execSQL("DROP TABLE IF EXISTS `metadata`")
} }
} }
private val MIGRATION_50_51: Migration = object : Migration(50, 51) { private val MIGRATION_50_51: Migration = object : Migration(50, 51) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `filters` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `sql` TEXT, `values` TEXT, `criterion` TEXT)") "CREATE TABLE IF NOT EXISTS `filters` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `sql` TEXT, `values` TEXT, `criterion` TEXT)")
db.execSQL("INSERT INTO `filters` (`title`, `sql`, `values`, `criterion`) " connection.execSQL("INSERT INTO `filters` (`title`, `sql`, `values`, `criterion`) "
+ "SELECT `item`, `value`, `value2`, `value3` FROM `store` WHERE `type` = 'filter' AND `deleted` = 0") + "SELECT `item`, `value`, `value2`, `value3` FROM `store` WHERE `type` = 'filter' AND `deleted` = 0")
db.execSQL("DELETE FROM `store` WHERE `type` = 'filter'") connection.execSQL("DELETE FROM `store` WHERE `type` = 'filter'")
} }
} }
private val MIGRATION_51_52: Migration = object : Migration(51, 52) { private val MIGRATION_51_52: Migration = object : Migration(51, 52) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `google_task_lists` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `remote_id` TEXT, `title` TEXT, `remote_order` INTEGER NOT NULL, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `color` INTEGER)") "CREATE TABLE IF NOT EXISTS `google_task_lists` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `remote_id` TEXT, `title` TEXT, `remote_order` INTEGER NOT NULL, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `color` INTEGER)")
db.execSQL("INSERT INTO `google_task_lists` (`remote_id`, `title`, `remote_order`, `last_sync`, `color`, `deleted`) " connection.execSQL("INSERT INTO `google_task_lists` (`remote_id`, `title`, `remote_order`, `last_sync`, `color`, `deleted`) "
+ "SELECT `item`, `value`, `value2`, `value3`, `value4`, `deleted` FROM `store` WHERE `type` = 'gtasks-list'") + "SELECT `item`, `value`, `value2`, `value3`, `value4`, `deleted` FROM `store` WHERE `type` = 'gtasks-list'")
db.execSQL("DROP TABLE IF EXISTS `store`") connection.execSQL("DROP TABLE IF EXISTS `store`")
} }
} }
private val MIGRATION_52_53: Migration = object : Migration(52, 53) { private val MIGRATION_52_53: Migration = object : Migration(52, 53) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `tagdata` RENAME TO `tagdata-temp`") connection.execSQL("ALTER TABLE `tagdata` RENAME TO `tagdata-temp`")
db.execSQL( connection.execSQL(
"CREATE TABLE `tagdata` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `name` TEXT, `color` INTEGER, `tagOrdering` TEXT)") "CREATE TABLE `tagdata` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `name` TEXT, `color` INTEGER, `tagOrdering` TEXT)")
db.execSQL("INSERT INTO `tagdata` (`remoteId`, `name`, `color`, `tagOrdering`) " connection.execSQL("INSERT INTO `tagdata` (`remoteId`, `name`, `color`, `tagOrdering`) "
+ "SELECT `remoteId`, `name`, `color`, `tagOrdering` FROM `tagdata-temp`") + "SELECT `remoteId`, `name`, `color`, `tagOrdering` FROM `tagdata-temp`")
db.execSQL("DROP TABLE `tagdata-temp`") connection.execSQL("DROP TABLE `tagdata-temp`")
db.execSQL("ALTER TABLE `userActivity` RENAME TO `userActivity-temp`") connection.execSQL("ALTER TABLE `userActivity` RENAME TO `userActivity-temp`")
db.execSQL( connection.execSQL(
"CREATE TABLE `userActivity` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `message` TEXT, `picture` TEXT, `target_id` TEXT, `created_at` INTEGER)") "CREATE TABLE `userActivity` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `message` TEXT, `picture` TEXT, `target_id` TEXT, `created_at` INTEGER)")
db.execSQL("INSERT INTO `userActivity` (`remoteId`, `message`, `picture`, `target_id`, `created_at`) " connection.execSQL("INSERT INTO `userActivity` (`remoteId`, `message`, `picture`, `target_id`, `created_at`) "
+ "SELECT `remoteId`, `message`, `picture`, `target_id`, `created_at` FROM `userActivity-temp`") + "SELECT `remoteId`, `message`, `picture`, `target_id`, `created_at` FROM `userActivity-temp`")
db.execSQL("DROP TABLE `userActivity-temp`") connection.execSQL("DROP TABLE `userActivity-temp`")
db.execSQL("ALTER TABLE `task_attachments` RENAME TO `task_attachments-temp`") connection.execSQL("ALTER TABLE `task_attachments` RENAME TO `task_attachments-temp`")
db.execSQL( connection.execSQL(
"CREATE TABLE `task_attachments` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `task_id` TEXT, `name` TEXT, `path` TEXT, `content_type` TEXT)") "CREATE TABLE `task_attachments` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `task_id` TEXT, `name` TEXT, `path` TEXT, `content_type` TEXT)")
db.execSQL("INSERT INTO `task_attachments` (`remoteId`, `task_id`, `name`, `path`, `content_type`) " connection.execSQL("INSERT INTO `task_attachments` (`remoteId`, `task_id`, `name`, `path`, `content_type`) "
+ "SELECT `remoteId`, `task_id`, `name`, `path`, `content_type` FROM `task_attachments-temp`") + "SELECT `remoteId`, `task_id`, `name`, `path`, `content_type` FROM `task_attachments-temp`")
db.execSQL("DROP TABLE `task_attachments-temp`") connection.execSQL("DROP TABLE `task_attachments-temp`")
} }
} }
private val MIGRATION_53_54: Migration = object : Migration(53, 54) { private val MIGRATION_53_54: Migration = object : Migration(53, 54) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
// need to drop columns that were removed in the past // need to drop columns that were removed in the past
db.execSQL("ALTER TABLE `task_list_metadata` RENAME TO `task_list_metadata-temp`") connection.execSQL("ALTER TABLE `task_list_metadata` RENAME TO `task_list_metadata-temp`")
db.execSQL( connection.execSQL(
"CREATE TABLE `task_list_metadata` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `tag_uuid` TEXT, `filter` TEXT, `task_ids` TEXT)") "CREATE TABLE `task_list_metadata` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `tag_uuid` TEXT, `filter` TEXT, `task_ids` TEXT)")
db.execSQL("INSERT INTO `task_list_metadata` (`remoteId`, `tag_uuid`, `filter`, `task_ids`) " connection.execSQL("INSERT INTO `task_list_metadata` (`remoteId`, `tag_uuid`, `filter`, `task_ids`) "
+ "SELECT `remoteId`, `tag_uuid`, `filter`, `task_ids` FROM `task_list_metadata-temp`") + "SELECT `remoteId`, `tag_uuid`, `filter`, `task_ids` FROM `task_list_metadata-temp`")
db.execSQL("DROP TABLE `task_list_metadata-temp`") connection.execSQL("DROP TABLE `task_list_metadata-temp`")
db.execSQL("ALTER TABLE `tasks` RENAME TO `tasks-temp`") connection.execSQL("ALTER TABLE `tasks` RENAME TO `tasks-temp`")
db.execSQL( connection.execSQL(
"CREATE TABLE `tasks` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT, `importance` INTEGER, `dueDate` INTEGER, `hideUntil` INTEGER, `created` INTEGER, `modified` INTEGER, `completed` INTEGER, `deleted` INTEGER, `notes` TEXT, `estimatedSeconds` INTEGER, `elapsedSeconds` INTEGER, `timerStart` INTEGER, `notificationFlags` INTEGER, `notifications` INTEGER, `lastNotified` INTEGER, `snoozeTime` INTEGER, `recurrence` TEXT, `repeatUntil` INTEGER, `calendarUri` TEXT, `remoteId` TEXT)") "CREATE TABLE `tasks` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT, `importance` INTEGER, `dueDate` INTEGER, `hideUntil` INTEGER, `created` INTEGER, `modified` INTEGER, `completed` INTEGER, `deleted` INTEGER, `notes` TEXT, `estimatedSeconds` INTEGER, `elapsedSeconds` INTEGER, `timerStart` INTEGER, `notificationFlags` INTEGER, `notifications` INTEGER, `lastNotified` INTEGER, `snoozeTime` INTEGER, `recurrence` TEXT, `repeatUntil` INTEGER, `calendarUri` TEXT, `remoteId` TEXT)")
db.execSQL("DROP INDEX `t_rid`") connection.execSQL("DROP INDEX `t_rid`")
db.execSQL("CREATE UNIQUE INDEX `t_rid` ON `tasks` (`remoteId`)") connection.execSQL("CREATE UNIQUE INDEX `t_rid` ON `tasks` (`remoteId`)")
db.execSQL("INSERT INTO `tasks` (`_id`, `title`, `importance`, `dueDate`, `hideUntil`, `created`, `modified`, `completed`, `deleted`, `notes`, `estimatedSeconds`, `elapsedSeconds`, `timerStart`, `notificationFlags`, `notifications`, `lastNotified`, `snoozeTime`, `recurrence`, `repeatUntil`, `calendarUri`, `remoteId`) " connection.execSQL("INSERT INTO `tasks` (`_id`, `title`, `importance`, `dueDate`, `hideUntil`, `created`, `modified`, `completed`, `deleted`, `notes`, `estimatedSeconds`, `elapsedSeconds`, `timerStart`, `notificationFlags`, `notifications`, `lastNotified`, `snoozeTime`, `recurrence`, `repeatUntil`, `calendarUri`, `remoteId`) "
+ "SELECT `_id`, `title`, `importance`, `dueDate`, `hideUntil`, `created`, `modified`, `completed`, `deleted`, `notes`, `estimatedSeconds`, `elapsedSeconds`, `timerStart`, `notificationFlags`, `notifications`, `lastNotified`, `snoozeTime`, `recurrence`, `repeatUntil`, `calendarUri`, `remoteId` FROM `tasks-temp`") + "SELECT `_id`, `title`, `importance`, `dueDate`, `hideUntil`, `created`, `modified`, `completed`, `deleted`, `notes`, `estimatedSeconds`, `elapsedSeconds`, `timerStart`, `notificationFlags`, `notifications`, `lastNotified`, `snoozeTime`, `recurrence`, `repeatUntil`, `calendarUri`, `remoteId` FROM `tasks-temp`")
db.execSQL("DROP TABLE `tasks-temp`") connection.execSQL("DROP TABLE `tasks-temp`")
} }
} }
private val MIGRATION_54_58: Migration = object : Migration(54, 58) { private val MIGRATION_54_58: Migration = object : Migration(54, 58) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `caldav_account` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uuid` TEXT, `name` TEXT, `url` TEXT, `username` TEXT, `password` TEXT)") "CREATE TABLE IF NOT EXISTS `caldav_account` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uuid` TEXT, `name` TEXT, `url` TEXT, `username` TEXT, `password` TEXT)")
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `caldav_calendar` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `uuid` TEXT, `name` TEXT, `color` INTEGER NOT NULL, `ctag` TEXT, `url` TEXT)") "CREATE TABLE IF NOT EXISTS `caldav_calendar` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `uuid` TEXT, `name` TEXT, `color` INTEGER NOT NULL, `ctag` TEXT, `url` TEXT)")
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `caldav_tasks` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `calendar` TEXT, `object` TEXT, `remote_id` TEXT, `etag` TEXT, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `vtodo` TEXT)") "CREATE TABLE IF NOT EXISTS `caldav_tasks` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `calendar` TEXT, `object` TEXT, `remote_id` TEXT, `etag` TEXT, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `vtodo` TEXT)")
} }
} }
private val MIGRATION_58_59: Migration = object : Migration(58, 59) { private val MIGRATION_58_59: Migration = object : Migration(58, 59) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `google_task_accounts` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `error` TEXT)") "CREATE TABLE IF NOT EXISTS `google_task_accounts` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `error` TEXT)")
db.execSQL("ALTER TABLE `google_task_lists` ADD COLUMN `account` TEXT") connection.execSQL("ALTER TABLE `google_task_lists` ADD COLUMN `account` TEXT")
db.execSQL("ALTER TABLE `caldav_account` ADD COLUMN `error` TEXT") connection.execSQL("ALTER TABLE `caldav_account` ADD COLUMN `error` TEXT")
} }
} }
private val MIGRATION_59_60: Migration = object : Migration(59, 60) { private val MIGRATION_59_60: Migration = object : Migration(59, 60) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `locations` ADD COLUMN `address` TEXT") connection.execSQL("ALTER TABLE `locations` ADD COLUMN `address` TEXT")
db.execSQL("ALTER TABLE `locations` ADD COLUMN `phone` TEXT") connection.execSQL("ALTER TABLE `locations` ADD COLUMN `phone` TEXT")
db.execSQL("ALTER TABLE `locations` ADD COLUMN `url` TEXT") connection.execSQL("ALTER TABLE `locations` ADD COLUMN `url` TEXT")
db.execSQL( connection.execSQL(
"ALTER TABLE `locations` ADD COLUMN `arrival` INTEGER DEFAULT 1 NOT NULL") "ALTER TABLE `locations` ADD COLUMN `arrival` INTEGER DEFAULT 1 NOT NULL")
db.execSQL( connection.execSQL(
"ALTER TABLE `locations` ADD COLUMN `departure` INTEGER DEFAULT 0 NOT NULL") "ALTER TABLE `locations` ADD COLUMN `departure` INTEGER DEFAULT 0 NOT NULL")
db.execSQL("ALTER TABLE `notification` ADD COLUMN `location` INTEGER") connection.execSQL("ALTER TABLE `notification` ADD COLUMN `location` INTEGER")
} }
} }
private val MIGRATION_60_61: Migration = object : Migration(60, 61) { private val MIGRATION_60_61: Migration = object : Migration(60, 61) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `places` (`place_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uid` TEXT, `name` TEXT, `address` TEXT, `phone` TEXT, `url` TEXT, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL)") "CREATE TABLE IF NOT EXISTS `places` (`place_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uid` TEXT, `name` TEXT, `address` TEXT, `phone` TEXT, `url` TEXT, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL)")
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `geofences` (`geofence_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `place` TEXT, `radius` INTEGER NOT NULL, `arrival` INTEGER NOT NULL, `departure` INTEGER NOT NULL)") "CREATE TABLE IF NOT EXISTS `geofences` (`geofence_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `place` TEXT, `radius` INTEGER NOT NULL, `arrival` INTEGER NOT NULL, `departure` INTEGER NOT NULL)")
db.execSQL( connection.execSQL(
"INSERT INTO `places` (`place_id`, `uid`, `name`, `address`, `phone`, `url`, `latitude`, `longitude`) SELECT `_id`, hex(randomblob(16)), `name`, `address`, `phone`, `url`, `latitude`, `longitude` FROM `locations`") "INSERT INTO `places` (`place_id`, `uid`, `name`, `address`, `phone`, `url`, `latitude`, `longitude`) SELECT `_id`, hex(randomblob(16)), `name`, `address`, `phone`, `url`, `latitude`, `longitude` FROM `locations`")
db.execSQL( connection.execSQL(
"INSERT INTO `geofences` (`geofence_id`, `task`, `place`, `radius`, `arrival`, `departure`) SELECT `_id`, `task`, `uid`, `radius`, `arrival`, `departure` FROM `locations` INNER JOIN `places` ON `_id` = `place_id`") "INSERT INTO `geofences` (`geofence_id`, `task`, `place`, `radius`, `arrival`, `departure`) SELECT `_id`, `task`, `uid`, `radius`, `arrival`, `departure` FROM `locations` INNER JOIN `places` ON `_id` = `place_id`")
db.execSQL("DROP TABLE `locations`") connection.execSQL("DROP TABLE `locations`")
} }
} }
private val MIGRATION_61_62: Migration = object : Migration(61, 62) { private val MIGRATION_61_62: Migration = object : Migration(61, 62) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `google_task_accounts` ADD COLUMN `etag` TEXT") connection.execSQL("ALTER TABLE `google_task_accounts` ADD COLUMN `etag` TEXT")
} }
} }
private val MIGRATION_62_63: Migration = object : Migration(62, 63) { private val MIGRATION_62_63: Migration = object : Migration(62, 63) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `google_tasks` RENAME TO `gt-temp`") connection.execSQL("ALTER TABLE `google_tasks` RENAME TO `gt-temp`")
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `google_tasks` (`gt_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `gt_task` INTEGER NOT NULL, `gt_remote_id` TEXT, `gt_list_id` TEXT, `gt_parent` INTEGER NOT NULL, `gt_remote_parent` TEXT, `gt_moved` INTEGER NOT NULL, `gt_order` INTEGER NOT NULL, `gt_remote_order` INTEGER NOT NULL, `gt_last_sync` INTEGER NOT NULL, `gt_deleted` INTEGER NOT NULL)") "CREATE TABLE IF NOT EXISTS `google_tasks` (`gt_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `gt_task` INTEGER NOT NULL, `gt_remote_id` TEXT, `gt_list_id` TEXT, `gt_parent` INTEGER NOT NULL, `gt_remote_parent` TEXT, `gt_moved` INTEGER NOT NULL, `gt_order` INTEGER NOT NULL, `gt_remote_order` INTEGER NOT NULL, `gt_last_sync` INTEGER NOT NULL, `gt_deleted` INTEGER NOT NULL)")
db.execSQL("INSERT INTO `google_tasks` (`gt_id`, `gt_task`, `gt_remote_id`, `gt_list_id`, `gt_parent`, `gt_remote_parent`, `gt_moved`, `gt_order`, `gt_remote_order`, `gt_last_sync`, `gt_deleted`) " connection.execSQL("INSERT INTO `google_tasks` (`gt_id`, `gt_task`, `gt_remote_id`, `gt_list_id`, `gt_parent`, `gt_remote_parent`, `gt_moved`, `gt_order`, `gt_remote_order`, `gt_last_sync`, `gt_deleted`) "
+ "SELECT `_id`, `task`, `remote_id`, `list_id`, `parent`, '', 0, `order`, `remote_order`, `last_sync`, `deleted` FROM `gt-temp`") + "SELECT `_id`, `task`, `remote_id`, `list_id`, `parent`, '', 0, `order`, `remote_order`, `last_sync`, `deleted` FROM `gt-temp`")
db.execSQL("DROP TABLE `gt-temp`") connection.execSQL("DROP TABLE `gt-temp`")
db.execSQL("UPDATE `google_task_lists` SET `last_sync` = 0") connection.execSQL("UPDATE `google_task_lists` SET `last_sync` = 0")
} }
} }
private val MIGRATION_63_64: Migration = object : Migration(63, 64) { private val MIGRATION_63_64: Migration = object : Migration(63, 64) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `caldav_tasks` RENAME TO `caldav-temp`") connection.execSQL("ALTER TABLE `caldav_tasks` RENAME TO `caldav-temp`")
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `caldav_tasks` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_vtodo` TEXT)") "CREATE TABLE IF NOT EXISTS `caldav_tasks` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_vtodo` TEXT)")
db.execSQL("INSERT INTO `caldav_tasks` (`cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_vtodo`)" connection.execSQL("INSERT INTO `caldav_tasks` (`cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_vtodo`)"
+ "SELECT `_id`, `task`, `calendar`, `object`, `remote_id`, `etag`, `last_sync`, `deleted`, `vtodo` FROM `caldav-temp`") + "SELECT `_id`, `task`, `calendar`, `object`, `remote_id`, `etag`, `last_sync`, `deleted`, `vtodo` FROM `caldav-temp`")
db.execSQL("DROP TABLE `caldav-temp`") connection.execSQL("DROP TABLE `caldav-temp`")
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `caldav_accounts` (`cda_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cda_uuid` TEXT, `cda_name` TEXT, `cda_url` TEXT, `cda_username` TEXT, `cda_password` TEXT, `cda_error` TEXT)") "CREATE TABLE IF NOT EXISTS `caldav_accounts` (`cda_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cda_uuid` TEXT, `cda_name` TEXT, `cda_url` TEXT, `cda_username` TEXT, `cda_password` TEXT, `cda_error` TEXT)")
db.execSQL("INSERT INTO `caldav_accounts` (`cda_id`, `cda_uuid`, `cda_name`, `cda_url`, `cda_username`, `cda_password`, `cda_error`) " connection.execSQL("INSERT INTO `caldav_accounts` (`cda_id`, `cda_uuid`, `cda_name`, `cda_url`, `cda_username`, `cda_password`, `cda_error`) "
+ "SELECT `_id`, `uuid`, `name`, `url`, `username`, `password`, `error` FROM `caldav_account`") + "SELECT `_id`, `uuid`, `name`, `url`, `username`, `password`, `error` FROM `caldav_account`")
db.execSQL("DROP TABLE `caldav_account`") connection.execSQL("DROP TABLE `caldav_account`")
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `caldav_lists` (`cdl_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cdl_account` TEXT, `cdl_uuid` TEXT, `cdl_name` TEXT, `cdl_color` INTEGER NOT NULL, `cdl_ctag` TEXT, `cdl_url` TEXT, `cdl_icon` INTEGER)") "CREATE TABLE IF NOT EXISTS `caldav_lists` (`cdl_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cdl_account` TEXT, `cdl_uuid` TEXT, `cdl_name` TEXT, `cdl_color` INTEGER NOT NULL, `cdl_ctag` TEXT, `cdl_url` TEXT, `cdl_icon` INTEGER)")
db.execSQL("INSERT INTO `caldav_lists` (`cdl_id`, `cdl_account`, `cdl_uuid`, `cdl_name`, `cdl_color`, `cdl_ctag`, `cdl_url`) " connection.execSQL("INSERT INTO `caldav_lists` (`cdl_id`, `cdl_account`, `cdl_uuid`, `cdl_name`, `cdl_color`, `cdl_ctag`, `cdl_url`) "
+ "SELECT `_id`, `account`, `uuid`, `name`, `color`, `ctag`, `url` FROM caldav_calendar") + "SELECT `_id`, `account`, `uuid`, `name`, `color`, `ctag`, `url` FROM caldav_calendar")
db.execSQL("DROP TABLE `caldav_calendar`") connection.execSQL("DROP TABLE `caldav_calendar`")
db.execSQL("ALTER TABLE `google_task_accounts` RENAME TO `gta-temp`") connection.execSQL("ALTER TABLE `google_task_accounts` RENAME TO `gta-temp`")
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `google_task_accounts` (`gta_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `gta_account` TEXT, `gta_error` TEXT, `gta_etag` TEXT)") "CREATE TABLE IF NOT EXISTS `google_task_accounts` (`gta_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `gta_account` TEXT, `gta_error` TEXT, `gta_etag` TEXT)")
db.execSQL("INSERT INTO `google_task_accounts` (`gta_id`, `gta_account`, `gta_error`, `gta_etag`) " connection.execSQL("INSERT INTO `google_task_accounts` (`gta_id`, `gta_account`, `gta_error`, `gta_etag`) "
+ "SELECT `_id`, `account`, `error`, `etag` FROM `gta-temp`") + "SELECT `_id`, `account`, `error`, `etag` FROM `gta-temp`")
db.execSQL("DROP TABLE `gta-temp`") connection.execSQL("DROP TABLE `gta-temp`")
db.execSQL("ALTER TABLE `google_task_lists` RENAME TO `gtl-temp`") connection.execSQL("ALTER TABLE `google_task_lists` RENAME TO `gtl-temp`")
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `google_task_lists` (`gtl_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `gtl_account` TEXT, `gtl_remote_id` TEXT, `gtl_title` TEXT, `gtl_remote_order` INTEGER NOT NULL, `gtl_last_sync` INTEGER NOT NULL, `gtl_color` INTEGER, `gtl_icon` INTEGER)") "CREATE TABLE IF NOT EXISTS `google_task_lists` (`gtl_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `gtl_account` TEXT, `gtl_remote_id` TEXT, `gtl_title` TEXT, `gtl_remote_order` INTEGER NOT NULL, `gtl_last_sync` INTEGER NOT NULL, `gtl_color` INTEGER, `gtl_icon` INTEGER)")
db.execSQL("INSERT INTO `google_task_lists` (`gtl_id`, `gtl_account`, `gtl_remote_id`, `gtl_title`, `gtl_remote_order`, `gtl_last_sync`, `gtl_color`) " connection.execSQL("INSERT INTO `google_task_lists` (`gtl_id`, `gtl_account`, `gtl_remote_id`, `gtl_title`, `gtl_remote_order`, `gtl_last_sync`, `gtl_color`) "
+ "SELECT `_id`, `account`, `remote_id`, `title`, `remote_order`, `last_sync`, `color` FROM `gtl-temp`") + "SELECT `_id`, `account`, `remote_id`, `title`, `remote_order`, `last_sync`, `color` FROM `gtl-temp`")
db.execSQL("DROP TABLE `gtl-temp`") connection.execSQL("DROP TABLE `gtl-temp`")
db.execSQL("ALTER TABLE `filters` ADD COLUMN `f_color` INTEGER") connection.execSQL("ALTER TABLE `filters` ADD COLUMN `f_color` INTEGER")
db.execSQL("ALTER TABLE `filters` ADD COLUMN `f_icon` INTEGER") connection.execSQL("ALTER TABLE `filters` ADD COLUMN `f_icon` INTEGER")
db.execSQL("ALTER TABLE `tagdata` ADD COLUMN `td_icon` INTEGER") connection.execSQL("ALTER TABLE `tagdata` ADD COLUMN `td_icon` INTEGER")
} }
} }
private val MIGRATION_64_65: Migration = object : Migration(64, 65) { private val MIGRATION_64_65: Migration = object : Migration(64, 65) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"ALTER TABLE `caldav_tasks` ADD COLUMN `cd_parent` INTEGER NOT NULL DEFAULT 0") "ALTER TABLE `caldav_tasks` ADD COLUMN `cd_parent` INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `cd_remote_parent` TEXT") connection.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `cd_remote_parent` TEXT")
} }
} }
private val MIGRATION_65_66: Migration = object : Migration(65, 66) { private val MIGRATION_65_66: Migration = object : Migration(65, 66) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("CREATE UNIQUE INDEX `place_uid` ON `places` (`uid`)") connection.execSQL("CREATE UNIQUE INDEX `place_uid` ON `places` (`uid`)")
db.execSQL("CREATE INDEX `geo_task` ON `geofences` (`task`)") connection.execSQL("CREATE INDEX `geo_task` ON `geofences` (`task`)")
db.execSQL("CREATE INDEX `tag_task` ON `tags` (`task`)") connection.execSQL("CREATE INDEX `tag_task` ON `tags` (`task`)")
db.execSQL("CREATE INDEX `gt_list_parent` ON `google_tasks` (`gt_list_id`, `gt_parent`)") connection.execSQL("CREATE INDEX `gt_list_parent` ON `google_tasks` (`gt_list_id`, `gt_parent`)")
db.execSQL("CREATE INDEX `gt_task` ON `google_tasks` (`gt_task`)") connection.execSQL("CREATE INDEX `gt_task` ON `google_tasks` (`gt_task`)")
db.execSQL("CREATE INDEX `cd_calendar_parent` ON `caldav_tasks` (`cd_calendar`, `cd_parent`)") connection.execSQL("CREATE INDEX `cd_calendar_parent` ON `caldav_tasks` (`cd_calendar`, `cd_parent`)")
db.execSQL("CREATE INDEX `cd_task` ON `caldav_tasks` (`cd_task`)") connection.execSQL("CREATE INDEX `cd_task` ON `caldav_tasks` (`cd_task`)")
} }
} }
private val MIGRATION_66_67: Migration = object : Migration(66, 67) { private val MIGRATION_66_67: Migration = object : Migration(66, 67) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"ALTER TABLE `caldav_accounts` ADD COLUMN `cda_repeat` INTEGER NOT NULL DEFAULT 0") "ALTER TABLE `caldav_accounts` ADD COLUMN `cda_repeat` INTEGER NOT NULL DEFAULT 0")
} }
} }
private val MIGRATION_67_68: Migration = object : Migration(67, 68) { private val MIGRATION_67_68: Migration = object : Migration(67, 68) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"CREATE INDEX `active_and_visible` ON `tasks` (`completed`, `deleted`, `hideUntil`)") "CREATE INDEX `active_and_visible` ON `tasks` (`completed`, `deleted`, `hideUntil`)")
} }
} }
private val MIGRATION_68_69: Migration = object : Migration(68, 69) { private val MIGRATION_68_69: Migration = object : Migration(68, 69) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"ALTER TABLE `tasks` ADD COLUMN `collapsed` INTEGER NOT NULL DEFAULT 0") "ALTER TABLE `tasks` ADD COLUMN `collapsed` INTEGER NOT NULL DEFAULT 0")
} }
} }
private val MIGRATION_69_70: Migration = object : Migration(69, 70) { private val MIGRATION_69_70: Migration = object : Migration(69, 70) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `tasks` ADD COLUMN `parent` INTEGER NOT NULL DEFAULT 0") connection.execSQL("ALTER TABLE `tasks` ADD COLUMN `parent` INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE `tasks` ADD COLUMN `parent_uuid` TEXT") connection.execSQL("ALTER TABLE `tasks` ADD COLUMN `parent_uuid` TEXT")
db.execSQL( connection.execSQL(
"UPDATE `tasks` SET `parent` = IFNULL((" "UPDATE `tasks` SET `parent` = IFNULL(("
+ " SELECT p.cd_task FROM caldav_tasks" + " SELECT p.cd_task FROM caldav_tasks"
+ " INNER JOIN caldav_tasks AS p ON p.cd_remote_id = caldav_tasks.cd_remote_parent" + " INNER JOIN caldav_tasks AS p ON p.cd_remote_id = caldav_tasks.cd_remote_parent"
@ -320,133 +318,133 @@ object Migrations {
+ " AND caldav_tasks.cd_deleted = 0" + " AND caldav_tasks.cd_deleted = 0"
+ " AND p.cd_calendar = caldav_tasks.cd_calendar" + " AND p.cd_calendar = caldav_tasks.cd_calendar"
+ " AND p.cd_deleted = 0), 0)") + " AND p.cd_deleted = 0), 0)")
db.execSQL("ALTER TABLE `caldav_tasks` RENAME TO `caldav_tasks-temp`") connection.execSQL("ALTER TABLE `caldav_tasks` RENAME TO `caldav_tasks-temp`")
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `caldav_tasks` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_vtodo` TEXT, `cd_remote_parent` TEXT)") "CREATE TABLE IF NOT EXISTS `caldav_tasks` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_vtodo` TEXT, `cd_remote_parent` TEXT)")
db.execSQL("INSERT INTO `caldav_tasks` (`cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_vtodo`, `cd_remote_parent`) " connection.execSQL("INSERT INTO `caldav_tasks` (`cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_vtodo`, `cd_remote_parent`) "
+ "SELECT `cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_vtodo`, `cd_remote_parent` FROM `caldav_tasks-temp`") + "SELECT `cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_vtodo`, `cd_remote_parent` FROM `caldav_tasks-temp`")
db.execSQL("DROP TABLE `caldav_tasks-temp`") connection.execSQL("DROP TABLE `caldav_tasks-temp`")
db.execSQL("CREATE INDEX `cd_task` ON `caldav_tasks` (`cd_task`)") connection.execSQL("CREATE INDEX `cd_task` ON `caldav_tasks` (`cd_task`)")
} }
} }
private val MIGRATION_70_71: Migration = object : Migration(70, 71) { private val MIGRATION_70_71: Migration = object : Migration(70, 71) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `caldav_accounts` ADD COLUMN `cda_encryption_key` TEXT") connection.execSQL("ALTER TABLE `caldav_accounts` ADD COLUMN `cda_encryption_key` TEXT")
db.execSQL( connection.execSQL(
"ALTER TABLE `caldav_accounts` ADD COLUMN `cda_account_type` INTEGER NOT NULL DEFAULT 0") "ALTER TABLE `caldav_accounts` ADD COLUMN `cda_account_type` INTEGER NOT NULL DEFAULT 0")
} }
} }
private val MIGRATION_71_72: Migration = object : Migration(71, 72) { private val MIGRATION_71_72: Migration = object : Migration(71, 72) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"ALTER TABLE `caldav_accounts` ADD COLUMN `cda_collapsed` INTEGER NOT NULL DEFAULT 0") "ALTER TABLE `caldav_accounts` ADD COLUMN `cda_collapsed` INTEGER NOT NULL DEFAULT 0")
db.execSQL( connection.execSQL(
"ALTER TABLE `google_task_accounts` ADD COLUMN `gta_collapsed` INTEGER NOT NULL DEFAULT 0") "ALTER TABLE `google_task_accounts` ADD COLUMN `gta_collapsed` INTEGER NOT NULL DEFAULT 0")
} }
} }
private val MIGRATION_72_73: Migration = object : Migration(72, 73) { private val MIGRATION_72_73: Migration = object : Migration(72, 73) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `places` ADD COLUMN `place_color` INTEGER NOT NULL DEFAULT 0") connection.execSQL("ALTER TABLE `places` ADD COLUMN `place_color` INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE `places` ADD COLUMN `place_icon` INTEGER NOT NULL DEFAULT -1") connection.execSQL("ALTER TABLE `places` ADD COLUMN `place_icon` INTEGER NOT NULL DEFAULT -1")
} }
} }
private val MIGRATION_73_74: Migration = object : Migration(73, 74) { private val MIGRATION_73_74: Migration = object : Migration(73, 74) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `tasks` RENAME TO `tasks-temp`") connection.execSQL("ALTER TABLE `tasks` RENAME TO `tasks-temp`")
db.execSQL("DROP INDEX `t_rid`") connection.execSQL("DROP INDEX `t_rid`")
db.execSQL("DROP INDEX `active_and_visible`") connection.execSQL("DROP INDEX `active_and_visible`")
db.execSQL("CREATE TABLE IF NOT EXISTS `tasks` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `importance` INTEGER NOT NULL, `dueDate` INTEGER NOT NULL, `hideUntil` INTEGER NOT NULL, `created` INTEGER NOT NULL, `modified` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `notes` TEXT, `estimatedSeconds` INTEGER NOT NULL, `elapsedSeconds` INTEGER NOT NULL, `timerStart` INTEGER NOT NULL, `notificationFlags` INTEGER NOT NULL, `notifications` INTEGER NOT NULL, `lastNotified` INTEGER NOT NULL, `snoozeTime` INTEGER NOT NULL, `recurrence` TEXT, `repeatUntil` INTEGER NOT NULL, `calendarUri` TEXT, `remoteId` TEXT, `collapsed` INTEGER NOT NULL, `parent` INTEGER NOT NULL, `parent_uuid` TEXT)") connection.execSQL("CREATE TABLE IF NOT EXISTS `tasks` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `importance` INTEGER NOT NULL, `dueDate` INTEGER NOT NULL, `hideUntil` INTEGER NOT NULL, `created` INTEGER NOT NULL, `modified` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `notes` TEXT, `estimatedSeconds` INTEGER NOT NULL, `elapsedSeconds` INTEGER NOT NULL, `timerStart` INTEGER NOT NULL, `notificationFlags` INTEGER NOT NULL, `notifications` INTEGER NOT NULL, `lastNotified` INTEGER NOT NULL, `snoozeTime` INTEGER NOT NULL, `recurrence` TEXT, `repeatUntil` INTEGER NOT NULL, `calendarUri` TEXT, `remoteId` TEXT, `collapsed` INTEGER NOT NULL, `parent` INTEGER NOT NULL, `parent_uuid` TEXT)")
db.execSQL("INSERT INTO `tasks` (`_id`, `title`, `importance`, `dueDate`, `hideUntil`, `created`, `modified`, `completed`, `deleted`, `notes`, `estimatedSeconds`, `elapsedSeconds`, `timerStart`, `notificationFlags`, `notifications`, `lastNotified`, `snoozeTime`, `recurrence`, `repeatUntil`, `calendarUri`, `remoteId`, `collapsed`, `parent`, `parent_uuid`) " connection.execSQL("INSERT INTO `tasks` (`_id`, `title`, `importance`, `dueDate`, `hideUntil`, `created`, `modified`, `completed`, `deleted`, `notes`, `estimatedSeconds`, `elapsedSeconds`, `timerStart`, `notificationFlags`, `notifications`, `lastNotified`, `snoozeTime`, `recurrence`, `repeatUntil`, `calendarUri`, `remoteId`, `collapsed`, `parent`, `parent_uuid`) "
+ "SELECT `_id`, `title`, IFNULL(`importance`, 3), IFNULL(`dueDate`, 0), IFNULL(`hideUntil`, 0), IFNULL(`created`, 0), IFNULL(`modified`, 0), IFNULL(`completed`, 0), IFNULL(`deleted`, 0), `notes`, IFNULL(`estimatedSeconds`, 0), IFNULL(`elapsedSeconds`, 0), IFNULL(`timerStart`, 0), IFNULL(`notificationFlags`, 0), IFNULL(`notifications`, 0), IFNULL(`lastNotified`, 0), IFNULL(`snoozeTime`, 0), `recurrence`, IFNULL(`repeatUntil`, 0), `calendarUri`, `remoteId`, `collapsed`, IFNULL(`parent`, 0), `parent_uuid` FROM `tasks-temp`") + "SELECT `_id`, `title`, IFNULL(`importance`, 3), IFNULL(`dueDate`, 0), IFNULL(`hideUntil`, 0), IFNULL(`created`, 0), IFNULL(`modified`, 0), IFNULL(`completed`, 0), IFNULL(`deleted`, 0), `notes`, IFNULL(`estimatedSeconds`, 0), IFNULL(`elapsedSeconds`, 0), IFNULL(`timerStart`, 0), IFNULL(`notificationFlags`, 0), IFNULL(`notifications`, 0), IFNULL(`lastNotified`, 0), IFNULL(`snoozeTime`, 0), `recurrence`, IFNULL(`repeatUntil`, 0), `calendarUri`, `remoteId`, `collapsed`, IFNULL(`parent`, 0), `parent_uuid` FROM `tasks-temp`")
db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `t_rid` ON `tasks` (`remoteId`)") connection.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `t_rid` ON `tasks` (`remoteId`)")
db.execSQL("CREATE INDEX IF NOT EXISTS `active_and_visible` ON `tasks` (`completed`, `deleted`, `hideUntil`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `active_and_visible` ON `tasks` (`completed`, `deleted`, `hideUntil`)")
db.execSQL("DROP TABLE `tasks-temp`") connection.execSQL("DROP TABLE `tasks-temp`")
} }
} }
private val MIGRATION_74_75: Migration = object : Migration(74, 75) { private val MIGRATION_74_75: Migration = object : Migration(74, 75) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `cd_order` INTEGER") connection.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `cd_order` INTEGER")
} }
} }
private val MIGRATION_75_76: Migration = object : Migration(75, 76) { private val MIGRATION_75_76: Migration = object : Migration(75, 76) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `tagdata` ADD COLUMN `td_order` INTEGER NOT NULL DEFAULT $NO_ORDER") connection.execSQL("ALTER TABLE `tagdata` ADD COLUMN `td_order` INTEGER NOT NULL DEFAULT $NO_ORDER")
db.execSQL("ALTER TABLE `caldav_lists` ADD COLUMN `cdl_order` INTEGER NOT NULL DEFAULT $NO_ORDER") connection.execSQL("ALTER TABLE `caldav_lists` ADD COLUMN `cdl_order` INTEGER NOT NULL DEFAULT $NO_ORDER")
db.execSQL("ALTER TABLE `filters` ADD COLUMN `f_order` INTEGER NOT NULL DEFAULT $NO_ORDER") connection.execSQL("ALTER TABLE `filters` ADD COLUMN `f_order` INTEGER NOT NULL DEFAULT $NO_ORDER")
db.execSQL("ALTER TABLE `places` ADD COLUMN `place_order` INTEGER NOT NULL DEFAULT $NO_ORDER") connection.execSQL("ALTER TABLE `places` ADD COLUMN `place_order` INTEGER NOT NULL DEFAULT $NO_ORDER")
} }
} }
private val MIGRATION_76_77: Migration = object : Migration(76, 77) { private val MIGRATION_76_77: Migration = object : Migration(76, 77) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"ALTER TABLE `caldav_lists` ADD COLUMN `cdl_access` INTEGER NOT NULL DEFAULT 0") "ALTER TABLE `caldav_lists` ADD COLUMN `cdl_access` INTEGER NOT NULL DEFAULT 0")
} }
} }
private val MIGRATION_77_78: Migration = object : Migration(77, 78) { private val MIGRATION_77_78: Migration = object : Migration(77, 78) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `principals` (`principal_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `principal_list` INTEGER NOT NULL, `principal` TEXT, `display_name` TEXT, `invite` INTEGER NOT NULL, `access` INTEGER NOT NULL, FOREIGN KEY(`principal_list`) REFERENCES `caldav_lists`(`cdl_id`) ON UPDATE NO ACTION ON DELETE CASCADE )") "CREATE TABLE IF NOT EXISTS `principals` (`principal_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `principal_list` INTEGER NOT NULL, `principal` TEXT, `display_name` TEXT, `invite` INTEGER NOT NULL, `access` INTEGER NOT NULL, FOREIGN KEY(`principal_list`) REFERENCES `caldav_lists`(`cdl_id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
db.execSQL( connection.execSQL(
"CREATE UNIQUE INDEX IF NOT EXISTS `index_principals_principal_list_principal` ON `principals` (`principal_list`, `principal`)" "CREATE UNIQUE INDEX IF NOT EXISTS `index_principals_principal_list_principal` ON `principals` (`principal_list`, `principal`)"
) )
} }
} }
private val MIGRATION_78_79: Migration = object : Migration(78, 79) { private val MIGRATION_78_79: Migration = object : Migration(78, 79) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL( connection.execSQL(
"ALTER TABLE `caldav_accounts` ADD COLUMN `cda_server_type` INTEGER NOT NULL DEFAULT $SERVER_UNKNOWN" "ALTER TABLE `caldav_accounts` ADD COLUMN `cda_server_type` INTEGER NOT NULL DEFAULT $SERVER_UNKNOWN"
) )
} }
} }
private val MIGRATION_79_80 = object : Migration(79, 80) { private val MIGRATION_79_80 = object : Migration(79, 80) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("DROP TABLE `principals`") connection.execSQL("DROP TABLE `principals`")
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `principals` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` INTEGER NOT NULL, `href` TEXT NOT NULL, `email` TEXT, `display_name` TEXT, FOREIGN KEY(`account`) REFERENCES `caldav_accounts`(`cda_id`) ON UPDATE NO ACTION ON DELETE CASCADE )" "CREATE TABLE IF NOT EXISTS `principals` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` INTEGER NOT NULL, `href` TEXT NOT NULL, `email` TEXT, `display_name` TEXT, FOREIGN KEY(`account`) REFERENCES `caldav_accounts`(`cda_id`) ON UPDATE NO ACTION ON DELETE CASCADE )"
) )
db.execSQL( connection.execSQL(
"CREATE UNIQUE INDEX IF NOT EXISTS `index_principals_account_href` ON `principals` (`account`, `href`)" "CREATE UNIQUE INDEX IF NOT EXISTS `index_principals_account_href` ON `principals` (`account`, `href`)"
) )
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `principal_access` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `principal` INTEGER NOT NULL, `list` INTEGER NOT NULL, `invite` INTEGER NOT NULL, `access` INTEGER NOT NULL, FOREIGN KEY(`principal`) REFERENCES `principals`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`list`) REFERENCES `caldav_lists`(`cdl_id`) ON UPDATE NO ACTION ON DELETE CASCADE )" "CREATE TABLE IF NOT EXISTS `principal_access` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `principal` INTEGER NOT NULL, `list` INTEGER NOT NULL, `invite` INTEGER NOT NULL, `access` INTEGER NOT NULL, FOREIGN KEY(`principal`) REFERENCES `principals`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`list`) REFERENCES `caldav_lists`(`cdl_id`) ON UPDATE NO ACTION ON DELETE CASCADE )"
) )
db.execSQL( connection.execSQL(
"CREATE UNIQUE INDEX IF NOT EXISTS `index_principal_access_list_principal` ON `principal_access` (`list`, `principal`)" "CREATE UNIQUE INDEX IF NOT EXISTS `index_principal_access_list_principal` ON `principal_access` (`list`, `principal`)"
) )
db.execSQL( connection.execSQL(
"CREATE INDEX IF NOT EXISTS `index_principal_access_principal` ON `principal_access` (`principal`)" "CREATE INDEX IF NOT EXISTS `index_principal_access_principal` ON `principal_access` (`principal`)"
) )
} }
} }
private val MIGRATION_80_81 = object : Migration(80, 81) { private val MIGRATION_80_81 = object : Migration(80, 81) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `alarms` ADD COLUMN `type` INTEGER NOT NULL DEFAULT 0") connection.execSQL("ALTER TABLE `alarms` ADD COLUMN `type` INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE `alarms` ADD COLUMN `repeat` INTEGER NOT NULL DEFAULT 0") connection.execSQL("ALTER TABLE `alarms` ADD COLUMN `repeat` INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE `alarms` ADD COLUMN `interval` INTEGER NOT NULL DEFAULT 0") connection.execSQL("ALTER TABLE `alarms` ADD COLUMN `interval` INTEGER NOT NULL DEFAULT 0")
db.execSQL( connection.execSQL(
"INSERT INTO `alarms` (`task`, `time`, `type`) SELECT `_id`, 0, $TYPE_REL_START FROM `tasks` WHERE `hideUntil` > 0 AND `notificationFlags` & $NOTIFY_AT_START = $NOTIFY_AT_START" "INSERT INTO `alarms` (`task`, `time`, `type`) SELECT `_id`, 0, $TYPE_REL_START FROM `tasks` WHERE `hideUntil` > 0 AND `notificationFlags` & $NOTIFY_AT_START = $NOTIFY_AT_START"
) )
db.execSQL( connection.execSQL(
"INSERT INTO `alarms` (`task`, `time`, `type`) SELECT `_id`, 0, $TYPE_REL_END FROM `tasks` WHERE `dueDate` > 0 AND `notificationFlags` & $NOTIFY_AT_DEADLINE = $NOTIFY_AT_DEADLINE" "INSERT INTO `alarms` (`task`, `time`, `type`) SELECT `_id`, 0, $TYPE_REL_END FROM `tasks` WHERE `dueDate` > 0 AND `notificationFlags` & $NOTIFY_AT_DEADLINE = $NOTIFY_AT_DEADLINE"
) )
db.execSQL( connection.execSQL(
"INSERT INTO `alarms` (`task`, `time`, `type`, `repeat`, `interval`) SELECT `_id`, ${HOURS.toMillis(24)}, $TYPE_REL_END, 6, ${HOURS.toMillis(24)} FROM `tasks` WHERE `dueDate` > 0 AND `notificationFlags` & $NOTIFY_AFTER_DEADLINE = $NOTIFY_AFTER_DEADLINE" "INSERT INTO `alarms` (`task`, `time`, `type`, `repeat`, `interval`) SELECT `_id`, ${HOURS.toMillis(24)}, $TYPE_REL_END, 6, ${HOURS.toMillis(24)} FROM `tasks` WHERE `dueDate` > 0 AND `notificationFlags` & $NOTIFY_AFTER_DEADLINE = $NOTIFY_AFTER_DEADLINE"
) )
db.execSQL( connection.execSQL(
"INSERT INTO `alarms` (`task`, `time`, `type`) SELECT `_id`, `notifications`, $TYPE_RANDOM FROM `tasks` WHERE `notifications` > 0" "INSERT INTO `alarms` (`task`, `time`, `type`) SELECT `_id`, `notifications`, $TYPE_RANDOM FROM `tasks` WHERE `notifications` > 0"
) )
db.execSQL( connection.execSQL(
"INSERT INTO `alarms` (`task`, `time`, `type`) SELECT `_id`, `snoozeTime`, $TYPE_SNOOZE FROM `tasks` WHERE `snoozeTime` > 0" "INSERT INTO `alarms` (`task`, `time`, `type`) SELECT `_id`, `snoozeTime`, $TYPE_SNOOZE FROM `tasks` WHERE `snoozeTime` > 0"
) )
} }
@ -454,171 +452,176 @@ object Migrations {
@Suppress("FunctionName") @Suppress("FunctionName")
private fun migration_81_82(fileStorage: FileStorage) = object : Migration(81, 82) { private fun migration_81_82(fileStorage: FileStorage) = object : Migration(81, 82) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db connection
.query("SELECT `cdl_account`, `cd_calendar`, `cd_object`, `cd_vtodo` FROM `caldav_tasks` INNER JOIN `caldav_lists` ON `cdl_uuid` = `cd_calendar`") .prepare("SELECT `cdl_account`, `cd_calendar`, `cd_object`, `cd_vtodo` FROM `caldav_tasks` INNER JOIN `caldav_lists` ON `cdl_uuid` = `cd_calendar`")
.use { .use {
while (it.moveToNext()) { while (it.step()) {
val file = fileStorage.getFile( val file = fileStorage.getFile(
it.getString("cdl_account"), it.getText(0),
it.getString("cd_calendar"), it.getText(1),
) )
?.apply { mkdirs() } ?.apply { mkdirs() }
?: continue ?: continue
val `object` = it.getString("cd_object") ?: continue if (it.isNull(2)) continue
fileStorage.write(File(file, `object`), it.getString("cd_vtodo")) val `object` = it.getText(2)
fileStorage.write(File(file, `object`), it.getText(3))
} }
} }
db.execSQL("ALTER TABLE `caldav_tasks` RENAME TO `caldav_tasks-temp`") connection.execSQL("ALTER TABLE `caldav_tasks` RENAME TO `caldav_tasks-temp`")
db.execSQL( connection.execSQL(
"CREATE TABLE IF NOT EXISTS `caldav_tasks` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_remote_parent` TEXT, `cd_order` INTEGER)" "CREATE TABLE IF NOT EXISTS `caldav_tasks` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_remote_parent` TEXT, `cd_order` INTEGER)"
) )
db.execSQL("DROP INDEX `cd_task`") connection.execSQL("DROP INDEX `cd_task`")
db.execSQL("CREATE INDEX IF NOT EXISTS `cd_task` ON `caldav_tasks` (`cd_task`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `cd_task` ON `caldav_tasks` (`cd_task`)")
db.execSQL( connection.execSQL(
"INSERT INTO `caldav_tasks` (`cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, `cd_order`) SELECT `cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, `cd_order` FROM `caldav_tasks-temp`" "INSERT INTO `caldav_tasks` (`cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, `cd_order`) SELECT `cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, `cd_order` FROM `caldav_tasks-temp`"
) )
db.execSQL("DROP TABLE `caldav_tasks-temp`") connection.execSQL("DROP TABLE `caldav_tasks-temp`")
} }
} }
private val MIGRATION_82_83 = object : Migration(82, 83) { private val MIGRATION_82_83 = object : Migration(82, 83) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `places` ADD COLUMN `radius` INTEGER NOT NULL DEFAULT 250") connection.execSQL("ALTER TABLE `places` ADD COLUMN `radius` INTEGER NOT NULL DEFAULT 250")
db.execSQL("CREATE TABLE IF NOT EXISTS `_new_alarms` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `time` INTEGER NOT NULL, `type` INTEGER NOT NULL DEFAULT 0, `repeat` INTEGER NOT NULL DEFAULT 0, `interval` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )") connection.execSQL("CREATE TABLE IF NOT EXISTS `_new_alarms` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `time` INTEGER NOT NULL, `type` INTEGER NOT NULL DEFAULT 0, `repeat` INTEGER NOT NULL DEFAULT 0, `interval` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
db.execSQL("INSERT INTO `_new_alarms` (`task`,`repeat`,`interval`,`_id`,`time`,`type`) SELECT `task`,`repeat`,`interval`,`alarms`.`_id`,`time`,`type` FROM `alarms` INNER JOIN `tasks` ON `tasks`.`_id` = `task`") connection.execSQL("INSERT INTO `_new_alarms` (`task`,`repeat`,`interval`,`_id`,`time`,`type`) SELECT `task`,`repeat`,`interval`,`alarms`.`_id`,`time`,`type` FROM `alarms` INNER JOIN `tasks` ON `tasks`.`_id` = `task`")
db.execSQL("DROP TABLE `alarms`") connection.execSQL("DROP TABLE `alarms`")
db.execSQL("ALTER TABLE `_new_alarms` RENAME TO `alarms`") connection.execSQL("ALTER TABLE `_new_alarms` RENAME TO `alarms`")
db.execSQL("CREATE INDEX IF NOT EXISTS `index_alarms_task` ON `alarms` (`task`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `index_alarms_task` ON `alarms` (`task`)")
db.execSQL("CREATE TABLE IF NOT EXISTS `_new_google_tasks` (`gt_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `gt_task` INTEGER NOT NULL, `gt_remote_id` TEXT, `gt_list_id` TEXT, `gt_parent` INTEGER NOT NULL, `gt_remote_parent` TEXT, `gt_moved` INTEGER NOT NULL, `gt_order` INTEGER NOT NULL, `gt_remote_order` INTEGER NOT NULL, `gt_last_sync` INTEGER NOT NULL, `gt_deleted` INTEGER NOT NULL, FOREIGN KEY(`gt_task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )") connection.execSQL("CREATE TABLE IF NOT EXISTS `_new_google_tasks` (`gt_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `gt_task` INTEGER NOT NULL, `gt_remote_id` TEXT, `gt_list_id` TEXT, `gt_parent` INTEGER NOT NULL, `gt_remote_parent` TEXT, `gt_moved` INTEGER NOT NULL, `gt_order` INTEGER NOT NULL, `gt_remote_order` INTEGER NOT NULL, `gt_last_sync` INTEGER NOT NULL, `gt_deleted` INTEGER NOT NULL, FOREIGN KEY(`gt_task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
db.execSQL("INSERT INTO `_new_google_tasks` (`gt_parent`,`gt_task`,`gt_remote_parent`,`gt_order`,`gt_last_sync`,`gt_id`,`gt_remote_id`,`gt_list_id`,`gt_moved`,`gt_remote_order`,`gt_deleted`) SELECT `gt_parent`,`gt_task`,`gt_remote_parent`,`gt_order`,`gt_last_sync`,`gt_id`,`gt_remote_id`,`gt_list_id`,`gt_moved`,`gt_remote_order`,`gt_deleted` FROM `google_tasks` INNER JOIN `tasks` ON `tasks`.`_id` = `gt_task`") connection.execSQL("INSERT INTO `_new_google_tasks` (`gt_parent`,`gt_task`,`gt_remote_parent`,`gt_order`,`gt_last_sync`,`gt_id`,`gt_remote_id`,`gt_list_id`,`gt_moved`,`gt_remote_order`,`gt_deleted`) SELECT `gt_parent`,`gt_task`,`gt_remote_parent`,`gt_order`,`gt_last_sync`,`gt_id`,`gt_remote_id`,`gt_list_id`,`gt_moved`,`gt_remote_order`,`gt_deleted` FROM `google_tasks` INNER JOIN `tasks` ON `tasks`.`_id` = `gt_task`")
db.execSQL("DROP TABLE `google_tasks`") connection.execSQL("DROP TABLE `google_tasks`")
db.execSQL("ALTER TABLE `_new_google_tasks` RENAME TO `google_tasks`") connection.execSQL("ALTER TABLE `_new_google_tasks` RENAME TO `google_tasks`")
db.execSQL("CREATE INDEX IF NOT EXISTS `gt_list_parent` ON `google_tasks` (`gt_list_id`, `gt_parent`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `gt_list_parent` ON `google_tasks` (`gt_list_id`, `gt_parent`)")
db.execSQL("CREATE INDEX IF NOT EXISTS `index_google_tasks_gt_task` ON `google_tasks` (`gt_task`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `index_google_tasks_gt_task` ON `google_tasks` (`gt_task`)")
db.execSQL("CREATE TABLE IF NOT EXISTS `_new_tags` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `tag_uid` TEXT, `task_uid` TEXT, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )") connection.execSQL("CREATE TABLE IF NOT EXISTS `_new_tags` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `tag_uid` TEXT, `task_uid` TEXT, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
db.execSQL("INSERT INTO `_new_tags` (`task`,`task_uid`,`name`,`tag_uid`,`_id`) SELECT `task`,`task_uid`,`name`,`tag_uid`,`tags`.`_id` FROM `tags` INNER JOIN `tasks` ON `tasks`.`_id` = `task`") connection.execSQL("INSERT INTO `_new_tags` (`task`,`task_uid`,`name`,`tag_uid`,`_id`) SELECT `task`,`task_uid`,`name`,`tag_uid`,`tags`.`_id` FROM `tags` INNER JOIN `tasks` ON `tasks`.`_id` = `task`")
db.execSQL("DROP TABLE `tags`") connection.execSQL("DROP TABLE `tags`")
db.execSQL("ALTER TABLE `_new_tags` RENAME TO `tags`") connection.execSQL("ALTER TABLE `_new_tags` RENAME TO `tags`")
db.execSQL("CREATE INDEX IF NOT EXISTS `index_tags_task` ON `tags` (`task`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `index_tags_task` ON `tags` (`task`)")
db.execSQL("CREATE TABLE IF NOT EXISTS `_new_notification` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `type` INTEGER NOT NULL, `location` INTEGER, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )") connection.execSQL("CREATE TABLE IF NOT EXISTS `_new_notification` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `type` INTEGER NOT NULL, `location` INTEGER, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
db.execSQL("INSERT INTO `_new_notification` (`uid`,`task`,`location`,`type`,`timestamp`) SELECT `uid`,`task`,`location`,`type`,`timestamp` FROM `notification` INNER JOIN `tasks` ON `tasks`.`_id` = `task`") connection.execSQL("INSERT INTO `_new_notification` (`uid`,`task`,`location`,`type`,`timestamp`) SELECT `uid`,`task`,`location`,`type`,`timestamp` FROM `notification` INNER JOIN `tasks` ON `tasks`.`_id` = `task`")
db.execSQL("DROP TABLE `notification`") connection.execSQL("DROP TABLE `notification`")
db.execSQL("ALTER TABLE `_new_notification` RENAME TO `notification`") connection.execSQL("ALTER TABLE `_new_notification` RENAME TO `notification`")
db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_notification_task` ON `notification` (`task`)") connection.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_notification_task` ON `notification` (`task`)")
db.execSQL("CREATE TABLE IF NOT EXISTS `_new_caldav_tasks` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_remote_parent` TEXT, `cd_order` INTEGER, FOREIGN KEY(`cd_task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )") connection.execSQL("CREATE TABLE IF NOT EXISTS `_new_caldav_tasks` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_remote_parent` TEXT, `cd_order` INTEGER, FOREIGN KEY(`cd_task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
db.execSQL("INSERT INTO `_new_caldav_tasks` (`cd_object`,`cd_deleted`,`cd_order`,`cd_remote_parent`,`cd_etag`,`cd_id`,`cd_calendar`,`cd_remote_id`,`cd_last_sync`,`cd_task`) SELECT `cd_object`,`cd_deleted`,`cd_order`,`cd_remote_parent`,`cd_etag`,`cd_id`,`cd_calendar`,`cd_remote_id`,`cd_last_sync`,`cd_task` FROM `caldav_tasks` INNER JOIN `tasks` ON `tasks`.`_id` = `cd_task`") connection.execSQL("INSERT INTO `_new_caldav_tasks` (`cd_object`,`cd_deleted`,`cd_order`,`cd_remote_parent`,`cd_etag`,`cd_id`,`cd_calendar`,`cd_remote_id`,`cd_last_sync`,`cd_task`) SELECT `cd_object`,`cd_deleted`,`cd_order`,`cd_remote_parent`,`cd_etag`,`cd_id`,`cd_calendar`,`cd_remote_id`,`cd_last_sync`,`cd_task` FROM `caldav_tasks` INNER JOIN `tasks` ON `tasks`.`_id` = `cd_task`")
db.execSQL("DROP TABLE `caldav_tasks`") connection.execSQL("DROP TABLE `caldav_tasks`")
db.execSQL("ALTER TABLE `_new_caldav_tasks` RENAME TO `caldav_tasks`") connection.execSQL("ALTER TABLE `_new_caldav_tasks` RENAME TO `caldav_tasks`")
db.execSQL("CREATE INDEX IF NOT EXISTS `index_caldav_tasks_cd_task` ON `caldav_tasks` (`cd_task`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `index_caldav_tasks_cd_task` ON `caldav_tasks` (`cd_task`)")
db.execSQL("CREATE TABLE IF NOT EXISTS `_new_geofences` (`geofence_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `place` TEXT, `arrival` INTEGER NOT NULL, `departure` INTEGER NOT NULL, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )") connection.execSQL("CREATE TABLE IF NOT EXISTS `_new_geofences` (`geofence_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `place` TEXT, `arrival` INTEGER NOT NULL, `departure` INTEGER NOT NULL, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
db.execSQL("INSERT INTO `_new_geofences` (`task`,`geofence_id`,`arrival`,`place`,`departure`) SELECT `task`,`geofence_id`,`arrival`,`place`,`departure` FROM `geofences` INNER JOIN `tasks` ON `tasks`.`_id` = `task`") connection.execSQL("INSERT INTO `_new_geofences` (`task`,`geofence_id`,`arrival`,`place`,`departure`) SELECT `task`,`geofence_id`,`arrival`,`place`,`departure` FROM `geofences` INNER JOIN `tasks` ON `tasks`.`_id` = `task`")
db.execSQL("DROP TABLE `geofences`") connection.execSQL("DROP TABLE `geofences`")
db.execSQL("ALTER TABLE `_new_geofences` RENAME TO `geofences`") connection.execSQL("ALTER TABLE `_new_geofences` RENAME TO `geofences`")
db.execSQL("CREATE INDEX IF NOT EXISTS `index_geofences_task` ON `geofences` (`task`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `index_geofences_task` ON `geofences` (`task`)")
db.execSQL("CREATE TABLE IF NOT EXISTS `_new_task_list_metadata` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `tag_uuid` TEXT, `filter` TEXT, `task_ids` TEXT)") connection.execSQL("CREATE TABLE IF NOT EXISTS `_new_task_list_metadata` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `tag_uuid` TEXT, `filter` TEXT, `task_ids` TEXT)")
db.execSQL("INSERT INTO `_new_task_list_metadata` (`filter`,`tag_uuid`,`_id`,`task_ids`) SELECT `filter`,`tag_uuid`,`_id`,`task_ids` FROM `task_list_metadata`") connection.execSQL("INSERT INTO `_new_task_list_metadata` (`filter`,`tag_uuid`,`_id`,`task_ids`) SELECT `filter`,`tag_uuid`,`_id`,`task_ids` FROM `task_list_metadata`")
db.execSQL("DROP TABLE `task_list_metadata`") connection.execSQL("DROP TABLE `task_list_metadata`")
db.execSQL("ALTER TABLE `_new_task_list_metadata` RENAME TO `task_list_metadata`") connection.execSQL("ALTER TABLE `_new_task_list_metadata` RENAME TO `task_list_metadata`")
db.execSQL("CREATE TABLE IF NOT EXISTS `_new_tasks` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `importance` INTEGER NOT NULL, `dueDate` INTEGER NOT NULL, `hideUntil` INTEGER NOT NULL, `created` INTEGER NOT NULL, `modified` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `notes` TEXT, `estimatedSeconds` INTEGER NOT NULL, `elapsedSeconds` INTEGER NOT NULL, `timerStart` INTEGER NOT NULL, `notificationFlags` INTEGER NOT NULL, `lastNotified` INTEGER NOT NULL, `recurrence` TEXT, `repeatUntil` INTEGER NOT NULL, `calendarUri` TEXT, `remoteId` TEXT, `collapsed` INTEGER NOT NULL, `parent` INTEGER NOT NULL)") connection.execSQL("CREATE TABLE IF NOT EXISTS `_new_tasks` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `importance` INTEGER NOT NULL, `dueDate` INTEGER NOT NULL, `hideUntil` INTEGER NOT NULL, `created` INTEGER NOT NULL, `modified` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `notes` TEXT, `estimatedSeconds` INTEGER NOT NULL, `elapsedSeconds` INTEGER NOT NULL, `timerStart` INTEGER NOT NULL, `notificationFlags` INTEGER NOT NULL, `lastNotified` INTEGER NOT NULL, `recurrence` TEXT, `repeatUntil` INTEGER NOT NULL, `calendarUri` TEXT, `remoteId` TEXT, `collapsed` INTEGER NOT NULL, `parent` INTEGER NOT NULL)")
db.execSQL("INSERT INTO `_new_tasks` (`parent`,`notes`,`timerStart`,`estimatedSeconds`,`importance`,`created`,`collapsed`,`dueDate`,`completed`,`repeatUntil`,`title`,`hideUntil`,`remoteId`,`recurrence`,`deleted`,`notificationFlags`,`calendarUri`,`modified`,`_id`,`lastNotified`,`elapsedSeconds`) SELECT `parent`,`notes`,`timerStart`,`estimatedSeconds`,`importance`,`created`,`collapsed`,`dueDate`,`completed`,`repeatUntil`,`title`,`hideUntil`,`remoteId`,`recurrence`,`deleted`,`notificationFlags`,`calendarUri`,`modified`,`_id`,`lastNotified`,`elapsedSeconds` FROM `tasks`") connection.execSQL("INSERT INTO `_new_tasks` (`parent`,`notes`,`timerStart`,`estimatedSeconds`,`importance`,`created`,`collapsed`,`dueDate`,`completed`,`repeatUntil`,`title`,`hideUntil`,`remoteId`,`recurrence`,`deleted`,`notificationFlags`,`calendarUri`,`modified`,`_id`,`lastNotified`,`elapsedSeconds`) SELECT `parent`,`notes`,`timerStart`,`estimatedSeconds`,`importance`,`created`,`collapsed`,`dueDate`,`completed`,`repeatUntil`,`title`,`hideUntil`,`remoteId`,`recurrence`,`deleted`,`notificationFlags`,`calendarUri`,`modified`,`_id`,`lastNotified`,`elapsedSeconds` FROM `tasks`")
db.execSQL("DROP TABLE `tasks`") connection.execSQL("DROP TABLE `tasks`")
db.execSQL("ALTER TABLE `_new_tasks` RENAME TO `tasks`") connection.execSQL("ALTER TABLE `_new_tasks` RENAME TO `tasks`")
db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `t_rid` ON `tasks` (`remoteId`)") connection.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `t_rid` ON `tasks` (`remoteId`)")
db.execSQL("CREATE INDEX IF NOT EXISTS `active_and_visible` ON `tasks` (`completed`, `deleted`, `hideUntil`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `active_and_visible` ON `tasks` (`completed`, `deleted`, `hideUntil`)")
} }
} }
private val MIGRATION_84_85 = object : Migration(84, 85) { private val MIGRATION_84_85 = object : Migration(84, 85) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `tasks` ADD COLUMN `repeat_from` INTEGER NOT NULL DEFAULT ${Task.RepeatFrom.DUE_DATE}") connection.execSQL("ALTER TABLE `tasks` ADD COLUMN `repeat_from` INTEGER NOT NULL DEFAULT ${Task.RepeatFrom.DUE_DATE}")
db connection
.query("SELECT `_id`, `repeatUntil`, `recurrence` FROM `tasks` WHERE `recurrence` IS NOT NULL AND `recurrence` != ''") .prepare("SELECT `_id`, `repeatUntil`, `recurrence` FROM `tasks` WHERE `recurrence` IS NOT NULL AND `recurrence` != ''")
.use { cursor -> .use { cursor ->
while (cursor.moveToNext()) { while (cursor.step()) {
val id = cursor.getLong("_id") val id = cursor.getLong(0)
val recurrence = val recurrence = if (!cursor.isNull(2)) {
cursor.getString("recurrence")?.takeIf { it.isNotBlank() } ?: continue cursor.getText(2).takeIf { it.isNotBlank() }
} else {
null
} ?: continue
val recur = newRecur(recurrence.withoutFrom()!!) val recur = newRecur(recurrence.withoutFrom()!!)
cursor.getLongOrNull("repeatUntil") if (!cursor.isNull(1)) {
?.takeIf { it > 0 } cursor.getLong(1).takeIf { it > 0 }?.let { recur.until = DateTime(it).toDate() }
?.let { recur.until = DateTime(it).toDate() } }
val repeatFrom = recurrence.repeatFrom() val repeatFrom = recurrence.repeatFrom()
db.execSQL("UPDATE `tasks` SET `repeat_from` = $repeatFrom, `recurrence` = '$recur' WHERE `_id` = $id") connection.execSQL("UPDATE `tasks` SET `repeat_from` = $repeatFrom, `recurrence` = '$recur' WHERE `_id` = $id")
} }
} }
db.execSQL("CREATE TABLE IF NOT EXISTS `_new_tasks` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `importance` INTEGER NOT NULL, `dueDate` INTEGER NOT NULL, `hideUntil` INTEGER NOT NULL, `created` INTEGER NOT NULL, `modified` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `notes` TEXT, `estimatedSeconds` INTEGER NOT NULL, `elapsedSeconds` INTEGER NOT NULL, `timerStart` INTEGER NOT NULL, `notificationFlags` INTEGER NOT NULL, `lastNotified` INTEGER NOT NULL, `recurrence` TEXT, `repeat_from` INTEGER NOT NULL DEFAULT 0, `calendarUri` TEXT, `remoteId` TEXT, `collapsed` INTEGER NOT NULL, `parent` INTEGER NOT NULL)") connection.execSQL("CREATE TABLE IF NOT EXISTS `_new_tasks` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `importance` INTEGER NOT NULL, `dueDate` INTEGER NOT NULL, `hideUntil` INTEGER NOT NULL, `created` INTEGER NOT NULL, `modified` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `notes` TEXT, `estimatedSeconds` INTEGER NOT NULL, `elapsedSeconds` INTEGER NOT NULL, `timerStart` INTEGER NOT NULL, `notificationFlags` INTEGER NOT NULL, `lastNotified` INTEGER NOT NULL, `recurrence` TEXT, `repeat_from` INTEGER NOT NULL DEFAULT 0, `calendarUri` TEXT, `remoteId` TEXT, `collapsed` INTEGER NOT NULL, `parent` INTEGER NOT NULL)")
db.execSQL("INSERT INTO `_new_tasks` (`parent`,`notes`,`timerStart`,`estimatedSeconds`,`importance`,`created`,`collapsed`,`dueDate`,`completed`,`title`,`hideUntil`,`remoteId`,`recurrence`,`deleted`,`notificationFlags`,`calendarUri`,`modified`,`_id`,`lastNotified`,`elapsedSeconds`,`repeat_from`) SELECT `parent`,`notes`,`timerStart`,`estimatedSeconds`,`importance`,`created`,`collapsed`,`dueDate`,`completed`,`title`,`hideUntil`,`remoteId`,`recurrence`,`deleted`,`notificationFlags`,`calendarUri`,`modified`,`_id`,`lastNotified`,`elapsedSeconds`,`repeat_from` FROM `tasks`") connection.execSQL("INSERT INTO `_new_tasks` (`parent`,`notes`,`timerStart`,`estimatedSeconds`,`importance`,`created`,`collapsed`,`dueDate`,`completed`,`title`,`hideUntil`,`remoteId`,`recurrence`,`deleted`,`notificationFlags`,`calendarUri`,`modified`,`_id`,`lastNotified`,`elapsedSeconds`,`repeat_from`) SELECT `parent`,`notes`,`timerStart`,`estimatedSeconds`,`importance`,`created`,`collapsed`,`dueDate`,`completed`,`title`,`hideUntil`,`remoteId`,`recurrence`,`deleted`,`notificationFlags`,`calendarUri`,`modified`,`_id`,`lastNotified`,`elapsedSeconds`,`repeat_from` FROM `tasks`")
db.execSQL("DROP TABLE `tasks`") connection.execSQL("DROP TABLE `tasks`")
db.execSQL("ALTER TABLE `_new_tasks` RENAME TO `tasks`") connection.execSQL("ALTER TABLE `_new_tasks` RENAME TO `tasks`")
db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `t_rid` ON `tasks` (`remoteId`)") connection.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `t_rid` ON `tasks` (`remoteId`)")
db.execSQL("CREATE INDEX IF NOT EXISTS `active_and_visible` ON `tasks` (`completed`, `deleted`, `hideUntil`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `active_and_visible` ON `tasks` (`completed`, `deleted`, `hideUntil`)")
} }
} }
private val MIGRATION_85_86 = object : Migration(85, 86) { private val MIGRATION_85_86 = object : Migration(85, 86) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("CREATE TABLE IF NOT EXISTS `attachment_file` (`file_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_uuid` TEXT NOT NULL, `filename` TEXT NOT NULL, `uri` TEXT NOT NULL)") connection.execSQL("CREATE TABLE IF NOT EXISTS `attachment_file` (`file_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_uuid` TEXT NOT NULL, `filename` TEXT NOT NULL, `uri` TEXT NOT NULL)")
db.execSQL("INSERT INTO `attachment_file` (`file_id`, `uri`,`filename`,`file_id`,`file_uuid`) SELECT `_id`, `path`,`name`,`_id`,`remoteId` FROM `task_attachments`") connection.execSQL("INSERT INTO `attachment_file` (`file_id`, `uri`,`filename`,`file_id`,`file_uuid`) SELECT `_id`, `path`,`name`,`_id`,`remoteId` FROM `task_attachments`")
db.execSQL("CREATE TABLE IF NOT EXISTS `attachment` (`attachment_id` INTEGER PRIMARY KEY AUTOINCREMENT, `task` INTEGER NOT NULL, `file` INTEGER NOT NULL, `file_uuid` TEXT NOT NULL, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`file`) REFERENCES `attachment_file`(`file_id`) ON UPDATE NO ACTION ON DELETE CASCADE)") connection.execSQL("CREATE TABLE IF NOT EXISTS `attachment` (`attachment_id` INTEGER PRIMARY KEY AUTOINCREMENT, `task` INTEGER NOT NULL, `file` INTEGER NOT NULL, `file_uuid` TEXT NOT NULL, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`file`) REFERENCES `attachment_file`(`file_id`) ON UPDATE NO ACTION ON DELETE CASCADE)")
db.execSQL("CREATE INDEX IF NOT EXISTS `index_attachment_task` ON `attachment` (`task`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `index_attachment_task` ON `attachment` (`task`)")
db.execSQL("CREATE INDEX IF NOT EXISTS `index_attachment_file` ON `attachment` (`file`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `index_attachment_file` ON `attachment` (`file`)")
db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_attachment_task_file` ON `attachment` (`task`, `file`)") connection.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_attachment_task_file` ON `attachment` (`task`, `file`)")
db.execSQL( connection.execSQL(
"INSERT INTO `attachment` (`task`, `file`, `file_uuid`) SELECT `tasks`.`_id`, `task_attachments`.`_id`, `task_attachments`.`remoteId` FROM `task_attachments` JOIN `tasks` ON `tasks`.`remoteId` = `task_attachments`.`task_id`" "INSERT INTO `attachment` (`task`, `file`, `file_uuid`) SELECT `tasks`.`_id`, `task_attachments`.`_id`, `task_attachments`.`remoteId` FROM `task_attachments` JOIN `tasks` ON `tasks`.`remoteId` = `task_attachments`.`task_id`"
) )
db.execSQL("DROP TABLE `task_attachments`") connection.execSQL("DROP TABLE `task_attachments`")
} }
} }
private val MIGRATION_86_87 = object : Migration(86, 87) { private val MIGRATION_86_87 = object : Migration(86, 87) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
db.execSQL("ALTER TABLE `tasks` ADD COLUMN `read_only` INTEGER NOT NULL DEFAULT 0") connection.execSQL("ALTER TABLE `tasks` ADD COLUMN `read_only` INTEGER NOT NULL DEFAULT 0")
db.execSQL("UPDATE `tasks` SET `read_only` = 1 WHERE `_id` IN (SELECT `cd_task` FROM `caldav_tasks` INNER JOIN `caldav_lists` ON `caldav_tasks`.`cd_calendar` = `caldav_lists`.`cdl_uuid` WHERE `cdl_access` = $ACCESS_READ_ONLY)") connection.execSQL("UPDATE `tasks` SET `read_only` = 1 WHERE `_id` IN (SELECT `cd_task` FROM `caldav_tasks` INNER JOIN `caldav_lists` ON `caldav_tasks`.`cd_calendar` = `caldav_lists`.`cdl_uuid` WHERE `cdl_access` = $ACCESS_READ_ONLY)")
} }
} }
private fun migration_87_88(context: Context) = object : Migration(87, 88) { private fun migration_87_88(context: Context) = object : Migration(87, 88) {
override fun migrate(db: SupportSQLiteDatabase) { override fun migrate(connection: SQLiteConnection) {
val prefs = Preferences(context) val prefs = Preferences(context)
val defaultList = prefs.getStringValue(R.string.p_default_list)?.split(":") val defaultList = prefs.getStringValue(R.string.p_default_list)?.split(":")
if ( if (
(defaultList?.size == 2) && (defaultList?.size == 2) &&
(defaultList[0].toIntOrNull()?.equals(DefaultFilterProvider.TYPE_GOOGLE_TASKS) == true) (defaultList[0].toIntOrNull()?.equals(DefaultFilterProvider.TYPE_GOOGLE_TASKS) == true)
) { ) {
db.query("SELECT `gtl_remote_id` FROM `google_task_lists` WHERE `gtl_id` = '${defaultList[1]}'").use { cursor -> connection.prepare("SELECT `gtl_remote_id` FROM `google_task_lists` WHERE `gtl_id` = '${defaultList[1]}'").use { cursor ->
if (cursor.moveToFirst()) { if (cursor.step()) {
cursor.getStringOrNull(0)?.let { uuid -> if (!cursor.isNull(0)) {
val uuid = cursor.getText(0)
prefs.setString(R.string.p_default_list, "${DefaultFilterProvider.TYPE_GOOGLE_TASKS}:$uuid") prefs.setString(R.string.p_default_list, "${DefaultFilterProvider.TYPE_GOOGLE_TASKS}:$uuid")
} }
} }
} }
} }
// migrate google task accounts and lists to caldav table // migrate google task accounts and lists to caldav table
db.execSQL("ALTER TABLE `caldav_lists` ADD COLUMN `cdl_last_sync` INTEGER NOT NULL DEFAULT 0") connection.execSQL("ALTER TABLE `caldav_lists` ADD COLUMN `cdl_last_sync` INTEGER NOT NULL DEFAULT 0")
db.execSQL("INSERT INTO `caldav_accounts` (`cda_account_type`, `cda_server_type`, `cda_uuid`, `cda_name`, `cda_username`, `cda_collapsed`) SELECT $TYPE_GOOGLE_TASKS, $SERVER_UNKNOWN, `gta_account`, `gta_account`, `gta_account`, `gta_collapsed` FROM `google_task_accounts`") connection.execSQL("INSERT INTO `caldav_accounts` (`cda_account_type`, `cda_server_type`, `cda_uuid`, `cda_name`, `cda_username`, `cda_collapsed`) SELECT $TYPE_GOOGLE_TASKS, $SERVER_UNKNOWN, `gta_account`, `gta_account`, `gta_account`, `gta_collapsed` FROM `google_task_accounts`")
db.execSQL("INSERT INTO `caldav_lists` (`cdl_account`, `cdl_uuid`, `cdl_name`, `cdl_color`, `cdl_icon`, `cdl_order`, `cdl_access`, `cdl_last_sync`) SELECT `gtl_account`, `gtl_remote_id`, `gtl_title`, `gtl_color`, `gtl_icon`, `gtl_remote_order`, $ACCESS_OWNER, `gtl_last_sync` FROM `google_task_lists`") connection.execSQL("INSERT INTO `caldav_lists` (`cdl_account`, `cdl_uuid`, `cdl_name`, `cdl_color`, `cdl_icon`, `cdl_order`, `cdl_access`, `cdl_last_sync`) SELECT `gtl_account`, `gtl_remote_id`, `gtl_title`, `gtl_color`, `gtl_icon`, `gtl_remote_order`, $ACCESS_OWNER, `gtl_last_sync` FROM `google_task_lists`")
db.execSQL("DROP TABLE `google_task_accounts`") connection.execSQL("DROP TABLE `google_task_accounts`")
db.execSQL("DROP TABLE `google_task_lists`") connection.execSQL("DROP TABLE `google_task_lists`")
// move cd_order to task table // move cd_order to task table
db.execSQL("ALTER TABLE `tasks` ADD COLUMN `order` INTEGER") connection.execSQL("ALTER TABLE `tasks` ADD COLUMN `order` INTEGER")
db.execSQL("ALTER TABLE `caldav_tasks` RENAME TO `caldav-temp`") connection.execSQL("ALTER TABLE `caldav_tasks` RENAME TO `caldav-temp`")
db.execSQL("CREATE TABLE IF NOT EXISTS `caldav_tasks` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_remote_parent` TEXT, `gt_moved` INTEGER NOT NULL, `gt_remote_order` INTEGER NOT NULL, FOREIGN KEY(`cd_task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE)") connection.execSQL("CREATE TABLE IF NOT EXISTS `caldav_tasks` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_remote_parent` TEXT, `gt_moved` INTEGER NOT NULL, `gt_remote_order` INTEGER NOT NULL, FOREIGN KEY(`cd_task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE)")
db.execSQL("DROP INDEX `index_caldav_tasks_cd_task`") connection.execSQL("DROP INDEX `index_caldav_tasks_cd_task`")
db.execSQL("CREATE INDEX IF NOT EXISTS `index_caldav_tasks_cd_task` ON `caldav_tasks` (`cd_task`)") connection.execSQL("CREATE INDEX IF NOT EXISTS `index_caldav_tasks_cd_task` ON `caldav_tasks` (`cd_task`)")
db.execSQL("INSERT INTO `caldav_tasks` (`cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, `gt_moved`, `gt_remote_order`) SELECT `cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, 0, 0 FROM `caldav-temp`") connection.execSQL("INSERT INTO `caldav_tasks` (`cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, `gt_moved`, `gt_remote_order`) SELECT `cd_id`, `cd_task`, `cd_calendar`, `cd_object`, `cd_remote_id`, `cd_etag`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, 0, 0 FROM `caldav-temp`")
db.execSQL("DROP TABLE `caldav-temp`") connection.execSQL("DROP TABLE `caldav-temp`")
db.execSQL("INSERT INTO `caldav_tasks` (`cd_task`, `cd_calendar`, `cd_remote_id`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, `gt_moved`, `gt_remote_order`) SELECT `gt_task`, `gt_list_id`, `gt_remote_id`, `gt_last_sync`, `gt_deleted`, `gt_remote_parent`, 0, 0 FROM google_tasks") connection.execSQL("INSERT INTO `caldav_tasks` (`cd_task`, `cd_calendar`, `cd_remote_id`, `cd_last_sync`, `cd_deleted`, `cd_remote_parent`, `gt_moved`, `gt_remote_order`) SELECT `gt_task`, `gt_list_id`, `gt_remote_id`, `gt_last_sync`, `gt_deleted`, `gt_remote_parent`, 0, 0 FROM google_tasks")
db.execSQL("DROP TABLE `google_tasks`") connection.execSQL("DROP TABLE `google_tasks`")
} }
} }
@ -672,7 +675,7 @@ object Migrations {
) )
private fun noop(from: Int, to: Int): Migration = object : Migration(from, to) { private fun noop(from: Int, to: Int): Migration = object : Migration(from, to) {
override fun migrate(db: SupportSQLiteDatabase) {} override fun migrate(connection: SQLiteConnection) {}
} }
fun String?.repeatFrom() = if (this?.contains("FROM=COMPLETION") == true) { fun String?.repeatFrom() = if (this?.contains("FROM=COMPLETION") == true) {

@ -18,7 +18,7 @@ import org.tasks.billing.BillingClientImpl
import org.tasks.billing.Inventory import org.tasks.billing.Inventory
import org.tasks.data.dao.AlarmDao import org.tasks.data.dao.AlarmDao
import org.tasks.data.dao.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.ContentProviderDao import org.tasks.data.dao.Astrid2ContentProviderDao
import org.tasks.data.dao.DeletionDao import org.tasks.data.dao.DeletionDao
import org.tasks.data.dao.FilterDao import org.tasks.data.dao.FilterDao
import org.tasks.data.dao.GoogleTaskDao import org.tasks.data.dao.GoogleTaskDao
@ -106,7 +106,7 @@ class ApplicationModule {
@Provides @Provides
@Singleton @Singleton
fun getContentProviderDao(db: Database): ContentProviderDao = db.contentProviderDao() fun getContentProviderDao(db: Database): Astrid2ContentProviderDao = db.contentProviderDao()
@Provides @Provides
@Singleton @Singleton

@ -2,7 +2,7 @@ package org.tasks.injection
import android.content.Context import android.content.Context
import androidx.room.Room import androidx.room.Room
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@ -37,8 +37,8 @@ internal class ProductionModule {
context = context, context = context,
name = databaseFile.absolutePath name = databaseFile.absolutePath
) )
.setDriver(BundledSQLiteDriver())
.addMigrations(*Migrations.migrations(context, fileStorage)) .addMigrations(*Migrations.migrations(context, fileStorage))
.openHelperFactory(FrameworkSQLiteOpenHelperFactory())
if (!BuildConfig.DEBUG || !preferences.getBoolean(R.string.p_crash_main_queries, false)) { if (!BuildConfig.DEBUG || !preferences.getBoolean(R.string.p_crash_main_queries, false)) {
builder.allowMainThreadQueries() builder.allowMainThreadQueries()
} }

@ -38,10 +38,12 @@ class TasksContentProvider : ContentProvider() {
URI_TODO_AGENDA -> { URI_TODO_AGENDA -> {
hilt.firebase.logEvent(R.string.event_todoagenda) hilt.firebase.logEvent(R.string.event_todoagenda)
hilt.contentProviderDao.rawQuery( hilt.contentProviderDao.rawQuery(
SupportSQLiteQueryBuilder SupportSQLiteQueryBuilder
.builder(TODO_AGENDA_TABLES) .builder(TODO_AGENDA_TABLES)
.selection(selection, selectionArgs) .selection(selection, selectionArgs)
.create()) .create()
.sql
)
} }
URI_TASKS -> hilt.contentProviderDao.getTasks() URI_TASKS -> hilt.contentProviderDao.getTasks()
URI_LISTS -> hilt.contentProviderDao.getLists() URI_LISTS -> hilt.contentProviderDao.getLists()

@ -0,0 +1,20 @@
package org.tasks.data
import androidx.room.RoomDatabase
import androidx.room.TransactionScope
import androidx.room.Transactor
import androidx.room.useReaderConnection
import androidx.room.useWriterConnection
import androidx.sqlite.SQLiteStatement
suspend fun <T> RoomDatabase.withTransaction(block: suspend TransactionScope<T>.() -> T): T =
useWriterConnection { transactor ->
transactor.withTransaction(Transactor.SQLiteTransactionType.DEFERRED) {
block()
}
}
suspend fun <T> RoomDatabase.rawQuery(query: String, block: (SQLiteStatement) -> T): T =
useReaderConnection { transactor -> transactor.usePrepared(query) { block(it) } }
suspend fun RoomDatabase.inTransaction(): Boolean = useReaderConnection { it.inTransaction() }

@ -0,0 +1,173 @@
package org.tasks.data
import androidx.room.util.getColumnIndex
import androidx.room.util.getColumnIndexOrThrow
import androidx.sqlite.SQLiteStatement
import org.tasks.data.entity.CaldavTask
import org.tasks.data.entity.Geofence
import org.tasks.data.entity.Place
import org.tasks.data.entity.Task
/*
room kmp doesn't support raw query yet 😢
https://issuetracker.google.com/issues/330586815
*/
fun SQLiteStatement.getTasks(): List<TaskContainer> {
val result = mutableListOf<TaskContainer>()
val _cursorIndexOfAccountType: Int = getColumnIndex(this, "accountType")
val _cursorIndexOfParentComplete: Int = getColumnIndex(this, "parentComplete")
val _cursorIndexOfTagsString: Int = getColumnIndex(this, "tags")
val _cursorIndexOfChildren: Int = getColumnIndex(this, "children")
val _cursorIndexOfSortGroup: Int = getColumnIndex(this, "sortGroup")
val _cursorIndexOfPrimarySort: Int = getColumnIndex(this, "primarySort")
val _cursorIndexOfSecondarySort: Int = getColumnIndex(this, "secondarySort")
val _cursorIndexOfIndent: Int = getColumnIndex(this, "indent")
val _cursorIndexOfId: Int = getColumnIndexOrThrow(this, "_id")
val _cursorIndexOfTitle: Int = getColumnIndexOrThrow(this, "title")
val _cursorIndexOfPriority: Int = getColumnIndexOrThrow(this, "importance")
val _cursorIndexOfDueDate: Int = getColumnIndexOrThrow(this, "dueDate")
val _cursorIndexOfHideUntil: Int = getColumnIndexOrThrow(this, "hideUntil")
val _cursorIndexOfCreationDate: Int = getColumnIndexOrThrow(this, "created")
val _cursorIndexOfModificationDate: Int = getColumnIndexOrThrow(this, "modified")
val _cursorIndexOfCompletionDate: Int = getColumnIndexOrThrow(this, "completed")
val _cursorIndexOfDeletionDate: Int = getColumnIndexOrThrow(this, "deleted")
val _cursorIndexOfNotes: Int = getColumnIndexOrThrow(this, "notes")
val _cursorIndexOfEstimatedSeconds: Int = getColumnIndexOrThrow(this, "estimatedSeconds")
val _cursorIndexOfElapsedSeconds: Int = getColumnIndexOrThrow(this, "elapsedSeconds")
val _cursorIndexOfTimerStart: Int = getColumnIndexOrThrow(this, "timerStart")
val _cursorIndexOfRingFlags: Int = getColumnIndexOrThrow(this, "notificationFlags")
val _cursorIndexOfReminderLast: Int = getColumnIndexOrThrow(this, "lastNotified")
val _cursorIndexOfRecurrence: Int = getColumnIndexOrThrow(this, "recurrence")
val _cursorIndexOfRepeatFrom: Int = getColumnIndexOrThrow(this, "repeat_from")
val _cursorIndexOfCalendarURI: Int = getColumnIndexOrThrow(this, "calendarUri")
val _cursorIndexOfRemoteId: Int = getColumnIndexOrThrow(this, "remoteId")
val _cursorIndexOfIsCollapsed: Int = getColumnIndexOrThrow(this, "collapsed")
val _cursorIndexOfParent: Int = getColumnIndexOrThrow(this, "parent")
val _cursorIndexOfOrder: Int = getColumnIndexOrThrow(this, "order")
val _cursorIndexOfReadOnly: Int = getColumnIndexOrThrow(this, "read_only")
val _cursorIndexOfId_1: Int = getColumnIndex(this, "cd_id")
val _cursorIndexOfTask: Int = getColumnIndex(this, "cd_task")
val _cursorIndexOfCalendar: Int = getColumnIndex(this, "cd_calendar")
val _cursorIndexOfRemoteId_1: Int = getColumnIndex(this, "cd_remote_id")
val _cursorIndexOfObj: Int = getColumnIndex(this, "cd_object")
val _cursorIndexOfEtag: Int = getColumnIndex(this, "cd_etag")
val _cursorIndexOfLastSync: Int = getColumnIndex(this, "cd_last_sync")
val _cursorIndexOfDeleted: Int = getColumnIndex(this, "cd_deleted")
val _cursorIndexOfRemoteParent: Int = getColumnIndex(this, "cd_remote_parent")
val _cursorIndexOfIsMoved: Int = getColumnIndex(this, "gt_moved")
val _cursorIndexOfRemoteOrder: Int = getColumnIndex(this, "gt_remote_order")
val _cursorIndexOfId_2: Int = getColumnIndex(this, "geofence_id")
val _cursorIndexOfTask_1: Int = getColumnIndex(this, "task")
val _cursorIndexOfPlace: Int = getColumnIndex(this, "place")
val _cursorIndexOfIsArrival: Int = getColumnIndex(this, "arrival")
val _cursorIndexOfIsDeparture: Int = getColumnIndex(this, "departure")
val _cursorIndexOfId_3: Int = getColumnIndex(this, "place_id")
val _cursorIndexOfUid: Int = getColumnIndex(this, "uid")
val _cursorIndexOfName: Int = getColumnIndex(this, "name")
val _cursorIndexOfAddress: Int = getColumnIndex(this, "address")
val _cursorIndexOfPhone: Int = getColumnIndex(this, "phone")
val _cursorIndexOfUrl: Int = getColumnIndex(this, "url")
val _cursorIndexOfLatitude: Int = getColumnIndex(this, "latitude")
val _cursorIndexOfLongitude: Int = getColumnIndex(this, "longitude")
val _cursorIndexOfColor: Int = getColumnIndex(this, "place_color")
val _cursorIndexOfIcon: Int = getColumnIndex(this, "place_icon")
val _cursorIndexOfOrder_1: Int = getColumnIndex(this, "place_order")
val _cursorIndexOfRadius: Int = getColumnIndex(this, "radius")
while (step()) {
val task = Task(
id = getLong(_cursorIndexOfId),
title = getTextOrNull(_cursorIndexOfTitle),
priority = getInt(_cursorIndexOfPriority),
dueDate = getLong(_cursorIndexOfDueDate),
hideUntil = getLong(_cursorIndexOfHideUntil),
creationDate = getLong(_cursorIndexOfCreationDate),
modificationDate = getLong(_cursorIndexOfModificationDate),
completionDate = getLong(_cursorIndexOfCompletionDate),
deletionDate = getLong(_cursorIndexOfDeletionDate),
notes = getTextOrNull(_cursorIndexOfNotes),
estimatedSeconds = getInt(_cursorIndexOfEstimatedSeconds),
elapsedSeconds = getInt(_cursorIndexOfElapsedSeconds),
timerStart = getLong(_cursorIndexOfTimerStart),
ringFlags = getInt(_cursorIndexOfRingFlags),
reminderLast = getLong(_cursorIndexOfReminderLast),
recurrence = getTextOrNull(_cursorIndexOfRecurrence),
repeatFrom = getInt(_cursorIndexOfRepeatFrom),
calendarURI = getTextOrNull(_cursorIndexOfCalendarURI),
remoteId = getTextOrNull(_cursorIndexOfRemoteId),
isCollapsed = getBoolean(_cursorIndexOfIsCollapsed),
parent = getLong(_cursorIndexOfParent),
order = getLongOrNull(_cursorIndexOfOrder),
readOnly = getBoolean(_cursorIndexOfReadOnly),
)
val caldavTask = getLongOrNull(_cursorIndexOfId_1)?.takeIf { it > 0 }?.let {
CaldavTask(
id = it,
task = getLong(_cursorIndexOfTask),
calendar = getTextOrNull(_cursorIndexOfCalendar),
remoteId = getTextOrNull(_cursorIndexOfRemoteId_1),
obj = getTextOrNull(_cursorIndexOfObj),
etag = getTextOrNull(_cursorIndexOfEtag),
lastSync = getLong(_cursorIndexOfLastSync),
deleted = getLong(_cursorIndexOfDeleted),
remoteParent = getTextOrNull(_cursorIndexOfRemoteParent),
isMoved = getBoolean(_cursorIndexOfIsMoved),
remoteOrder = getLong(_cursorIndexOfRemoteOrder),
)
}
val accountType = getInt(_cursorIndexOfAccountType)
val geofence = getLongOrNull(_cursorIndexOfId_2)?.takeIf { it > 0 }?.let {
Geofence(
id = it,
task = getLong(_cursorIndexOfTask_1),
place = getTextOrNull(_cursorIndexOfPlace),
isArrival = getBoolean(_cursorIndexOfIsArrival),
isDeparture = getBoolean(_cursorIndexOfIsDeparture),
)
}
val place = getLongOrNull(_cursorIndexOfId_3)?.takeIf { it > 0 }?.let {
Place(
id = it,
uid = getTextOrNull(_cursorIndexOfUid),
name = getTextOrNull(_cursorIndexOfName),
address = getTextOrNull(_cursorIndexOfAddress),
phone = getTextOrNull(_cursorIndexOfPhone),
url = getTextOrNull(_cursorIndexOfUrl),
latitude = getDouble(_cursorIndexOfLatitude),
longitude = getDouble(_cursorIndexOfLongitude),
color = getInt(_cursorIndexOfColor),
icon = getInt(_cursorIndexOfIcon),
order = getInt(_cursorIndexOfOrder_1),
radius = getInt(_cursorIndexOfRadius),
)
}
result.add(
TaskContainer(
task = task,
caldavTask = caldavTask,
accountType = accountType,
location = if (geofence != null && place != null) {
Location(geofence, place)
} else {
null
},
tagsString = getTextOrNull(_cursorIndexOfTagsString),
indent = getIntOrNull(_cursorIndexOfIndent) ?: 0,
sortGroup = getLongOrNull(_cursorIndexOfSortGroup),
children = getIntOrNull(_cursorIndexOfChildren) ?: 0,
primarySort = getLongOrNull(_cursorIndexOfPrimarySort) ?: 0,
secondarySort = getLongOrNull(_cursorIndexOfSecondarySort) ?: 0,
parentComplete = getBoolean(_cursorIndexOfParentComplete),
)
)
}
return result
}
private fun SQLiteStatement.getTextOrNull(index: Int): String? =
if (index == -1 || isNull(index)) null else this.getText(index)
private fun SQLiteStatement.getLongOrNull(index: Int): Long? =
if (index == -1 || isNull(index)) null else this.getLong(index)
private fun SQLiteStatement.getIntOrNull(index: Int): Int? =
if (index == -1 || isNull(index)) null else this.getInt(index)

@ -1,15 +1,12 @@
package org.tasks.data.dao package org.tasks.data.dao
import android.database.Cursor
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import androidx.room.RawQuery
import androidx.sqlite.db.SupportSQLiteQuery
import org.tasks.data.entity.TagData import org.tasks.data.entity.TagData
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
@Dao @Dao
interface ContentProviderDao { interface Astrid2ContentProviderDao {
@Query("SELECT name FROM tags WHERE task = :taskId ORDER BY UPPER(name) ASC") @Query("SELECT name FROM tags WHERE task = :taskId ORDER BY UPPER(name) ASC")
suspend fun getTagNames(taskId: Long): List<String> suspend fun getTagNames(taskId: Long): List<String>
@ -30,16 +27,4 @@ interface ContentProviderDao {
@Query("SELECT * FROM tagdata WHERE name IS NOT NULL AND name != '' ORDER BY UPPER(name) ASC") @Query("SELECT * FROM tagdata WHERE name IS NOT NULL AND name != '' ORDER BY UPPER(name) ASC")
suspend fun tagDataOrderedByName(): List<TagData> suspend fun tagDataOrderedByName(): List<TagData>
@Query("SELECT * FROM tasks")
fun getTasks(): Cursor
@Query("""
SELECT caldav_lists.*, caldav_accounts.cda_name
FROM caldav_lists
INNER JOIN caldav_accounts ON cdl_account = cda_uuid""")
fun getLists(): Cursor
@RawQuery
fun rawQuery(query: SupportSQLiteQuery): Cursor
} }

@ -3,10 +3,8 @@ package org.tasks.data.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import androidx.room.RawQuery
import androidx.room.Update import androidx.room.Update
import androidx.room.withTransaction import androidx.room.execSQL
import androidx.sqlite.db.SimpleSQLiteQuery
import co.touchlab.kermit.Logger import co.touchlab.kermit.Logger
import org.tasks.data.BuildConfig import org.tasks.data.BuildConfig
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
@ -16,8 +14,11 @@ 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.Alarm import org.tasks.data.entity.Alarm
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.getTasks
import org.tasks.data.rawQuery
import org.tasks.data.sql.Criterion import org.tasks.data.sql.Criterion
import org.tasks.data.sql.Functions import org.tasks.data.sql.Functions
import org.tasks.data.withTransaction
import org.tasks.time.DateTimeUtils2 import org.tasks.time.DateTimeUtils2
private const val MAX_TIME = 9999999999999 private const val MAX_TIME = 9999999999999
@ -109,28 +110,25 @@ FROM (
abstract suspend fun clearCompletedCalendarEvents(): Int abstract suspend fun clearCompletedCalendarEvents(): Int
open suspend fun fetchTasks(callback: suspend () -> List<String>): List<TaskContainer> = open suspend fun fetchTasks(callback: suspend () -> List<String>): List<TaskContainer> =
database.withTransaction { database.withTransaction {
val start = if (BuildConfig.DEBUG) DateTimeUtils2.currentTimeMillis() else 0 val start = if (BuildConfig.DEBUG) DateTimeUtils2.currentTimeMillis() else 0
val queries = callback() val queries = callback()
val last = queries.size - 1 val last = queries.size - 1
for (i in 0 until last) { for (i in 0 until last) {
query(SimpleSQLiteQuery(queries[i])) execSQL(queries[i])
}
val result = fetchTasks(SimpleSQLiteQuery(queries[last]))
Logger.v("TaskDao") {
"${DateTimeUtils2.currentTimeMillis() - start}ms: ${queries.joinToString(";\n")}"
}
result
} }
val result = usePrepared(queries[last]) { it.getTasks() }
Logger.v("TaskDao") {
"${DateTimeUtils2.currentTimeMillis() - start}ms: ${queries.joinToString(";\n")}"
}
result
}
@RawQuery suspend fun fetchTasks(query: String): List<TaskContainer> =
internal abstract suspend fun query(query: SimpleSQLiteQuery): Int database.rawQuery(query) { it.getTasks() }
@RawQuery
abstract suspend fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer>
@RawQuery suspend fun countRaw(query: String): Int =
abstract suspend fun countRaw(query: SimpleSQLiteQuery): Int database.rawQuery(query) { if (it.step()) it.getInt(0) else 0 }
suspend fun touch(ids: List<Long>, now: Long = DateTimeUtils2.currentTimeMillis()) = suspend fun touch(ids: List<Long>, now: Long = DateTimeUtils2.currentTimeMillis()) =
ids.eachChunk { internalTouch(it, now) } ids.eachChunk { internalTouch(it, now) }

@ -11,7 +11,7 @@ import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.dao.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.entity.CaldavTask import org.tasks.data.entity.CaldavTask
import org.tasks.data.dao.ContentProviderDao import org.tasks.data.dao.Astrid2ContentProviderDao
import org.tasks.data.dao.DeletionDao import org.tasks.data.dao.DeletionDao
import org.tasks.data.entity.Filter import org.tasks.data.entity.Filter
import org.tasks.data.dao.FilterDao import org.tasks.data.dao.FilterDao
@ -78,7 +78,7 @@ abstract class Database : RoomDatabase() {
abstract fun taskDao(): TaskDao abstract fun taskDao(): TaskDao
abstract fun caldavDao(): CaldavDao abstract fun caldavDao(): CaldavDao
abstract fun deletionDao(): DeletionDao abstract fun deletionDao(): DeletionDao
abstract fun contentProviderDao(): ContentProviderDao abstract fun contentProviderDao(): Astrid2ContentProviderDao
abstract fun upgraderDao(): UpgraderDao abstract fun upgraderDao(): UpgraderDao
abstract fun principalDao(): PrincipalDao abstract fun principalDao(): PrincipalDao

@ -234,13 +234,15 @@
+| | | \--- androidx.sqlite:sqlite-android:2.5.0-alpha02 +| | | \--- androidx.sqlite:sqlite-android:2.5.0-alpha02
+| | | +--- androidx.annotation:annotation:1.8.0 (*) +| | | +--- androidx.annotation:annotation:1.8.0 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) +| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| | | \--- androidx.sqlite:sqlite-framework:2.5.0-alpha02 (c) +| | | +--- androidx.sqlite:sqlite-framework:2.5.0-alpha02 (c)
+| | | \--- androidx.sqlite:sqlite-bundled:2.5.0-alpha02 (c)
+| | +--- androidx.sqlite:sqlite-framework:2.5.0-alpha02 +| | +--- androidx.sqlite:sqlite-framework:2.5.0-alpha02
+| | | \--- androidx.sqlite:sqlite-framework-android:2.5.0-alpha02 +| | | \--- androidx.sqlite:sqlite-framework-android:2.5.0-alpha02
+| | | +--- androidx.annotation:annotation:1.8.0 (*) +| | | +--- androidx.annotation:annotation:1.8.0 (*)
+| | | +--- androidx.sqlite:sqlite:2.5.0-alpha02 (*) +| | | +--- androidx.sqlite:sqlite:2.5.0-alpha02 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) +| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| | | \--- androidx.sqlite:sqlite:2.5.0-alpha02 (c) +| | | +--- androidx.sqlite:sqlite:2.5.0-alpha02 (c)
+| | | \--- androidx.sqlite:sqlite-bundled:2.5.0-alpha02 (c)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) +| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| | +--- org.jetbrains.kotlinx:atomicfu:0.17.0 +| | +--- org.jetbrains.kotlinx:atomicfu:0.17.0
+| | | \--- org.jetbrains.kotlinx:atomicfu-jvm:0.17.0 +| | | \--- org.jetbrains.kotlinx:atomicfu-jvm:0.17.0
@ -603,6 +605,12 @@
++--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.0 (*) ++--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.0 (*)
++--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0 (*) ++--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0 (*)
++--- androidx.room:room-runtime:2.7.0-alpha02 (*) ++--- androidx.room:room-runtime:2.7.0-alpha02 (*)
++--- androidx.sqlite:sqlite-bundled:2.5.0-alpha02
+| \--- androidx.sqlite:sqlite-bundled-android:2.5.0-alpha02
+| +--- androidx.sqlite:sqlite:2.5.0-alpha02 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| +--- androidx.sqlite:sqlite:2.5.0-alpha02 (c)
+| \--- androidx.sqlite:sqlite-framework:2.5.0-alpha02 (c)
++--- androidx.appcompat:appcompat:1.6.1 (*) ++--- androidx.appcompat:appcompat:1.6.1 (*)
++--- io.noties.markwon:core:4.6.2 ++--- io.noties.markwon:core:4.6.2
+| +--- androidx.annotation:annotation:1.1.0 -> 1.8.0 (*) +| +--- androidx.annotation:annotation:1.1.0 -> 1.8.0 (*)

@ -594,13 +594,15 @@
+| | | \--- androidx.sqlite:sqlite-android:2.5.0-alpha02 +| | | \--- androidx.sqlite:sqlite-android:2.5.0-alpha02
+| | | +--- androidx.annotation:annotation:1.8.0 (*) +| | | +--- androidx.annotation:annotation:1.8.0 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) +| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| | | \--- androidx.sqlite:sqlite-framework:2.5.0-alpha02 (c) +| | | +--- androidx.sqlite:sqlite-framework:2.5.0-alpha02 (c)
+| | | \--- androidx.sqlite:sqlite-bundled:2.5.0-alpha02 (c)
+| | +--- androidx.sqlite:sqlite-framework:2.5.0-alpha02 +| | +--- androidx.sqlite:sqlite-framework:2.5.0-alpha02
+| | | \--- androidx.sqlite:sqlite-framework-android:2.5.0-alpha02 +| | | \--- androidx.sqlite:sqlite-framework-android:2.5.0-alpha02
+| | | +--- androidx.annotation:annotation:1.8.0 (*) +| | | +--- androidx.annotation:annotation:1.8.0 (*)
+| | | +--- androidx.sqlite:sqlite:2.5.0-alpha02 (*) +| | | +--- androidx.sqlite:sqlite:2.5.0-alpha02 (*)
+| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) +| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| | | \--- androidx.sqlite:sqlite:2.5.0-alpha02 (c) +| | | +--- androidx.sqlite:sqlite:2.5.0-alpha02 (c)
+| | | \--- androidx.sqlite:sqlite-bundled:2.5.0-alpha02 (c)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*) +| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| | +--- org.jetbrains.kotlinx:atomicfu:0.17.0 +| | +--- org.jetbrains.kotlinx:atomicfu:0.17.0
+| | | \--- org.jetbrains.kotlinx:atomicfu-jvm:0.17.0 +| | | \--- org.jetbrains.kotlinx:atomicfu-jvm:0.17.0
@ -832,6 +834,12 @@
++--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.0 (*) ++--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.0 (*)
++--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0 (*) ++--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0 (*)
++--- androidx.room:room-runtime:2.7.0-alpha02 (*) ++--- androidx.room:room-runtime:2.7.0-alpha02 (*)
++--- androidx.sqlite:sqlite-bundled:2.5.0-alpha02
+| \--- androidx.sqlite:sqlite-bundled-android:2.5.0-alpha02
+| +--- androidx.sqlite:sqlite:2.5.0-alpha02 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.0 (*)
+| +--- androidx.sqlite:sqlite:2.5.0-alpha02 (c)
+| \--- androidx.sqlite:sqlite-framework:2.5.0-alpha02 (c)
++--- androidx.appcompat:appcompat:1.6.1 (*) ++--- androidx.appcompat:appcompat:1.6.1 (*)
++--- io.noties.markwon:core:4.6.2 ++--- io.noties.markwon:core:4.6.2
+| +--- androidx.annotation:annotation:1.1.0 -> 1.8.0 (*) +| +--- androidx.annotation:annotation:1.1.0 -> 1.8.0 (*)

@ -83,6 +83,7 @@ androidx-preference = { module = "androidx.preference:preference", version.ref =
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" } androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
androidx-room = { module = "androidx.room:room-runtime", version.ref = "room" } androidx-room = { module = "androidx.room:room-runtime", version.ref = "room" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room"} androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room"}
androidx-sqlite = { module = "androidx.sqlite:sqlite-bundled", version = "2.5.0-alpha02" }
androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" } androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" }
androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" } androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" }
androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test" } androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test" }

Loading…
Cancel
Save