From 8810c1f08d7e88dd1ebe6df55d72f45d9c0e609f Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Fri, 20 Sep 2019 02:27:15 +0200 Subject: [PATCH] Drop function if already exists and conflict --- .../fr/postgresjson/migration/Function.kt | 12 ++++++++- .../fr/postgresjson/migration/Migrations.kt | 3 ++- .../kotlin/fr/postgresjson/MigrationTest.kt | 26 +++++++++++++++++-- .../kotlin/fr/postgresjson/RequesterTest.kt | 12 ++++----- .../kotlin/fr/postgresjson/TestAbstract.kt | 2 +- .../Test1/function_test_duplicate.sql | 8 ++++++ .../Test2/function_test_duplicate.sql | 8 ++++++ 7 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 src/test/resources/sql/function/Test1/function_test_duplicate.sql create mode 100644 src/test/resources/sql/function/Test2/function_test_duplicate.sql diff --git a/src/main/kotlin/fr/postgresjson/migration/Function.kt b/src/main/kotlin/fr/postgresjson/migration/Function.kt index 3f657a5..b3ce805 100644 --- a/src/main/kotlin/fr/postgresjson/migration/Function.kt +++ b/src/main/kotlin/fr/postgresjson/migration/Function.kt @@ -1,9 +1,11 @@ package fr.postgresjson.migration +import com.github.jasync.sql.db.postgresql.exceptions.GenericDatabaseException import fr.postgresjson.connexion.Connection import fr.postgresjson.migration.Migration.Action import fr.postgresjson.migration.Migration.Status import java.util.* +import java.util.concurrent.CompletionException import fr.postgresjson.definition.Function as DefinitionFunction data class Function( @@ -34,7 +36,15 @@ data class Function( ) override fun up(): Status { - connection.sendQuery(up.script) + try { + connection.sendQuery(up.script) + } catch (e: CompletionException) { + val cause = e.cause + if (cause is GenericDatabaseException && cause.errorMessage.fields['C'] == "42P13") { + connection.sendQuery("drop function ${down.getDefinition()}") + connection.sendQuery(up.script) + } + } this::class.java.classLoader.getResource("sql/migration/insertFunction.sql")!!.readText().let { connection.selectOne(it, listOf(up.name, up.getDefinition(), up.script, down.script))?.let { function -> diff --git a/src/main/kotlin/fr/postgresjson/migration/Migrations.kt b/src/main/kotlin/fr/postgresjson/migration/Migrations.kt index 423f00a..75ea6b8 100644 --- a/src/main/kotlin/fr/postgresjson/migration/Migrations.kt +++ b/src/main/kotlin/fr/postgresjson/migration/Migrations.kt @@ -137,7 +137,8 @@ data class Migrations private constructor( fun addFunction(newDefinition: DefinitionFunction, callback: (Function) -> Unit = {}): Migrations { val currentFunction = functions[newDefinition.name] if (currentFunction === null || currentFunction `is different from` newDefinition) { - functions[newDefinition.name] = Function(newDefinition, newDefinition, connection).apply { + val oldDefinition = functions[newDefinition.name]?.up ?: newDefinition + functions[newDefinition.name] = Function(newDefinition, oldDefinition, connection).apply { doExecute = Action.UP } } else { diff --git a/src/test/kotlin/fr/postgresjson/MigrationTest.kt b/src/test/kotlin/fr/postgresjson/MigrationTest.kt index bc02160..e161af5 100644 --- a/src/test/kotlin/fr/postgresjson/MigrationTest.kt +++ b/src/test/kotlin/fr/postgresjson/MigrationTest.kt @@ -79,7 +79,7 @@ class MigrationTest(): TestAbstract() { @Test fun `run migrations force down`() { val resources = File(this::class.java.getResource("/sql/real_migrations").toURI()) - val resourcesFunctions = File(this::class.java.getResource("/sql/function").toURI()) + val resourcesFunctions = File(this::class.java.getResource("/sql/function/Test").toURI()) Migrations(listOf(resources, resourcesFunctions), connection).apply { up().apply { size `should be equal to` 6 @@ -94,7 +94,7 @@ class MigrationTest(): TestAbstract() { @Test fun `run functions migrations`() { - val resources = File(this::class.java.getResource("/sql/function").toURI()) + val resources = File(this::class.java.getResource("/sql/function/Test").toURI()) Migrations(resources, connection).apply { run().size `should be equal to` 5 } @@ -107,4 +107,26 @@ class MigrationTest(): TestAbstract() { Assertions.assertEquals(objTest!!.id, 3) Assertions.assertEquals(objTest.name, "test") } + + @Test + fun `run functions migrations and drop if exist`() { + val resources = File(this::class.java.getResource("/sql/function/Test1").toURI()) + Migrations(resources, connection).apply { + run().size `should be equal to` 1 + } + + val objTest: RequesterTest.ObjTest? = Requester(connection) + .addFunction(resources) + .getFunction("test_function_duplicate") + .selectOne(listOf("test")) + + Assertions.assertEquals(objTest!!.id, 3) + Assertions.assertEquals(objTest.name, "test") + + + val resources2 = File(this::class.java.getResource("/sql/function/Test2").toURI()) + Migrations(resources2, connection).apply { + run().size `should be equal to` 1 + } + } } \ No newline at end of file diff --git a/src/test/kotlin/fr/postgresjson/RequesterTest.kt b/src/test/kotlin/fr/postgresjson/RequesterTest.kt index 3ffbd77..ae0f4ef 100644 --- a/src/test/kotlin/fr/postgresjson/RequesterTest.kt +++ b/src/test/kotlin/fr/postgresjson/RequesterTest.kt @@ -25,7 +25,7 @@ class RequesterTest: TestAbstract() { @Test fun `get function from file`() { - val resources = File(this::class.java.getResource("/sql/function").toURI()) + val resources = File(this::class.java.getResource("/sql/function/Test").toURI()) val objTest: ObjTest? = Requester(connection) .addFunction(resources) .getFunction("test_function") @@ -48,7 +48,7 @@ class RequesterTest: TestAbstract() { @Test fun `call exec on function`() { - val resources = File(this::class.java.getResource("/sql/function").toURI()) + val resources = File(this::class.java.getResource("/sql/function/Test").toURI()) val result = Requester(connection) .addFunction(resources) .getFunction("test_function") @@ -70,7 +70,7 @@ class RequesterTest: TestAbstract() { @Test fun `call sendQuery on function`() { - val resources = File(this::class.java.getResource("/sql/function").toURI()) + val resources = File(this::class.java.getResource("/sql/function/Test").toURI()) val result = Requester(connection) .addFunction(resources) .getFunction("function_void") @@ -81,7 +81,7 @@ class RequesterTest: TestAbstract() { @Test fun `call selectOne on function`() { - val resources = File(this::class.java.getResource("/sql/function").toURI()) + val resources = File(this::class.java.getResource("/sql/function/Test").toURI()) val obj: ObjTest = Requester(connection) .addFunction(resources) .getFunction("test_function") @@ -92,7 +92,7 @@ class RequesterTest: TestAbstract() { @Test fun `call selectOne on function with object`() { - val resources = File(this::class.java.getResource("/sql/function").toURI()) + val resources = File(this::class.java.getResource("/sql/function/Test").toURI()) val obj2 = ObjTest("original") val obj: ObjTest = Requester(connection) .addFunction(resources) @@ -116,7 +116,7 @@ class RequesterTest: TestAbstract() { @Test fun `call select (multiple) on function`() { - val resources = File(this::class.java.getResource("/sql/function").toURI()) + val resources = File(this::class.java.getResource("/sql/function/Test").toURI()) val obj: List? = Requester(connection) .addFunction(resources) .getFunction("test_function_multiple") diff --git a/src/test/kotlin/fr/postgresjson/TestAbstract.kt b/src/test/kotlin/fr/postgresjson/TestAbstract.kt index ebc70f8..346f148 100644 --- a/src/test/kotlin/fr/postgresjson/TestAbstract.kt +++ b/src/test/kotlin/fr/postgresjson/TestAbstract.kt @@ -9,7 +9,7 @@ import java.io.File @TestInstance(PER_CLASS) abstract class TestAbstract { - protected val connection = Connection(database = "test", username = "test", password = "test") + protected val connection = Connection(database = "test_json", username = "test", password = "test") @BeforeEach fun beforeAll() { diff --git a/src/test/resources/sql/function/Test1/function_test_duplicate.sql b/src/test/resources/sql/function/Test1/function_test_duplicate.sql new file mode 100644 index 0000000..526f242 --- /dev/null +++ b/src/test/resources/sql/function/Test1/function_test_duplicate.sql @@ -0,0 +1,8 @@ +CREATE OR REPLACE FUNCTION test_function_duplicate (name text default 'plop', out result json) +LANGUAGE plpgsql +AS +$$ +BEGIN + result = json_build_object('id', 3, 'name', name); +END; +$$ \ No newline at end of file diff --git a/src/test/resources/sql/function/Test2/function_test_duplicate.sql b/src/test/resources/sql/function/Test2/function_test_duplicate.sql new file mode 100644 index 0000000..2c8e3d6 --- /dev/null +++ b/src/test/resources/sql/function/Test2/function_test_duplicate.sql @@ -0,0 +1,8 @@ +CREATE OR REPLACE FUNCTION test_function_duplicate (name text default 'plop') returns text + LANGUAGE plpgsql +AS +$$ +BEGIN + return name; +END; +$$ \ No newline at end of file