WIP: Compiled SQL function #33
@@ -1,7 +1,7 @@
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
val containerAlwaysOn: String by project
|
val containerAlwaysOn: String by project
|
||||||
val disableLint: String by project
|
val disableLint: String by project
|
||||||
val projectName = "postgres-json"
|
val projectName: String by project
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
jacoco
|
jacoco
|
||||||
@@ -87,7 +87,7 @@ val sourcesJar by tasks.creating(Jar::class) {
|
|||||||
|
|
||||||
apply(plugin = "docker-compose")
|
apply(plugin = "docker-compose")
|
||||||
dockerCompose {
|
dockerCompose {
|
||||||
setProjectName(projectName)
|
setProjectName(projectName.toString())
|
||||||
setProperty("useComposeFiles", listOf("docker-compose.yml"))
|
setProperty("useComposeFiles", listOf("docker-compose.yml"))
|
||||||
setProperty("stopContainers", !containerAlwaysOn.toBoolean())
|
setProperty("stopContainers", !containerAlwaysOn.toBoolean())
|
||||||
isRequiredBy(project.tasks.test)
|
isRequiredBy(project.tasks.test)
|
||||||
@@ -96,7 +96,7 @@ dockerCompose {
|
|||||||
publishing {
|
publishing {
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
name = projectName
|
name = projectName.toString()
|
||||||
url = uri("https://maven.pkg.github.com/flecomte/postgres-json")
|
url = uri("https://maven.pkg.github.com/flecomte/postgres-json")
|
||||||
credentials {
|
credentials {
|
||||||
username = System.getenv("GITHUB_USERNAME")
|
username = System.getenv("GITHUB_USERNAME")
|
||||||
@@ -106,7 +106,7 @@ publishing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
publications {
|
publications {
|
||||||
create<MavenPublication>(projectName) {
|
create<MavenPublication>(projectName.toString()) {
|
||||||
from(components["java"])
|
from(components["java"])
|
||||||
artifact(sourcesJar)
|
artifact(sourcesJar)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,3 +8,4 @@ systemProp.sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacoco
|
|||||||
org.gradle.jvmargs=-Xmx4096M
|
org.gradle.jvmargs=-Xmx4096M
|
||||||
containerAlwaysOn=false
|
containerAlwaysOn=false
|
||||||
disableLint=false
|
disableLint=false
|
||||||
|
projectName=postgres-json
|
||||||
@@ -36,5 +36,10 @@ sealed class Returns(
|
|||||||
isSetOf: Boolean,
|
isSetOf: Boolean,
|
||||||
) : Returns("any", isSetOf)
|
) : Returns("any", isSetOf)
|
||||||
|
|
||||||
|
class Unknown(
|
||||||
|
definition: String,
|
||||||
|
isSetOf: Boolean,
|
||||||
|
) : Returns(definition, isSetOf)
|
||||||
|
|
||||||
class Void : Returns("void", false)
|
class Void : Returns("void", false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import fr.postgresjson.definition.Parameter.Direction.OUT
|
|||||||
import fr.postgresjson.definition.ParameterType
|
import fr.postgresjson.definition.ParameterType
|
||||||
import fr.postgresjson.definition.Resource.ParseException
|
import fr.postgresjson.definition.Resource.ParseException
|
||||||
import fr.postgresjson.definition.Returns
|
import fr.postgresjson.definition.Returns
|
||||||
|
import fr.postgresjson.definition.Returns.Primitive
|
||||||
|
import fr.postgresjson.definition.Returns.Unknown
|
||||||
import fr.postgresjson.definition.Returns.Void
|
import fr.postgresjson.definition.Returns.Void
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.text.RegexOption.IGNORE_CASE
|
import kotlin.text.RegexOption.IGNORE_CASE
|
||||||
@@ -34,6 +36,7 @@ internal fun ScriptPart.getFunctionName(): NextScript<String> {
|
|||||||
throw FunctionNameMalformed(this, e)
|
throw FunctionNameMalformed(this, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class FunctionNameMalformed(val script: ScriptPart, cause: Throwable? = null) :
|
internal class FunctionNameMalformed(val script: ScriptPart, cause: Throwable? = null) :
|
||||||
ParseException("Function name is malformed", cause)
|
ParseException("Function name is malformed", cause)
|
||||||
|
|
||||||
@@ -78,6 +81,7 @@ private fun ScriptPart.toParameter(): Parameter {
|
|||||||
default = script.getParameterDefault().trimSpace().apply { script = nextScriptPart }.value,
|
default = script.getParameterDefault().trimSpace().apply { script = nextScriptPart }.value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ScriptPart.getParameterMode(): NextScript<Direction> {
|
private fun ScriptPart.getParameterMode(): NextScript<Direction> {
|
||||||
return when {
|
return when {
|
||||||
restOfScript.startsWith("inout ", true) -> NextScript(INOUT, restOfScript.drop("inout ".length))
|
restOfScript.startsWith("inout ", true) -> NextScript(INOUT, restOfScript.drop("inout ".length))
|
||||||
@@ -95,6 +99,7 @@ private fun ScriptPart.getParameterName(): NextScript<String> {
|
|||||||
throw ParameterNameMalformed(this, e)
|
throw ParameterNameMalformed(this, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ParameterNameMalformed(val script: ScriptPart, cause: Throwable) :
|
private class ParameterNameMalformed(val script: ScriptPart, cause: Throwable) :
|
||||||
ParseException("Parameter name is malformed", cause)
|
ParseException("Parameter name is malformed", cause)
|
||||||
|
|
||||||
@@ -153,8 +158,52 @@ private class ParameterDefaultMalformed(val script: ScriptPart) :
|
|||||||
* TODO Finalize this
|
* TODO Finalize this
|
||||||
*/
|
*/
|
||||||
internal fun ScriptPart.getReturns(): NextScript<Returns> {
|
internal fun ScriptPart.getReturns(): NextScript<Returns> {
|
||||||
|
val rest = this.trimSpace()
|
||||||
|
if (!rest.restOfScript.startsWith("returns")) {
|
||||||
return NextScript(Void(), "")
|
return NextScript(Void(), "")
|
||||||
}
|
}
|
||||||
|
var returns = ScriptPart(rest.restOfScript.drop("returns".length))
|
||||||
|
.getNextScript { this.afterBeginBy(Regex("\\s+language\\s+", IGNORE_CASE), Regex("\\s+as\\s+", IGNORE_CASE)) }
|
||||||
|
.trimSpace()
|
||||||
|
.value
|
||||||
|
.trimStart()
|
||||||
|
|
||||||
|
val isSetOf = returns.startsWith("SETOF", ignoreCase = true)
|
||||||
|
|
||||||
|
if (isSetOf) {
|
||||||
|
returns = returns.drop("SETOF".length).trimStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
val returnsClass = if (returns.isBlank()) {
|
||||||
|
Void()
|
||||||
|
} else if (primitiveList.contains(ScriptPart(returns).getParameterType().value.name)) {
|
||||||
|
Primitive(returns, isSetOf)
|
||||||
|
} else {
|
||||||
|
Unknown(returns, isSetOf)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextScript(returnsClass, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
private val primitiveList = listOf(
|
||||||
|
"text",
|
||||||
|
"varchar",
|
||||||
|
"character varying",
|
||||||
|
"character",
|
||||||
|
"char",
|
||||||
|
"int",
|
||||||
|
"smallint",
|
||||||
|
"integer",
|
||||||
|
"bigint",
|
||||||
|
"decimal",
|
||||||
|
"real",
|
||||||
|
"double precision",
|
||||||
|
"float",
|
||||||
|
"numeric",
|
||||||
|
"boolean",
|
||||||
|
"json",
|
||||||
|
"jsonb",
|
||||||
|
)
|
||||||
|
|
||||||
class ParseError(message: String? = null, cause: Throwable? = null) :
|
class ParseError(message: String? = null, cause: Throwable? = null) :
|
||||||
ParseException(message ?: "Parsing fail", cause)
|
ParseException(message ?: "Parsing fail", cause)
|
||||||
|
|||||||
@@ -156,7 +156,10 @@ internal data class Context(
|
|||||||
val script: String,
|
val script: String,
|
||||||
) {
|
) {
|
||||||
fun afterBeginBy(vararg texts: String): Boolean = texts.any {
|
fun afterBeginBy(vararg texts: String): Boolean = texts.any {
|
||||||
script.substring(index + 1).take(it.length) == it
|
script.drop(index + 1).take(it.length) == it
|
||||||
|
}
|
||||||
|
fun afterBeginBy(vararg texts: Regex): Boolean = texts.any {
|
||||||
|
it.matchAt(script, index + 1) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
val nextChar: Char? get() = script.substring(index + 1).getOrNull(0)
|
val nextChar: Char? get() = script.substring(index + 1).getOrNull(0)
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ import fr.postgresjson.serializer.toTypeReference
|
|||||||
import io.kotest.core.spec.style.StringSpec
|
import io.kotest.core.spec.style.StringSpec
|
||||||
import io.kotest.matchers.nulls.shouldBeNull
|
import io.kotest.matchers.nulls.shouldBeNull
|
||||||
import io.kotest.matchers.nulls.shouldNotBeNull
|
import io.kotest.matchers.nulls.shouldNotBeNull
|
||||||
|
import org.amshove.kluent.`should be equal to`
|
||||||
|
import org.junit.jupiter.api.assertThrows
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import kotlin.reflect.full.hasAnnotation
|
import kotlin.reflect.full.hasAnnotation
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
import org.amshove.kluent.`should be equal to`
|
|
||||||
import org.junit.jupiter.api.assertThrows
|
|
||||||
|
|
||||||
class ConnectionTest : StringSpec({
|
class ConnectionTest : StringSpec({
|
||||||
val connection = TestConnection()
|
val connection = TestConnection()
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package fr.postgresjson.definition
|
package fr.postgresjson.definition
|
||||||
|
|
||||||
|
import fr.postgresjson.definition.Returns.Primitive
|
||||||
import fr.postgresjson.definition.parse.parseFunction
|
import fr.postgresjson.definition.parse.parseFunction
|
||||||
import io.kotest.core.spec.style.FreeSpec
|
import io.kotest.core.spec.style.FreeSpec
|
||||||
import io.kotest.matchers.collections.shouldHaveSize
|
import io.kotest.matchers.collections.shouldHaveSize
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
|
import org.amshove.kluent.shouldBeInstanceOf
|
||||||
|
|
||||||
class FunctionTest : FreeSpec({
|
class FunctionTest : FreeSpec({
|
||||||
"Function name" - {
|
"Function name" - {
|
||||||
@@ -83,7 +85,6 @@ class FunctionTest: FreeSpec({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
"Parameters" - {
|
"Parameters" - {
|
||||||
"One parameter text" - {
|
"One parameter text" - {
|
||||||
val param = parseFunction(
|
val param = parseFunction(
|
||||||
@@ -215,6 +216,50 @@ class FunctionTest: FreeSpec({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"Function Returns" - {
|
||||||
|
"should return the type text" {
|
||||||
|
val returns = parseFunction(
|
||||||
|
// language=PostgreSQL
|
||||||
|
"""
|
||||||
|
create or replace function myfun() returns text language plpgsql as
|
||||||
|
$$ begin; end$$;
|
||||||
|
""".trimIndent()
|
||||||
|
).returns
|
||||||
|
|
||||||
|
returns shouldBeInstanceOf Primitive::class
|
||||||
|
returns.definition shouldBe "text"
|
||||||
|
returns.isSetOf shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
"should return the type character varying" {
|
||||||
|
val returns = parseFunction(
|
||||||
|
// language=PostgreSQL
|
||||||
|
"""
|
||||||
|
create or replace function myfun() returns character varying language plpgsql as
|
||||||
|
$$ begin; end$$;
|
||||||
|
""".trimIndent()
|
||||||
|
).returns
|
||||||
|
|
||||||
|
returns shouldBeInstanceOf Primitive::class
|
||||||
|
returns.definition shouldBe "character varying"
|
||||||
|
returns.isSetOf shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
"should return the type character varying(255)" {
|
||||||
|
val returns = parseFunction(
|
||||||
|
// language=PostgreSQL
|
||||||
|
"""
|
||||||
|
create or replace function myfun() returns character varying(255) language plpgsql as
|
||||||
|
$$ begin; end$$;
|
||||||
|
""".trimIndent()
|
||||||
|
).returns
|
||||||
|
|
||||||
|
returns shouldBeInstanceOf Primitive::class
|
||||||
|
returns.definition shouldBe "character varying(255)"
|
||||||
|
returns.isSetOf shouldBe false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// "function returns" - {
|
// "function returns" - {
|
||||||
// "should return the type text if function return text" {
|
// "should return the type text if function return text" {
|
||||||
// Function(
|
// Function(
|
||||||
|
|||||||
Reference in New Issue
Block a user