Fix db crash on big projects
This commit is contained in:
@@ -238,13 +238,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private fun getProjectsCustom(selection: String, selectionArgs: Array<String>, orderBy: String?, db: SQLiteDatabase): List<DBProject> {
|
private fun getProjectsCustom(selection: String, selectionArgs: Array<String>, orderBy: String?, db: SQLiteDatabase): List<DBProject> {
|
||||||
val cursor = db.query(table_projects, columnsProjects, selection, selectionArgs, null, null, orderBy)
|
return queryAll(db, table_projects, columnsProjects, selection, selectionArgs, orderBy, ::getProjectFromCursor)
|
||||||
val projects: MutableList<DBProject> = ArrayList()
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
projects.add(getProjectFromCursor(cursor))
|
|
||||||
}
|
|
||||||
cursor.close()
|
|
||||||
return projects
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("Range")
|
@SuppressLint("Range")
|
||||||
@@ -305,11 +299,17 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
|
|
||||||
fun deleteProject(id: Long) {
|
fun deleteProject(id: Long) {
|
||||||
val db = writableDatabase
|
val db = writableDatabase
|
||||||
for (b in getBillsOfProject(id)) {
|
val projectId = arrayOf(id.toString())
|
||||||
deleteBill(b.id)
|
inTransaction {
|
||||||
|
db.delete(
|
||||||
|
table_billowers,
|
||||||
|
"$key_billId IN (SELECT $key_id FROM $table_bills WHERE $key_projectid = ?)",
|
||||||
|
projectId
|
||||||
|
)
|
||||||
|
db.delete(table_bills, "$key_projectid = ?", projectId)
|
||||||
|
db.delete(table_members, "$key_projectid = ?", projectId)
|
||||||
|
db.delete(table_projects, "$key_id = ?", projectId)
|
||||||
}
|
}
|
||||||
db.delete(table_members, "$key_projectid = ?", arrayOf(id.toString()))
|
|
||||||
db.delete(table_projects, "$key_id = ?", arrayOf(id.toString()))
|
|
||||||
SecureStorage.removePasswordSync(context, "ProjectPassword_$id")
|
SecureStorage.removePasswordSync(context, "ProjectPassword_$id")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,14 +413,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private fun getMembersCustom(selection: String, selectionArgs: Array<String>, orderBy: String?): List<DBMember> {
|
private fun getMembersCustom(selection: String, selectionArgs: Array<String>, orderBy: String?): List<DBMember> {
|
||||||
val db = readableDatabase
|
return queryAll(readableDatabase, table_members, columnsMembers, selection, selectionArgs, orderBy, ::getMemberFromCursor)
|
||||||
val cursor = db.query(table_members, columnsMembers, selection, selectionArgs, null, null, orderBy)
|
|
||||||
val members: MutableList<DBMember> = ArrayList()
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
members.add(getMemberFromCursor(cursor))
|
|
||||||
}
|
|
||||||
cursor.close()
|
|
||||||
return members
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("Range")
|
@SuppressLint("Range")
|
||||||
@@ -644,29 +637,20 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
@WorkerThread
|
@WorkerThread
|
||||||
private fun getBillsCustom(selection: String, selectionArgs: Array<String>, orderBy: String?): List<DBBill> {
|
private fun getBillsCustom(selection: String, selectionArgs: Array<String>, orderBy: String?): List<DBBill> {
|
||||||
val db = readableDatabase
|
val db = readableDatabase
|
||||||
val cursor = db.query(table_bills, columnsBills, selection, selectionArgs, null, null, orderBy)
|
return db.transactionally {
|
||||||
val bills: MutableList<DBBill> = ArrayList()
|
val bills = queryAll(db, table_bills, columnsBills, selection, selectionArgs, orderBy, ::getBillFromCursor)
|
||||||
while (cursor.moveToNext()) {
|
if (bills.isNotEmpty()) {
|
||||||
bills.add(getBillFromCursor(cursor))
|
val owersByBill = queryAll(
|
||||||
}
|
db, table_billowers, columnsBillowers,
|
||||||
cursor.close()
|
|
||||||
if (bills.isEmpty()) return bills
|
|
||||||
|
|
||||||
// Every ower of the matched bills in one query, keyed back to its bill.
|
|
||||||
//
|
|
||||||
// Fetching them per bill meant a project with thousands of bills issued thousands of
|
|
||||||
// queries on every list refresh - and that refresh runs on resume, on switching project,
|
|
||||||
// and after each sync. The bill ids are re-selected as a subquery rather than bound as
|
|
||||||
// arguments, which would blow past SQLite's limit on bound variables for a large project.
|
|
||||||
val owersByBill = getBillOwersCustom(
|
|
||||||
"$key_billId IN (SELECT $key_id FROM $table_bills WHERE $selection)",
|
"$key_billId IN (SELECT $key_id FROM $table_bills WHERE $selection)",
|
||||||
selectionArgs,
|
selectionArgs, null, ::getBillOwerFromCursor
|
||||||
null
|
|
||||||
).groupBy { it.billId }
|
).groupBy { it.billId }
|
||||||
for (bill in bills) {
|
for (bill in bills) {
|
||||||
bill.billOwers = owersByBill[bill.id].orEmpty()
|
bill.billOwers = owersByBill[bill.id].orEmpty()
|
||||||
}
|
}
|
||||||
return bills
|
}
|
||||||
|
bills
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("Range")
|
@SuppressLint("Range")
|
||||||
@@ -714,14 +698,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private fun getBillOwersCustom(selection: String, selectionArgs: Array<String>, orderBy: String?): List<DBBillOwer> {
|
private fun getBillOwersCustom(selection: String, selectionArgs: Array<String>, orderBy: String?): List<DBBillOwer> {
|
||||||
val db = readableDatabase
|
return queryAll(readableDatabase, table_billowers, columnsBillowers, selection, selectionArgs, orderBy, ::getBillOwerFromCursor)
|
||||||
val cursor = db.query(table_billowers, columnsBillowers, selection, selectionArgs, null, null, orderBy)
|
|
||||||
val billOwers: MutableList<DBBillOwer> = ArrayList()
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
billOwers.add(getBillOwerFromCursor(cursor))
|
|
||||||
}
|
|
||||||
cursor.close()
|
|
||||||
return billOwers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("Range")
|
@SuppressLint("Range")
|
||||||
@@ -788,14 +765,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private fun getCategoriesCustom(selection: String, selectionArgs: Array<String>, orderBy: String?): List<DBCategory> {
|
private fun getCategoriesCustom(selection: String, selectionArgs: Array<String>, orderBy: String?): List<DBCategory> {
|
||||||
val db = readableDatabase
|
return queryAll(readableDatabase, table_categories, columnsCategories, selection, selectionArgs, orderBy, ::getCategoryFromCursor)
|
||||||
val cursor = db.query(table_categories, columnsCategories, selection, selectionArgs, null, null, orderBy)
|
|
||||||
val categories: MutableList<DBCategory> = ArrayList()
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
categories.add(getCategoryFromCursor(cursor))
|
|
||||||
}
|
|
||||||
cursor.close()
|
|
||||||
return categories
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("Range")
|
@SuppressLint("Range")
|
||||||
@@ -904,13 +874,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private fun getPaymentModesCustom(selection: String, selectionArgs: Array<String>, orderBy: String?, db: SQLiteDatabase): List<DBPaymentMode> {
|
private fun getPaymentModesCustom(selection: String, selectionArgs: Array<String>, orderBy: String?, db: SQLiteDatabase): List<DBPaymentMode> {
|
||||||
val cursor = db.query(table_payment_modes, columnsPaymentModes, selection, selectionArgs, null, null, orderBy)
|
return queryAll(db, table_payment_modes, columnsPaymentModes, selection, selectionArgs, orderBy, ::getPaymentModeFromCursor)
|
||||||
val paymentModes: MutableList<DBPaymentMode> = ArrayList()
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
paymentModes.add(getPaymentModeFromCursor(cursor))
|
|
||||||
}
|
|
||||||
cursor.close()
|
|
||||||
return paymentModes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("Range")
|
@SuppressLint("Range")
|
||||||
@@ -1020,13 +984,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private fun getCurrenciesCustom(selection: String, selectionArgs: Array<String>, orderBy: String?, db: SQLiteDatabase): List<DBCurrency> {
|
private fun getCurrenciesCustom(selection: String, selectionArgs: Array<String>, orderBy: String?, db: SQLiteDatabase): List<DBCurrency> {
|
||||||
val cursor = db.query(table_currencies, columnsCurrencies, selection, selectionArgs, null, null, orderBy)
|
return queryAll(db, table_currencies, columnsCurrencies, selection, selectionArgs, orderBy, ::getCurrencyFromCursor)
|
||||||
val currencies: MutableList<DBCurrency> = ArrayList()
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
currencies.add(getCurrencyFromCursor(cursor))
|
|
||||||
}
|
|
||||||
cursor.close()
|
|
||||||
return currencies
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("Range")
|
@SuppressLint("Range")
|
||||||
@@ -1074,6 +1032,36 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
|
|
||||||
// --- Common Helpers ---
|
// --- Common Helpers ---
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private fun <T> queryAll(
|
||||||
|
db: SQLiteDatabase,
|
||||||
|
table: String,
|
||||||
|
columns: Array<String>,
|
||||||
|
selection: String?,
|
||||||
|
selectionArgs: Array<String>?,
|
||||||
|
orderBy: String?,
|
||||||
|
fromCursor: (Cursor) -> T
|
||||||
|
): List<T> = db.transactionally {
|
||||||
|
val items: MutableList<T> = ArrayList()
|
||||||
|
db.query(table, columns, selection, selectionArgs, null, null, orderBy).use { cursor ->
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
items.add(fromCursor(cursor))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> SQLiteDatabase.transactionally(block: () -> T): T {
|
||||||
|
beginTransaction()
|
||||||
|
try {
|
||||||
|
val result = block()
|
||||||
|
setTransactionSuccessful()
|
||||||
|
return result
|
||||||
|
} finally {
|
||||||
|
endTransaction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun syncIfRemote(proj: DBProject) {
|
fun syncIfRemote(proj: DBProject) {
|
||||||
if (!proj.isLocal) {
|
if (!proj.isLocal) {
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
@@ -1146,13 +1134,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private fun getAccountProjectsCustom(selection: String, selectionArgs: Array<String>, orderBy: String?, db: SQLiteDatabase): List<DBAccountProject> {
|
private fun getAccountProjectsCustom(selection: String, selectionArgs: Array<String>, orderBy: String?, db: SQLiteDatabase): List<DBAccountProject> {
|
||||||
val cursor = db.query(table_account_projects, columnsAccountProjects, selection, selectionArgs, null, null, orderBy)
|
return queryAll(db, table_account_projects, columnsAccountProjects, selection, selectionArgs, orderBy, ::getAccountProjectFromCursor)
|
||||||
val accountProjects: MutableList<DBAccountProject> = ArrayList()
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
accountProjects.add(getAccountProjectFromCursor(cursor))
|
|
||||||
}
|
|
||||||
cursor.close()
|
|
||||||
return accountProjects
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("Range")
|
@SuppressLint("Range")
|
||||||
|
|||||||
Reference in New Issue
Block a user