WIP: Compiled SQL function #33
@@ -32,6 +32,7 @@ internal fun parseFunction(script: String, source: Path? = null): Function {
|
||||
internal fun ScriptPart.getFunctionName(): NextScript<String> {
|
||||
try {
|
||||
return getNextScript { status.isNotEscaped() && afterBeginBy("(", " ", "\n") }
|
||||
.changeValue { unescapeOrLowercase() }
|
||||
} catch (e: ParseException) {
|
||||
throw FunctionNameMalformed(this, e)
|
||||
}
|
||||
@@ -95,6 +96,7 @@ private fun ScriptPart.getParameterMode(): NextScript<Direction> {
|
||||
private fun ScriptPart.getParameterName(): NextScript<String> {
|
||||
try {
|
||||
return getNextScript { afterBeginBy(" ", "\n") && status.isNotEscaped() }
|
||||
.changeValue { unescapeOrLowercase() }
|
||||
} catch (e: ParseException) {
|
||||
throw ParameterNameMalformed(this, e)
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ internal fun ScriptPart.getNextScript(isEnd: Context.() -> Boolean = { false }):
|
||||
|
||||
if (isEnd(Context(index, c, status.copy(), restOfScript))) {
|
||||
return NextScript(
|
||||
restOfScript.take(index + 1).unescape(),
|
||||
restOfScript.take(index + 1),
|
||||
restOfScript.drop(index + 1),
|
||||
)
|
||||
}
|
||||
@@ -77,13 +77,19 @@ internal fun ScriptPart.getNextScript(isEnd: Context.() -> Boolean = { false }):
|
||||
throw ParseError()
|
||||
}
|
||||
|
||||
private fun String.unescape(): String {
|
||||
internal fun ScriptPart.unescapeOrLowercase(): ScriptPart = restOfScript
|
||||
.run(String::unescapeOrLowercase)
|
||||
.let(::ScriptPart)
|
||||
|
||||
internal fun String.unescapeOrLowercase(): String {
|
||||
val first = take(1)
|
||||
val last = takeLast(1)
|
||||
return if (first == last && first in listOf("\"", "'")) {
|
||||
return if (first == last && first == "'") {
|
||||
drop(1).dropLast(1).replace("$first$first", first).lowercase()
|
||||
} else if (first == last && first == "\"") {
|
||||
drop(1).dropLast(1).replace("$first$first", first)
|
||||
} else {
|
||||
this
|
||||
this.lowercase()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +148,14 @@ internal inline fun ScriptPart.change(block: String.() -> String): ScriptPart {
|
||||
return ScriptPart(restOfScript.run(block))
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
internal inline fun <T> NextScript<T>.changeValue(block: T.() -> T): NextScript<T> {
|
||||
contract {
|
||||
callsInPlace(block, EXACTLY_ONCE)
|
||||
}
|
||||
return NextScript(value.run(block), nextScriptPart.restOfScript)
|
||||
}
|
||||
|
||||
internal fun ScriptPart.getNextInteger(): NextScript<Int?> {
|
||||
val digits = restOfScript.takeWhile { it.isDigit() }
|
||||
val restOfScript = restOfScript.trimStart { it.isDigit() }
|
||||
|
||||
@@ -24,7 +24,7 @@ class FunctionTest : FreeSpec({
|
||||
}
|
||||
}
|
||||
|
||||
"first letter caps" {
|
||||
"first letter caps without quoted" {
|
||||
parseFunction(
|
||||
// language=PostgreSQL
|
||||
"""
|
||||
@@ -32,7 +32,7 @@ class FunctionTest : FreeSpec({
|
||||
$$ begin; end$$;
|
||||
""".trimIndent()
|
||||
).apply {
|
||||
name shouldBe "Myfun"
|
||||
name shouldBe "myfun"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class FunctionTest : FreeSpec({
|
||||
}
|
||||
}
|
||||
|
||||
"escaped name with double quote in name" {
|
||||
"quoted name with double quote in name" {
|
||||
parseFunction(
|
||||
// language=PostgreSQL
|
||||
"""
|
||||
@@ -153,6 +153,20 @@ class FunctionTest : FreeSpec({
|
||||
param[1].name shouldBe "#@€"
|
||||
}
|
||||
}
|
||||
"Parameters with Caps" - {
|
||||
val param = parseFunction(
|
||||
// language=PostgreSQL
|
||||
"""
|
||||
create or replace function myfun("One" text, Two text) returns text language plpgsql as
|
||||
$$ begin end;$$;
|
||||
""".trimIndent()
|
||||
).parameters
|
||||
|
||||
"should have first parameter name" {
|
||||
param[0].name shouldBe "One"
|
||||
param[1].name shouldBe "two"
|
||||
}
|
||||
}
|
||||
|
||||
"Parameters with type `character varying(255)`" - {
|
||||
val param = parseFunction(
|
||||
|
||||
Reference in New Issue
Block a user