Implement begining of migration
Move Function/Query definition
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package fr.postgresjson.connexion
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
import com.github.jasync.sql.db.Connection
|
||||
import com.github.jasync.sql.db.QueryResult
|
||||
import com.github.jasync.sql.db.pool.ConnectionPool
|
||||
import com.github.jasync.sql.db.postgresql.PostgreSQLConnection
|
||||
@@ -28,6 +29,8 @@ class Connection(
|
||||
return connection
|
||||
}
|
||||
|
||||
fun <A> inTransaction(f: (Connection) -> CompletableFuture<A>) = connect().inTransaction(f)
|
||||
|
||||
fun <T, R : EntityI<T?>?> selectOne(sql: String, typeReference: TypeReference<R>, values: List<Any?> = emptyList()): R? {
|
||||
val future = connect().sendPreparedStatement(sql, compileArgs(values))
|
||||
val json = future.get().rows[0].getString(0)
|
||||
|
||||
@@ -5,17 +5,13 @@ import com.github.jasync.sql.db.QueryResult
|
||||
import fr.postgresjson.entity.EntityI
|
||||
import java.io.File
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.text.RegexOption.IGNORE_CASE
|
||||
import kotlin.text.RegexOption.MULTILINE
|
||||
import fr.postgresjson.definition.Function as DefinitionFunction
|
||||
|
||||
class Requester (
|
||||
private val connection: Connection,
|
||||
queries: List<Query> = listOf(),
|
||||
functions: List<Function> = listOf())
|
||||
private val queries: MutableMap<String, Query> = mutableMapOf(),
|
||||
private val functions: MutableMap<String, Function> = mutableMapOf())
|
||||
{
|
||||
private val queries = mutableMapOf<String, Query>()
|
||||
private val functions = mutableMapOf<String, Function>()
|
||||
|
||||
fun addQuery(name: String, query: Query): Requester {
|
||||
queries[name] = query
|
||||
return this
|
||||
@@ -38,14 +34,14 @@ class Requester (
|
||||
return this
|
||||
}
|
||||
|
||||
fun addFunction(function: Function): Requester {
|
||||
functions[function.name] = function
|
||||
fun addFunction(definition: DefinitionFunction): Requester {
|
||||
functions[definition.name] = Function(definition, connection)
|
||||
return this
|
||||
}
|
||||
|
||||
fun addFunction(sql: String): Requester {
|
||||
getDefinitions(sql).forEach {
|
||||
functions[it.name] = it
|
||||
DefinitionFunction.build(sql).forEach {
|
||||
functions[it.name] = Function(it, connection)
|
||||
}
|
||||
return this
|
||||
}
|
||||
@@ -64,36 +60,6 @@ class Requester (
|
||||
return this
|
||||
}
|
||||
|
||||
private fun getDefinitions(functionContent: String): List<Function> {
|
||||
val functionRegex = """create .*(procedure|function) *(?<name>[^(\s]+)\s*\((?<params>(\s*((IN|OUT|INOUT|VARIADIC)?\s+)?([^\s,)]+\s+)?([^\s,)]+)(\s+(?:default\s|=)\s*[^\s,)]+)?\s*(,|(?=\))))*)\) *(?<return>RETURNS *[^ ]+)?"""
|
||||
.toRegex(setOf(IGNORE_CASE, MULTILINE))
|
||||
|
||||
val paramsRegex = """\s*(?<param>((?<direction>IN|OUT|INOUT|VARIADIC)?\s+)?(?<name>[^\s,)]+\s+)?(?<type>[^\s,)]+)(\s+(?<default>default\s|=)\s*[^\s,)]+)?)\s*(,|$)"""
|
||||
.toRegex(setOf(IGNORE_CASE, MULTILINE))
|
||||
|
||||
return functionRegex.findAll(functionContent).map { queryMatch ->
|
||||
val functionName = queryMatch.groups["name"]?.value?.trim()
|
||||
val functionParameters = queryMatch.groups["params"]?.value?.trim()
|
||||
val returns = queryMatch.groups["return"]?.value?.trim()
|
||||
|
||||
/* Create parameters definition */
|
||||
val parameters = if (functionParameters !== null) {
|
||||
val matchesParams = paramsRegex.findAll(functionParameters)
|
||||
matchesParams.map { paramsMatch ->
|
||||
Function.Parameter(
|
||||
paramsMatch.groups["name"]!!.value.trim(),
|
||||
paramsMatch.groups["type"]!!.value.trim(),
|
||||
paramsMatch.groups["direction"]?.value?.trim(),
|
||||
paramsMatch.groups["default"]?.value?.trim())
|
||||
}.toList()
|
||||
} else {
|
||||
listOf()
|
||||
}
|
||||
|
||||
Function(functionName!!, parameters, connection)
|
||||
}.toList()
|
||||
}
|
||||
|
||||
fun getFunction(name: String): Function {
|
||||
if (functions[name] === null) {
|
||||
throw Exception("No function defined for $name")
|
||||
@@ -103,80 +69,59 @@ class Requester (
|
||||
|
||||
fun getQuery(path: String): Query {
|
||||
if (queries[path] === null) {
|
||||
throw Exception("No query defined for $path")
|
||||
throw Exception("No query defined in $path")
|
||||
}
|
||||
return queries[path]!!
|
||||
}
|
||||
|
||||
class Query(private val sql: String, private val connection : Connection) {
|
||||
class Query(private val sql: String, override val connection : Connection): Executable {
|
||||
override fun toString(): String {
|
||||
return sql
|
||||
}
|
||||
|
||||
fun <T, R : EntityI<T?>?> selectOne(typeReference: TypeReference<R>, values: List<Any?> = emptyList()): R? {
|
||||
override fun <T, R : EntityI<T?>?> selectOne(typeReference: TypeReference<R>, values: List<Any?>): R? {
|
||||
return connection.selectOne(this.toString(), typeReference, values)
|
||||
}
|
||||
|
||||
inline fun <T, reified R : EntityI<T?>?> selectOne(values: List<Any?> = emptyList()): R? = selectOne(object: TypeReference<R>() {}, values)
|
||||
|
||||
fun <T, R : List<EntityI<T?>?>> select(typeReference: TypeReference<R>, values: List<Any?> = emptyList()): R? {
|
||||
override fun <T, R : List<EntityI<T?>?>> select(typeReference: TypeReference<R>, values: List<Any?>): R? {
|
||||
return connection.select(this.toString(), typeReference, values)
|
||||
}
|
||||
|
||||
inline fun <T, reified R : List<EntityI<T?>?>> select(values: List<Any?> = emptyList()): R? = select(object: TypeReference<R>() {}, values)
|
||||
|
||||
fun exec(values: List<Any?> = emptyList()): CompletableFuture<QueryResult> {
|
||||
override fun exec(values: List<Any?>): CompletableFuture<QueryResult> {
|
||||
return connection.exec(sql, values)
|
||||
}
|
||||
}
|
||||
|
||||
class Function(val name: String, val parameters: List<Parameter>, private val connection : Connection) {
|
||||
|
||||
class Parameter(val name: String, val type: String, direction: Direction? = Direction.IN, val default: Any? = null)
|
||||
{
|
||||
val direction: Direction
|
||||
|
||||
init {
|
||||
if (direction === null) {
|
||||
this.direction = Direction.IN
|
||||
} else {
|
||||
this.direction = direction
|
||||
}
|
||||
}
|
||||
constructor(name: String, type: String, direction: String? = "IN", default: Any? = null) : this(
|
||||
name = name,
|
||||
type = type,
|
||||
direction = direction?.let { Direction.valueOf(direction.toUpperCase())},
|
||||
default = default
|
||||
)
|
||||
enum class Direction { IN, OUT, INOUT }
|
||||
}
|
||||
|
||||
class Function(val definition: DefinitionFunction, override val connection : Connection): Executable {
|
||||
override fun toString(): String {
|
||||
return name
|
||||
return definition.name
|
||||
}
|
||||
|
||||
fun <T, R : EntityI<T?>?> selectOne(typeReference: TypeReference<R>, values: List<String?> = emptyList()): R? {
|
||||
override fun <T, R : EntityI<T?>?> selectOne(typeReference: TypeReference<R>, values: List<Any?>): R? {
|
||||
val args = compileArgs(values)
|
||||
val sql = "SELECT * FROM $name ($args)"
|
||||
val sql = "SELECT * FROM ${definition.name} ($args)"
|
||||
|
||||
return connection.selectOne(sql, typeReference, values)
|
||||
}
|
||||
|
||||
inline fun <T, reified R: EntityI<T?>?> selectOne(values: List<String?> = emptyList()): R? = selectOne(object: TypeReference<R>() {}, values)
|
||||
|
||||
fun <T, R : List<EntityI<T?>?>> select(typeReference: TypeReference<R>, values: List<Any?> = emptyList()): R? {
|
||||
override fun <T, R : List<EntityI<T?>?>> select(typeReference: TypeReference<R>, values: List<Any?>): R? {
|
||||
val args = compileArgs(values)
|
||||
val sql = "SELECT * FROM $name ($args)"
|
||||
val sql = "SELECT * FROM ${definition.name} ($args)"
|
||||
|
||||
return connection.select(sql, typeReference, values)
|
||||
}
|
||||
|
||||
inline fun <T, reified R: List<EntityI<T?>?>> select(values: List<Any?> = emptyList()): R? = select(object: TypeReference<R>() {}, values)
|
||||
|
||||
fun exec(values: List<Any?> = emptyList()): CompletableFuture<QueryResult> {
|
||||
override fun exec(values: List<Any?>): CompletableFuture<QueryResult> {
|
||||
val args = compileArgs(values)
|
||||
val sql = "SELECT * FROM $name ($args)"
|
||||
val sql = "SELECT * FROM ${definition.name} ($args)"
|
||||
|
||||
return connection.exec(sql, values)
|
||||
}
|
||||
@@ -184,16 +129,27 @@ class Requester (
|
||||
private fun compileArgs(values: List<Any?>): String {
|
||||
val placeholders = values
|
||||
.filterIndexed { index, any ->
|
||||
this.parameters[index].default === null || any !== null
|
||||
definition.parameters[index].default === null || any !== null
|
||||
}
|
||||
.mapIndexed { index, any ->
|
||||
"?::" + this.parameters[index].type
|
||||
"?::" + definition.parameters[index].type
|
||||
}
|
||||
|
||||
return placeholders.joinToString(separator=", ")
|
||||
}
|
||||
}
|
||||
|
||||
interface Executable {
|
||||
val connection : Connection
|
||||
override fun toString(): String
|
||||
|
||||
fun <T, R : EntityI<T?>?> selectOne(typeReference: TypeReference<R>, values: List<Any?> = emptyList()): R?
|
||||
|
||||
fun <T, R : List<EntityI<T?>?>> select(typeReference: TypeReference<R>, values: List<Any?> = emptyList()): R?
|
||||
|
||||
fun exec(values: List<Any?> = emptyList()): CompletableFuture<QueryResult>
|
||||
}
|
||||
|
||||
class RequesterFactory(
|
||||
private val host: String = "localhost",
|
||||
private val port: Int = 5432,
|
||||
|
||||
48
src/main/kotlin/fr/postgresjson/definition/Function.kt
Normal file
48
src/main/kotlin/fr/postgresjson/definition/Function.kt
Normal file
@@ -0,0 +1,48 @@
|
||||
package fr.postgresjson.definition
|
||||
|
||||
import java.io.File
|
||||
|
||||
|
||||
open class Function (
|
||||
override val name: String,
|
||||
override val script: String,
|
||||
override val parameters: List<Parameter>
|
||||
) : Resource, ParametersInterface {
|
||||
override var source: File? = null
|
||||
|
||||
companion object {
|
||||
fun build(source: File): List<Function> {
|
||||
return build(source.readText())
|
||||
}
|
||||
|
||||
fun build(functionContent: String): List<Function> {
|
||||
val functionRegex = """create .*(procedure|function) *(?<name>[^(\s]+)\s*\((?<params>(\s*((IN|OUT|INOUT|VARIADIC)?\s+)?([^\s,)]+\s+)?([^\s,)]+)(\s+(?:default\s|=)\s*[^\s,)]+)?\s*(,|(?=\))))*)\) *(?<return>RETURNS *[^ ]+)?"""
|
||||
.toRegex(setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE))
|
||||
|
||||
val paramsRegex = """\s*(?<param>((?<direction>IN|OUT|INOUT|VARIADIC)?\s+)?(?<name>[^\s,)]+\s+)?(?<type>[^\s,)]+)(\s+(?<default>default\s|=)\s*[^\s,)]+)?)\s*(,|$)"""
|
||||
.toRegex(setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE))
|
||||
|
||||
return functionRegex.findAll(functionContent).map { queryMatch ->
|
||||
val functionName = queryMatch.groups["name"]?.value?.trim()
|
||||
val functionParameters = queryMatch.groups["params"]?.value?.trim()
|
||||
val returns = queryMatch.groups["return"]?.value?.trim()
|
||||
|
||||
/* Create parameters definition */
|
||||
val parameters = if (functionParameters !== null) {
|
||||
val matchesParams = paramsRegex.findAll(functionParameters)
|
||||
matchesParams.map { paramsMatch ->
|
||||
Parameter(
|
||||
paramsMatch.groups["name"]!!.value.trim(),
|
||||
paramsMatch.groups["type"]!!.value.trim(),
|
||||
paramsMatch.groups["direction"]?.value?.trim(),
|
||||
paramsMatch.groups["default"]?.value?.trim())
|
||||
}.toList()
|
||||
} else {
|
||||
listOf()
|
||||
}
|
||||
|
||||
Function(functionName!!, functionContent, parameters)
|
||||
}.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/main/kotlin/fr/postgresjson/definition/Parameter.kt
Normal file
34
src/main/kotlin/fr/postgresjson/definition/Parameter.kt
Normal file
@@ -0,0 +1,34 @@
|
||||
package fr.postgresjson.definition
|
||||
|
||||
interface ParameterI {
|
||||
val name: String
|
||||
val type: String
|
||||
val direction: Parameter.Direction
|
||||
val default: String
|
||||
}
|
||||
|
||||
class Parameter(val name: String, val type: String, direction: Direction? = Direction.IN, val default: Any? = null)
|
||||
{
|
||||
val direction: Direction
|
||||
|
||||
init {
|
||||
if (direction === null) {
|
||||
this.direction = Direction.IN
|
||||
} else {
|
||||
this.direction = direction
|
||||
}
|
||||
}
|
||||
|
||||
constructor(name: String, type: String, direction: String? = "IN", default: Any? = null) : this(
|
||||
name = name,
|
||||
type = type,
|
||||
direction = direction?.let { Direction.valueOf(direction.toUpperCase())},
|
||||
default = default
|
||||
)
|
||||
|
||||
enum class Direction { IN, OUT, INOUT }
|
||||
}
|
||||
|
||||
interface ParametersInterface {
|
||||
val parameters: List<Parameter>
|
||||
}
|
||||
13
src/main/kotlin/fr/postgresjson/definition/Resource.kt
Normal file
13
src/main/kotlin/fr/postgresjson/definition/Resource.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package fr.postgresjson.definition
|
||||
|
||||
import java.io.File
|
||||
|
||||
interface Resource {
|
||||
val name: String
|
||||
val script: String
|
||||
var source: File?
|
||||
}
|
||||
|
||||
interface ResourceCollection {
|
||||
val parameters: List<Parameter>
|
||||
}
|
||||
41
src/main/kotlin/fr/postgresjson/migration/Function.kt
Normal file
41
src/main/kotlin/fr/postgresjson/migration/Function.kt
Normal file
@@ -0,0 +1,41 @@
|
||||
package fr.postgresjson.migration
|
||||
|
||||
import fr.postgresjson.connexion.Connection
|
||||
import fr.postgresjson.definition.Function as DefinitionFunction
|
||||
|
||||
class Function(
|
||||
private val up: DefinitionFunction,
|
||||
private val down: DefinitionFunction,
|
||||
private val connection: Connection
|
||||
): Migration {
|
||||
enum class Status(i: Int) { OK(2), UP_FAIL(0), DOWN_FAIL(1) }
|
||||
|
||||
override fun up(): Int {
|
||||
connection.exec(up.script)
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun down(): Int {
|
||||
connection.exec(down.script)
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun test(): Int {
|
||||
connection.inTransaction {
|
||||
connection.exec(up.script)
|
||||
connection.exec(down.script)
|
||||
it.sendQuery("ROLLBACK");
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun status(): Int {
|
||||
val result = connection.inTransaction {
|
||||
connection.exec(up.script)
|
||||
connection.exec(down.script)
|
||||
it.sendQuery("ROLLBACK")
|
||||
}.join()
|
||||
|
||||
return result.rowsAffected.toInt()
|
||||
}
|
||||
}
|
||||
101
src/main/kotlin/fr/postgresjson/migration/Migrations.kt
Normal file
101
src/main/kotlin/fr/postgresjson/migration/Migrations.kt
Normal file
@@ -0,0 +1,101 @@
|
||||
package fr.postgresjson.migration
|
||||
|
||||
import com.github.jasync.sql.db.util.size
|
||||
import fr.postgresjson.connexion.Connection
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import fr.postgresjson.definition.Function as DefinitionFunction
|
||||
|
||||
|
||||
interface Migration {
|
||||
fun up(): Int
|
||||
fun down(): Int
|
||||
fun test(): Int
|
||||
fun status(): Int
|
||||
}
|
||||
|
||||
class Migrations(directory: File, private val connection: Connection): Migration {
|
||||
private val queries: MutableList<Query> = mutableListOf()
|
||||
private val functions: MutableMap<String, Function> = mutableMapOf()
|
||||
|
||||
init {
|
||||
directory.walk().filter {
|
||||
it.isDirectory
|
||||
}.forEach { directory ->
|
||||
directory.walk().filter {
|
||||
it.isFile
|
||||
}.forEach { file ->
|
||||
if (file.name.endsWith(".up.sql")) {
|
||||
val up = file.readText()
|
||||
val down = file.path.substring(0, file.path.size - 7).let {
|
||||
try {
|
||||
File("$it.down.sql").readText()
|
||||
} catch (e: FileNotFoundException) {
|
||||
throw DownMigrationNotDefined("$it.down.sql", e)
|
||||
}
|
||||
}
|
||||
addQuery(up, down)
|
||||
} else if (file.name.endsWith(".down.sql")) {
|
||||
// Nothing
|
||||
} else {
|
||||
val fileContent = file.readText()
|
||||
addFunction(fileContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DownMigrationNotDefined(path: String, cause: FileNotFoundException): Throwable("The file $path whas not found", cause)
|
||||
|
||||
fun addFunction(definition: DefinitionFunction): Migrations {
|
||||
functions[definition.name] = Function(definition, definition, connection)
|
||||
return this
|
||||
}
|
||||
|
||||
fun addFunction(sql: String): Migrations {
|
||||
DefinitionFunction.build(sql).forEach {
|
||||
functions[it.name] = Function(it, it, connection)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun addQuery(up: String, down: String): Migrations {
|
||||
queries.add(Query(up, down, connection))
|
||||
return this
|
||||
}
|
||||
|
||||
override fun up(): Int {
|
||||
var count = 0
|
||||
queries.forEach {
|
||||
it.up()
|
||||
++count
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
override fun down(): Int {
|
||||
var count = 0
|
||||
queries.forEach {
|
||||
it.down()
|
||||
++count
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
override fun test(): Int {
|
||||
var count = 0
|
||||
connection.inTransaction {
|
||||
count += up()
|
||||
count += down()
|
||||
it.sendQuery("ROLLBACK");
|
||||
}.join()
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
override fun status(): Int {
|
||||
TODO("not implemented")
|
||||
}
|
||||
}
|
||||
40
src/main/kotlin/fr/postgresjson/migration/Query.kt
Normal file
40
src/main/kotlin/fr/postgresjson/migration/Query.kt
Normal file
@@ -0,0 +1,40 @@
|
||||
package fr.postgresjson.migration
|
||||
|
||||
import fr.postgresjson.connexion.Connection
|
||||
|
||||
class Query(
|
||||
private val up: String,
|
||||
private val down: String,
|
||||
private val connection: Connection
|
||||
): Migration {
|
||||
enum class Status(i: Int) { OK(2), UP_FAIL(0), DOWN_FAIL(1) }
|
||||
|
||||
override fun up(): Int {
|
||||
connection.exec(up)
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun down(): Int {
|
||||
connection.exec(down)
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun test(): Int {
|
||||
connection.inTransaction {
|
||||
connection.exec(up)
|
||||
connection.exec(down)
|
||||
it.sendQuery("ROLLBACK");
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun status(): Int {
|
||||
val result = connection.inTransaction {
|
||||
connection.exec(up)
|
||||
connection.exec(down)
|
||||
it.sendQuery("ROLLBACK")
|
||||
}.join()
|
||||
|
||||
return result.rowsAffected.toInt()
|
||||
}
|
||||
}
|
||||
42
src/test/kotlin/fr/postgresjson/MigrationTest.kt
Normal file
42
src/test/kotlin/fr/postgresjson/MigrationTest.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
package fr.postgresjson
|
||||
|
||||
import fr.postgresjson.migration.Migrations
|
||||
import org.amshove.kluent.`should be equal to`
|
||||
import org.amshove.kluent.invoking
|
||||
import org.amshove.kluent.shouldThrow
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import java.io.File
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class MigrationTest(): TestAbstract() {
|
||||
@Test
|
||||
fun upQuery() {
|
||||
val resources = File(this::class.java.getResource("/sql/migrations").toURI())
|
||||
val m = Migrations(resources, getConnextion())
|
||||
m.up() `should be equal to` 1
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `migration up Query should throw error if no down`() {
|
||||
val resources = File(this::class.java.getResource("/sql/migration_without_down").toURI())
|
||||
invoking {
|
||||
Migrations(resources, getConnextion())
|
||||
} shouldThrow Migrations.DownMigrationNotDefined::class
|
||||
}
|
||||
|
||||
@Test
|
||||
fun downQuery() {
|
||||
val resources = File(this::class.java.getResource("/sql/migrations").toURI())
|
||||
val m = Migrations(resources, getConnextion())
|
||||
m.down() `should be equal to` 1
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test up and down migrations`() {
|
||||
val resources = File(this::class.java.getResource("/sql/real_migrations").toURI())
|
||||
val m = Migrations(resources, getConnextion())
|
||||
m.test() `should be equal to` 2
|
||||
m.test() `should be equal to` 2
|
||||
}
|
||||
}
|
||||
1
src/test/resources/sql/migration_without_down/1.up.sql
Normal file
1
src/test/resources/sql/migration_without_down/1.up.sql
Normal file
@@ -0,0 +1 @@
|
||||
SELECT 1;
|
||||
1
src/test/resources/sql/migrations/1.down.sql
Normal file
1
src/test/resources/sql/migrations/1.down.sql
Normal file
@@ -0,0 +1 @@
|
||||
SELECT 1;
|
||||
1
src/test/resources/sql/migrations/1.up.sql
Normal file
1
src/test/resources/sql/migrations/1.up.sql
Normal file
@@ -0,0 +1 @@
|
||||
SELECT 1;
|
||||
1
src/test/resources/sql/real_migrations/1.down.sql
Normal file
1
src/test/resources/sql/real_migrations/1.down.sql
Normal file
@@ -0,0 +1 @@
|
||||
DROP TABLE migration1;
|
||||
3
src/test/resources/sql/real_migrations/1.up.sql
Normal file
3
src/test/resources/sql/real_migrations/1.up.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
CREATE TABLE migration1 (
|
||||
id INT
|
||||
);
|
||||
Reference in New Issue
Block a user