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