From 448a9646d6d6c2e71d52976f7a0c5b72c4ad6b15 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Sat, 22 Feb 2025 20:13:26 -0500 Subject: [PATCH] Complete query migration --- src/main/kotlin/Parameters.kt | 28 +++--- src/test/kotlin/ParametersTest.kt | 11 ++- src/test/kotlin/query/PatchTest.kt | 105 +++++++++++++++++++++ src/test/kotlin/query/RemoveFieldsTest.kt | 106 ++++++++++++++++++++++ 4 files changed, 235 insertions(+), 15 deletions(-) create mode 100644 src/test/kotlin/query/PatchTest.kt create mode 100644 src/test/kotlin/query/RemoveFieldsTest.kt diff --git a/src/main/kotlin/Parameters.kt b/src/main/kotlin/Parameters.kt index 29dc500..c44cd34 100644 --- a/src/main/kotlin/Parameters.kt +++ b/src/main/kotlin/Parameters.kt @@ -73,23 +73,26 @@ object Parameters { when (param.type) { ParameterType.NUMBER -> { when (param.value) { - null -> stmt.setNull(idx, Types.NULL) - is Byte -> stmt.setByte(idx, param.value) + null -> stmt.setNull(idx, Types.NULL) + is Byte -> stmt.setByte(idx, param.value) is Short -> stmt.setShort(idx, param.value) - is Int -> stmt.setInt(idx, param.value) - is Long -> stmt.setLong(idx, param.value) - else -> throw DocumentException( + is Int -> stmt.setInt(idx, param.value) + is Long -> stmt.setLong(idx, param.value) + else -> throw DocumentException( "Number parameter must be Byte, Short, Int, or Long " + - "(${param.value::class.simpleName})") + "(${param.value::class.simpleName})" + ) } } + ParameterType.STRING -> { when (param.value) { - null -> stmt.setNull(idx, Types.NULL) + null -> stmt.setNull(idx, Types.NULL) is String -> stmt.setString(idx, param.value) - else -> stmt.setString(idx, param.value.toString()) + else -> stmt.setString(idx, param.value.toString()) } } + ParameterType.JSON -> stmt.setString(idx, Configuration.json.encodeToString(param.value)) } } @@ -108,11 +111,10 @@ object Parameters { */ fun fieldNames(names: Collection, parameterName: String = ":name") = when (Configuration.dialect("generate field name parameters")) { - Dialect.POSTGRESQL -> listOf(Parameter(parameterName, ParameterType.STRING, if (names.size == 1) { - names.elementAt(0) - } else { - names.joinToString(",").let { "{$it}" } - })) + Dialect.POSTGRESQL -> listOf( + Parameter(parameterName, ParameterType.STRING, names.joinToString(",").let { "{$it}" }) + ) + Dialect.SQLITE -> names.mapIndexed { index, name -> Parameter("$parameterName$index", ParameterType.STRING, name) } diff --git a/src/test/kotlin/ParametersTest.kt b/src/test/kotlin/ParametersTest.kt index 385caa6..154f872 100644 --- a/src/test/kotlin/ParametersTest.kt +++ b/src/test/kotlin/ParametersTest.kt @@ -2,6 +2,7 @@ package solutions.bitbadger.documents import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.assertThrows import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotSame @@ -14,7 +15,7 @@ import kotlin.test.assertSame class ParametersTest { /** - * Clear the connection string (resets Dialect) + * Reset the dialect */ @AfterEach fun cleanUp() { @@ -65,7 +66,7 @@ class ParametersTest { assertEquals(1, nameParams.size, "There should be one name parameter") assertEquals(":name", nameParams[0].name, "The parameter name is incorrect") assertEquals(ParameterType.STRING, nameParams[0].type, "The parameter type is incorrect") - assertEquals("test", nameParams[0].value, "The parameter value is incorrect") + assertEquals("{test}", nameParams[0].value, "The parameter value is incorrect") } @Test @@ -106,4 +107,10 @@ class ParametersTest { assertEquals(ParameterType.STRING, nameParams[2].type, "The third parameter type is incorrect") assertEquals("today", nameParams[2].value, "The third parameter value is incorrect") } + + @Test + @DisplayName("fieldNames fails if dialect not set") + fun fieldNamesFails() { + assertThrows { Parameters.fieldNames(listOf()) } + } } diff --git a/src/test/kotlin/query/PatchTest.kt b/src/test/kotlin/query/PatchTest.kt new file mode 100644 index 0000000..707f186 --- /dev/null +++ b/src/test/kotlin/query/PatchTest.kt @@ -0,0 +1,105 @@ +package solutions.bitbadger.documents.query + +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import solutions.bitbadger.documents.Configuration +import solutions.bitbadger.documents.Dialect +import solutions.bitbadger.documents.DocumentException +import solutions.bitbadger.documents.Field +import kotlin.test.assertEquals + +/** + * Unit tests for the `Patch` object + */ +@DisplayName("Patch (Query)") +class PatchTest { + + /** Test table name */ + private val tbl = "test_table" + + /** + * Reset the dialect + */ + @AfterEach + fun cleanUp() { + Configuration.dialectValue = null + } + + @Test + @DisplayName("byId generates correctly (PostgreSQL)") + fun byIdPostgres() { + Configuration.dialectValue = Dialect.POSTGRESQL + assertEquals( + "UPDATE $tbl SET data = data || :data WHERE data->>'id' = :id", + Patch.byId(tbl), "Patch query not constructed correctly" + ) + } + + @Test + @DisplayName("byId generates correctly (SQLite)") + fun byIdSQLite() { + Configuration.dialectValue = Dialect.SQLITE + assertEquals( + "UPDATE $tbl SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id", + Patch.byId(tbl), "Patch query not constructed correctly" + ) + } + + @Test + @DisplayName("byFields generates correctly (PostgreSQL)") + fun byFieldsPostgres() { + Configuration.dialectValue = Dialect.POSTGRESQL + assertEquals( + "UPDATE $tbl SET data = data || :data WHERE data->>'z' = :y", + Patch.byFields(tbl, listOf(Field.equal("z", "", ":y"))), + "Patch query not constructed correctly" + ) + } + + @Test + @DisplayName("byFields generates correctly (SQLite)") + fun byFieldsSQLite() { + Configuration.dialectValue = Dialect.SQLITE + assertEquals( + "UPDATE $tbl SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y", + Patch.byFields(tbl, listOf(Field.equal("z", "", ":y"))), + "Patch query not constructed correctly" + ) + } + + @Test + @DisplayName("byContains generates correctly (PostgreSQL)") + fun byContainsPostgres() { + Configuration.dialectValue = Dialect.POSTGRESQL + assertEquals( + "UPDATE $tbl SET data = data || :data WHERE data @> :criteria", Patch.byContains(tbl), + "Patch query not constructed correctly" + ) + } + + @Test + @DisplayName("byContains fails (SQLite)") + fun byContainsSQLite() { + Configuration.dialectValue = Dialect.SQLITE + assertThrows { Patch.byContains(tbl) } + } + + @Test + @DisplayName("byJsonPath generates correctly (PostgreSQL)") + fun byJsonPathPostgres() { + Configuration.dialectValue = Dialect.POSTGRESQL + assertEquals( + "UPDATE $tbl SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)", + Patch.byJsonPath(tbl), "Patch query not constructed correctly" + ) + } + + @Test + @DisplayName("byJsonPath fails (SQLite)") + fun byJsonPathSQLite() { + Configuration.dialectValue = Dialect.SQLITE + assertThrows { Patch.byJsonPath(tbl) } + } +} diff --git a/src/test/kotlin/query/RemoveFieldsTest.kt b/src/test/kotlin/query/RemoveFieldsTest.kt new file mode 100644 index 0000000..e06ff44 --- /dev/null +++ b/src/test/kotlin/query/RemoveFieldsTest.kt @@ -0,0 +1,106 @@ +package solutions.bitbadger.documents.query + +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import solutions.bitbadger.documents.* +import kotlin.test.assertEquals + +/** + * Unit tests for the `RemoveFields` object + */ +@DisplayName("RemoveFields (Query)") +class RemoveFieldsTest { + + /** Test table name */ + private val tbl = "test_table" + + /** + * Reset the dialect + */ + @AfterEach + fun cleanUp() { + Configuration.dialectValue = null + } + + @Test + @DisplayName("byId generates correctly (PostgreSQL)") + fun byIdPostgres() { + Configuration.dialectValue = Dialect.POSTGRESQL + assertEquals( + "UPDATE $tbl SET data = data - :name::text[] WHERE data->>'id' = :id", + RemoveFields.byId(tbl, Parameters.fieldNames(listOf("a", "z"))), + "Remove Fields query not constructed correctly" + ) + } + + @Test + @DisplayName("byId generates correctly (SQLite)") + fun byIdSQLite() { + Configuration.dialectValue = Dialect.SQLITE + assertEquals( + "UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id", + RemoveFields.byId(tbl, Parameters.fieldNames(listOf("a", "z"))), + "Remove Field query not constructed correctly" + ) + } + + @Test + @DisplayName("byFields generates correctly (PostgreSQL)") + fun byFieldsPostgres() { + Configuration.dialectValue = Dialect.POSTGRESQL + assertEquals( + "UPDATE $tbl SET data = data - :name::text[] WHERE data->>'f' > :g", + RemoveFields.byFields(tbl, Parameters.fieldNames(listOf("b", "c")), listOf(Field.greater("f", "", ":g"))), + "Remove Field query not constructed correctly" + ) + } + + @Test + @DisplayName("byFields generates correctly (SQLite)") + fun byFieldsSQLite() { + Configuration.dialectValue = Dialect.SQLITE + assertEquals( + "UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g", + RemoveFields.byFields(tbl, Parameters.fieldNames(listOf("b", "c")), listOf(Field.greater("f", "", ":g"))), + "Remove Field query not constructed correctly" + ) + } + + @Test + @DisplayName("byContains generates correctly (PostgreSQL)") + fun byContainsPostgres() { + Configuration.dialectValue = Dialect.POSTGRESQL + assertEquals( + "UPDATE $tbl SET data = data - :name::text[] WHERE data @> :criteria", + RemoveFields.byContains(tbl, Parameters.fieldNames(listOf("m", "n"))), + "Remove Field query not constructed correctly" + ) + } + + @Test + @DisplayName("byContains fails (SQLite)") + fun byContainsSQLite() { + Configuration.dialectValue = Dialect.SQLITE + assertThrows { RemoveFields.byContains(tbl, listOf()) } + } + + @Test + @DisplayName("byJsonPath generates correctly (PostgreSQL)") + fun byJsonPathPostgres() { + Configuration.dialectValue = Dialect.POSTGRESQL + assertEquals( + "UPDATE $tbl SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)", + RemoveFields.byJsonPath(tbl, Parameters.fieldNames(listOf("o", "p"))), + "Remove Field query not constructed correctly" + ) + } + + @Test + @DisplayName("byJsonPath fails (SQLite)") + fun byJsonPathSQLite() { + Configuration.dialectValue = Dialect.SQLITE + assertThrows { RemoveFields.byJsonPath(tbl, listOf()) } + } +} \ No newline at end of file