@@ -4,21 +4,20 @@ import android.content.ContentValues
4
4
import android.content.Context
5
5
import android.database.Cursor
6
6
import android.database.sqlite.SQLiteDatabase
7
- import android.database.sqlite.SQLiteException
8
7
import android.provider.BaseColumns._ID
9
- import androidx.core.database.sqlite.transaction
10
8
import org.odk.collect.db.sqlite.CursorExt.first
11
9
import org.odk.collect.db.sqlite.CursorExt.foldAndClose
12
10
import org.odk.collect.db.sqlite.CursorExt.getInt
13
11
import org.odk.collect.db.sqlite.CursorExt.getString
14
12
import org.odk.collect.db.sqlite.CursorExt.getStringOrNull
15
13
import org.odk.collect.db.sqlite.CursorExt.rowToMap
16
- import org.odk.collect.db.sqlite.DatabaseConnection
17
14
import org.odk.collect.db.sqlite.DatabaseMigrator
18
15
import org.odk.collect.db.sqlite.SQLiteColumns.ROW_ID
19
16
import org.odk.collect.db.sqlite.SQLiteDatabaseExt.delete
20
17
import org.odk.collect.db.sqlite.SQLiteDatabaseExt.doesColumnExist
18
+ import org.odk.collect.db.sqlite.SQLiteDatabaseExt.getColumnNames
21
19
import org.odk.collect.db.sqlite.SQLiteDatabaseExt.query
20
+ import org.odk.collect.db.sqlite.SynchronizedDatabaseConnection
22
21
import org.odk.collect.entities.storage.EntitiesRepository
23
22
import org.odk.collect.entities.storage.Entity
24
23
@@ -39,13 +38,12 @@ private object EntitiesTable {
39
38
40
39
class DatabaseEntitiesRepository (context : Context , dbPath : String ) : EntitiesRepository {
41
40
42
- private val databaseConnection: DatabaseConnection = DatabaseConnection (
41
+ private val databaseConnection = SynchronizedDatabaseConnection (
43
42
context,
44
43
dbPath,
45
44
" entities.db" ,
46
45
EntitiesDatabaseMigrator (),
47
- 1 ,
48
- true
46
+ 1
49
47
)
50
48
51
49
override fun save (list : String , vararg entities : Entity ) {
@@ -60,7 +58,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep
60
58
61
59
updatePropertyColumns(list, entities.first())
62
60
63
- databaseConnection.writeableDatabase. transaction {
61
+ databaseConnection.transaction {
64
62
entities.forEach { entity ->
65
63
val existing = if (listExists) {
66
64
query(
@@ -121,32 +119,34 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep
121
119
}
122
120
123
121
override fun getLists (): Set <String > {
124
- return databaseConnection
125
- .readableDatabase
126
- .query(ListsTable .TABLE_NAME )
127
- .foldAndClose(emptySet()) { set, cursor -> set + cursor.getString(ListsTable .COLUMN_NAME ) }
122
+ return databaseConnection.withConnection {
123
+ readableDatabase
124
+ .query(ListsTable .TABLE_NAME )
125
+ .foldAndClose(emptySet()) { set, cursor -> set + cursor.getString(ListsTable .COLUMN_NAME ) }
126
+ }
128
127
}
129
128
130
129
override fun updateListHash (list : String , hash : String ) {
131
130
val contentValues = ContentValues ().also {
132
131
it.put(ListsTable .COLUMN_HASH , hash)
133
132
}
134
133
135
- databaseConnection
136
- .writeableDatabase
137
- .update(
134
+ databaseConnection.withConnection {
135
+ writableDatabase.update(
138
136
ListsTable .TABLE_NAME ,
139
137
contentValues,
140
138
" ${ListsTable .COLUMN_NAME } = ?" ,
141
139
arrayOf(list)
142
140
)
141
+ }
143
142
}
144
143
145
144
override fun getListHash (list : String ): String? {
146
- return databaseConnection
147
- .readableDatabase
148
- .query(ListsTable .TABLE_NAME , " ${ListsTable .COLUMN_NAME } = ?" , arrayOf(list))
149
- .first { it.getStringOrNull(ListsTable .COLUMN_HASH ) }
145
+ return databaseConnection.withConnection {
146
+ readableDatabase
147
+ .query(ListsTable .TABLE_NAME , " ${ListsTable .COLUMN_NAME } = ?" , arrayOf(list))
148
+ .first { it.getStringOrNull(ListsTable .COLUMN_HASH ) }
149
+ }
150
150
}
151
151
152
152
override fun getEntities (list : String ): List <Entity .Saved > {
@@ -168,23 +168,27 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep
168
168
return 0
169
169
}
170
170
171
- return databaseConnection.readableDatabase.rawQuery(
172
- """
173
- SELECT COUNT(*)
174
- FROM $list
175
- """ .trimIndent(),
176
- null
177
- ).first {
178
- it.getInt(0 )
179
- }!!
171
+ return databaseConnection.withConnection {
172
+ readableDatabase.rawQuery(
173
+ """
174
+ SELECT COUNT(*)
175
+ FROM $list
176
+ """ .trimIndent(),
177
+ null
178
+ ).first {
179
+ it.getInt(0 )
180
+ }!!
181
+ }
180
182
}
181
183
182
184
override fun clear () {
183
- getLists().forEach {
184
- databaseConnection.writeableDatabase.delete(it)
185
- }
185
+ databaseConnection.withConnection {
186
+ getLists().forEach {
187
+ writableDatabase.delete(it)
188
+ }
186
189
187
- databaseConnection.writeableDatabase.delete(ListsTable .TABLE_NAME )
190
+ writableDatabase.delete(ListsTable .TABLE_NAME )
191
+ }
188
192
}
189
193
190
194
override fun addList (list : String ) {
@@ -195,12 +199,14 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep
195
199
}
196
200
197
201
override fun delete (id : String ) {
198
- getLists().forEach {
199
- databaseConnection.writeableDatabase.delete(
200
- it,
201
- " ${EntitiesTable .COLUMN_ID } = ?" ,
202
- arrayOf(id)
203
- )
202
+ databaseConnection.withConnection {
203
+ getLists().forEach {
204
+ writableDatabase.delete(
205
+ it,
206
+ " ${EntitiesTable .COLUMN_ID } = ?" ,
207
+ arrayOf(id)
208
+ )
209
+ }
204
210
}
205
211
206
212
updateRowIdTables()
@@ -229,7 +235,11 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep
229
235
return emptyList()
230
236
}
231
237
232
- return if (databaseConnection.readableDatabase.doesColumnExist(list, property)) {
238
+ val propertyExists = databaseConnection.withConnection {
239
+ readableDatabase.doesColumnExist(list, property)
240
+ }
241
+
242
+ return if (propertyExists) {
233
243
queryWithAttachedRowId(
234
244
list,
235
245
selectionColumn = property,
@@ -251,45 +261,50 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep
251
261
return null
252
262
}
253
263
254
- return databaseConnection.readableDatabase
255
- .rawQuery(
256
- """
257
- SELECT *, i.$ROW_ID
258
- FROM $list e, ${getRowIdTableName(list)} i
259
- WHERE e._id = i._id AND i.$ROW_ID = ?
260
- """ .trimIndent(),
261
- arrayOf((index + 1 ).toString())
262
- ).first {
263
- mapCursorRowToEntity(list, it, it.getInt(ROW_ID ))
264
- }
264
+ return databaseConnection.withConnection {
265
+ readableDatabase
266
+ .rawQuery(
267
+ """
268
+ SELECT *, i.$ROW_ID
269
+ FROM $list e, ${getRowIdTableName(list)} i
270
+ WHERE e._id = i._id AND i.$ROW_ID = ?
271
+ """ .trimIndent(),
272
+ arrayOf((index + 1 ).toString())
273
+ ).first {
274
+ mapCursorRowToEntity(list, it, it.getInt(ROW_ID ))
275
+ }
276
+ }
265
277
}
266
278
267
279
private fun queryWithAttachedRowId (list : String ): Cursor {
268
- return databaseConnection.readableDatabase
269
- .rawQuery(
270
- """
271
- SELECT *, i.$ROW_ID
272
- FROM $list e, ${getRowIdTableName(list)} i
273
- WHERE e._id = i._id
274
- """ .trimIndent(),
275
- null
276
- )
280
+ return databaseConnection.withConnection {
281
+ readableDatabase
282
+ .rawQuery(
283
+ """
284
+ SELECT *, i.$ROW_ID
285
+ FROM $list e, ${getRowIdTableName(list)} i
286
+ WHERE e._id = i._id
287
+ """ .trimIndent(),
288
+ null
289
+ )
290
+ }
277
291
}
278
292
279
293
private fun queryWithAttachedRowId (
280
294
list : String ,
281
295
selectionColumn : String ,
282
296
selectionArg : String
283
297
): Cursor {
284
- return databaseConnection.readableDatabase
285
- .rawQuery(
298
+ return databaseConnection.withConnection {
299
+ readableDatabase .rawQuery(
286
300
"""
287
301
SELECT *, i.$ROW_ID
288
302
FROM $list e, ${getRowIdTableName(list)} i
289
303
WHERE e._id = i._id AND $selectionColumn = ?
290
304
""" .trimIndent(),
291
305
arrayOf(selectionArg)
292
306
)
307
+ }
293
308
}
294
309
295
310
/* *
@@ -300,34 +315,38 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep
300
315
* function, but that's not available in all the supported versions of Android.
301
316
*/
302
317
private fun updateRowIdTables () {
303
- getLists().forEach {
304
- databaseConnection.writeableDatabase.execSQL(
305
- """
306
- DROP TABLE IF EXISTS ${getRowIdTableName(it)} ;
307
- """ .trimIndent()
308
- )
318
+ databaseConnection.withConnection {
319
+ getLists().forEach {
320
+ writableDatabase.execSQL(
321
+ """
322
+ DROP TABLE IF EXISTS ${getRowIdTableName(it)} ;
323
+ """ .trimIndent()
324
+ )
309
325
310
- databaseConnection.writeableDatabase.execSQL(
311
- """
312
- CREATE TABLE ${getRowIdTableName(it)} AS SELECT _id FROM $it ;
313
- """ .trimIndent()
314
- )
326
+ writableDatabase.execSQL(
327
+ """
328
+ CREATE TABLE ${getRowIdTableName(it)} AS SELECT _id FROM $it ;
329
+ """ .trimIndent()
330
+ )
331
+ }
315
332
}
316
333
}
317
334
318
335
private fun getRowIdTableName (it : String ) = " ${it} _row_numbers"
319
336
320
337
private fun listExists (list : String ): Boolean {
321
- return databaseConnection.readableDatabase
322
- .query(
323
- ListsTable .TABLE_NAME ,
324
- selection = " ${ListsTable .COLUMN_NAME } = ?" ,
325
- selectionArgs = arrayOf(list)
326
- ).use { it.count } > 0
338
+ return databaseConnection.withConnection {
339
+ readableDatabase
340
+ .query(
341
+ ListsTable .TABLE_NAME ,
342
+ selection = " ${ListsTable .COLUMN_NAME } = ?" ,
343
+ selectionArgs = arrayOf(list)
344
+ ).use { it.count } > 0
345
+ }
327
346
}
328
347
329
348
private fun createList (list : String ) {
330
- databaseConnection.writeableDatabase. transaction {
349
+ databaseConnection.transaction {
331
350
val contentValues = ContentValues ()
332
351
contentValues.put(ListsTable .COLUMN_NAME , list)
333
352
insertOrThrow(
@@ -359,15 +378,21 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep
359
378
}
360
379
361
380
private fun updatePropertyColumns (list : String , entity : Entity ) {
362
- entity.properties.map { it.first }.forEach {
363
- try {
364
- databaseConnection.writeableDatabase.execSQL(
365
- """
366
- ALTER TABLE $list ADD "$it " text NOT NULL DEFAULT "";
367
- """ .trimIndent()
368
- )
369
- } catch (e: SQLiteException ) {
370
- // Ignored
381
+ val columnNames = databaseConnection.withConnection {
382
+ readableDatabase.getColumnNames(list)
383
+ }
384
+
385
+ val missingColumns =
386
+ entity.properties.map { it.first }.filterNot { columnNames.contains(it) }
387
+ if (missingColumns.isNotEmpty()) {
388
+ databaseConnection.resetTransaction {
389
+ missingColumns.forEach {
390
+ execSQL(
391
+ """
392
+ ALTER TABLE $list ADD "$it " text NOT NULL DEFAULT "";
393
+ """ .trimIndent()
394
+ )
395
+ }
371
396
}
372
397
}
373
398
}
0 commit comments