From cda25c54802d361d59020c5115a715ff0dcd45ff Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Wed, 20 Nov 2024 09:29:41 +0000 Subject: [PATCH 1/3] Fix list names with special characters --- .../entities/DatabaseEntitiesRepository.kt | 14 +++++++------- .../android/entities/EntitiesRepositoryTest.kt | 13 +++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt b/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt index b3e127ca42c..acb18078bc8 100644 --- a/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt +++ b/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt @@ -110,7 +110,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep } insertOrThrow( - list, + "\"$list\"", null, contentValues ) @@ -278,7 +278,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep .rawQuery( """ SELECT *, i.$ROW_ID - FROM $list e, ${getRowIdTableName(list)} i + FROM "$list" e, ${getRowIdTableName(list)} i WHERE e._id = i._id ORDER BY i.$ROW_ID """.trimIndent(), @@ -323,14 +323,14 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep writableDatabase.execSQL( """ - CREATE TABLE ${getRowIdTableName(it)} AS SELECT _id FROM $it ORDER BY _id; + CREATE TABLE ${getRowIdTableName(it)} AS SELECT _id FROM "$it" ORDER BY _id; """.trimIndent() ) } } } - private fun getRowIdTableName(it: String) = "${it}_row_numbers" + private fun getRowIdTableName(it: String) = "\"${it}_row_numbers\"" private fun listExists(list: String): Boolean { return databaseConnection.withConnection { @@ -355,7 +355,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep execSQL( """ - CREATE TABLE IF NOT EXISTS $list ( + CREATE TABLE IF NOT EXISTS "$list" ( $_ID integer PRIMARY KEY, ${EntitiesTable.COLUMN_ID} text, ${EntitiesTable.COLUMN_LABEL} text, @@ -369,7 +369,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep execSQL( """ - CREATE UNIQUE INDEX IF NOT EXISTS ${list}_unique_id_index ON $list (${EntitiesTable.COLUMN_ID}); + CREATE UNIQUE INDEX IF NOT EXISTS "${list}_unique_id_index" ON "$list" (${EntitiesTable.COLUMN_ID}); """.trimIndent() ) } @@ -377,7 +377,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep private fun updatePropertyColumns(list: String, entity: Entity) { val columnNames = databaseConnection.withConnection { - readableDatabase.getColumnNames(list) + readableDatabase.getColumnNames("\"$list\"") } val missingColumns = entity.properties diff --git a/collect_app/src/test/java/org/odk/collect/android/entities/EntitiesRepositoryTest.kt b/collect_app/src/test/java/org/odk/collect/android/entities/EntitiesRepositoryTest.kt index 018721f55e3..78196cb2b78 100644 --- a/collect_app/src/test/java/org/odk/collect/android/entities/EntitiesRepositoryTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/entities/EntitiesRepositoryTest.kt @@ -275,6 +275,19 @@ abstract class EntitiesRepositoryTest { assertThat(repository.getLists(), equalTo(emptySet())) } + @Test + fun `#save supports creating list names with with dots and dashes`() { + val repository = buildSubject() + + val wine = Entity.New("1", "Léoville Barton 2008") + + repository.save("favourite-wines", wine) + assertThat(repository.getEntities("favourite-wines")[0], sameEntityAs(wine)) + + repository.save("favourite.wines", wine) + assertThat(repository.getEntities("favourite.wines")[0], sameEntityAs(wine)) + } + @Test fun `#clear deletes all entities`() { val repository = buildSubject() From 4810d08b624a9c00465c73f3446e5ea42ca2369c Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Wed, 20 Nov 2024 10:06:26 +0000 Subject: [PATCH 2/3] Make sure all queries uses quoted list names --- .../entities/DatabaseEntitiesRepository.kt | 14 ++--- .../entities/EntitiesRepositoryTest.kt | 52 +++++++++++++++++++ 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt b/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt index acb18078bc8..9ba3e983165 100644 --- a/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt +++ b/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt @@ -172,7 +172,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep readableDatabase.rawQuery( """ SELECT COUNT(*) - FROM $list + FROM "$list" """.trimIndent(), null ).first { @@ -262,7 +262,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep .rawQuery( """ SELECT *, i.$ROW_ID - FROM $list e, ${getRowIdTableName(list)} i + FROM "$list" e, "${getRowIdTableName(list)}" i WHERE e._id = i._id AND i.$ROW_ID = ? """.trimIndent(), arrayOf((index + 1).toString()) @@ -278,7 +278,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep .rawQuery( """ SELECT *, i.$ROW_ID - FROM "$list" e, ${getRowIdTableName(list)} i + FROM "$list" e, "${getRowIdTableName(list)}" i WHERE e._id = i._id ORDER BY i.$ROW_ID """.trimIndent(), @@ -296,7 +296,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep readableDatabase.rawQuery( """ SELECT *, i.$ROW_ID - FROM $list e, ${getRowIdTableName(list)} i + FROM "$list" e, "${getRowIdTableName(list)}" i WHERE e._id = i._id AND $selectionColumn = ? ORDER BY i.$ROW_ID """.trimIndent(), @@ -317,20 +317,20 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep getLists().forEach { writableDatabase.execSQL( """ - DROP TABLE IF EXISTS ${getRowIdTableName(it)}; + DROP TABLE IF EXISTS "${getRowIdTableName(it)}"; """.trimIndent() ) writableDatabase.execSQL( """ - CREATE TABLE ${getRowIdTableName(it)} AS SELECT _id FROM "$it" ORDER BY _id; + CREATE TABLE "${getRowIdTableName(it)}" AS SELECT _id FROM "$it" ORDER BY _id; """.trimIndent() ) } } } - private fun getRowIdTableName(it: String) = "\"${it}_row_numbers\"" + private fun getRowIdTableName(it: String) = "${it}_row_numbers" private fun listExists(list: String): Boolean { return databaseConnection.withConnection { diff --git a/collect_app/src/test/java/org/odk/collect/android/entities/EntitiesRepositoryTest.kt b/collect_app/src/test/java/org/odk/collect/android/entities/EntitiesRepositoryTest.kt index 78196cb2b78..d5b48e6be9b 100644 --- a/collect_app/src/test/java/org/odk/collect/android/entities/EntitiesRepositoryTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/entities/EntitiesRepositoryTest.kt @@ -471,6 +471,25 @@ abstract class EntitiesRepositoryTest { assertThat(repository.getById("wines", "3"), equalTo(null)) } + @Test + fun `#getById supports list names with dots and dashes`() { + val repository = buildSubject() + + val leoville = Entity.New("1", "Léoville Barton 2008") + val canet = Entity.New("2", "Pontet-Canet 2014") + repository.save("favourite-wines", leoville) + repository.save("other.favourite.wines", canet) + + val favouriteWines = repository.getEntities("favourite-wines") + val otherFavouriteWines = repository.getEntities("other.favourite.wines") + + val queriedLeoville = repository.getById("favourite-wines", "1") + assertThat(queriedLeoville, equalTo(favouriteWines.first { it.id == "1" })) + + val queriedCanet = repository.getById("other.favourite.wines", "2") + assertThat(queriedCanet, equalTo(otherFavouriteWines.first { it.id == "2" })) + } + @Test fun `#getByAllByProperty returns entities with matching property value`() { val repository = buildSubject() @@ -623,6 +642,21 @@ abstract class EntitiesRepositoryTest { assertThat(repository.getCount("whiskys"), equalTo(1)) } + @Test + fun `#getCount supports list names with dots and dashes`() { + val repository = buildSubject() + + val leoville = Entity.New("1", "Léoville Barton 2008") + val dows = Entity.New("2", "Dow's 1983") + repository.save("favourite-wines", leoville, dows) + + val springbank = Entity.New("1", "Springbank 10") + repository.save("favourite.whiskys", springbank) + + assertThat(repository.getCount("favourite-wines"), equalTo(2)) + assertThat(repository.getCount("favourite.whiskys"), equalTo(1)) + } + @Test fun `#getByIndex returns matching entity`() { val repository = buildSubject() @@ -649,6 +683,24 @@ abstract class EntitiesRepositoryTest { assertThat(repository.getByIndex("wine", 0), equalTo(null)) } + @Test + fun `#getByIndex supports list names with dots and dashes`() { + val repository = buildSubject() + + val leoville = Entity.New("1", "Léoville Barton 2008") + val canet = Entity.New("2", "Pontet-Canet 2014") + repository.save("favourite-wines", leoville) + repository.save("other.favourite.wines", canet) + + val leovilleIndex = + repository.getEntities("favourite-wines").first { it.id == leoville.id }.index + assertThat(repository.getByIndex("favourite-wines", leovilleIndex), sameEntityAs(leoville)) + + val canetIndex = + repository.getEntities("other.favourite.wines").first { it.id == canet.id }.index + assertThat(repository.getByIndex("other.favourite.wines", canetIndex), sameEntityAs(canet)) + } + @Test fun `#getListVersion returns list version`() { val repository = buildSubject() From e7425850f127c8fac115ed08e8d89e6a947b64a1 Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Wed, 20 Nov 2024 11:20:39 +0000 Subject: [PATCH 3/3] Fix adding new properties to lists with special characters --- .../entities/DatabaseEntitiesRepository.kt | 6 ++--- .../entities/EntitiesRepositoryTest.kt | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt b/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt index 9ba3e983165..4d89d544110 100644 --- a/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt +++ b/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt @@ -65,7 +65,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep entities.forEach { entity -> val existing = if (listExists) { query( - list, + "\"$list\"", "${EntitiesTable.COLUMN_ID} = ?", arrayOf(entity.id) ).first { mapCursorRowToEntity(it, 0) } @@ -92,7 +92,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep } update( - list, + "\"$list\"", contentValues, "${EntitiesTable.COLUMN_ID} = ?", arrayOf(entity.id) @@ -390,7 +390,7 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep missingColumns.forEach { execSQL( """ - ALTER TABLE $list ADD "$it" text NOT NULL DEFAULT ""; + ALTER TABLE "$list" ADD "$it" text NOT NULL DEFAULT ""; """.trimIndent() ) } diff --git a/collect_app/src/test/java/org/odk/collect/android/entities/EntitiesRepositoryTest.kt b/collect_app/src/test/java/org/odk/collect/android/entities/EntitiesRepositoryTest.kt index d5b48e6be9b..759bbc87fa0 100644 --- a/collect_app/src/test/java/org/odk/collect/android/entities/EntitiesRepositoryTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/entities/EntitiesRepositoryTest.kt @@ -162,6 +162,31 @@ abstract class EntitiesRepositoryTest { assertThat(wines[0].properties, contains("window" to "2019-2038", "score" to "92")) } + @Test + fun `#save adds new properties for lists with dashes`() { + val repository = buildSubject() + + val wine = Entity.New( + "1", + "Léoville Barton 2008", + properties = listOf("window" to "2019-2038"), + version = 1 + ) + repository.save("favourite-wines", wine) + + val updatedWine = Entity.New( + wine.id, + "Léoville Barton 2008", + properties = listOf("score" to "92"), + version = 2 + ) + repository.save("favourite-wines", updatedWine) + + val wines = repository.getEntities("favourite-wines") + assertThat(wines.size, equalTo(1)) + assertThat(wines[0].properties, contains("window" to "2019-2038", "score" to "92")) + } + @Test fun `#save adds new properties to existing entities`() { val repository = buildSubject()