diff --git a/src/main/kotlin/fr/postgresjson/definition/parse/ParsingFunction.kt b/src/main/kotlin/fr/postgresjson/definition/parse/ParsingFunction.kt index 9b5b749..5a947fa 100644 --- a/src/main/kotlin/fr/postgresjson/definition/parse/ParsingFunction.kt +++ b/src/main/kotlin/fr/postgresjson/definition/parse/ParsingFunction.kt @@ -32,6 +32,7 @@ internal fun parseFunction(script: String, source: Path? = null): Function { internal fun ScriptPart.getFunctionName(): NextScript { 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 { private fun ScriptPart.getParameterName(): NextScript { try { return getNextScript { afterBeginBy(" ", "\n") && status.isNotEscaped() } + .changeValue { unescapeOrLowercase() } } catch (e: ParseException) { throw ParameterNameMalformed(this, e) } diff --git a/src/main/kotlin/fr/postgresjson/definition/parse/ParsingHelper.kt b/src/main/kotlin/fr/postgresjson/definition/parse/ParsingHelper.kt index e718113..1e73829 100644 --- a/src/main/kotlin/fr/postgresjson/definition/parse/ParsingHelper.kt +++ b/src/main/kotlin/fr/postgresjson/definition/parse/ParsingHelper.kt @@ -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 NextScript.changeValue(block: T.() -> T): NextScript { + contract { + callsInPlace(block, EXACTLY_ONCE) + } + return NextScript(value.run(block), nextScriptPart.restOfScript) +} + internal fun ScriptPart.getNextInteger(): NextScript { val digits = restOfScript.takeWhile { it.isDigit() } val restOfScript = restOfScript.trimStart { it.isDigit() } diff --git a/src/test/kotlin/fr/postgresjson/definition/FunctionTest.kt b/src/test/kotlin/fr/postgresjson/definition/FunctionTest.kt index 0a40efc..8471c9e 100644 --- a/src/test/kotlin/fr/postgresjson/definition/FunctionTest.kt +++ b/src/test/kotlin/fr/postgresjson/definition/FunctionTest.kt @@ -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(