WIP: Compiled SQL function #33
@@ -35,6 +35,7 @@ class FunctionGenerator(private val functionsDirectories: List<URI>) {
|
|||||||
base + default
|
base + default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<Parameter>.toMapOf(): String {
|
private fun List<Parameter>.toMapOf(): String {
|
||||||
return filter { it.direction == IN || it.direction == INOUT }
|
return filter { it.direction == IN || it.direction == INOUT }
|
||||||
.joinToString(", ", prefix = "mapOf(", postfix = ")") { """"${it.kotlinName}" to ${it.kotlinName}""" }
|
.joinToString(", ", prefix = "mapOf(", postfix = ")") { """"${it.kotlinName}" to ${it.kotlinName}""" }
|
||||||
@@ -49,6 +50,7 @@ class FunctionGenerator(private val functionsDirectories: List<URI>) {
|
|||||||
"character" -> "String"
|
"character" -> "String"
|
||||||
"char" -> "String"
|
"char" -> "String"
|
||||||
"int" -> "Int"
|
"int" -> "Int"
|
||||||
|
"float" -> "Float"
|
||||||
"boolean" -> "Boolean"
|
"boolean" -> "Boolean"
|
||||||
"json" -> "S"
|
"json" -> "S"
|
||||||
"jsonb" -> "S"
|
"jsonb" -> "S"
|
||||||
@@ -69,45 +71,68 @@ class FunctionGenerator(private val functionsDirectories: List<URI>) {
|
|||||||
return name.toCamelCase().trimStart('_')
|
return name.toCamelCase().trimStart('_')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val functions: List<Function>
|
||||||
|
get() = functionsDirectories
|
||||||
|
.flatMap { it.searchSqlFiles() }
|
||||||
|
.filterIsInstance<Function>()
|
||||||
|
|
||||||
fun generate(outputDirectory: URI) {
|
fun generate(outputDirectory: URI) {
|
||||||
File(outputDirectory.path).apply {
|
File(outputDirectory.path).apply {
|
||||||
logger.debug("Create Directory: $absolutePath")
|
logger.debug("Create Directory: $absolutePath")
|
||||||
mkdirs()
|
mkdirs()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.functionsDirectories
|
functions
|
||||||
.flatMap { it.searchSqlFiles() }
|
.map { function ->
|
||||||
.filterIsInstance<Function>()
|
File("${outputDirectory.path}${function.kotlinName}.kt").apply {
|
||||||
.map { it.run {
|
writeText(generate(function))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generate(functionName: String): String {
|
||||||
|
return functions
|
||||||
|
.first { it.name == functionName }
|
||||||
|
.let { generate(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generate(function: Function): String = function.run {
|
||||||
val args = parameters.toKotlinArgs()
|
val args = parameters.toKotlinArgs()
|
||||||
|
|
||||||
File("${outputDirectory.path}${kotlinName}.kt").apply {
|
val hasInputArgs: Boolean = parameters.filter { it.direction != OUT }.any { it.kotlinType == "S" }
|
||||||
logger.debug("Create kotlin file: $absolutePath")
|
val hasReturn: Boolean = parameters.any { it.direction != IN } || (returns != "" && returns != "void")
|
||||||
val hasGenerics: Boolean = parameters.filter { it.direction != OUT }.any { it.kotlinType == "S" }
|
|
||||||
val genericsType = if (hasGenerics) ", S: Serializable" else ""
|
|
||||||
|
|
||||||
val hasReturn = parameters.any { it.direction != IN } || (it.returns != "" && it.returns != "void")
|
val generics = mutableListOf<String>()
|
||||||
val returnTypeGenerics = if (hasReturn) "reified E: EntityI" else ""
|
if (hasReturn) generics.add("reified E: Any?")
|
||||||
val returnType = if (hasReturn) ": List<E>" else ""
|
if (hasInputArgs) generics.add("S: Serializable")
|
||||||
val returnWord = if (hasReturn) "return " else ""
|
|
||||||
val select = if (hasReturn) "select<E>" else "exec"
|
|
||||||
val function = if (hasGenerics || hasReturn) """inline fun <$returnTypeGenerics$genericsType>""" else "fun"
|
|
||||||
|
|
||||||
val importEntityI = if (hasReturn) "import fr.postgresjson.entity.EntityI\n" else ""
|
val functionDecl = if (generics.isNotEmpty()) "inline fun <${generics.joinToString(", ")}>" else "fun"
|
||||||
val importSerializable = if (hasGenerics) "import fr.postgresjson.entity.Serializable\n" else ""
|
|
||||||
val importSelect = if (hasReturn) "import fr.postgresjson.connexion.select\n" else ""
|
|
||||||
|
|
||||||
writeText("""
|
val importSerializable = if (hasInputArgs) "import fr.postgresjson.entity.Serializable\n" else ""
|
||||||
|
|
||||||
|
if (hasReturn) {
|
||||||
|
return """
|
||||||
|
|package fr.postgresjson.functionGenerator.generated
|
||||||
|
|
|
||||||
|
|import com.fasterxml.jackson.core.type.TypeReference
|
||||||
|
|import fr.postgresjson.connexion.Requester
|
||||||
|
|$importSerializable
|
||||||
|
|$functionDecl Requester.$kotlinName($args): E {
|
||||||
|
| return getFunction("$name")
|
||||||
|
| .selectAny<E>(object : TypeReference<E>() {}, ${parameters.toMapOf()})
|
||||||
|
|}
|
||||||
|
""".trimMargin()
|
||||||
|
} else {
|
||||||
|
return """
|
||||||
|package fr.postgresjson.functionGenerator.generated
|
|package fr.postgresjson.functionGenerator.generated
|
||||||
|
|
|
|
||||||
|import fr.postgresjson.connexion.Requester
|
|import fr.postgresjson.connexion.Requester
|
||||||
|$importSelect$importSerializable$importEntityI
|
|$importSerializable
|
||||||
|$function Requester.$kotlinName($args)$returnType {
|
|$functionDecl Requester.$kotlinName($args): Unit {
|
||||||
| ${returnWord}getFunction("${it.name}")
|
| getFunction("$name")
|
||||||
| .$select(${parameters.toMapOf()})
|
| .exec(${parameters.toMapOf()})
|
||||||
|}
|
|}
|
||||||
""".trimMargin())
|
""".trimMargin()
|
||||||
}}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,8 @@ import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@TestInstance(PER_CLASS)
|
@TestInstance(PER_CLASS)
|
||||||
abstract class TestAbstract {
|
open class TestAbstract {
|
||||||
protected val connection = Connection(database = "json_test", username = "test", password = "test", port = 5555)
|
protected val connection = Connection(database = "json_test", username = "test", password = "test", port = 35555)
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun beforeAll() {
|
fun beforeAll() {
|
||||||
|
|||||||
@@ -1,14 +1,103 @@
|
|||||||
package fr.postgresjson.functionGenerator
|
package fr.postgresjson.functionGenerator
|
||||||
|
|
||||||
import java.net.URI
|
import fr.postgresjson.definition.Function
|
||||||
|
import kotlin.test.assertEquals
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
class FunctionGeneratorTest {
|
class FunctionGeneratorTest {
|
||||||
|
private val functionDirectory = this::class.java.getResource("/sql/function/Test")!!.toURI()
|
||||||
|
private val generator = FunctionGenerator(functionDirectory)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun generate() {
|
fun `generate function`() {
|
||||||
val functionDirectory = this::class.java.getResource("/sql/function/Test")!!.toURI()
|
val functionSql = """
|
||||||
FunctionGenerator(functionDirectory)
|
|create or replace function test_function_object (inout resource json)
|
||||||
.generate(URI( "./src/test/kotlin/fr/postgresjson/functionGenerator/generated/"))
|
|language plpgsql
|
||||||
|
|as
|
||||||
|
|$$
|
||||||
|
|begin
|
||||||
|
| resource = json_build_object('id', '1e5f5d41-6d14-4007-897b-0ed2616bec96', 'name', 'changedName');
|
||||||
|
|end;
|
||||||
|
|$$
|
||||||
|
""".trimMargin()
|
||||||
|
|
||||||
|
val expectedGenerated = """
|
||||||
|
|package fr.postgresjson.functionGenerator.generated
|
||||||
|
|
|
||||||
|
|import com.fasterxml.jackson.core.type.TypeReference
|
||||||
|
|import fr.postgresjson.connexion.Requester
|
||||||
|
|import fr.postgresjson.entity.Serializable
|
||||||
|
|
|
||||||
|
|inline fun <reified E: Any?, S: Serializable> Requester.testFunctionObject(resource: S): E {
|
||||||
|
| return getFunction("test_function_object")
|
||||||
|
| .selectAny<E>(object : TypeReference<E>() {}, mapOf("resource" to resource))
|
||||||
|
|}
|
||||||
|
""".trimMargin()
|
||||||
|
|
||||||
|
val generated: String = generator.generate(Function(functionSql))
|
||||||
|
|
||||||
|
assertEquals(expectedGenerated, generated)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate function with return void`() {
|
||||||
|
val functionSql = """
|
||||||
|
|create or replace function test_function_void (name text default 'plop') returns void
|
||||||
|
|language plpgsql
|
||||||
|
|as
|
||||||
|
|$$
|
||||||
|
|begin
|
||||||
|
| perform 1;
|
||||||
|
|end;
|
||||||
|
|$$;
|
||||||
|
""".trimMargin()
|
||||||
|
|
||||||
|
val expectedGenerated = """
|
||||||
|
|package fr.postgresjson.functionGenerator.generated
|
||||||
|
|
|
||||||
|
|import fr.postgresjson.connexion.Requester
|
||||||
|
|
|
||||||
|
|fun Requester.testFunctionVoid(name: String = "plop"): Unit {
|
||||||
|
| getFunction("test_function_void")
|
||||||
|
| .exec(mapOf("name" to name))
|
||||||
|
|}
|
||||||
|
""".trimMargin()
|
||||||
|
|
||||||
|
val generated: String = generator.generate(Function(functionSql))
|
||||||
|
|
||||||
|
assertEquals(expectedGenerated, generated)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generate function with multiple args and defaults`() {
|
||||||
|
val functionSql = """
|
||||||
|
|create or replace function test_function_multiple (name text default 'plop', in hi text default 'hello', out result json)
|
||||||
|
|language plpgsql
|
||||||
|
|as
|
||||||
|
|$$
|
||||||
|
|begin
|
||||||
|
| result = json_build_array(
|
||||||
|
| json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name),
|
||||||
|
| json_build_object('id', '8d20abb0-7f77-4b6c-9991-44acd3c88faa', 'name', hi)
|
||||||
|
| );
|
||||||
|
|end;
|
||||||
|
|$$
|
||||||
|
""".trimMargin()
|
||||||
|
|
||||||
|
val expectedGenerated = """
|
||||||
|
|package fr.postgresjson.functionGenerator.generated
|
||||||
|
|
|
||||||
|
|import com.fasterxml.jackson.core.type.TypeReference
|
||||||
|
|import fr.postgresjson.connexion.Requester
|
||||||
|
|
|
||||||
|
|inline fun <reified E: Any?> Requester.testFunctionMultiple(name: String = "plop", hi: String = "hello"): E {
|
||||||
|
| return getFunction("test_function_multiple")
|
||||||
|
| .selectAny<E>(object : TypeReference<E>() {}, mapOf("name" to name, "hi" to hi))
|
||||||
|
|}
|
||||||
|
""".trimMargin()
|
||||||
|
|
||||||
|
val generated: String = generator.generate(Function(functionSql))
|
||||||
|
|
||||||
|
assertEquals(expectedGenerated, generated)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user