Fix/function with same name #34

Open
flecomte wants to merge 3 commits from fix/function_with_same_name into master
6 changed files with 52 additions and 25 deletions
Showing only changes of commit 9d28ea6f0d - Show all commits

View File

@@ -97,7 +97,7 @@ class Function(val definition: Function, override val connection: Connection) :
} }
private fun compileArgs(values: Map<String, Any?>): String { private fun compileArgs(values: Map<String, Any?>): String {
val parameters = definition.getParametersIndexedByName() val parameters = definition.parametersIndexedByName
val placeholders = values val placeholders = values
.filter { entry -> .filter { entry ->
val parameter = parameters[entry.key] ?: parameters["_" + entry.key] ?: error("Parameter ${entry.key} of function ${definition.name} not exist") val parameter = parameters[entry.key] ?: parameters["_" + entry.key] ?: error("Parameter ${entry.key} of function ${definition.name} not exist")

View File

@@ -10,13 +10,13 @@ class Requester(
private val queries: MutableMap<String, Query> = mutableMapOf(), private val queries: MutableMap<String, Query> = mutableMapOf(),
private val functions: MutableMap<String, Function> = mutableMapOf() private val functions: MutableMap<String, Function> = mutableMapOf()
) { ) {
constructor(connection: Connection) : this(connection, mutableMapOf(), mutableMapOf()) constructor(connection: Connection): this(connection, mutableMapOf(), mutableMapOf())
constructor( constructor(
connection: Connection, connection: Connection,
queriesDirectory: URI? = null, queriesDirectory: URI? = null,
functionsDirectory: URI? = null functionsDirectory: URI? = null
) : this( ): this(
connection = connection, connection = connection,
queries = queriesDirectory?.toQuery(connection) ?: mutableMapOf(), queries = queriesDirectory?.toQuery(connection) ?: mutableMapOf(),
functions = functionsDirectory?.toFunction(connection) ?: mutableMapOf(), functions = functionsDirectory?.toFunction(connection) ?: mutableMapOf(),
@@ -63,6 +63,11 @@ class Requester(
fun getQuery(path: String): Query = queries[path] ?: throw NoQueryDefined(path) fun getQuery(path: String): Query = queries[path] ?: throw NoQueryDefined(path)
class NoFunctionDefined(name: String) : Exception("No function defined for $name")
class NoQueryDefined(path: String) : Exception("No query defined in $path") fun <A> inTransaction(block: Requester.() -> A?): A? = connection.inTransaction {
this@Requester.block()
}
class NoFunctionDefined(name: String): Exception("No function defined for $name")
class NoQueryDefined(path: String): Exception("No query defined in $path")
} }

View File

@@ -1,11 +1,13 @@
package fr.postgresjson.definition package fr.postgresjson.definition
import fr.postgresjson.utils.Algorithm.MD5
import fr.postgresjson.utils.hash
import java.nio.file.Path import java.nio.file.Path
class Function( class Function(
override val script: String, override val script: String,
override val source: Path? = null override val source: Path? = null
) : Resource, ParametersInterface { ): Resource, ParametersInterface {
val returns: String val returns: String
override val name: String override val name: String
override val parameters: List<Parameter> override val parameters: List<Parameter>
@@ -46,24 +48,32 @@ class Function(
} }
} }
class FunctionNotFound(cause: Throwable? = null) : Resource.ParseException("Function not found in script", cause) class FunctionNotFound(cause: Throwable? = null): Resource.ParseException("Function not found in script", cause)
fun getDefinition(): String { val definition: String
return parameters get() {
.filter { it.direction == Parameter.Direction.IN } return parameters
.joinToString(", ") { "${it.name} ${it.type}" } .filter { it.direction == Parameter.Direction.IN }
.let { "$name ($it)" } .joinToString(", ") { "${it.name} ${it.type}" }
} .let { "$name ($it)" }
}
fun getParametersIndexedByName(): Map<String, Parameter> { val definitionHash: String
return parameters.associateBy { it.name } get() {
} return definition.hash(MD5)
}
val parametersIndexedByName: Map<String, Parameter>
get() {
return parameters.associateBy { it.name }
}
infix fun `has same definition`(other: Function): Boolean { infix fun `has same definition`(other: Function): Boolean {
return other.getDefinition() == this.getDefinition() return other.definition == this.definition
} }
infix fun `is different from`(other: Function): Boolean { infix fun `is different from`(other: Function): Boolean {
return other.script != this.script return other.script != this.script
} }
} }

View File

@@ -43,14 +43,14 @@ data class Function(
} catch (e: CompletionException) { } catch (e: CompletionException) {
val cause = e.cause val cause = e.cause
if (cause is GenericDatabaseException && cause.errorMessage.fields['C'] == "42P13") { if (cause is GenericDatabaseException && cause.errorMessage.fields['C'] == "42P13") {
connection.sendQuery("drop function ${down.getDefinition()}") connection.sendQuery("drop function ${down.definition}")
connection.sendQuery(up.script) connection.sendQuery(up.script)
} }
} }
this::class.java.classLoader this::class.java.classLoader
.getResource("sql/migration/insertFunction.sql")!!.readText() .getResource("sql/migration/insertFunction.sql")!!.readText()
.let { connection.selectOne<MigrationEntity>(it, listOf(up.name, up.getDefinition(), up.script, down.script)) } .let { connection.selectOne<MigrationEntity>(it, listOf(up.name, up.definition, up.script, down.script)) }
?.let { function -> ?.let { function ->
executedAt = function.executedAt executedAt = function.executedAt
doExecute = Action.OK doExecute = Action.OK

View File

@@ -133,17 +133,17 @@ class Migrations private constructor(
Throwable("The file $path was not found", cause) Throwable("The file $path was not found", cause)
fun addFunction(newDefinition: DefinitionFunction, callback: (Function) -> Unit = {}): Migrations { fun addFunction(newDefinition: DefinitionFunction, callback: (Function) -> Unit = {}): Migrations {
val currentFunction = functions[newDefinition.name] val currentFunction = functions[newDefinition.definitionHash]
if (currentFunction === null || currentFunction `is different from` newDefinition) { if (currentFunction === null || currentFunction `is different from` newDefinition) {
val oldDefinition = functions[newDefinition.name]?.up ?: newDefinition val oldDefinition = functions[newDefinition.definitionHash]?.up ?: newDefinition
functions[newDefinition.name] = Function(newDefinition, oldDefinition, connection).apply { functions[newDefinition.definitionHash] = Function(newDefinition, oldDefinition, connection).apply {
doExecute = Action.UP doExecute = Action.UP
} }
} else { } else {
functions[newDefinition.name]?.doExecute = Action.OK functions[newDefinition.definitionHash]?.doExecute = Action.OK
} }
callback(functions[newDefinition.name]!!) callback(functions[newDefinition.definitionHash]!!)
return this return this
} }
@@ -215,7 +215,7 @@ class Migrations private constructor(
return list.toMap() return list.toMap()
} }
internal fun down(force: Boolean = false): Map<String, Status> { fun down(force: Boolean = false): Map<String, Status> {
val list: MutableMap<String, Status> = mutableMapOf() val list: MutableMap<String, Status> = mutableMapOf()
migrationsScripts.forEach { migrationsScripts.forEach {
it.value.let { query -> it.value.let { query ->

View File

@@ -0,0 +1,12 @@
package fr.postgresjson.utils
import java.math.BigInteger
import java.security.MessageDigest
internal enum class Algorithm(name: String) {
MD5("MD5")
}
internal fun String.hash(algorithm: Algorithm): String {
val md = MessageDigest.getInstance(algorithm.name)
return BigInteger(1, md.digest(toByteArray())).toString(16).padStart(32, '0')
}