From 4ff30e1201bb36ad1f7c83e16c0861bd98f3955a Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Wed, 14 Jul 2021 20:18:45 +0200 Subject: [PATCH 01/28] Remove update object by deserialize --- .../fr/postgresjson/connexion/Connection.kt | 9 +-------- .../fr/postgresjson/serializer/Serializer.kt | 5 ----- src/test/kotlin/fr/postgresjson/RequesterTest.kt | 4 ++-- .../kotlin/fr/postgresjson/SerializerTest.kt | 16 ---------------- 4 files changed, 3 insertions(+), 31 deletions(-) diff --git a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt index a09ec1e..2cd0712 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt @@ -55,19 +55,12 @@ class Connection( values: List, block: (QueryResult, R?) -> Unit ): R? { - val primaryObject = values.firstOrNull { - it is EntityI && typeReference.type.typeName == it::class.java.name - } as R? val result = exec(sql, compileArgs(values)) val json = result.rows.firstOrNull()?.getString(0) return if (json === null) { null } else { - if (primaryObject != null) { - serializer.deserialize(json, primaryObject) - } else { - serializer.deserialize(json, typeReference) - } + serializer.deserialize(json, typeReference) }.also { block(result, it) } diff --git a/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt b/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt index bf4719a..e32ace3 100644 --- a/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt +++ b/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt @@ -42,13 +42,8 @@ class Serializer(val mapper: ObjectMapper = jacksonObjectMapper()) { inline fun deserializeList(json: String): E { return deserializeList(json, object : TypeReference() {}) } - - fun deserialize(json: String, target: E): E { - return mapper.readerForUpdating(target).readValue(json) - } } fun Serializable.serialize(pretty: Boolean = false) = Serializer().serialize(this, pretty) fun List.serialize(pretty: Boolean = false) = Serializer().serialize(this, pretty) -inline fun E.deserialize(json: String) = Serializer().deserialize(json, this) inline fun String.deserialize() = Serializer().deserialize(this) diff --git a/src/test/kotlin/fr/postgresjson/RequesterTest.kt b/src/test/kotlin/fr/postgresjson/RequesterTest.kt index ea02185..f60d11f 100644 --- a/src/test/kotlin/fr/postgresjson/RequesterTest.kt +++ b/src/test/kotlin/fr/postgresjson/RequesterTest.kt @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test import java.util.UUID class RequesterTest : TestAbstract() { - class ObjTest(var name: String, id: UUID = UUID.fromString("5623d902-3067-42f3-bfd9-095dbb12c29f")) : UuidEntity(id) + class ObjTest(val name: String, id: UUID = UUID.fromString("5623d902-3067-42f3-bfd9-095dbb12c29f")) : UuidEntity(id) @Test fun `get query from file`() { @@ -100,7 +100,7 @@ class RequesterTest : TestAbstract() { .selectOne("resource" to obj2)!! assertEquals("changedName", obj.name) - assertEquals("changedName", obj2.name) + assertEquals("original", obj2.name) } @Test diff --git a/src/test/kotlin/fr/postgresjson/SerializerTest.kt b/src/test/kotlin/fr/postgresjson/SerializerTest.kt index cbef4b1..eee238f 100644 --- a/src/test/kotlin/fr/postgresjson/SerializerTest.kt +++ b/src/test/kotlin/fr/postgresjson/SerializerTest.kt @@ -69,20 +69,4 @@ internal class SerializerTest { assertEquals(obj.val1, objDeserialized!!.val1) assertEquals(obj.val2, objDeserialized.val2) } - - @Test - fun deserializeUpdate() { - val objDeserialized: ObjTest = serializer.deserialize(objSerializedUpdate, obj) - assertTrue(obj === objDeserialized) - assertEquals("update", objDeserialized.val1) - assertEquals(123, objDeserialized.val2) - } - - @Test - fun deserializeUpdate2() { - val objDeserialized = obj.deserialize(objSerializedUpdate) - assertTrue(obj === objDeserialized) - assertEquals("update", objDeserialized.val1) - assertEquals(123, objDeserialized.val2) - } } From 4ad69238c7839d452421f047ccc4d1d3927fff89 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Wed, 14 Jul 2021 20:20:52 +0200 Subject: [PATCH 02/28] Add tests --- .../fr/postgresjson/connexion/Requester.kt | 7 +- .../kotlin/fr/postgresjson/RequesterTest.kt | 89 ++++++++++++++++++- 2 files changed, 93 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/fr/postgresjson/connexion/Requester.kt b/src/main/kotlin/fr/postgresjson/connexion/Requester.kt index e3d502e..fbba376 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Requester.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Requester.kt @@ -60,14 +60,14 @@ class Requester( fun getFunction(name: String): Function { if (functions[name] === null) { - throw Exception("No function defined for $name") + throw NoFunctionDefined(name) } return functions[name]!! } fun getQuery(path: String): Query { if (queries[path] === null) { - throw Exception("No query defined in $path") + throw NoQueryDefined(path) } return queries[path]!! } @@ -107,4 +107,7 @@ class Requester( return req } } + + class NoFunctionDefined(name: String) : Exception("No function defined for $name") + class NoQueryDefined(path: String) : Exception("No query defined in $path") } diff --git a/src/test/kotlin/fr/postgresjson/RequesterTest.kt b/src/test/kotlin/fr/postgresjson/RequesterTest.kt index f60d11f..6478da1 100644 --- a/src/test/kotlin/fr/postgresjson/RequesterTest.kt +++ b/src/test/kotlin/fr/postgresjson/RequesterTest.kt @@ -2,15 +2,48 @@ package fr.postgresjson import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Requester +import fr.postgresjson.connexion.Requester.NoFunctionDefined +import fr.postgresjson.connexion.Requester.NoQueryDefined import fr.postgresjson.entity.UuidEntity import org.junit.Assert import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertThrows import org.junit.jupiter.api.Test import java.util.UUID class RequesterTest : TestAbstract() { class ObjTest(val name: String, id: UUID = UUID.fromString("5623d902-3067-42f3-bfd9-095dbb12c29f")) : UuidEntity(id) + @Test + fun `function toString`() { + val resources = this::class.java.getResource("/sql/function/Test").toURI() + val name: String = Requester(connection) + .addFunction(resources) + .getFunction("test_function") + .name + + assertEquals("test_function", name) + } + @Test + fun `add function as string`() { + val sql = """ + CREATE OR REPLACE FUNCTION test_function (name text default 'plop', IN hi text default 'hello', out result json) + LANGUAGE plpgsql + AS + $$ + BEGIN + result = json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name); + END; + $$ + """.trimIndent() + val name: String = Requester(connection) + .addFunction(sql) + .getFunction("test_function") + .name + + assertEquals("test_function", name) + } + @Test fun `get query from file`() { val resources = this::class.java.getResource("/sql/query").toURI() @@ -23,6 +56,36 @@ class RequesterTest : TestAbstract() { assertEquals(objTest.name, "test") } + @Test + fun `get query from file with wrong name throw exception`() { + val resources = this::class.java.getResource("/sql/query").toURI() + assertThrows(NoQueryDefined::class.java) { + Requester(connection) + .addQuery(resources) + .getQuery("wrongName") + } + } + + @Test + fun `get queries from file`() { + val resources = this::class.java.getResource("/sql/query").toURI() + val name: String = Requester(connection) + .addQuery(resources) + .getQueries()[0].name + + assertEquals(name, "DeleteTest") + } + + @Test + fun `get function from file with wrong name throw exception`() { + val resources = this::class.java.getResource("/sql/function/Test").toURI() + assertThrows(NoFunctionDefined::class.java) { + Requester(connection) + .addFunction(resources) + .getFunction("wrongName") + } + } + @Test fun `get function from file`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() @@ -91,7 +154,7 @@ class RequesterTest : TestAbstract() { } @Test - fun `call selectOne on function with object`() { + fun `call selectOne on function with object and named argument`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() val obj2 = ObjTest("original") val obj: ObjTest = Requester(connection) @@ -103,6 +166,30 @@ class RequesterTest : TestAbstract() { assertEquals("original", obj2.name) } + @Test + fun `call selectOne on function with object`() { + val resources = this::class.java.getResource("/sql/function/Test").toURI() + val obj2 = ObjTest("original") + val obj: ObjTest = Requester(connection) + .addFunction(resources) + .getFunction("test_function_object") + .selectOne(obj2)!! + + assertEquals("changedName", obj.name) + assertEquals("original", obj2.name) + } + + @Test + fun `call selectOne on function with object and no arguments`() { + val resources = this::class.java.getResource("/sql/function/Test").toURI() + val obj: ObjTest = Requester(connection) + .addFunction(resources) + .getFunction("test_function") + .selectOne()!! + + assertEquals("plop", obj.name) + } + @Test fun `call selectOne on query`() { val resources = this::class.java.getResource("/sql/query").toURI() From 486b038ad2af654dfe38f7decf7f696246837986 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Wed, 14 Jul 2021 22:40:34 +0200 Subject: [PATCH 03/28] remove RequesterFactory --- docs/usage/stored-procedure.md | 2 +- .../postgresjson/connexion/EmbedExecutable.kt | 2 +- .../connexion/FunctionConverter.kt | 16 +++++++ .../postgresjson/connexion/QueryConverter.kt | 16 +++++++ .../fr/postgresjson/connexion/Requester.kt | 48 +++++-------------- .../fr/postgresjson/definition/Parameter.kt | 4 +- .../fr/postgresjson/definition/Resource.kt | 6 +-- .../kotlin/fr/postgresjson/RequesterTest.kt | 21 ++++++++ 8 files changed, 71 insertions(+), 44 deletions(-) create mode 100644 src/main/kotlin/fr/postgresjson/connexion/FunctionConverter.kt create mode 100644 src/main/kotlin/fr/postgresjson/connexion/QueryConverter.kt diff --git a/docs/usage/stored-procedure.md b/docs/usage/stored-procedure.md index a5a95b1..3305a7b 100644 --- a/docs/usage/stored-procedure.md +++ b/docs/usage/stored-procedure.md @@ -12,7 +12,7 @@ import fr.postgresjson.connexion.Connection val connection: Connection = TODO() -val requester = Requester.RequesterFactory( +val requester = Requester( connection = connection, functionsDirectory = this::class.java.getResource("/sql/functions")?.toURI() ?: error("No sql function found") ).createRequester() diff --git a/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt b/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt index cf8355b..8936b5c 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt @@ -4,7 +4,7 @@ import com.fasterxml.jackson.core.type.TypeReference import com.github.jasync.sql.db.QueryResult import fr.postgresjson.entity.EntityI -interface EmbedExecutable { +sealed interface EmbedExecutable { val connection: Connection override fun toString(): String val name: String diff --git a/src/main/kotlin/fr/postgresjson/connexion/FunctionConverter.kt b/src/main/kotlin/fr/postgresjson/connexion/FunctionConverter.kt new file mode 100644 index 0000000..3b53e92 --- /dev/null +++ b/src/main/kotlin/fr/postgresjson/connexion/FunctionConverter.kt @@ -0,0 +1,16 @@ +package fr.postgresjson.connexion + +import fr.postgresjson.utils.searchSqlFiles +import java.net.URI +import fr.postgresjson.definition.Function as DefinitionFunction + +fun DefinitionFunction.toConnection(connection: Connection): Function = Function(this, connection) + +fun Sequence.toConnection(connection: Connection): Sequence = map { it.toConnection(connection) } + +fun Sequence.toMutableMap(): MutableMap = map { it.name to it }.toMap().toMutableMap() + +internal fun URI.toFunction(connection: Connection): MutableMap = searchSqlFiles() + .filterIsInstance(DefinitionFunction::class.java) + .toConnection(connection) + .toMutableMap() diff --git a/src/main/kotlin/fr/postgresjson/connexion/QueryConverter.kt b/src/main/kotlin/fr/postgresjson/connexion/QueryConverter.kt new file mode 100644 index 0000000..76b0861 --- /dev/null +++ b/src/main/kotlin/fr/postgresjson/connexion/QueryConverter.kt @@ -0,0 +1,16 @@ +package fr.postgresjson.connexion + +import fr.postgresjson.utils.searchSqlFiles +import java.net.URI +import fr.postgresjson.definition.Query as QueryDefinition + +fun QueryDefinition.toConnection(connection: Connection): Query = Query(name, script, connection) + +fun Sequence.toConnection(connection: Connection): Sequence = map { it.toConnection(connection) } + +fun Sequence.toMutableMap(): MutableMap = map { it.name to it }.toMap().toMutableMap() + +internal fun URI.toQuery(connection: Connection): MutableMap = searchSqlFiles() + .filterIsInstance(QueryDefinition::class.java) + .toConnection(connection) + .toMutableMap() diff --git a/src/main/kotlin/fr/postgresjson/connexion/Requester.kt b/src/main/kotlin/fr/postgresjson/connexion/Requester.kt index fbba376..664adc8 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Requester.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Requester.kt @@ -10,6 +10,18 @@ class Requester( private val queries: MutableMap = mutableMapOf(), private val functions: MutableMap = mutableMapOf() ) { + constructor(connection: Connection) : this(connection, mutableMapOf(), mutableMapOf()) + + constructor( + connection: Connection, + queriesDirectory: URI? = null, + functionsDirectory: URI? = null + ) : this( + connection = connection, + queries = queriesDirectory?.toQuery(connection) ?: mutableMapOf(), + functions = functionsDirectory?.toFunction(connection) ?: mutableMapOf(), + ) + fun addQuery(query: Query): Requester { queries[query.name] = query return this @@ -72,42 +84,6 @@ class Requester( return queries[path]!! } - class RequesterFactory( - private val connection: Connection, - private val queriesDirectory: URI? = null, - private val functionsDirectory: URI? = null - ) { - constructor( - host: String = "localhost", - port: Int = 5432, - database: String, - username: String, - password: String, - queriesDirectory: URI? = null, - functionsDirectory: URI? = null - ) : this( - Connection(host = host, port = port, database = database, username = username, password = password), - queriesDirectory, - functionsDirectory - ) - - fun createRequester(): Requester { - return initRequester(Requester(connection)) - } - - private fun initRequester(req: Requester): Requester { - if (queriesDirectory !== null) { - req.addQuery(queriesDirectory) - } - - if (functionsDirectory !== null) { - req.addFunction(functionsDirectory) - } - - return req - } - } - class NoFunctionDefined(name: String) : Exception("No function defined for $name") class NoQueryDefined(path: String) : Exception("No query defined in $path") } diff --git a/src/main/kotlin/fr/postgresjson/definition/Parameter.kt b/src/main/kotlin/fr/postgresjson/definition/Parameter.kt index 03fbf7f..9b97294 100644 --- a/src/main/kotlin/fr/postgresjson/definition/Parameter.kt +++ b/src/main/kotlin/fr/postgresjson/definition/Parameter.kt @@ -1,5 +1,7 @@ package fr.postgresjson.definition +import java.util.Locale + interface ParameterI { val name: String val type: String @@ -21,7 +23,7 @@ class Parameter(val name: String, val type: String, direction: Direction? = Dire constructor(name: String, type: String, direction: String? = "IN", default: Any? = null) : this( name = name, type = type, - direction = direction?.let { Direction.valueOf(direction.toUpperCase()) }, + direction = direction?.let { Direction.valueOf(direction.uppercase(Locale.getDefault())) }, default = default ) diff --git a/src/main/kotlin/fr/postgresjson/definition/Resource.kt b/src/main/kotlin/fr/postgresjson/definition/Resource.kt index a86157a..fe34a99 100644 --- a/src/main/kotlin/fr/postgresjson/definition/Resource.kt +++ b/src/main/kotlin/fr/postgresjson/definition/Resource.kt @@ -4,7 +4,7 @@ import java.io.File import java.net.URL import java.nio.file.Path -interface Resource { +sealed interface Resource { val name: String val script: String var source: Path? @@ -34,7 +34,3 @@ interface Resource { } } } - -interface ResourceCollection { - val parameters: List -} diff --git a/src/test/kotlin/fr/postgresjson/RequesterTest.kt b/src/test/kotlin/fr/postgresjson/RequesterTest.kt index 6478da1..6aee193 100644 --- a/src/test/kotlin/fr/postgresjson/RequesterTest.kt +++ b/src/test/kotlin/fr/postgresjson/RequesterTest.kt @@ -24,6 +24,27 @@ class RequesterTest : TestAbstract() { assertEquals("test_function", name) } + + @Test + fun `requester constructor function directory`() { + val resources = this::class.java.getResource("/sql/function/Test").toURI() + val name: String = Requester(connection, functionsDirectory = resources) + .getFunction("test_function") + .name + + assertEquals("test_function", name) + } + + @Test + fun `requester constructor query directory`() { + val resources = this::class.java.getResource("/sql/query/Test").toURI() + val name: String = Requester(connection, queriesDirectory = resources) + .getQuery("DeleteTest") + .name + + assertEquals("DeleteTest", name) + } + @Test fun `add function as string`() { val sql = """ From 682fd5caba27c8ff53e57085a4804260f7976760 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Wed, 14 Jul 2021 23:30:29 +0200 Subject: [PATCH 04/28] clean Requester --- .../connexion/FunctionConverter.kt | 6 +- .../postgresjson/connexion/QueryConverter.kt | 6 +- .../fr/postgresjson/connexion/Requester.kt | 67 ++++++------------ .../fr/postgresjson/definition/Function.kt | 2 +- .../fr/postgresjson/definition/Migration.kt | 4 +- .../fr/postgresjson/definition/Query.kt | 3 +- .../fr/postgresjson/definition/Resource.kt | 2 +- .../kotlin/fr/postgresjson/MigrationTest.kt | 6 +- .../kotlin/fr/postgresjson/RequesterTest.kt | 70 +++++++++---------- 9 files changed, 67 insertions(+), 99 deletions(-) diff --git a/src/main/kotlin/fr/postgresjson/connexion/FunctionConverter.kt b/src/main/kotlin/fr/postgresjson/connexion/FunctionConverter.kt index 3b53e92..fd48d53 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/FunctionConverter.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/FunctionConverter.kt @@ -4,13 +4,13 @@ import fr.postgresjson.utils.searchSqlFiles import java.net.URI import fr.postgresjson.definition.Function as DefinitionFunction -fun DefinitionFunction.toConnection(connection: Connection): Function = Function(this, connection) +fun DefinitionFunction.toRunnable(connection: Connection): Function = Function(this, connection) -fun Sequence.toConnection(connection: Connection): Sequence = map { it.toConnection(connection) } +fun Sequence.toRunnable(connection: Connection): Sequence = map { it.toRunnable(connection) } fun Sequence.toMutableMap(): MutableMap = map { it.name to it }.toMap().toMutableMap() internal fun URI.toFunction(connection: Connection): MutableMap = searchSqlFiles() .filterIsInstance(DefinitionFunction::class.java) - .toConnection(connection) + .toRunnable(connection) .toMutableMap() diff --git a/src/main/kotlin/fr/postgresjson/connexion/QueryConverter.kt b/src/main/kotlin/fr/postgresjson/connexion/QueryConverter.kt index 76b0861..ca975b2 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/QueryConverter.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/QueryConverter.kt @@ -4,13 +4,13 @@ import fr.postgresjson.utils.searchSqlFiles import java.net.URI import fr.postgresjson.definition.Query as QueryDefinition -fun QueryDefinition.toConnection(connection: Connection): Query = Query(name, script, connection) +fun QueryDefinition.toRunnable(connection: Connection): Query = Query(name, script, connection) -fun Sequence.toConnection(connection: Connection): Sequence = map { it.toConnection(connection) } +fun Sequence.toRunnable(connection: Connection): Sequence = map { it.toRunnable(connection) } fun Sequence.toMutableMap(): MutableMap = map { it.name to it }.toMap().toMutableMap() internal fun URI.toQuery(connection: Connection): MutableMap = searchSqlFiles() .filterIsInstance(QueryDefinition::class.java) - .toConnection(connection) + .toRunnable(connection) .toMutableMap() diff --git a/src/main/kotlin/fr/postgresjson/connexion/Requester.kt b/src/main/kotlin/fr/postgresjson/connexion/Requester.kt index 664adc8..75626a4 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Requester.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Requester.kt @@ -22,67 +22,46 @@ class Requester( functions = functionsDirectory?.toFunction(connection) ?: mutableMapOf(), ) - fun addQuery(query: Query): Requester { + fun addQuery(query: Query) { queries[query.name] = query - return this } - fun addQuery(query: QueryDefinition): Requester = addQuery(query.name, query.script) + fun addQuery(query: QueryDefinition) = addQuery(query.toRunnable(connection)) - fun addQuery(name: String, sql: String): Requester { + fun addQuery(name: String, sql: String) { addQuery(Query(name, sql, connection)) - return this } - fun addQuery(queriesDirectory: URI): Requester { - queriesDirectory.searchSqlFiles() - .forEach { - if (it is QueryDefinition) { - addQuery(it) - } - } - return this + fun addQuery(queriesDirectory: URI) { + queriesDirectory + .searchSqlFiles() + .filterIsInstance(QueryDefinition::class.java) + .forEach(this::addQuery) } - fun getQueries(): List { - return queries.map { it.value } + fun getQueries(): List = queries.map { it.value } + + fun addFunction(definition: DefinitionFunction) { + definition + .run { toRunnable(connection) } + .run { functions[name] = this } } - fun addFunction(definition: DefinitionFunction): Requester { - functions[definition.name] = Function(definition, connection) - return this + fun addFunction(sql: String) { + DefinitionFunction(sql) + .run { toRunnable(connection) } + .run { functions[name] = this } } - fun addFunction(sql: String): Requester { - DefinitionFunction(sql).let { - functions[it.name] = Function(it, connection) - } - return this - } - - fun addFunction(functionsDirectory: URI): Requester { + fun addFunctions(functionsDirectory: URI) { functionsDirectory.searchSqlFiles() - .forEach { - if (it is DefinitionFunction) { - addFunction(it) - } - } - return this + .filterIsInstance(DefinitionFunction::class.java) + .forEach(this::addFunction) } - fun getFunction(name: String): Function { - if (functions[name] === null) { - throw NoFunctionDefined(name) - } - return functions[name]!! - } + fun getFunction(name: String): Function = functions[name] ?: throw NoFunctionDefined(name) - fun getQuery(path: String): Query { - if (queries[path] === null) { - throw NoQueryDefined(path) - } - return queries[path]!! - } + fun getQuery(path: String): Query = queries[path] ?: throw NoQueryDefined(path) class NoFunctionDefined(name: String) : Exception("No function defined for $name") class NoQueryDefined(path: String) : Exception("No query defined in $path") diff --git a/src/main/kotlin/fr/postgresjson/definition/Function.kt b/src/main/kotlin/fr/postgresjson/definition/Function.kt index cab23c8..dc095dc 100644 --- a/src/main/kotlin/fr/postgresjson/definition/Function.kt +++ b/src/main/kotlin/fr/postgresjson/definition/Function.kt @@ -5,7 +5,7 @@ import java.nio.file.Path class Function( override val script: String, - override var source: Path? = null + override val source: Path? = null ) : Resource, ParametersInterface { val returns: String override val name: String diff --git a/src/main/kotlin/fr/postgresjson/definition/Migration.kt b/src/main/kotlin/fr/postgresjson/definition/Migration.kt index 6b4889c..8c484c1 100644 --- a/src/main/kotlin/fr/postgresjson/definition/Migration.kt +++ b/src/main/kotlin/fr/postgresjson/definition/Migration.kt @@ -4,14 +4,12 @@ import java.nio.file.Path class Migration( override val script: String, - source: Path + override var source: Path ) : Resource { override val name: String val direction: Direction - override var source: Path? = null init { - this.source = source this.direction = source.fileName.toString() .let { when { diff --git a/src/main/kotlin/fr/postgresjson/definition/Query.kt b/src/main/kotlin/fr/postgresjson/definition/Query.kt index 9c698ac..1989f18 100644 --- a/src/main/kotlin/fr/postgresjson/definition/Query.kt +++ b/src/main/kotlin/fr/postgresjson/definition/Query.kt @@ -4,9 +4,8 @@ import java.nio.file.Path class Query( override val script: String, - source: Path + override var source: Path ) : Resource { - override var source: Path? = source override val name: String = getNameFromComment(script) ?: getNameFromFile(source) /** Try to get name from comment in file */ diff --git a/src/main/kotlin/fr/postgresjson/definition/Resource.kt b/src/main/kotlin/fr/postgresjson/definition/Resource.kt index fe34a99..4a96a9b 100644 --- a/src/main/kotlin/fr/postgresjson/definition/Resource.kt +++ b/src/main/kotlin/fr/postgresjson/definition/Resource.kt @@ -7,7 +7,7 @@ import java.nio.file.Path sealed interface Resource { val name: String val script: String - var source: Path? + val source: Path? open class ParseException(message: String, cause: Throwable? = null) : Exception(message, cause) diff --git a/src/test/kotlin/fr/postgresjson/MigrationTest.kt b/src/test/kotlin/fr/postgresjson/MigrationTest.kt index ec4059c..0e4ed2f 100644 --- a/src/test/kotlin/fr/postgresjson/MigrationTest.kt +++ b/src/test/kotlin/fr/postgresjson/MigrationTest.kt @@ -99,8 +99,7 @@ class MigrationTest() : TestAbstract() { run().size `should be equal to` 5 } - val objTest: RequesterTest.ObjTest? = Requester(connection) - .addFunction(resources) + val objTest: RequesterTest.ObjTest? = Requester(connection, functionsDirectory = resources) .getFunction("test_function") .selectOne(listOf("test", "plip")) @@ -115,8 +114,7 @@ class MigrationTest() : TestAbstract() { run().size `should be equal to` 1 } - val objTest: RequesterTest.ObjTest? = Requester(connection) - .addFunction(resources) + val objTest: RequesterTest.ObjTest? = Requester(connection, functionsDirectory = resources) .getFunction("test_function_duplicate") .selectOne(listOf("test")) diff --git a/src/test/kotlin/fr/postgresjson/RequesterTest.kt b/src/test/kotlin/fr/postgresjson/RequesterTest.kt index 6aee193..7522c3b 100644 --- a/src/test/kotlin/fr/postgresjson/RequesterTest.kt +++ b/src/test/kotlin/fr/postgresjson/RequesterTest.kt @@ -15,10 +15,10 @@ class RequesterTest : TestAbstract() { class ObjTest(val name: String, id: UUID = UUID.fromString("5623d902-3067-42f3-bfd9-095dbb12c29f")) : UuidEntity(id) @Test - fun `function toString`() { + fun `requester constructor empty`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() val name: String = Requester(connection) - .addFunction(resources) + .apply { addFunctions(resources) } .getFunction("test_function") .name @@ -58,18 +58,29 @@ class RequesterTest : TestAbstract() { $$ """.trimIndent() val name: String = Requester(connection) - .addFunction(sql) + .apply { addFunction(sql) } .getFunction("test_function") .name assertEquals("test_function", name) } + @Test + fun `add query from string`() { + val result: Int = Requester(connection) + .apply { addQuery("simpleTest", "select 42;") } + .getQuery("simpleTest") + .exec() + .rows[0].getInt(0)!! + + assertEquals(result, 42) + } + @Test fun `get query from file`() { val resources = this::class.java.getResource("/sql/query").toURI() val objTest: ObjTest? = Requester(connection) - .addQuery(resources) + .apply { addQuery(resources) } .getQuery("selectOne") .selectOne() @@ -81,8 +92,7 @@ class RequesterTest : TestAbstract() { fun `get query from file with wrong name throw exception`() { val resources = this::class.java.getResource("/sql/query").toURI() assertThrows(NoQueryDefined::class.java) { - Requester(connection) - .addQuery(resources) + Requester(connection, queriesDirectory = resources) .getQuery("wrongName") } } @@ -90,8 +100,7 @@ class RequesterTest : TestAbstract() { @Test fun `get queries from file`() { val resources = this::class.java.getResource("/sql/query").toURI() - val name: String = Requester(connection) - .addQuery(resources) + val name: String = Requester(connection, queriesDirectory = resources) .getQueries()[0].name assertEquals(name, "DeleteTest") @@ -101,8 +110,7 @@ class RequesterTest : TestAbstract() { fun `get function from file with wrong name throw exception`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() assertThrows(NoFunctionDefined::class.java) { - Requester(connection) - .addFunction(resources) + Requester(connection, functionsDirectory = resources) .getFunction("wrongName") } } @@ -110,8 +118,7 @@ class RequesterTest : TestAbstract() { @Test fun `get function from file`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() - val objTest: ObjTest? = Requester(connection) - .addFunction(resources) + val objTest: ObjTest? = Requester(connection, functionsDirectory = resources) .getFunction("test_function") .selectOne(listOf("test", "plip")) @@ -122,8 +129,7 @@ class RequesterTest : TestAbstract() { @Test fun `call exec on query`() { val resources = this::class.java.getResource("/sql/query").toURI() - val result = Requester(connection) - .addQuery(resources) + val result = Requester(connection, queriesDirectory = resources) .getQuery("selectOne") .exec() @@ -133,8 +139,7 @@ class RequesterTest : TestAbstract() { @Test fun `call exec on function`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() - val result = Requester(connection) - .addFunction(resources) + val result = Requester(connection, functionsDirectory = resources) .getFunction("test_function") .exec(listOf("test", "plip")) @@ -144,8 +149,7 @@ class RequesterTest : TestAbstract() { @Test fun `call sendQuery on query with name`() { val resources = this::class.java.getResource("/sql/query").toURI() - val result = Requester(connection) - .addQuery(resources) + val result = Requester(connection, queriesDirectory = resources) .getQuery("DeleteTest") .sendQuery() @@ -155,8 +159,7 @@ class RequesterTest : TestAbstract() { @Test fun `call sendQuery on function`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() - val result = Requester(connection) - .addFunction(resources) + val result = Requester(connection, functionsDirectory = resources) .getFunction("function_void") .sendQuery(listOf("test")) @@ -166,8 +169,7 @@ class RequesterTest : TestAbstract() { @Test fun `call selectOne on function`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() - val obj: ObjTest = Requester(connection) - .addFunction(resources) + val obj: ObjTest = Requester(connection, functionsDirectory = resources) .getFunction("test_function") .selectOne(mapOf("name" to "myName"))!! @@ -178,8 +180,7 @@ class RequesterTest : TestAbstract() { fun `call selectOne on function with object and named argument`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() val obj2 = ObjTest("original") - val obj: ObjTest = Requester(connection) - .addFunction(resources) + val obj: ObjTest = Requester(connection, functionsDirectory = resources) .getFunction("test_function_object") .selectOne("resource" to obj2)!! @@ -191,8 +192,7 @@ class RequesterTest : TestAbstract() { fun `call selectOne on function with object`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() val obj2 = ObjTest("original") - val obj: ObjTest = Requester(connection) - .addFunction(resources) + val obj: ObjTest = Requester(connection, functionsDirectory = resources) .getFunction("test_function_object") .selectOne(obj2)!! @@ -203,8 +203,7 @@ class RequesterTest : TestAbstract() { @Test fun `call selectOne on function with object and no arguments`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() - val obj: ObjTest = Requester(connection) - .addFunction(resources) + val obj: ObjTest = Requester(connection, functionsDirectory = resources) .getFunction("test_function") .selectOne()!! @@ -214,8 +213,7 @@ class RequesterTest : TestAbstract() { @Test fun `call selectOne on query`() { val resources = this::class.java.getResource("/sql/query").toURI() - val obj: ObjTest = Requester(connection) - .addQuery(resources) + val obj: ObjTest = Requester(connection, queriesDirectory = resources) .getQuery("selectOneWithParameters") .selectOne(mapOf("name" to "myName"))!! @@ -225,8 +223,7 @@ class RequesterTest : TestAbstract() { @Test fun `call select (multiple) on function`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() - val obj: List? = Requester(connection) - .addFunction(resources) + val obj: List? = Requester(connection, functionsDirectory = resources) .getFunction("test_function_multiple") .select(mapOf("name" to "myName")) @@ -236,8 +233,7 @@ class RequesterTest : TestAbstract() { @Test fun `call select paginated on query`() { val resources = this::class.java.getResource("/sql/query").toURI() - val result: Paginated = Requester(connection) - .addQuery(resources) + val result: Paginated = Requester(connection, queriesDirectory = resources) .getQuery("selectPaginated") .select(1, 2, mapOf("name" to "ff")) Assert.assertNotNull(result) @@ -250,8 +246,7 @@ class RequesterTest : TestAbstract() { @Test fun `call select paginated on function`() { val resources = this::class.java.getResource("/sql/function").toURI() - val result: Paginated = Requester(connection) - .addFunction(resources) + val result: Paginated = Requester(connection, functionsDirectory = resources) .getFunction("test_function_paginated") .select(1, 2, mapOf("name" to "ff")) Assert.assertNotNull(result) @@ -264,8 +259,7 @@ class RequesterTest : TestAbstract() { @Test fun `call selectOne on query with extra parameter`() { val resources = this::class.java.getResource("/sql/query").toURI() - val obj: ObjTest = Requester(connection) - .addQuery(resources) + val obj: ObjTest = Requester(connection, queriesDirectory = resources) .getQuery("selectOneWithParameters") .selectOne(mapOf("name" to "myName")) { assertEquals("myName", it!!.name) From b70230fd4cf972bae3177a0025dafe52f653cd5c Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Wed, 14 Jul 2021 23:45:10 +0200 Subject: [PATCH 05/28] remove useless Migration.status() --- .../kotlin/fr/postgresjson/migration/Function.kt | 12 +----------- .../fr/postgresjson/migration/MigrationScript.kt | 12 +----------- .../kotlin/fr/postgresjson/migration/Migrations.kt | 1 - src/test/kotlin/fr/postgresjson/RequesterTest.kt | 10 ++++++++++ 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/main/kotlin/fr/postgresjson/migration/Function.kt b/src/main/kotlin/fr/postgresjson/migration/Function.kt index 71c5a3e..7395f54 100644 --- a/src/main/kotlin/fr/postgresjson/migration/Function.kt +++ b/src/main/kotlin/fr/postgresjson/migration/Function.kt @@ -71,17 +71,7 @@ data class Function( it.sendQuery("ROLLBACK") }.join() - return Status.OK // TODO - } - - override fun status(): Status { - connection.inTransaction { - up() - down() - it.sendQuery("ROLLBACK") - }.join() - - return Status.OK // TODO + return Status.OK } fun copy(): Function { diff --git a/src/main/kotlin/fr/postgresjson/migration/MigrationScript.kt b/src/main/kotlin/fr/postgresjson/migration/MigrationScript.kt index 80a72c2..fccd0dd 100644 --- a/src/main/kotlin/fr/postgresjson/migration/MigrationScript.kt +++ b/src/main/kotlin/fr/postgresjson/migration/MigrationScript.kt @@ -44,17 +44,7 @@ data class MigrationScript( it.sendQuery("ROLLBACK") }.join() - return Migration.Status.OK // TODO - } - - override fun status(): Migration.Status { - connection.inTransaction { - up() - down() - it.sendQuery("ROLLBACK") - }.join() - - return Migration.Status.OK // TODO + return Migration.Status.OK } fun copy(): MigrationScript { diff --git a/src/main/kotlin/fr/postgresjson/migration/Migrations.kt b/src/main/kotlin/fr/postgresjson/migration/Migrations.kt index 984fe0a..751018f 100644 --- a/src/main/kotlin/fr/postgresjson/migration/Migrations.kt +++ b/src/main/kotlin/fr/postgresjson/migration/Migrations.kt @@ -28,7 +28,6 @@ interface Migration { fun up(): Status fun down(): Status fun test(): Status - fun status(): Status enum class Status(i: Int) { OK(2), UP_FAIL(0), DOWN_FAIL(1) } enum class Action { OK, UP, DOWN } diff --git a/src/test/kotlin/fr/postgresjson/RequesterTest.kt b/src/test/kotlin/fr/postgresjson/RequesterTest.kt index 7522c3b..0879d90 100644 --- a/src/test/kotlin/fr/postgresjson/RequesterTest.kt +++ b/src/test/kotlin/fr/postgresjson/RequesterTest.kt @@ -45,6 +45,16 @@ class RequesterTest : TestAbstract() { assertEquals("DeleteTest", name) } + @Test + fun `function toString`() { + val resources = this::class.java.getResource("/sql/function/Test").toURI() + val name: String = Requester(connection, functionsDirectory = resources) + .getFunction("test_function") + .toString() + + assertEquals("test_function", name) + } + @Test fun `add function as string`() { val sql = """ From 8a5697cd31765991070eb914d00c4efa00ec897c Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Wed, 14 Jul 2021 23:51:42 +0200 Subject: [PATCH 06/28] Add tests --- src/test/kotlin/fr/postgresjson/RequesterTest.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/test/kotlin/fr/postgresjson/RequesterTest.kt b/src/test/kotlin/fr/postgresjson/RequesterTest.kt index 0879d90..5f737a4 100644 --- a/src/test/kotlin/fr/postgresjson/RequesterTest.kt +++ b/src/test/kotlin/fr/postgresjson/RequesterTest.kt @@ -231,7 +231,7 @@ class RequesterTest : TestAbstract() { } @Test - fun `call select (multiple) on function`() { + fun `call select (multiple) on function with named argument`() { val resources = this::class.java.getResource("/sql/function/Test").toURI() val obj: List? = Requester(connection, functionsDirectory = resources) .getFunction("test_function_multiple") @@ -240,6 +240,16 @@ class RequesterTest : TestAbstract() { assertEquals("myName", obj!![0].name) } + @Test + fun `call select (multiple) on function with ordered arguments`() { + val resources = this::class.java.getResource("/sql/function/Test").toURI() + val obj: List? = Requester(connection, functionsDirectory = resources) + .getFunction("test_function_multiple") + .select(listOf("myName")) + + assertEquals("myName", obj!![0].name) + } + @Test fun `call select paginated on query`() { val resources = this::class.java.getResource("/sql/query").toURI() From eb3a732440a1d4d26459f6882fceb01dfb10751d Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Thu, 15 Jul 2021 03:07:19 +0200 Subject: [PATCH 07/28] Rename select to selectOne --- .../kotlin/fr/postgresjson/connexion/Connection.kt | 10 +++++----- .../fr/postgresjson/connexion/EmbedExecutable.kt | 4 ++-- .../kotlin/fr/postgresjson/connexion/Executable.kt | 6 +++--- .../kotlin/fr/postgresjson/connexion/Function.kt | 14 +++++++------- src/main/kotlin/fr/postgresjson/connexion/Query.kt | 12 ++++++------ 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt index 2cd0712..8355ef3 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt @@ -49,7 +49,7 @@ class Connection( fun inTransaction(f: (Connection) -> CompletableFuture) = connect().inTransaction(f) - override fun select( + override fun selectOne( sql: String, typeReference: TypeReference, values: List, @@ -71,16 +71,16 @@ class Connection( values: List = emptyList(), noinline block: SelectOneCallback = {} ): R? = - select(sql, object : TypeReference() {}, values, block) + selectOne(sql, object : TypeReference() {}, values, block) - override fun select( + override fun selectOne( sql: String, typeReference: TypeReference, values: Map, block: (QueryResult, R?) -> Unit ): R? { return replaceArgs(sql, values) { - select(this.sql, typeReference, this.parameters, block) + selectOne(this.sql, typeReference, parameters, block) } } @@ -89,7 +89,7 @@ class Connection( values: Map, noinline block: SelectOneCallback = {} ): R? = - select(sql, object : TypeReference() {}, values, block) + selectOne(sql, object : TypeReference() {}, values, block) override fun select( sql: String, diff --git a/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt b/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt index 8936b5c..ec9f1a9 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt @@ -13,13 +13,13 @@ sealed interface EmbedExecutable { /** * Select One entity with list of parameters */ - fun select( + fun selectOne( typeReference: TypeReference, values: List = emptyList(), block: SelectOneCallback = {} ): R? - fun select( + fun selectOne( typeReference: TypeReference, values: Map, block: SelectOneCallback = {} diff --git a/src/main/kotlin/fr/postgresjson/connexion/Executable.kt b/src/main/kotlin/fr/postgresjson/connexion/Executable.kt index 5055063..5684ebd 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Executable.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Executable.kt @@ -7,21 +7,21 @@ import fr.postgresjson.entity.EntityI interface Executable { /* Select One */ - fun select( + fun selectOne( sql: String, typeReference: TypeReference, values: List = emptyList(), block: SelectOneCallback = {} ): R? - fun select( + fun selectOne( sql: String, typeReference: TypeReference, values: Map, block: SelectOneCallback = {} ): R? - /* Select Miltiples */ + /* Select Multiples */ fun select( sql: String, diff --git a/src/main/kotlin/fr/postgresjson/connexion/Function.kt b/src/main/kotlin/fr/postgresjson/connexion/Function.kt index 824f0cb..bdd73c0 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Function.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Function.kt @@ -17,7 +17,7 @@ class Function(val definition: Function, override val connection: Connection) : /** * Select One entity with list of parameters */ - override fun select( + override fun selectOne( typeReference: TypeReference, values: List, block: (QueryResult, R?) -> Unit @@ -25,25 +25,25 @@ class Function(val definition: Function, override val connection: Connection) : val args = compileArgs(values) val sql = "SELECT * FROM ${definition.name} ($args)" - return connection.select(sql, typeReference, values, block) + return connection.selectOne(sql, typeReference, values, block) } inline fun selectOne( values: List = emptyList(), noinline block: SelectOneCallback = {} ): R? = - select(object : TypeReference() {}, values, block) + selectOne(object : TypeReference() {}, values, block) inline fun selectOne( value: R, noinline block: SelectOneCallback = {} ): R? = - select(object : TypeReference() {}, listOf(value), block) + selectOne(object : TypeReference() {}, listOf(value), block) /** * Select One entity with named parameters */ - override fun select( + override fun selectOne( typeReference: TypeReference, values: Map, block: (QueryResult, R?) -> Unit @@ -51,14 +51,14 @@ class Function(val definition: Function, override val connection: Connection) : val args = compileArgs(values) val sql = "SELECT * FROM ${definition.name} ($args)" - return connection.select(sql, typeReference, values, block) + return connection.selectOne(sql, typeReference, values, block) } inline fun selectOne( values: Map, noinline block: SelectOneCallback = {} ): R? = - select(object : TypeReference() {}, values, block) + selectOne(object : TypeReference() {}, values, block) inline fun selectOne( vararg values: Pair, diff --git a/src/main/kotlin/fr/postgresjson/connexion/Query.kt b/src/main/kotlin/fr/postgresjson/connexion/Query.kt index be9109d..620cdff 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Query.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Query.kt @@ -11,33 +11,33 @@ class Query(override val name: String, private val sql: String, override val con /* Select One */ - override fun select( + override fun selectOne( typeReference: TypeReference, values: List, block: (QueryResult, R?) -> Unit ): R? { - return connection.select(this.toString(), typeReference, values, block) + return connection.selectOne(this.toString(), typeReference, values, block) } inline fun selectOne( values: List = emptyList(), noinline block: SelectOneCallback = {} ): R? = - select(object : TypeReference() {}, values, block) + selectOne(object : TypeReference() {}, values, block) - override fun select( + override fun selectOne( typeReference: TypeReference, values: Map, block: (QueryResult, R?) -> Unit ): R? { - return connection.select(this.toString(), typeReference, values, block) + return connection.selectOne(this.toString(), typeReference, values, block) } inline fun selectOne( values: Map, noinline block: SelectOneCallback = {} ): R? = - select(object : TypeReference() {}, values, block) + selectOne(object : TypeReference() {}, values, block) /* Select Multiples */ From 69f85b5bf5e521b7d538b5a0f361e81a470016fe Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Fri, 16 Jul 2021 01:53:12 +0200 Subject: [PATCH 08/28] Extract Reified method to Extension Add update method --- .../fr/postgresjson/connexion/Connection.kt | 4 +- .../postgresjson/connexion/EmbedExecutable.kt | 62 ++++++++- .../connexion/EmbedExecutableReified.kt | 68 +++++++++ .../fr/postgresjson/connexion/Executable.kt | 72 ++++++++++ .../fr/postgresjson/connexion/Function.kt | 129 ++++-------------- .../kotlin/fr/postgresjson/connexion/Query.kt | 91 +++++------- .../kotlin/fr/postgresjson/ConnectionTest.kt | 2 +- src/test/kotlin/fr/postgresjson/EntityTest.kt | 4 +- .../kotlin/fr/postgresjson/MigrationTest.kt | 25 ++-- .../kotlin/fr/postgresjson/RequesterTest.kt | 92 +++++++++---- .../kotlin/fr/postgresjson/SerializerTest.kt | 2 - .../kotlin/fr/postgresjson/TestAbstract.kt | 10 +- .../sql/query/Test/selectMultiple.sql | 4 + .../query/Test/selectMultipleOrderedArgs.sql | 4 + 14 files changed, 357 insertions(+), 212 deletions(-) create mode 100644 src/main/kotlin/fr/postgresjson/connexion/EmbedExecutableReified.kt create mode 100644 src/test/resources/sql/query/Test/selectMultiple.sql create mode 100644 src/test/resources/sql/query/Test/selectMultipleOrderedArgs.sql diff --git a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt index 8355ef3..70cd4c5 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt @@ -44,7 +44,7 @@ class Connection( } fun disconnect() { - connection?.run { disconnect() } + connection?.disconnect() } fun inTransaction(f: (Connection) -> CompletableFuture) = connect().inTransaction(f) @@ -97,7 +97,7 @@ class Connection( values: List, block: (QueryResult, List) -> Unit ): List { - val result = exec(sql, compileArgs(values)) + val result = exec(sql, values) val json = result.rows[0].getString(0) return if (json === null) { listOf() as List diff --git a/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt b/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt index ec9f1a9..79ce38b 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt @@ -10,8 +10,19 @@ sealed interface EmbedExecutable { val name: String /* Select One */ + /** - * Select One entity with list of parameters + * Update [EntityI] with one entity as argument + */ + fun update( + typeReference: TypeReference, + value: R, + block: SelectOneCallback = {} + ): R? = + selectOne(typeReference, listOf(value), block) + + /** + * Select One [EntityI] with [List] of parameters */ fun selectOne( typeReference: TypeReference, @@ -19,26 +30,60 @@ sealed interface EmbedExecutable { block: SelectOneCallback = {} ): R? + /** + * Select One [EntityI] with [Map] of parameters + */ fun selectOne( typeReference: TypeReference, values: Map, block: SelectOneCallback = {} ): R? + /** + * Select One [EntityI] with multiple [Pair] of parameters + */ + fun selectOne( + typeReference: TypeReference, + vararg values: Pair, + block: SelectOneCallback = {} + ): R? = + selectOne(typeReference, values.toMap(), block) + /* Select Multiples */ + + /** + * Select Multiple [EntityI] with [List] of parameters + */ fun select( typeReference: TypeReference>, values: List = emptyList(), block: SelectCallback = {} ): List + /** + * Select Multiple [EntityI] with [Map] of parameters + */ fun select( typeReference: TypeReference>, values: Map, block: SelectCallback = {} ): List + /** + * Select Multiple [EntityI] with multiple [Pair] of parameters + */ + fun select( + typeReference: TypeReference>, + vararg values: Pair, + block: SelectCallback = {} + ): List = + select(typeReference, values.toMap(), block) + /* Select Paginated */ + + /** + * Select Paginated [EntityI] with [Map] of parameters + */ fun select( page: Int, limit: Int, @@ -47,6 +92,18 @@ sealed interface EmbedExecutable { block: SelectPaginatedCallback = {} ): Paginated + /** + * Select Paginated [EntityI] with multiple [Pair] of parameters + */ + fun select( + page: Int, + limit: Int, + typeReference: TypeReference>, + vararg values: Pair, + block: SelectPaginatedCallback = {} + ): Paginated = + select(page, limit, typeReference, values.toMap(), block) + fun exec(values: List = emptyList()): QueryResult fun exec(values: Map): QueryResult fun exec(vararg values: Pair): QueryResult = exec(values.toMap()) @@ -57,6 +114,5 @@ sealed interface EmbedExecutable { fun sendQuery(values: List = emptyList()): Int fun sendQuery(values: Map): Int - fun sendQuery(vararg values: Pair): Int = - sendQuery(values.toMap()) + fun sendQuery(vararg values: Pair): Int = sendQuery(values.toMap()) } diff --git a/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutableReified.kt b/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutableReified.kt new file mode 100644 index 0000000..ce64766 --- /dev/null +++ b/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutableReified.kt @@ -0,0 +1,68 @@ +package fr.postgresjson.connexion + +import com.fasterxml.jackson.core.type.TypeReference +import fr.postgresjson.entity.EntityI + +/* Select One */ + +inline fun EmbedExecutable.update( + value: R, + noinline block: SelectOneCallback = {} +): R? = + update(object : TypeReference() {}, value, block) + +inline fun EmbedExecutable.selectOne( + values: List = emptyList(), + noinline block: SelectOneCallback = {} +): R? = + selectOne(object : TypeReference() {}, values, block) + +inline fun EmbedExecutable.selectOne( + values: Map, + noinline block: SelectOneCallback = {} +): R? = + selectOne(object : TypeReference() {}, values, block) + +inline fun EmbedExecutable.selectOne( + vararg values: Pair, + noinline block: SelectOneCallback = {} +): R? = + selectOne(object : TypeReference() {}, values = values, block) + +/* Select Multiples */ + +inline fun EmbedExecutable.select( + values: List = emptyList(), + noinline block: SelectCallback = {} +): List = + select(object : TypeReference>() {}, values, block) + +inline fun EmbedExecutable.select( + values: Map, + noinline block: SelectCallback = {} +): List = + select(object : TypeReference>() {}, values, block) + +inline fun EmbedExecutable.select( + vararg values: Pair, + noinline block: SelectCallback = {} +): List = + select(object : TypeReference>() {}, values = values, block) + +/* Select Paginated */ + +inline fun EmbedExecutable.select( + page: Int, + limit: Int, + values: Map = emptyMap(), + noinline block: SelectPaginatedCallback = {} +): Paginated = + select(page, limit, object : TypeReference>() {}, values, block) + +inline fun EmbedExecutable.select( + page: Int, + limit: Int, + vararg values: Pair, + noinline block: SelectPaginatedCallback = {} +): Paginated = + select(page, limit, object : TypeReference>() {}, values = values, block) diff --git a/src/main/kotlin/fr/postgresjson/connexion/Executable.kt b/src/main/kotlin/fr/postgresjson/connexion/Executable.kt index 5684ebd..2a7fe5a 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Executable.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Executable.kt @@ -5,8 +5,23 @@ import com.github.jasync.sql.db.QueryResult import fr.postgresjson.entity.EntityI interface Executable { + /* Select One */ + /** + * Update [EntityI] with one entity as argument + */ + fun update( + sql: String, + typeReference: TypeReference, + value: R, + block: SelectOneCallback = {} + ): R? = + selectOne(sql, typeReference, listOf(value), block) + + /** + * Select One [EntityI] with [List] of parameters + */ fun selectOne( sql: String, typeReference: TypeReference, @@ -14,6 +29,9 @@ interface Executable { block: SelectOneCallback = {} ): R? + /** + * Select One [EntityI] with [Map] of parameters + */ fun selectOne( sql: String, typeReference: TypeReference, @@ -21,8 +39,22 @@ interface Executable { block: SelectOneCallback = {} ): R? + /** + * Select One [EntityI] with multiple [Pair] of parameters + */ + fun selectOne( + sql: String, + typeReference: TypeReference, + vararg values: Pair, + block: SelectOneCallback = {} + ): R? = + selectOne(sql, typeReference, values.toMap(), block) + /* Select Multiples */ + /** + * Select Multiple [EntityI] with [List] of parameters + */ fun select( sql: String, typeReference: TypeReference>, @@ -30,6 +62,9 @@ interface Executable { block: SelectCallback = {} ): List + /** + * Select Multiple [EntityI] with [Map] of parameters + */ fun select( sql: String, typeReference: TypeReference>, @@ -37,8 +72,22 @@ interface Executable { block: SelectCallback = {} ): List + /** + * Select Multiple [EntityI] with multiple [Pair] of parameters + */ + fun select( + sql: String, + typeReference: TypeReference>, + vararg values: Pair, + block: SelectCallback = {} + ): List = + select(sql, typeReference, values.toMap(), block) + /* Select Paginated */ + /** + * Select Paginated [EntityI] with [Map] of parameters + */ fun select( sql: String, page: Int, @@ -48,8 +97,31 @@ interface Executable { block: SelectPaginatedCallback = {} ): Paginated + /** + * Select Paginated [EntityI] with multiple [Pair] of parameters + */ + fun select( + sql: String, + page: Int, + limit: Int, + typeReference: TypeReference>, + vararg values: Pair, + block: SelectPaginatedCallback = {} + ): Paginated = + select(sql, page, limit, typeReference, values.toMap(), block) + + fun exec(sql: String, value: R): QueryResult = exec(sql, listOf(value)) fun exec(sql: String, values: List = emptyList()): QueryResult fun exec(sql: String, values: Map): QueryResult + fun exec(sql: String, vararg values: Pair): QueryResult = exec(sql, values.toMap()) + + fun perform(sql: String, value: R) { perform(sql, listOf(value)) } + fun perform(sql: String, values: List) { exec(sql, values) } + fun perform(sql: String, values: Map) { exec(sql, values) } + fun perform(sql: String, vararg values: Pair) = perform(sql, values.toMap()) + + fun sendQuery(sql: String, value: R): Int = sendQuery(sql, listOf(value)) fun sendQuery(sql: String, values: List = emptyList()): Int fun sendQuery(sql: String, values: Map): Int + fun sendQuery(sql: String, vararg values: Pair): Int = sendQuery(sql, values.toMap()) } diff --git a/src/main/kotlin/fr/postgresjson/connexion/Function.kt b/src/main/kotlin/fr/postgresjson/connexion/Function.kt index bdd73c0..613d3bf 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Function.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Function.kt @@ -12,112 +12,64 @@ class Function(val definition: Function, override val connection: Connection) : override val name: String = definition.name + /** + * Update [EntityI] + */ + override fun update( + typeReference: TypeReference, + value: R, + block: (QueryResult, R?) -> Unit + ): R? = + connection.update(compileSql(value), typeReference, value, block) + /* Select One */ /** - * Select One entity with list of parameters + * Select One [EntityI] with [List] of parameters */ override fun selectOne( typeReference: TypeReference, values: List, block: (QueryResult, R?) -> Unit - ): R? { - val args = compileArgs(values) - val sql = "SELECT * FROM ${definition.name} ($args)" - - return connection.selectOne(sql, typeReference, values, block) - } - - inline fun selectOne( - values: List = emptyList(), - noinline block: SelectOneCallback = {} ): R? = - selectOne(object : TypeReference() {}, values, block) - - inline fun selectOne( - value: R, - noinline block: SelectOneCallback = {} - ): R? = - selectOne(object : TypeReference() {}, listOf(value), block) + connection.selectOne(compileSql(values), typeReference, values, block) /** - * Select One entity with named parameters + * Select One [EntityI] with named parameters */ override fun selectOne( typeReference: TypeReference, values: Map, block: (QueryResult, R?) -> Unit - ): R? { - val args = compileArgs(values) - val sql = "SELECT * FROM ${definition.name} ($args)" - - return connection.selectOne(sql, typeReference, values, block) - } - - inline fun selectOne( - values: Map, - noinline block: SelectOneCallback = {} ): R? = - selectOne(object : TypeReference() {}, values, block) - - inline fun selectOne( - vararg values: Pair, - noinline block: SelectOneCallback = {} - ): R? = - selectOne(values.toMap(), block) + connection.selectOne(compileSql(values), typeReference, values, block) /* Select Multiples */ /** - * Select list of entities with list of parameters + * Select multiple [EntityI] with [List] of parameters */ override fun select( typeReference: TypeReference>, values: List, block: (QueryResult, List) -> Unit - ): List { - val args = compileArgs(values) - val sql = "SELECT * FROM ${definition.name} ($args)" - - return connection.select(sql, typeReference, values, block) - } - - inline fun select( - values: List = emptyList(), - noinline block: SelectCallback = {} ): List = - select(object : TypeReference>() {}, values, block) + connection.select(compileSql(values), typeReference, values, block) /** - * Select list of entities with named parameters + * Select multiple [EntityI] with named parameters */ override fun select( typeReference: TypeReference>, values: Map, block: (QueryResult, List) -> Unit - ): List { - val args = compileArgs(values) - val sql = "SELECT * FROM ${definition.name} ($args)" - - return connection.select(sql, typeReference, values, block) - } - - inline fun select( - values: Map, - noinline block: SelectCallback = {} ): List = - select(object : TypeReference>() {}, values, block) - - inline fun select( - vararg values: Pair, - noinline block: SelectCallback = {} - ): List = - select(values.toMap(), block) + connection.select(compileSql(values), typeReference, values, block) /* Select Paginated */ /** - * Select Multiple with pagination + * Select Multiple [EntityI] with pagination */ override fun select( page: Int, @@ -131,43 +83,14 @@ class Function(val definition: Function, override val connection: Connection) : .plus("offset" to offset) .plus("limit" to limit) - val args = compileArgs(newValues) - val sql = "SELECT * FROM ${definition.name} ($args)" - - return connection.select(sql, page, limit, typeReference, values, block) + return connection.select(compileSql(newValues), page, limit, typeReference, values, block) } - inline fun select( - page: Int, - limit: Int, - values: Map = emptyMap(), - noinline block: SelectPaginatedCallback = {} - ): Paginated = - select(page, limit, object : TypeReference>() {}, values, block) + /* Execute function without treatments */ - inline fun select( - page: Int, - limit: Int, - vararg values: Pair, - noinline block: SelectPaginatedCallback = {} - ): Paginated = - select(page, limit, object : TypeReference>() {}, values.toMap(), block) + override fun exec(values: List): QueryResult = connection.exec(compileSql(values), values) - /* Execute function without traitements */ - - override fun exec(values: List): QueryResult { - val args = compileArgs(values) - val sql = "SELECT * FROM ${definition.name} ($args)" - - return connection.exec(sql, values) - } - - override fun exec(values: Map): QueryResult { - val args = compileArgs(values) - val sql = "SELECT * FROM ${definition.name} ($args)" - - return connection.exec(sql, values) - } + override fun exec(values: Map): QueryResult = connection.exec(compileSql(values), values) override fun sendQuery(values: List): Int { exec(values) @@ -179,6 +102,8 @@ class Function(val definition: Function, override val connection: Connection) : return 0 } + private fun compileArgs(value: R): String = compileArgs(listOf(value)) + private fun compileArgs(values: List): String { val placeholders = values .filterIndexed { index, value -> @@ -205,4 +130,8 @@ class Function(val definition: Function, override val connection: Connection) : return placeholders.joinToString(separator = ", ") } + + private fun compileSql(value: R): String = "SELECT * FROM ${definition.name} (${compileArgs(value)})" + private fun compileSql(values: List): String = "SELECT * FROM ${definition.name} (${compileArgs(values)})" + private fun compileSql(values: Map): String = "SELECT * FROM ${definition.name} (${compileArgs(values)})" } diff --git a/src/main/kotlin/fr/postgresjson/connexion/Query.kt b/src/main/kotlin/fr/postgresjson/connexion/Query.kt index 620cdff..4567cfd 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Query.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Query.kt @@ -9,101 +9,78 @@ class Query(override val name: String, private val sql: String, override val con return sql } + /** + * Update [EntityI] + */ + override fun update( + typeReference: TypeReference, + value: R, + block: (QueryResult, R?) -> Unit + ): R? = + connection.update(sql, typeReference, value, block) + /* Select One */ + /** + * Select One [EntityI] with [List] of parameters + */ override fun selectOne( typeReference: TypeReference, values: List, block: (QueryResult, R?) -> Unit - ): R? { - return connection.selectOne(this.toString(), typeReference, values, block) - } - - inline fun selectOne( - values: List = emptyList(), - noinline block: SelectOneCallback = {} ): R? = - selectOne(object : TypeReference() {}, values, block) + connection.selectOne(sql, typeReference, values, block) + /** + * Select One [EntityI] with named parameters + */ override fun selectOne( typeReference: TypeReference, values: Map, block: (QueryResult, R?) -> Unit - ): R? { - return connection.selectOne(this.toString(), typeReference, values, block) - } - - inline fun selectOne( - values: Map, - noinline block: SelectOneCallback = {} ): R? = - selectOne(object : TypeReference() {}, values, block) + connection.selectOne(sql, typeReference, values, block) /* Select Multiples */ + /** + * Select multiple [EntityI] with [List] of parameters + */ override fun select( typeReference: TypeReference>, values: List, block: (QueryResult, List) -> Unit - ): List { - return connection.select(this.toString(), typeReference, values, block) - } - - inline fun select( - values: List = emptyList(), - noinline block: SelectCallback = {} ): List = - select(object : TypeReference>() {}, values, block) + connection.select(sql, typeReference, values, block) override fun select( typeReference: TypeReference>, values: Map, block: (QueryResult, List) -> Unit - ): List { - return connection.select(this.toString(), typeReference, values, block) - } - - inline fun select( - values: Map, - noinline block: SelectCallback = {} ): List = - select(object : TypeReference>() {}, values, block) + connection.select(sql, typeReference, values, block) + /* Select Paginated */ + + /** + * Select Multiple [EntityI] with pagination + */ override fun select( page: Int, limit: Int, typeReference: TypeReference>, values: Map, block: (QueryResult, Paginated) -> Unit - ): Paginated { - return connection.select(this.toString(), page, limit, typeReference, values, block) - } - - /* Select Paginated */ - - inline fun select( - page: Int, - limit: Int, - values: Map = emptyMap(), - noinline block: SelectPaginatedCallback = {} ): Paginated = - select(page, limit, object : TypeReference>() {}, values, block) + connection.select(sql, page, limit, typeReference, values, block) - /* Execute function without traitements */ + /* Execute function without treatments */ - override fun exec(values: List): QueryResult { - return connection.exec(sql, values) - } + override fun exec(values: List): QueryResult = connection.exec(sql, values) - override fun exec(values: Map): QueryResult { - return connection.exec(sql, values) - } + override fun exec(values: Map): QueryResult = connection.exec(sql, values) - override fun sendQuery(values: List): Int { - return connection.sendQuery(sql, values) - } + override fun sendQuery(values: List): Int = connection.sendQuery(sql, values) - override fun sendQuery(values: Map): Int { - return connection.sendQuery(sql, values) - } + override fun sendQuery(values: Map): Int = connection.sendQuery(sql, values) } diff --git a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt index 8dfdc3e..a0645ac 100644 --- a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt +++ b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt @@ -12,7 +12,7 @@ import org.junit.jupiter.api.TestInstance import java.util.UUID @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class ConnectionTest() : TestAbstract() { +class ConnectionTest : TestAbstract() { private class ObjTest(val name: String, id: UUID = UUID.fromString("2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00")) : UuidEntity(id) private class ObjTest2(val title: String, var test: ObjTest?) : UuidEntity() private class ObjTest3(val first: String, var seconde: String, var third: Int) : UuidEntity() diff --git a/src/test/kotlin/fr/postgresjson/EntityTest.kt b/src/test/kotlin/fr/postgresjson/EntityTest.kt index 7b49fdb..1111f8d 100644 --- a/src/test/kotlin/fr/postgresjson/EntityTest.kt +++ b/src/test/kotlin/fr/postgresjson/EntityTest.kt @@ -16,11 +16,11 @@ import java.util.UUID @TestInstance(TestInstance.Lifecycle.PER_CLASS) class EntityTest() { private class User(id: UUID = UUID.randomUUID()) : Entity(id) - private class ObjTest(var name: String) : UuidEntityExtended(User(), User()) + private class ObjTest(val name: String) : UuidEntityExtended(User(), User()) @Test fun getObject() { - val obj: ObjTest? = ObjTest("plop") + val obj = ObjTest("plop") assertTrue(obj is ObjTest) assertTrue(obj is UuidEntityExtended) assertTrue(obj is EntityI) diff --git a/src/test/kotlin/fr/postgresjson/MigrationTest.kt b/src/test/kotlin/fr/postgresjson/MigrationTest.kt index 0e4ed2f..6d97a34 100644 --- a/src/test/kotlin/fr/postgresjson/MigrationTest.kt +++ b/src/test/kotlin/fr/postgresjson/MigrationTest.kt @@ -1,6 +1,7 @@ package fr.postgresjson import fr.postgresjson.connexion.Requester +import fr.postgresjson.connexion.selectOne import fr.postgresjson.migration.Migration import fr.postgresjson.migration.Migrations import org.amshove.kluent.`should be equal to` @@ -13,10 +14,10 @@ import org.junit.jupiter.api.TestInstance import java.util.UUID @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class MigrationTest() : TestAbstract() { +class MigrationTest : TestAbstract() { @Test fun `run up query`() { - val resources = this::class.java.getResource("/sql/migrations").toURI() + val resources = this::class.java.getResource("/sql/migrations")!!.toURI() val m = Migrations(connection, resources) m.up().apply { this `should contain` Pair("1", Migration.Status.OK) @@ -28,7 +29,7 @@ class MigrationTest() : TestAbstract() { @Test fun `migration up Query should throw error if no down`() { - val resources = this::class.java.getResource("/sql/migration_without_down").toURI() + val resources = this::class.java.getResource("/sql/migration_without_down")!!.toURI() invoking { Migrations(resources, connection) } shouldThrow Migrations.DownMigrationNotDefined::class @@ -36,7 +37,7 @@ class MigrationTest() : TestAbstract() { @Test fun `run forced down query`() { - val resources = this::class.java.getResource("/sql/migrations").toURI() + val resources = this::class.java.getResource("/sql/migrations")!!.toURI() val m = Migrations(resources, connection) repeat(3) { m.down(true).apply { @@ -48,7 +49,7 @@ class MigrationTest() : TestAbstract() { @Test fun `run dry migrations`() { - val resources = this::class.java.getResource("/sql/real_migrations").toURI() + val resources = this::class.java.getResource("/sql/real_migrations")!!.toURI() Migrations(resources, connection).apply { runDry().size `should be equal to` 2 } @@ -59,7 +60,7 @@ class MigrationTest() : TestAbstract() { @Test fun `run dry migrations launch twice`() { - val resources = this::class.java.getResource("/sql/real_migrations").toURI() + val resources = this::class.java.getResource("/sql/real_migrations")!!.toURI() Migrations(resources, connection).apply { runDry().size `should be equal to` 2 runDry().size `should be equal to` 2 @@ -68,7 +69,7 @@ class MigrationTest() : TestAbstract() { @Test fun `run migrations`() { - val resources = this::class.java.getResource("/sql/real_migrations").toURI() + val resources = this::class.java.getResource("/sql/real_migrations")!!.toURI() Migrations(resources, connection).apply { run().apply { size `should be equal to` 1 @@ -78,8 +79,8 @@ class MigrationTest() : TestAbstract() { @Test fun `run migrations force down`() { - val resources = this::class.java.getResource("/sql/real_migrations").toURI() - val resourcesFunctions = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/real_migrations")!!.toURI() + val resourcesFunctions = this::class.java.getResource("/sql/function/Test")!!.toURI() Migrations(listOf(resources, resourcesFunctions), connection).apply { up().apply { size `should be equal to` 6 @@ -94,7 +95,7 @@ class MigrationTest() : TestAbstract() { @Test fun `run functions migrations`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/function/Test")!!.toURI() Migrations(resources, connection).apply { run().size `should be equal to` 5 } @@ -109,7 +110,7 @@ class MigrationTest() : TestAbstract() { @Test fun `run functions migrations and drop if exist`() { - val resources = this::class.java.getResource("/sql/function/Test1").toURI() + val resources = this::class.java.getResource("/sql/function/Test1")!!.toURI() Migrations(resources, connection).apply { run().size `should be equal to` 1 } @@ -121,7 +122,7 @@ class MigrationTest() : TestAbstract() { Assertions.assertEquals(objTest!!.id, UUID.fromString("457daad5-4f1b-4eb7-80ec-6882adb8cc7d")) Assertions.assertEquals(objTest.name, "test") - val resources2 = this::class.java.getResource("/sql/function/Test2").toURI() + val resources2 = this::class.java.getResource("/sql/function/Test2")!!.toURI() Migrations(resources2, connection).apply { run().size `should be equal to` 1 } diff --git a/src/test/kotlin/fr/postgresjson/RequesterTest.kt b/src/test/kotlin/fr/postgresjson/RequesterTest.kt index 5f737a4..8a529ef 100644 --- a/src/test/kotlin/fr/postgresjson/RequesterTest.kt +++ b/src/test/kotlin/fr/postgresjson/RequesterTest.kt @@ -4,6 +4,9 @@ import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester.NoFunctionDefined import fr.postgresjson.connexion.Requester.NoQueryDefined +import fr.postgresjson.connexion.select +import fr.postgresjson.connexion.selectOne +import fr.postgresjson.connexion.update import fr.postgresjson.entity.UuidEntity import org.junit.Assert import org.junit.jupiter.api.Assertions.assertEquals @@ -16,7 +19,7 @@ class RequesterTest : TestAbstract() { @Test fun `requester constructor empty`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/function/Test")!!.toURI() val name: String = Requester(connection) .apply { addFunctions(resources) } .getFunction("test_function") @@ -27,7 +30,7 @@ class RequesterTest : TestAbstract() { @Test fun `requester constructor function directory`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/function/Test")?.toURI() val name: String = Requester(connection, functionsDirectory = resources) .getFunction("test_function") .name @@ -37,7 +40,7 @@ class RequesterTest : TestAbstract() { @Test fun `requester constructor query directory`() { - val resources = this::class.java.getResource("/sql/query/Test").toURI() + val resources = this::class.java.getResource("/sql/query/Test")?.toURI() val name: String = Requester(connection, queriesDirectory = resources) .getQuery("DeleteTest") .name @@ -47,7 +50,7 @@ class RequesterTest : TestAbstract() { @Test fun `function toString`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/function/Test")?.toURI() val name: String = Requester(connection, functionsDirectory = resources) .getFunction("test_function") .toString() @@ -88,7 +91,7 @@ class RequesterTest : TestAbstract() { @Test fun `get query from file`() { - val resources = this::class.java.getResource("/sql/query").toURI() + val resources = this::class.java.getResource("/sql/query")!!.toURI() val objTest: ObjTest? = Requester(connection) .apply { addQuery(resources) } .getQuery("selectOne") @@ -100,7 +103,7 @@ class RequesterTest : TestAbstract() { @Test fun `get query from file with wrong name throw exception`() { - val resources = this::class.java.getResource("/sql/query").toURI() + val resources = this::class.java.getResource("/sql/query")?.toURI() assertThrows(NoQueryDefined::class.java) { Requester(connection, queriesDirectory = resources) .getQuery("wrongName") @@ -109,7 +112,7 @@ class RequesterTest : TestAbstract() { @Test fun `get queries from file`() { - val resources = this::class.java.getResource("/sql/query").toURI() + val resources = this::class.java.getResource("/sql/query")?.toURI() val name: String = Requester(connection, queriesDirectory = resources) .getQueries()[0].name @@ -118,7 +121,7 @@ class RequesterTest : TestAbstract() { @Test fun `get function from file with wrong name throw exception`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/function/Test")?.toURI() assertThrows(NoFunctionDefined::class.java) { Requester(connection, functionsDirectory = resources) .getFunction("wrongName") @@ -127,7 +130,7 @@ class RequesterTest : TestAbstract() { @Test fun `get function from file`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/function/Test")?.toURI() val objTest: ObjTest? = Requester(connection, functionsDirectory = resources) .getFunction("test_function") .selectOne(listOf("test", "plip")) @@ -138,7 +141,7 @@ class RequesterTest : TestAbstract() { @Test fun `call exec on query`() { - val resources = this::class.java.getResource("/sql/query").toURI() + val resources = this::class.java.getResource("/sql/query")?.toURI() val result = Requester(connection, queriesDirectory = resources) .getQuery("selectOne") .exec() @@ -148,7 +151,7 @@ class RequesterTest : TestAbstract() { @Test fun `call exec on function`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/function/Test")?.toURI() val result = Requester(connection, functionsDirectory = resources) .getFunction("test_function") .exec(listOf("test", "plip")) @@ -158,7 +161,7 @@ class RequesterTest : TestAbstract() { @Test fun `call sendQuery on query with name`() { - val resources = this::class.java.getResource("/sql/query").toURI() + val resources = this::class.java.getResource("/sql/query")?.toURI() val result = Requester(connection, queriesDirectory = resources) .getQuery("DeleteTest") .sendQuery() @@ -168,7 +171,7 @@ class RequesterTest : TestAbstract() { @Test fun `call sendQuery on function`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/function/Test")?.toURI() val result = Requester(connection, functionsDirectory = resources) .getFunction("function_void") .sendQuery(listOf("test")) @@ -178,7 +181,7 @@ class RequesterTest : TestAbstract() { @Test fun `call selectOne on function`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/function/Test")?.toURI() val obj: ObjTest = Requester(connection, functionsDirectory = resources) .getFunction("test_function") .selectOne(mapOf("name" to "myName"))!! @@ -188,7 +191,7 @@ class RequesterTest : TestAbstract() { @Test fun `call selectOne on function with object and named argument`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/function/Test")?.toURI() val obj2 = ObjTest("original") val obj: ObjTest = Requester(connection, functionsDirectory = resources) .getFunction("test_function_object") @@ -200,11 +203,11 @@ class RequesterTest : TestAbstract() { @Test fun `call selectOne on function with object`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/function/Test")?.toURI() val obj2 = ObjTest("original") val obj: ObjTest = Requester(connection, functionsDirectory = resources) .getFunction("test_function_object") - .selectOne(obj2)!! + .update(obj2)!! assertEquals("changedName", obj.name) assertEquals("original", obj2.name) @@ -212,7 +215,7 @@ class RequesterTest : TestAbstract() { @Test fun `call selectOne on function with object and no arguments`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() + val resources = this::class.java.getResource("/sql/function/Test")?.toURI() val obj: ObjTest = Requester(connection, functionsDirectory = resources) .getFunction("test_function") .selectOne()!! @@ -222,7 +225,7 @@ class RequesterTest : TestAbstract() { @Test fun `call selectOne on query`() { - val resources = this::class.java.getResource("/sql/query").toURI() + val resources = this::class.java.getResource("/sql/query")?.toURI() val obj: ObjTest = Requester(connection, queriesDirectory = resources) .getQuery("selectOneWithParameters") .selectOne(mapOf("name" to "myName"))!! @@ -232,27 +235,60 @@ class RequesterTest : TestAbstract() { @Test fun `call select (multiple) on function with named argument`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() - val obj: List? = Requester(connection, functionsDirectory = resources) + val resources = this::class.java.getResource("/sql/function/Test")?.toURI() + val obj: List = Requester(connection, functionsDirectory = resources) .getFunction("test_function_multiple") .select(mapOf("name" to "myName")) - assertEquals("myName", obj!![0].name) + assertEquals("myName", obj[0].name) } @Test fun `call select (multiple) on function with ordered arguments`() { - val resources = this::class.java.getResource("/sql/function/Test").toURI() - val obj: List? = Requester(connection, functionsDirectory = resources) + val resources = this::class.java.getResource("/sql/function/Test")?.toURI() + val obj: List = Requester(connection, functionsDirectory = resources) .getFunction("test_function_multiple") .select(listOf("myName")) - assertEquals("myName", obj!![0].name) + assertEquals("myName", obj[0].name) + } + + @Test + fun `call select multiple (named arguments)`() { + val resources = this::class.java.getResource("/sql/query")?.toURI() + val result: List = Requester(connection, queriesDirectory = resources) + .getQuery("selectMultiple") + .select(mapOf("name" to "ff")) + Assert.assertNotNull(result) + Assert.assertEquals("ff", result[0].name) + Assert.assertEquals("ff-2", result[1].name) + } + + @Test + fun `call select multiple (named arguments as pair)`() { + val resources = this::class.java.getResource("/sql/query")?.toURI() + val result: List = Requester(connection, queriesDirectory = resources) + .getQuery("selectMultiple") + .select("name" to "ff") + Assert.assertNotNull(result) + Assert.assertEquals("ff", result[0].name) + Assert.assertEquals("ff-2", result[1].name) + } + + @Test + fun `call select multiple (ordered argument)`() { + val resources = this::class.java.getResource("/sql/query")?.toURI() + val result: List = Requester(connection, queriesDirectory = resources) + .getQuery("selectMultipleOrderedArgs") + .select(listOf("ff", "aa")) + Assert.assertNotNull(result) + Assert.assertEquals("ff", result[0].name) + Assert.assertEquals("aa-2", result[1].name) } @Test fun `call select paginated on query`() { - val resources = this::class.java.getResource("/sql/query").toURI() + val resources = this::class.java.getResource("/sql/query")?.toURI() val result: Paginated = Requester(connection, queriesDirectory = resources) .getQuery("selectPaginated") .select(1, 2, mapOf("name" to "ff")) @@ -265,7 +301,7 @@ class RequesterTest : TestAbstract() { @Test fun `call select paginated on function`() { - val resources = this::class.java.getResource("/sql/function").toURI() + val resources = this::class.java.getResource("/sql/function")?.toURI() val result: Paginated = Requester(connection, functionsDirectory = resources) .getFunction("test_function_paginated") .select(1, 2, mapOf("name" to "ff")) @@ -278,7 +314,7 @@ class RequesterTest : TestAbstract() { @Test fun `call selectOne on query with extra parameter`() { - val resources = this::class.java.getResource("/sql/query").toURI() + val resources = this::class.java.getResource("/sql/query")?.toURI() val obj: ObjTest = Requester(connection, queriesDirectory = resources) .getQuery("selectOneWithParameters") .selectOne(mapOf("name" to "myName")) { diff --git a/src/test/kotlin/fr/postgresjson/SerializerTest.kt b/src/test/kotlin/fr/postgresjson/SerializerTest.kt index eee238f..b8d7b3a 100644 --- a/src/test/kotlin/fr/postgresjson/SerializerTest.kt +++ b/src/test/kotlin/fr/postgresjson/SerializerTest.kt @@ -2,7 +2,6 @@ package fr.postgresjson import fr.postgresjson.entity.UuidEntity import fr.postgresjson.serializer.Serializer -import fr.postgresjson.serializer.deserialize import fr.postgresjson.serializer.serialize import org.joda.time.DateTime import org.junit.jupiter.api.Assertions.assertEquals @@ -21,7 +20,6 @@ internal class SerializerTest { private val objSerialized: String = """{"val1":"plop","val2":123,"id":"829b1a29-5db8-47f9-9562-961c561ac528"}""" private val objSerializedWithExtra: String = """{"val1":"plop","val2":123,"id":"829b1a29-5db8-47f9-9562-961c561ac528","toto":"tata"}""" - private val objSerializedUpdate = """{"val1":"update","val2":123}""" private lateinit var obj: ObjTest @BeforeEach diff --git a/src/test/kotlin/fr/postgresjson/TestAbstract.kt b/src/test/kotlin/fr/postgresjson/TestAbstract.kt index dce0d78..433ed07 100644 --- a/src/test/kotlin/fr/postgresjson/TestAbstract.kt +++ b/src/test/kotlin/fr/postgresjson/TestAbstract.kt @@ -13,7 +13,7 @@ abstract class TestAbstract { @BeforeEach fun beforeAll() { - val initSQL = File(this::class.java.getResource("/fixtures/init.sql").toURI()) + val initSQL = File(this::class.java.getResource("/fixtures/init.sql")!!.toURI()) connection .connect() .sendQuery(initSQL.readText()) @@ -22,9 +22,9 @@ abstract class TestAbstract { @AfterEach fun afterAll() { - val downSQL = File(this::class.java.getResource("/fixtures/down.sql").toURI()) - connection.connect().apply { - sendQuery(downSQL.readText()).join() - }.disconnect() + val downSQL = File(this::class.java.getResource("/fixtures/down.sql")!!.toURI()) + connection + .apply { connect().sendQuery(downSQL.readText()).join() } + .disconnect() } } diff --git a/src/test/resources/sql/query/Test/selectMultiple.sql b/src/test/resources/sql/query/Test/selectMultiple.sql new file mode 100644 index 0000000..a8e41d6 --- /dev/null +++ b/src/test/resources/sql/query/Test/selectMultiple.sql @@ -0,0 +1,4 @@ +SELECT json_build_array( + json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', :name::text), + json_build_object('id', '6085c12e-e94d-4ae1-b7ad-23acc7a82a98', 'name', :name::text || '-2') +) \ No newline at end of file diff --git a/src/test/resources/sql/query/Test/selectMultipleOrderedArgs.sql b/src/test/resources/sql/query/Test/selectMultipleOrderedArgs.sql new file mode 100644 index 0000000..b7764a4 --- /dev/null +++ b/src/test/resources/sql/query/Test/selectMultipleOrderedArgs.sql @@ -0,0 +1,4 @@ +SELECT json_build_array( + json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', ?::text), + json_build_object('id', '6085c12e-e94d-4ae1-b7ad-23acc7a82a98', 'name', ?::text || '-2') +) \ No newline at end of file From 95e7a3274714b0b3f2d5f1386752cbd2ab4bf45e Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sat, 17 Jul 2021 00:24:31 +0200 Subject: [PATCH 09/28] Extract Reified method of connection to Extension Add update method --- .../fr/postgresjson/connexion/Connection.kt | 72 +++++------- .../fr/postgresjson/connexion/Executable.kt | 4 +- .../connexion/ExecutableReified.kt | 106 ++++++++++++++++++ .../fr/postgresjson/connexion/Function.kt | 10 -- .../kotlin/fr/postgresjson/connexion/Query.kt | 13 +-- .../fr/postgresjson/migration/Function.kt | 1 + .../postgresjson/migration/MigrationScript.kt | 1 + .../kotlin/fr/postgresjson/ConnectionTest.kt | 2 + 8 files changed, 143 insertions(+), 66 deletions(-) create mode 100644 src/main/kotlin/fr/postgresjson/connexion/ExecutableReified.kt diff --git a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt index 70cd4c5..f04118d 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt @@ -49,6 +49,9 @@ class Connection( fun inTransaction(f: (Connection) -> CompletableFuture) = connect().inTransaction(f) + /** + * Select One [EntityI] with [List] of parameters + */ override fun selectOne( sql: String, typeReference: TypeReference, @@ -66,13 +69,9 @@ class Connection( } } - inline fun selectOne( - sql: String, - values: List = emptyList(), - noinline block: SelectOneCallback = {} - ): R? = - selectOne(sql, object : TypeReference() {}, values, block) - + /** + * Select One [EntityI] with named parameters + */ override fun selectOne( sql: String, typeReference: TypeReference, @@ -84,13 +83,11 @@ class Connection( } } - inline fun selectOne( - sql: String, - values: Map, - noinline block: SelectOneCallback = {} - ): R? = - selectOne(sql, object : TypeReference() {}, values, block) + /* Select Multiples */ + /** + * Select multiple [EntityI] with [List] of parameters + */ override fun select( sql: String, typeReference: TypeReference>, @@ -108,13 +105,25 @@ class Connection( } } - inline fun select( + /** + * Select multiple [EntityI] with [Map] of parameters + */ + override fun select( sql: String, - values: List = emptyList(), - noinline block: SelectCallback = {} - ): List = - select(sql, object : TypeReference>() {}, values, block) + typeReference: TypeReference>, + values: Map, + block: (QueryResult, List) -> Unit + ): List { + return replaceArgs(sql, values) { + select(this.sql, typeReference, this.parameters, block) + } + } + /* Select Paginated */ + + /** + * Select Multiple [EntityI] with pagination + */ override fun select( sql: String, page: Int, @@ -150,33 +159,6 @@ class Connection( } } - inline fun select( - sql: String, - page: Int, - limit: Int, - values: Map = emptyMap(), - noinline block: SelectPaginatedCallback = {} - ): Paginated = - select(sql, page, limit, object : TypeReference>() {}, values, block) - - override fun select( - sql: String, - typeReference: TypeReference>, - values: Map, - block: (QueryResult, List) -> Unit - ): List { - return replaceArgs(sql, values) { - select(this.sql, typeReference, this.parameters, block) - } - } - - inline fun select( - sql: String, - values: Map, - noinline block: SelectCallback = {} - ): List = - select(sql, object : TypeReference>() {}, values, block) - override fun exec(sql: String, values: List): QueryResult { val compiledValues = compileArgs(values) return stopwatchQuery(sql, compiledValues) { diff --git a/src/main/kotlin/fr/postgresjson/connexion/Executable.kt b/src/main/kotlin/fr/postgresjson/connexion/Executable.kt index 2a7fe5a..ba4bf69 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Executable.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Executable.kt @@ -6,7 +6,7 @@ import fr.postgresjson.entity.EntityI interface Executable { - /* Select One */ + /* Update */ /** * Update [EntityI] with one entity as argument @@ -19,6 +19,8 @@ interface Executable { ): R? = selectOne(sql, typeReference, listOf(value), block) + /* Select One */ + /** * Select One [EntityI] with [List] of parameters */ diff --git a/src/main/kotlin/fr/postgresjson/connexion/ExecutableReified.kt b/src/main/kotlin/fr/postgresjson/connexion/ExecutableReified.kt new file mode 100644 index 0000000..25686e1 --- /dev/null +++ b/src/main/kotlin/fr/postgresjson/connexion/ExecutableReified.kt @@ -0,0 +1,106 @@ +package fr.postgresjson.connexion + +import com.fasterxml.jackson.core.type.TypeReference +import fr.postgresjson.entity.EntityI + +/* Update */ + +/** + * Update [EntityI] with one entity as argument + */ +inline fun Executable.update( + sql: String, + value: R, + noinline block: SelectOneCallback = {} +): R? = + update(sql, object : TypeReference() {}, value, block) + +/* Select One */ + +/** + * Select One [EntityI] with [List] of parameters + */ +inline fun Executable.selectOne( + sql: String, + values: List = emptyList(), + noinline block: SelectOneCallback = {} +): R? = + selectOne(sql, object : TypeReference() {}, values, block) + +/** + * Select One [EntityI] with [Map] of parameters + */ +inline fun Executable.selectOne( + sql: String, + values: Map, + noinline block: SelectOneCallback = {} +): R? = + selectOne(sql, object : TypeReference() {}, values, block) + +/** + * Select One [EntityI] with multiple [Pair] of parameters + */ +inline fun Executable.selectOne( + sql: String, + vararg values: Pair, + noinline block: SelectOneCallback = {} +): R? = + selectOne(sql, object : TypeReference() {}, values = values, block) + +/* Select Multiples */ + +/** + * Select Multiple [EntityI] with [List] of parameters + */ +inline fun Executable.select( + sql: String, + values: List = emptyList(), + noinline block: SelectCallback = {} +): List = + select(sql, object : TypeReference>() {}, values, block) + +/** + * Select Multiple [EntityI] with [Map] of parameters + */ +inline fun Executable.select( + sql: String, + values: Map, + noinline block: SelectCallback = {} +): List = + select(sql, object : TypeReference>() {}, values, block) + +/** + * Select Multiple [EntityI] with multiple [Pair] of parameters + */ +inline fun Executable.select( + sql: String, + vararg values: Pair, + noinline block: SelectCallback = {} +): List = + select(sql, object : TypeReference>() {}, values = values, block) + +/* Select Paginated */ + +/** + * Select Paginated [EntityI] with [Map] of parameters + */ +inline fun Executable.select( + sql: String, + page: Int, + limit: Int, + values: Map = emptyMap(), + noinline block: SelectPaginatedCallback = {} +): Paginated = + select(sql, page, limit, object : TypeReference>() {}, values, block) + +/** + * Select Paginated [EntityI] with multiple [Pair] of parameters + */ +inline fun Executable.select( + sql: String, + page: Int, + limit: Int, + vararg values: Pair, + noinline block: SelectPaginatedCallback = {} +): Paginated = + select(sql, page, limit, object : TypeReference>() {}, values = values, block) diff --git a/src/main/kotlin/fr/postgresjson/connexion/Function.kt b/src/main/kotlin/fr/postgresjson/connexion/Function.kt index 613d3bf..867091e 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Function.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Function.kt @@ -12,16 +12,6 @@ class Function(val definition: Function, override val connection: Connection) : override val name: String = definition.name - /** - * Update [EntityI] - */ - override fun update( - typeReference: TypeReference, - value: R, - block: (QueryResult, R?) -> Unit - ): R? = - connection.update(compileSql(value), typeReference, value, block) - /* Select One */ /** diff --git a/src/main/kotlin/fr/postgresjson/connexion/Query.kt b/src/main/kotlin/fr/postgresjson/connexion/Query.kt index 4567cfd..fc3e1ae 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Query.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Query.kt @@ -9,16 +9,6 @@ class Query(override val name: String, private val sql: String, override val con return sql } - /** - * Update [EntityI] - */ - override fun update( - typeReference: TypeReference, - value: R, - block: (QueryResult, R?) -> Unit - ): R? = - connection.update(sql, typeReference, value, block) - /* Select One */ /** @@ -53,6 +43,9 @@ class Query(override val name: String, private val sql: String, override val con ): List = connection.select(sql, typeReference, values, block) + /** + * Select multiple [EntityI] with [Map] of parameters + */ override fun select( typeReference: TypeReference>, values: Map, diff --git a/src/main/kotlin/fr/postgresjson/migration/Function.kt b/src/main/kotlin/fr/postgresjson/migration/Function.kt index 7395f54..b874fb3 100644 --- a/src/main/kotlin/fr/postgresjson/migration/Function.kt +++ b/src/main/kotlin/fr/postgresjson/migration/Function.kt @@ -2,6 +2,7 @@ package fr.postgresjson.migration import com.github.jasync.sql.db.postgresql.exceptions.GenericDatabaseException import fr.postgresjson.connexion.Connection +import fr.postgresjson.connexion.selectOne import fr.postgresjson.migration.Migration.Action import fr.postgresjson.migration.Migration.Status import java.util.Date diff --git a/src/main/kotlin/fr/postgresjson/migration/MigrationScript.kt b/src/main/kotlin/fr/postgresjson/migration/MigrationScript.kt index fccd0dd..67cbc99 100644 --- a/src/main/kotlin/fr/postgresjson/migration/MigrationScript.kt +++ b/src/main/kotlin/fr/postgresjson/migration/MigrationScript.kt @@ -1,6 +1,7 @@ package fr.postgresjson.migration import fr.postgresjson.connexion.Connection +import fr.postgresjson.connexion.selectOne import fr.postgresjson.entity.Entity import fr.postgresjson.migration.Migration.Action import java.util.Date diff --git a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt index a0645ac..8aa0c71 100644 --- a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt +++ b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt @@ -1,6 +1,8 @@ package fr.postgresjson import fr.postgresjson.connexion.Paginated +import fr.postgresjson.connexion.select +import fr.postgresjson.connexion.selectOne import fr.postgresjson.entity.Parameter import fr.postgresjson.entity.UuidEntity import org.junit.Assert.assertEquals From fdc6681f27e1413ddbaf289c2b1098be9a929cb6 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sat, 17 Jul 2021 15:15:59 +0200 Subject: [PATCH 10/28] Add gradle argument to stay on the pg container --- build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index f483389..d97506f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,5 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +val containerAlwaysOn: Boolean by project plugins { jacoco @@ -86,7 +87,7 @@ apply(plugin = "docker-compose") dockerCompose { projectName = "postgres-json" useComposeFiles = listOf("docker-compose.yml") - stopContainers = true + stopContainers = if (project.hasProperty("containerAlwaysOn")) containerAlwaysOn else false isRequiredBy(project.tasks.test) } From aa70ea9170165c40726db151102ce368a1ee199b Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sat, 17 Jul 2021 15:17:38 +0200 Subject: [PATCH 11/28] add extention "toTypeReference" --- src/main/kotlin/fr/postgresjson/serializer/Serializer.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt b/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt index e32ace3..ec19f69 100644 --- a/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt +++ b/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt @@ -47,3 +47,5 @@ class Serializer(val mapper: ObjectMapper = jacksonObjectMapper()) { fun Serializable.serialize(pretty: Boolean = false) = Serializer().serialize(this, pretty) fun List.serialize(pretty: Boolean = false) = Serializer().serialize(this, pretty) inline fun String.deserialize() = Serializer().deserialize(this) + +inline fun T.toTypeReference(): TypeReference = object : TypeReference() {} From a2f265d227cbf1dcf1072494efa161751e31cc78 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sat, 17 Jul 2021 15:18:12 +0200 Subject: [PATCH 12/28] Test connexion update Entity --- src/test/kotlin/fr/postgresjson/ConnectionTest.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt index 8aa0c71..6c1b698 100644 --- a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt +++ b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt @@ -5,6 +5,7 @@ import fr.postgresjson.connexion.select import fr.postgresjson.connexion.selectOne import fr.postgresjson.entity.Parameter import fr.postgresjson.entity.UuidEntity +import fr.postgresjson.serializer.toTypeReference import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue @@ -66,6 +67,15 @@ class ConnectionTest : TestAbstract() { assertEquals(obj.name, "myName") } + @Test + fun `test update Entity`() { + val obj = ObjTest("before", id = UUID.fromString("1e5f5d41-6d14-4007-897b-0ed2616bec96")) + val objUpdated: ObjTest? = connection.update("select ?::jsonb || jsonb_build_object('name', 'after');", obj.toTypeReference(), obj) + assertTrue(objUpdated is ObjTest) + assertTrue(objUpdated!!.id == UUID.fromString("1e5f5d41-6d14-4007-897b-0ed2616bec96")) + assertTrue(objUpdated.name == "after") + } + @Test fun callExec() { val o = ObjTest("myName") From fa66114d0124dc27ecd416c530550beaa21116eb Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sat, 17 Jul 2021 15:39:08 +0200 Subject: [PATCH 13/28] test call request without args --- .../fr/postgresjson/connexion/Executable.kt | 2 +- .../fr/postgresjson/serializer/Serializer.kt | 4 ++-- .../kotlin/fr/postgresjson/ConnectionTest.kt | 20 ++++++++++++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/fr/postgresjson/connexion/Executable.kt b/src/main/kotlin/fr/postgresjson/connexion/Executable.kt index ba4bf69..b7488db 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Executable.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Executable.kt @@ -27,7 +27,7 @@ interface Executable { fun selectOne( sql: String, typeReference: TypeReference, - values: List = emptyList(), + values: List, block: SelectOneCallback = {} ): R? diff --git a/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt b/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt index ec19f69..df8e250 100644 --- a/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt +++ b/src/main/kotlin/fr/postgresjson/serializer/Serializer.kt @@ -3,7 +3,7 @@ package fr.postgresjson.serializer import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.datatype.joda.JodaModule @@ -15,7 +15,7 @@ class Serializer(val mapper: ObjectMapper = jacksonObjectMapper()) { init { val module = SimpleModule() mapper.registerModule(module) - mapper.propertyNamingStrategy = PropertyNamingStrategy.SNAKE_CASE + mapper.propertyNamingStrategy = PropertyNamingStrategies.SNAKE_CASE mapper.registerModule(JodaModule()) mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) diff --git a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt index 6c1b698..4dd9a5d 100644 --- a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt +++ b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt @@ -1,10 +1,12 @@ package fr.postgresjson +import com.fasterxml.jackson.core.type.TypeReference import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.select import fr.postgresjson.connexion.selectOne import fr.postgresjson.entity.Parameter import fr.postgresjson.entity.UuidEntity +import fr.postgresjson.serializer.deserialize import fr.postgresjson.serializer.toTypeReference import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull @@ -13,6 +15,7 @@ import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import java.util.UUID +import kotlin.test.assertNull @TestInstance(TestInstance.Lifecycle.PER_CLASS) class ConnectionTest : TestAbstract() { @@ -51,12 +54,27 @@ class ConnectionTest : TestAbstract() { } @Test - fun callRequestWithArgs() { + fun `test call request with args`() { val result: ObjTest? = connection.selectOne("select json_build_object('id', '2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00', 'name', ?::text)", listOf("myName")) assertNotNull(result) assertEquals("myName", result!!.name) } + @Test + fun `test call request without args`() { + val result: ObjTest? = connection.selectOne("select json_build_object('id', '2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00', 'name', 'myName')", object : TypeReference() {}) { + assertEquals("myName", this.rows[0].getString(0)?.deserialize()?.name) + } + assertNotNull(result) + assertEquals("myName", result!!.name) + } + + @Test + fun `test call request return null`() { + val result: ObjTest? = connection.selectOne("select null;", object : TypeReference() {}) + assertNull(result) + } + @Test fun callRequestWithArgsEntity() { val o = ObjTest("myName", id = UUID.fromString("2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00")) From 23b4a1750514c89097b1d6b80d05ff82ad3d6240 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sat, 17 Jul 2021 16:32:18 +0200 Subject: [PATCH 14/28] Add arguments to gradle test --- build.gradle.kts | 11 +++++++---- gradle.properties | 4 +++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d97506f..09ee113 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -val containerAlwaysOn: Boolean by project +val containerAlwaysOn: String by project +val disableLint: String by project plugins { jacoco @@ -10,7 +11,7 @@ plugins { id("org.jlleitschuh.gradle.ktlint") version "10.0.0" id("org.owasp.dependencycheck") version "6.1.1" id("fr.coppernic.versioning") version "3.2.1" - id("com.avast.gradle.docker-compose") version "0.14.0" + id("com.avast.gradle.docker-compose") version "0.14.4" id("org.sonarqube") version "+" } @@ -43,7 +44,9 @@ tasks.test { useJUnit() useJUnitPlatform() systemProperty("junit.jupiter.execution.parallel.enabled", true) - finalizedBy(tasks.ktlintCheck) + if (disableLint.toBoolean() == false) { + finalizedBy(tasks.ktlintCheck) + } } tasks.jacocoTestReport { @@ -87,7 +90,7 @@ apply(plugin = "docker-compose") dockerCompose { projectName = "postgres-json" useComposeFiles = listOf("docker-compose.yml") - stopContainers = if (project.hasProperty("containerAlwaysOn")) containerAlwaysOn else false + stopContainers = !containerAlwaysOn.toBoolean() isRequiredBy(project.tasks.test) } diff --git a/gradle.properties b/gradle.properties index d1c3969..00a8cd7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,4 +6,6 @@ systemProp.sonar.projectName=PostgresJson systemProp.sonar.organization=flecomte systemProp.sonar.java.coveragePlugin=jacoco systemProp.sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml -org.gradle.jvmargs=-Xmx4096M \ No newline at end of file +org.gradle.jvmargs=-Xmx4096M +containerAlwaysOn=false +disableLint=false \ No newline at end of file From 7a0ef832110ba7233092c55aea9d19677ead067a Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sat, 17 Jul 2021 16:32:49 +0200 Subject: [PATCH 15/28] test call request return nothing --- src/test/kotlin/fr/postgresjson/ConnectionTest.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt index 4dd9a5d..887cba6 100644 --- a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt +++ b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt @@ -75,6 +75,12 @@ class ConnectionTest : TestAbstract() { assertNull(result) } + @Test + fun `test call request return nothing`() { + val result: ObjTest? = connection.selectOne("select * from test where false;", object : TypeReference() {}) + assertNull(result) + } + @Test fun callRequestWithArgsEntity() { val o = ObjTest("myName", id = UUID.fromString("2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00")) From 6e9175d84f5077228789d57d97c2d4ed5b9463ff Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sat, 17 Jul 2021 18:51:28 +0200 Subject: [PATCH 16/28] create Exception QueryError test select paginated without total --- .../fr/postgresjson/connexion/Connection.kt | 56 +++++++++++++++++-- .../kotlin/fr/postgresjson/ConnectionTest.kt | 53 ++++++++++++++++++ 2 files changed, 104 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt index f04118d..fdb4bb1 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt @@ -3,6 +3,8 @@ package fr.postgresjson.connexion import com.fasterxml.jackson.core.type.TypeReference import com.github.jasync.sql.db.Connection import com.github.jasync.sql.db.QueryResult +import com.github.jasync.sql.db.ResultSet +import com.github.jasync.sql.db.general.ArrayRowData import com.github.jasync.sql.db.pool.ConnectionPool import com.github.jasync.sql.db.postgresql.PostgreSQLConnection import com.github.jasync.sql.db.postgresql.PostgreSQLConnectionBuilder @@ -12,6 +14,7 @@ import fr.postgresjson.entity.Serializable import fr.postgresjson.serializer.Serializer import fr.postgresjson.utils.LoggerDelegate import org.slf4j.Logger +import java.lang.ClassCastException import java.util.concurrent.CompletableFuture typealias SelectOneCallback = QueryResult.(T?) -> Unit @@ -142,8 +145,15 @@ class Connection( } return line.run { - val json = rows[0].getString(0) - val entities = if (json === null) { + val firstLine = rows.firstOrNull() ?: queryError("The query has no return", sql, newValues) + if (!(firstLine as ArrayRowData).mapping.keys.contains("total")) queryError("""The query not return the "total" column""", sql, newValues, rows) + val total = try { + firstLine.getInt("total") ?: queryError("The query return \"total\" must not be null", sql, newValues, rows) + } catch (e: ClassCastException) { + queryError("""Column "total" must be an integer""", sql, newValues, rows) + } + val json = firstLine.getString(0) + val entities = if (json == null) { listOf() as List } else { serializer.deserializeList(json, typeReference) @@ -152,7 +162,7 @@ class Connection( entities, offset, limit, - rows[0].getInt("total") ?: error("The query not return total") + total ) }.also { block(line, it) @@ -201,7 +211,7 @@ class Connection( val paramRegex = "(? val name = match.groups[1]!!.value - values[name] ?: values[name.trimStart('_')] ?: error("Parameter $name missing") + values[name] ?: values[name.trimStart('_')] ?: queryError("Parameter $name missing", sql, values) }.toList() var newSql = sql @@ -218,7 +228,7 @@ class Connection( var i = 0 if (values.isNotEmpty()) { val newSql = paramRegex.replace(sql) { - values[i] ?: error("Parameter $i missing") + values[i] ?: queryError("Parameter $i missing", sql, values) val valToReplace = values[i].toString() ++i "'$valToReplace'" @@ -266,4 +276,40 @@ class Connection( throw e } } + + class QueryError(msg: String) : Exception(msg) + + private fun queryError( + msg: String, + sql: String, + parameters: List, + result: ResultSet? = null + ): Nothing = throw QueryError( + """ + |$msg + | + |${parameters.joinToString(", ") { it.toString() }.prependIndent(" > ")} + |${sql.prependIndent(" > ")} + |${result?.let { "-----" }?.prependIndent(" > ")} + |${result?.columnNames()?.joinToString(" | ")?.prependIndent(" > ")} + |${result?.map { it.joinToString(" | ") }?.joinToString("\n")?.prependIndent(" > ")} + """.trimMargin().trim(' ', '\n') + ) + + private fun queryError( + msg: String, + sql: String, + parameters: Map, + result: ResultSet? = null + ): Nothing = throw QueryError( + """ + |$msg + | + |${parameters.map { it.key + ": " + it.value }.joinToString(", ").prependIndent(" > ")} + |${sql.prependIndent(" > ")} + |${result?.let { "-----" }?.prependIndent(" > ")} + |${result?.columnNames()?.joinToString(" | ")?.prependIndent(" > ")} + |${result?.map { it.joinToString(" | ") }?.joinToString("\n")?.prependIndent(" > ")} + """.trimMargin().trim(' ') + ) } diff --git a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt index 887cba6..817d402 100644 --- a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt +++ b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt @@ -1,6 +1,7 @@ package fr.postgresjson import com.fasterxml.jackson.core.type.TypeReference +import fr.postgresjson.connexion.Connection.QueryError import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.select import fr.postgresjson.connexion.selectOne @@ -14,6 +15,7 @@ import org.junit.Assert.assertTrue import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.assertThrows import java.util.UUID import kotlin.test.assertNull @@ -197,6 +199,57 @@ class ConnectionTest : TestAbstract() { assertEquals(result.offset, 0) } + @Test + fun `test select paginated without result`() { + val result: Paginated = connection.select( + """ + SELECT null, + 10 as total + LIMIT :limit + OFFSET :offset + """.trimIndent(), + 1, + 2, + object : TypeReference>() {} + ) + assertNotNull(result) + assertTrue(result.result.isEmpty()) + assertEquals(0, result.result.size) + assertEquals(result.total, 10) + assertEquals(result.offset, 0) + } + + @Test + fun `test select paginated without total`() { + val exception = assertThrows { + val result: Paginated = connection.select( + """ + SELECT null + LIMIT :limit + OFFSET :offset + """.trimIndent(), + 1, + 2, + object : TypeReference>() {} + ) + } + + assertEquals( + """ + |The query not return the "total" column + | + | > offset: 0, limit: 2 + | > SELECT null + | > LIMIT :limit + | > OFFSET :offset + | > ----- + | > ?column? + | > null + """.trimMargin(), + exception.message + ) + } + @Test fun `selectOne with extra parameters`() { val params: Map = mapOf( From 71742c0c4f6543dcb0082a66fc5b4be6870ec584 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sat, 17 Jul 2021 19:06:01 +0200 Subject: [PATCH 17/28] remove default parameter for sendQuery --- src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt b/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt index 79ce38b..c34cdd4 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt @@ -112,7 +112,7 @@ sealed interface EmbedExecutable { fun perform(values: Map) { exec(values) } fun perform(vararg values: Pair) = perform(values.toMap()) - fun sendQuery(values: List = emptyList()): Int + fun sendQuery(values: List): Int fun sendQuery(values: Map): Int fun sendQuery(vararg values: Pair): Int = sendQuery(values.toMap()) } From 35d43abc4b2cd14b8b5c9352efb7ff43c58a0444 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sat, 17 Jul 2021 23:42:55 +0200 Subject: [PATCH 18/28] Add documentation on methods --- .../kotlin/fr/postgresjson/connexion/Connection.kt | 10 +++++++++- .../fr/postgresjson/connexion/EmbedExecutable.kt | 11 ++++++++++- .../kotlin/fr/postgresjson/connexion/Executable.kt | 12 ++++++++++++ .../kotlin/fr/postgresjson/connexion/Function.kt | 6 ++++++ src/main/kotlin/fr/postgresjson/connexion/Query.kt | 6 ++++++ 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt index fdb4bb1..c716ecb 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt @@ -182,6 +182,9 @@ class Connection( } } + /** + * Warning: this method not use prepared statement + */ override fun sendQuery(sql: String, values: List): Int { val compiledValues = compileArgs(values) return stopwatchQuery(sql, compiledValues) { @@ -191,6 +194,9 @@ class Connection( } } + /** + * Warning: this method not use prepared statement + */ override fun sendQuery(sql: String, values: Map): Int { return replaceArgs(sql, values) { sendQuery(this.sql, this.parameters) @@ -224,9 +230,11 @@ class Connection( } private fun replaceArgsIntoSql(sql: String, values: List, block: (String) -> T): T { - val paramRegex = "(? = select(page, limit, typeReference, values.toMap(), block) - fun exec(values: List = emptyList()): QueryResult + fun exec(values: List): QueryResult fun exec(values: Map): QueryResult fun exec(vararg values: Pair): QueryResult = exec(values.toMap()) @@ -112,7 +112,16 @@ sealed interface EmbedExecutable { fun perform(values: Map) { exec(values) } fun perform(vararg values: Pair) = perform(values.toMap()) + /** + * Warning: this method not use prepared statement + */ fun sendQuery(values: List): Int + /** + * Warning: this method not use prepared statement + */ fun sendQuery(values: Map): Int + /** + * Warning: this method not use prepared statement + */ fun sendQuery(vararg values: Pair): Int = sendQuery(values.toMap()) } diff --git a/src/main/kotlin/fr/postgresjson/connexion/Executable.kt b/src/main/kotlin/fr/postgresjson/connexion/Executable.kt index b7488db..9114661 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Executable.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Executable.kt @@ -122,8 +122,20 @@ interface Executable { fun perform(sql: String, values: Map) { exec(sql, values) } fun perform(sql: String, vararg values: Pair) = perform(sql, values.toMap()) + /** + * Warning: this method not use prepared statement + */ fun sendQuery(sql: String, value: R): Int = sendQuery(sql, listOf(value)) + /** + * Warning: this method not use prepared statement + */ fun sendQuery(sql: String, values: List = emptyList()): Int + /** + * Warning: this method not use prepared statement + */ fun sendQuery(sql: String, values: Map): Int + /** + * Warning: this method not use prepared statement + */ fun sendQuery(sql: String, vararg values: Pair): Int = sendQuery(sql, values.toMap()) } diff --git a/src/main/kotlin/fr/postgresjson/connexion/Function.kt b/src/main/kotlin/fr/postgresjson/connexion/Function.kt index 867091e..d110c67 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Function.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Function.kt @@ -82,11 +82,17 @@ class Function(val definition: Function, override val connection: Connection) : override fun exec(values: Map): QueryResult = connection.exec(compileSql(values), values) + /** + * Warning: this method not use prepared statement + */ override fun sendQuery(values: List): Int { exec(values) return 0 } + /** + * Warning: this method not use prepared statement + */ override fun sendQuery(values: Map): Int { exec(values) return 0 diff --git a/src/main/kotlin/fr/postgresjson/connexion/Query.kt b/src/main/kotlin/fr/postgresjson/connexion/Query.kt index fc3e1ae..b4a246f 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Query.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Query.kt @@ -73,7 +73,13 @@ class Query(override val name: String, private val sql: String, override val con override fun exec(values: Map): QueryResult = connection.exec(sql, values) + /** + * Warning: this method not use prepared statement + */ override fun sendQuery(values: List): Int = connection.sendQuery(sql, values) + /** + * Warning: this method not use prepared statement + */ override fun sendQuery(values: Map): Int = connection.sendQuery(sql, values) } From a4a4ef5f6f3fa726fbc49daa990068e953c74122 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sun, 18 Jul 2021 01:19:27 +0200 Subject: [PATCH 19/28] Add more tests remove "sendQuery" on function sendQuery now return QueryResult remove null on queryError message --- .../fr/postgresjson/connexion/Connection.kt | 26 +++---- .../postgresjson/connexion/EmbedExecutable.kt | 13 ---- .../fr/postgresjson/connexion/Executable.kt | 8 +- .../fr/postgresjson/connexion/Function.kt | 16 ---- .../kotlin/fr/postgresjson/connexion/Query.kt | 8 +- .../fr/postgresjson/migration/Function.kt | 28 +++---- .../kotlin/fr/postgresjson/ConnectionTest.kt | 20 ++--- .../kotlin/fr/postgresjson/RequesterTest.kt | 73 +++++++++++++++++-- .../Test/selectMultipleDifferentArgs.sql | 1 + .../query/Test/selectMultipleWithSameArgs.sql | 1 + 10 files changed, 116 insertions(+), 78 deletions(-) create mode 100644 src/test/resources/sql/query/Test/selectMultipleDifferentArgs.sql create mode 100644 src/test/resources/sql/query/Test/selectMultipleWithSameArgs.sql diff --git a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt index c716ecb..9a3609d 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Connection.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Connection.kt @@ -185,11 +185,11 @@ class Connection( /** * Warning: this method not use prepared statement */ - override fun sendQuery(sql: String, values: List): Int { + override fun sendQuery(sql: String, values: List): QueryResult { val compiledValues = compileArgs(values) return stopwatchQuery(sql, compiledValues) { replaceArgsIntoSql(sql, compiledValues) { - connect().sendQuery(it).join().rowsAffected.toInt() + connect().sendQuery(it).join() } } } @@ -197,7 +197,7 @@ class Connection( /** * Warning: this method not use prepared statement */ - override fun sendQuery(sql: String, values: Map): Int { + override fun sendQuery(sql: String, values: Map): QueryResult { return replaceArgs(sql, values) { sendQuery(this.sql, this.parameters) } @@ -217,7 +217,7 @@ class Connection( val paramRegex = "(? val name = match.groups[1]!!.value - values[name] ?: values[name.trimStart('_')] ?: queryError("Parameter $name missing", sql, values) + values[name] ?: values[name.trimStart('_')] ?: queryError("""Parameter "$name" missing""", sql, values) }.toList() var newSql = sql @@ -296,11 +296,11 @@ class Connection( """ |$msg | - |${parameters.joinToString(", ") { it.toString() }.prependIndent(" > ")} + |${parameters.joinToString(", ") { it.toString() }.prependIndent(" > ") ?: ""} |${sql.prependIndent(" > ")} - |${result?.let { "-----" }?.prependIndent(" > ")} - |${result?.columnNames()?.joinToString(" | ")?.prependIndent(" > ")} - |${result?.map { it.joinToString(" | ") }?.joinToString("\n")?.prependIndent(" > ")} + |${result?.let { "-----" }?.prependIndent(" > ") ?: ""} + |${result?.columnNames()?.joinToString(" | ")?.prependIndent(" > ") ?: ""} + |${result?.map { it.joinToString(" | ") }?.joinToString("\n")?.prependIndent(" > ") ?: ""} """.trimMargin().trim(' ', '\n') ) @@ -313,11 +313,11 @@ class Connection( """ |$msg | - |${parameters.map { it.key + ": " + it.value }.joinToString(", ").prependIndent(" > ")} + |${parameters.map { ":" + it.key + " = " + it.value }.joinToString(", ").prependIndent(" > ") ?: ""} |${sql.prependIndent(" > ")} - |${result?.let { "-----" }?.prependIndent(" > ")} - |${result?.columnNames()?.joinToString(" | ")?.prependIndent(" > ")} - |${result?.map { it.joinToString(" | ") }?.joinToString("\n")?.prependIndent(" > ")} - """.trimMargin().trim(' ') + |${result?.let { "-----" }?.prependIndent(" > ") ?: ""} + |${result?.columnNames()?.joinToString(" | ")?.prependIndent(" > ") ?: ""} + |${result?.map { it.joinToString(" | ") }?.joinToString("\n")?.prependIndent(" > ") ?: ""} + """.trimMargin().trim(' ', '\n') ) } diff --git a/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt b/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt index 6ec42b2..3b7b6e4 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/EmbedExecutable.kt @@ -111,17 +111,4 @@ sealed interface EmbedExecutable { fun perform(values: List) { exec(values) } fun perform(values: Map) { exec(values) } fun perform(vararg values: Pair) = perform(values.toMap()) - - /** - * Warning: this method not use prepared statement - */ - fun sendQuery(values: List): Int - /** - * Warning: this method not use prepared statement - */ - fun sendQuery(values: Map): Int - /** - * Warning: this method not use prepared statement - */ - fun sendQuery(vararg values: Pair): Int = sendQuery(values.toMap()) } diff --git a/src/main/kotlin/fr/postgresjson/connexion/Executable.kt b/src/main/kotlin/fr/postgresjson/connexion/Executable.kt index 9114661..5d9b7d8 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Executable.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Executable.kt @@ -125,17 +125,17 @@ interface Executable { /** * Warning: this method not use prepared statement */ - fun sendQuery(sql: String, value: R): Int = sendQuery(sql, listOf(value)) + fun sendQuery(sql: String, value: R): QueryResult = sendQuery(sql, listOf(value)) /** * Warning: this method not use prepared statement */ - fun sendQuery(sql: String, values: List = emptyList()): Int + fun sendQuery(sql: String, values: List): QueryResult /** * Warning: this method not use prepared statement */ - fun sendQuery(sql: String, values: Map): Int + fun sendQuery(sql: String, values: Map): QueryResult /** * Warning: this method not use prepared statement */ - fun sendQuery(sql: String, vararg values: Pair): Int = sendQuery(sql, values.toMap()) + fun sendQuery(sql: String, vararg values: Pair): QueryResult = sendQuery(sql, values.toMap()) } diff --git a/src/main/kotlin/fr/postgresjson/connexion/Function.kt b/src/main/kotlin/fr/postgresjson/connexion/Function.kt index d110c67..83096f6 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Function.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Function.kt @@ -82,22 +82,6 @@ class Function(val definition: Function, override val connection: Connection) : override fun exec(values: Map): QueryResult = connection.exec(compileSql(values), values) - /** - * Warning: this method not use prepared statement - */ - override fun sendQuery(values: List): Int { - exec(values) - return 0 - } - - /** - * Warning: this method not use prepared statement - */ - override fun sendQuery(values: Map): Int { - exec(values) - return 0 - } - private fun compileArgs(value: R): String = compileArgs(listOf(value)) private fun compileArgs(values: List): String { diff --git a/src/main/kotlin/fr/postgresjson/connexion/Query.kt b/src/main/kotlin/fr/postgresjson/connexion/Query.kt index b4a246f..f3bb60b 100644 --- a/src/main/kotlin/fr/postgresjson/connexion/Query.kt +++ b/src/main/kotlin/fr/postgresjson/connexion/Query.kt @@ -76,10 +76,14 @@ class Query(override val name: String, private val sql: String, override val con /** * Warning: this method not use prepared statement */ - override fun sendQuery(values: List): Int = connection.sendQuery(sql, values) + fun sendQuery(values: List): QueryResult = connection.sendQuery(sql, values) /** * Warning: this method not use prepared statement */ - override fun sendQuery(values: Map): Int = connection.sendQuery(sql, values) + fun sendQuery(values: Map): QueryResult = connection.sendQuery(sql, values) + /** + * Warning: this method not use prepared statement + */ + fun sendQuery(vararg values: Pair): QueryResult = sendQuery(values.toMap()) } diff --git a/src/main/kotlin/fr/postgresjson/migration/Function.kt b/src/main/kotlin/fr/postgresjson/migration/Function.kt index b874fb3..05cca70 100644 --- a/src/main/kotlin/fr/postgresjson/migration/Function.kt +++ b/src/main/kotlin/fr/postgresjson/migration/Function.kt @@ -47,21 +47,25 @@ data class Function( } } - this::class.java.classLoader.getResource("sql/migration/insertFunction.sql")!!.readText().let { - connection.selectOne(it, listOf(up.name, up.getDefinition(), up.script, down.script))?.let { function -> + this::class.java.classLoader + .getResource("sql/migration/insertFunction.sql")!!.readText() + .let { connection.selectOne(it, listOf(up.name, up.getDefinition(), up.script, down.script)) } + ?.let { function -> executedAt = function.executedAt doExecute = Action.OK } - } + return Status.OK } override fun down(): Status { connection.sendQuery(down.script) - this::class.java.classLoader.getResource("sql/migration/deleteFunction.sql")!!.readText().let { - connection.sendQuery(it, listOf(down.name)) - } + this::class.java.classLoader + .getResource("sql/migration/deleteFunction.sql")!! + .readText() + .let { connection.sendQuery(it, listOf(down.name)) } + return Status.OK } @@ -75,13 +79,9 @@ data class Function( return Status.OK } - fun copy(): Function { - return this.copy(up = up, down = down, connection = connection, executedAt = executedAt).also { - it.doExecute = this.doExecute - } - } + fun copy(): Function = this + .copy(up = up, down = down, connection = connection, executedAt = executedAt) + .also { it.doExecute = this.doExecute } - infix fun `is different from`(other: DefinitionFunction): Boolean { - return other.script != this.up.script - } + infix fun `is different from`(other: DefinitionFunction): Boolean = other.script != this.up.script } diff --git a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt index 817d402..7d0165f 100644 --- a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt +++ b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt @@ -236,16 +236,16 @@ class ConnectionTest : TestAbstract() { assertEquals( """ - |The query not return the "total" column - | - | > offset: 0, limit: 2 - | > SELECT null - | > LIMIT :limit - | > OFFSET :offset - | > ----- - | > ?column? - | > null - """.trimMargin(), + The query not return the "total" column + + > :offset = 0, :limit = 2 + > SELECT null + > LIMIT :limit + > OFFSET :offset + > ----- + > ?column? + > null + """.trimIndent(), exception.message ) } diff --git a/src/test/kotlin/fr/postgresjson/RequesterTest.kt b/src/test/kotlin/fr/postgresjson/RequesterTest.kt index 8a529ef..ec55c85 100644 --- a/src/test/kotlin/fr/postgresjson/RequesterTest.kt +++ b/src/test/kotlin/fr/postgresjson/RequesterTest.kt @@ -1,5 +1,6 @@ package fr.postgresjson +import fr.postgresjson.connexion.Connection.QueryError import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester.NoFunctionDefined @@ -160,23 +161,83 @@ class RequesterTest : TestAbstract() { } @Test - fun `call sendQuery on query with name`() { + fun `call exec on query with name`() { val resources = this::class.java.getResource("/sql/query")?.toURI() val result = Requester(connection, queriesDirectory = resources) .getQuery("DeleteTest") - .sendQuery() + .exec() - assertEquals(0, result) + assertEquals(0, result.rowsAffected) } @Test - fun `call sendQuery on function`() { + fun `call sendQuery with same name of arguments`() { + val resources = this::class.java.getResource("/sql/query")?.toURI() + Requester(connection, queriesDirectory = resources) + .getQuery("selectMultipleWithSameArgs") + .sendQuery("name" to "myName").run { + assertEquals("myName", rows[0].getString("firstName")) + assertEquals("myName", rows[0].getString("secondName")) + } + } + + @Test + fun `call sendQuery with arguments on not same orders`() { + val resources = this::class.java.getResource("/sql/query")?.toURI() + Requester(connection, queriesDirectory = resources) + .getQuery("selectMultipleDifferentArgs") + .sendQuery("first" to "firstName", "second" to "secondName").run { + assertEquals("firstName", rows[0].getString("firstName")) + assertEquals("secondName", rows[0].getString("secondName")) + } + + Requester(connection, queriesDirectory = resources) + .getQuery("selectMultipleDifferentArgs") + .sendQuery("second" to "secondName", "first" to "firstName").run { + assertEquals("firstName", rows[0].getString("firstName")) + assertEquals("secondName", rows[0].getString("secondName")) + } + + Requester(connection, queriesDirectory = resources) + .getQuery("selectMultipleDifferentArgs") + .sendQuery("second" to "secondName", "first" to "firstName").run { + assertEquals("firstName", rows[0].getString(0)) + assertEquals("secondName", rows[0].getString(1)) + } + } + + @Test + fun `call sendQuery with wrong number of arguments`() { + val resources = this::class.java.getResource("/sql/query")?.toURI() + + assertThrows(QueryError::class.java) { + Requester(connection, queriesDirectory = resources) + .getQuery("selectMultipleDifferentArgs") + .sendQuery("first" to "firstName").run { + assertEquals("firstName", rows[0].getString(0)) + assertEquals("secondName", rows[0].getString(1)) + } + }.let { + assertEquals( + """ + Parameter "second" missing + + > :first = firstName + > SELECT :first::text as "firstName", :second::text as "secondName"; + """.trimIndent(), + it.message + ) + } + } + + @Test + fun `call exec on function with pair as arguments`() { val resources = this::class.java.getResource("/sql/function/Test")?.toURI() val result = Requester(connection, functionsDirectory = resources) .getFunction("function_void") - .sendQuery(listOf("test")) + .exec("name" to "test") - assertEquals(0, result) + assertEquals(1, result.rowsAffected) } @Test diff --git a/src/test/resources/sql/query/Test/selectMultipleDifferentArgs.sql b/src/test/resources/sql/query/Test/selectMultipleDifferentArgs.sql new file mode 100644 index 0000000..b63306e --- /dev/null +++ b/src/test/resources/sql/query/Test/selectMultipleDifferentArgs.sql @@ -0,0 +1 @@ +SELECT :first::text as "firstName", :second::text as "secondName"; \ No newline at end of file diff --git a/src/test/resources/sql/query/Test/selectMultipleWithSameArgs.sql b/src/test/resources/sql/query/Test/selectMultipleWithSameArgs.sql new file mode 100644 index 0000000..9928968 --- /dev/null +++ b/src/test/resources/sql/query/Test/selectMultipleWithSameArgs.sql @@ -0,0 +1 @@ +SELECT :name::text as "firstName", :name::text as "secondName"; \ No newline at end of file From ab1422ec161ac33c488f6d2aecccafb7b5aa5408 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sun, 18 Jul 2021 02:22:08 +0200 Subject: [PATCH 20/28] Escape SQL argument for sendQuery --- .idea/runConfigurations/tests.xml | 6 +++++ .../fr/postgresjson/connexion/Connection.kt | 26 +++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/.idea/runConfigurations/tests.xml b/.idea/runConfigurations/tests.xml index ceb308e..a2d3df1 100644 --- a/.idea/runConfigurations/tests.xml +++ b/.idea/runConfigurations/tests.xml @@ -2,6 +2,12 @@ + + + + inTransaction(f: (Connection) -> CompletableFuture) = connect().inTransaction(f) + fun inTransaction(block: Connection.() -> A?): A? = connect().run { + sendQuery("BEGIN") + try { + block().apply { sendQuery("COMMIT") } + } catch (e: Throwable) { + sendQuery("ROLLBACK") + throw e + } + } /** * Select One [EntityI] with [List] of parameters diff --git a/src/main/kotlin/fr/postgresjson/migration/Function.kt b/src/main/kotlin/fr/postgresjson/migration/Function.kt index 05cca70..20a31dc 100644 --- a/src/main/kotlin/fr/postgresjson/migration/Function.kt +++ b/src/main/kotlin/fr/postgresjson/migration/Function.kt @@ -73,8 +73,8 @@ data class Function( connection.inTransaction { up() down() - it.sendQuery("ROLLBACK") - }.join() + sendQuery("ROLLBACK") + } return Status.OK } diff --git a/src/main/kotlin/fr/postgresjson/migration/MigrationScript.kt b/src/main/kotlin/fr/postgresjson/migration/MigrationScript.kt index 67cbc99..7092879 100644 --- a/src/main/kotlin/fr/postgresjson/migration/MigrationScript.kt +++ b/src/main/kotlin/fr/postgresjson/migration/MigrationScript.kt @@ -42,8 +42,8 @@ data class MigrationScript( connection.inTransaction { up() down() - it.sendQuery("ROLLBACK") - }.join() + sendQuery("ROLLBACK") + } return Migration.Status.OK } diff --git a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt index 4bea302..a388552 100644 --- a/src/test/kotlin/fr/postgresjson/ConnectionTest.kt +++ b/src/test/kotlin/fr/postgresjson/ConnectionTest.kt @@ -360,4 +360,20 @@ class ConnectionTest : TestAbstract() { assertEquals("myName", rows[0].getString(0)) } } + + @Test + fun `select one in transaction`() { + connection.inTransaction { + selectOne( + "SELECT json_build_object('first', :first::json, 'second', :second::json)", + mapOf( + "first" to ParameterObject("one"), + "second" to ParameterObject("two") + ) + ).let { result -> + assertEquals("one", result!!.first.third) + assertEquals("two", result.second.third) + } + } + } } From 2b89fee02726e13a35ce8049725c96a5b26b9b6c Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Tue, 20 Jul 2021 02:06:18 +0200 Subject: [PATCH 28/28] fix doc link --- docs/usage/stored-procedure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/stored-procedure.md b/docs/usage/stored-procedure.md index 3305a7b..02a567e 100644 --- a/docs/usage/stored-procedure.md +++ b/docs/usage/stored-procedure.md @@ -42,7 +42,7 @@ class UserForCreate( ): Serializable ``` 3. and, define Repositories -*[See SQL function](./migrations.md#Stored procedure migrations)* +[See SQL function](../migrations/migrations.md) ```kotlin import fr.postgresjson.connexion.Requester