feature #2: save executed migration in DB

This commit is contained in:
2019-07-05 10:04:14 +02:00
parent 6cc3152215
commit 9c02fd21ca
12 changed files with 147 additions and 65 deletions

View File

@@ -6,6 +6,7 @@ import java.io.File
open class Function (
override val script: String
) : Resource, ParametersInterface {
val returns: String?
override val name: String
override val parameters: List<Parameter>
override var source: File? = null
@@ -21,7 +22,7 @@ open class Function (
if (queryMatch !== null) {
val functionName = queryMatch.groups.get("name")?.value?.trim()
val functionParameters = queryMatch.groups["params"]?.value?.trim()
val returns = queryMatch.groups["return"]?.value?.trim()
this.returns = queryMatch.groups["return"]?.value?.trim()
/* Create parameters definition */
val parameters = if (functionParameters !== null) {
@@ -45,6 +46,18 @@ open class Function (
abstract class ParseException(message: String, cause: Throwable? = null): Exception(message, cause)
class FunctionNotFound(cause: Throwable? = null): ParseException("Function not found in script", cause)
fun getDefinition (): String {
return "$name (" + parameters.joinToString(", ") + ") $returns"
}
infix fun `has same definition` (other: Function): Boolean {
return other.getDefinition() == this.getDefinition()
}
infix fun `is same` (other: Function): Boolean {
return other.script == this.script
}
companion object {
fun build(source: File): List<Function> {
return source.readText()

View File

@@ -1,6 +1,9 @@
package fr.postgresjson.migration
import fr.postgresjson.connexion.Connection
import fr.postgresjson.migration.Migration.Action
import fr.postgresjson.migration.Migration.Status
import java.io.File
import java.util.*
import fr.postgresjson.definition.Function as DefinitionFunction
@@ -11,6 +14,7 @@ class Function(
override var executedAt: Date? = null
): Migration {
val name = up.name
override var doExecute: Action? = null
init {
if (up.name !== down.name) {
@@ -30,39 +34,44 @@ class Function(
executedAt
)
override fun up(): Migration.Status {
override fun up(): Status {
connection.exec(up.script)
// TODO insert to migration Table
return Migration.Status.OK
File(this::class.java.getResource("/sql/migration/insertFunction.sql").toURI()).let {
connection.selectOne<String, MigrationEntity?>(it.readText(), listOf(up))?.let { function ->
executedAt = function.executedAt
doExecute = Action.OK
}
}
return Status.OK
}
override fun down(): Migration.Status {
override fun down(): Status {
connection.exec(down.script)
// TODO insert to migration Table
return Migration.Status.OK
File(this::class.java.getResource("/sql/migration/deleteFunction.sql").toURI()).let {
connection.exec(it.readText(), listOf(down))
}
return Status.OK
}
override fun test(): Migration.Status {
override fun test(): Status {
connection.inTransaction {
up()
down()
it.sendQuery("ROLLBACK");
}.join()
return Migration.Status.OK // TODO
return Status.OK // TODO
}
override fun status(): Migration.Status {
override fun status(): Status {
val result = connection.inTransaction {
up()
down()
it.sendQuery("ROLLBACK")
}.join()
return Migration.Status.OK // TODO
}
override fun doExecute(): Boolean {
return executedAt === null
return Status.OK // TODO
}
}

View File

@@ -3,6 +3,8 @@ package fr.postgresjson.migration
import com.github.jasync.sql.db.util.size
import fr.postgresjson.connexion.Connection
import fr.postgresjson.entity.Entity
import fr.postgresjson.migration.Migration.Action
import fr.postgresjson.migration.Migration.Status
import java.io.File
import java.io.FileNotFoundException
import java.util.*
@@ -10,8 +12,7 @@ import fr.postgresjson.definition.Function as DefinitionFunction
class MigrationEntity(
val filename: String,
val definition: String,
val executedAt: Date,
val executedAt: Date?,
val up: String,
val down: String,
val version: Int
@@ -19,13 +20,14 @@ class MigrationEntity(
interface Migration {
var executedAt: Date?
var doExecute: Action?
fun up(): Status
fun down(): Status
fun test(): Status
fun status(): Status
fun doExecute(): Boolean
enum class Status(i: Int) { OK(2), UP_FAIL(0), DOWN_FAIL(1) }
enum class Action { OK, UP, DOWN}
}
class Migrations(directory: File, private val connection: Connection) {
@@ -37,6 +39,17 @@ class Migrations(directory: File, private val connection: Connection) {
initDB()
getMigrationFromDB()
getMigrationFromDirectory(directory)
queries.forEach { (_, query) ->
if (query.doExecute === null) {
query.doExecute = Action.DOWN
}
}
functions.forEach { (_, function) ->
if (function.doExecute === null) {
function.doExecute = Action.DOWN
}
}
}
/**
@@ -64,8 +77,8 @@ class Migrations(directory: File, private val connection: Connection) {
private fun getMigrationFromDirectory(directory: File) {
directory.walk().filter {
it.isDirectory
}.forEach { directory ->
directory.walk().filter {
}.forEach { subDirectory ->
subDirectory.walk().filter {
it.isFile
}.forEach { file ->
if (file.name.endsWith(".up.sql")) {
@@ -92,10 +105,24 @@ class Migrations(directory: File, private val connection: Connection) {
enum class Direction { UP, DOWN }
class DownMigrationNotDefined(path: String, cause: FileNotFoundException): Throwable("The file $path whas not found", cause)
fun addFunction(definition: DefinitionFunction): Migrations {
fun addFunction(definition: DefinitionFunction, callback: (Function) -> Unit = {}): Migrations {
if (functions[definition.name] === null) {
functions[definition.name] = Function(definition, definition, connection)
// TODO define down migration
functions[definition.name] = Function(definition, definition, connection).apply {
doExecute = Action.UP
}
} else {
functions[definition.name]!!.apply {
if (up `is same` definition) {
doExecute = Action.OK
} else {
doExecute = Action.UP
}
}
}
callback(functions[definition.name]!!)
return this
}
@@ -104,10 +131,19 @@ class Migrations(directory: File, private val connection: Connection) {
return this
}
fun addQuery(name: String, up: String, down: String): Migrations {
fun addQuery(name: String, up: String, down: String, callback: (Query) -> Unit = {}): Migrations {
if (queries[name] === null) {
queries[name] = Query(name, up, down, connection)
queries[name] = Query(name, up, down, connection).apply {
doExecute = Action.UP
}
} else {
queries[name]!!.apply {
doExecute = Action.OK
}
}
callback(queries[name]!!)
return this
}
@@ -123,11 +159,11 @@ class Migrations(directory: File, private val connection: Connection) {
}
}
fun up(): Map<String, Migration.Status> {
val list: MutableMap<String, Migration.Status> = mutableMapOf()
fun up(): Map<String, Status> {
val list: MutableMap<String, Status> = mutableMapOf()
queries.forEach {
it.value.let { query ->
if (query.doExecute()) {
if (query.doExecute == Action.UP) {
query.up().let { status ->
list[query.name] = status
}
@@ -137,7 +173,7 @@ class Migrations(directory: File, private val connection: Connection) {
functions.forEach {
it.value.let { function ->
if (function.doExecute()) {
if (function.doExecute == Action.UP) {
function.up().let { status ->
list[function.name] = status
}
@@ -148,11 +184,11 @@ class Migrations(directory: File, private val connection: Connection) {
return list.toMap()
}
fun down(): Map<String, Migration.Status> {
val list: MutableMap<String, Migration.Status> = mutableMapOf()
fun down(force: Boolean = false): Map<String, Status> {
val list: MutableMap<String, Status> = mutableMapOf()
queries.forEach {
it.value.let { query ->
if (query.doExecute()) {
if (query.doExecute == Action.DOWN || force) {
query.down().let { status ->
list[query.name] = status
}
@@ -162,7 +198,7 @@ class Migrations(directory: File, private val connection: Connection) {
functions.forEach {
it.value.let { function ->
if (function.doExecute()) {
if (function.doExecute == Action.DOWN || force) {
function.down().let { status ->
list[function.name] = status
}
@@ -173,17 +209,17 @@ class Migrations(directory: File, private val connection: Connection) {
return list.toMap()
}
fun test(): Map<Pair<String, Direction>, Migration.Status> {
var list: MutableMap<Pair<String, Direction>, Migration.Status> = mutableMapOf()
connection.connect().let {
it.sendQuery("BEGIN").join()
fun test(): Map<Pair<String, Direction>, Status> {
val list: MutableMap<Pair<String, Direction>, Status> = mutableMapOf()
connection.connect().apply {
sendQuery("BEGIN").join()
up().map {
list.set(Pair(it.key, Direction.UP), it.value)
list[Pair(it.key, Direction.UP)] = it.value
}
down().map {
list.set(Pair(it.key, Direction.DOWN), it.value)
down(true).map {
list[Pair(it.key, Direction.DOWN)] = it.value
}
it.sendQuery("ROLLBACK").join()
sendQuery("ROLLBACK").join()
}
return list.toMap()

View File

@@ -2,6 +2,8 @@ package fr.postgresjson.migration
import fr.postgresjson.connexion.Connection
import fr.postgresjson.entity.Entity
import fr.postgresjson.migration.Migration.Action
import java.io.File
import java.util.*
class Query(
@@ -11,16 +13,27 @@ class Query(
private val connection: Connection,
override var executedAt: Date? = null
): Migration, Entity<String?>(name) {
override var doExecute: Action? = null
override fun up(): Migration.Status {
connection.exec(up).join()
// TODO insert to migration Table
File(this::class.java.getResource("/sql/migration/insertHistory.sql").toURI()).let {
connection.selectOne<String, MigrationEntity?>(it.readText(), listOf(name, up, down))?.let { query ->
executedAt = query.executedAt
doExecute = Action.OK
}
}
return Migration.Status.OK
}
override fun down(): Migration.Status {
connection.exec(down).join()
// TODO insert to migration Table
File(this::class.java.getResource("/sql/migration/deleteHistory.sql").toURI()).let {
connection.exec(it.readText(), listOf(name))
}
return Migration.Status.OK
}
@@ -44,8 +57,4 @@ class Query(
return Migration.Status.OK // TODO
}
override fun doExecute(): Boolean {
return executedAt === null
}
}

View File

@@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.PropertyNamingStrategy
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
@@ -27,6 +28,7 @@ class Serializer(val mapper: ObjectMapper = jacksonObjectMapper()) {
module.addDeserializer(UuidEntity::class.java, EntityUuidDeserializer(collection))
module.addDeserializer(IdEntity::class.java, EntityIdDeserializer(collection))
mapper.registerModule(module)
mapper.propertyNamingStrategy = PropertyNamingStrategy.SNAKE_CASE
}
fun <T> serialize(source: EntityI<T>): String {

View File

@@ -1,3 +1,3 @@
DELETE
FROM migration.history
WHERE filename = :filename;
WHERE filename = ?;

View File

@@ -1,2 +1,2 @@
SELECT json_object_agg(filename, f)
SELECT json_agg(f order by f.version)
FROM migration.functions f;

View File

@@ -1,2 +1,2 @@
SELECT json_object_agg(filename, f)
FROM migration.functions f;
SELECT json_agg(h order by h.version)
FROM migration.history h;

View File

@@ -1,2 +1,3 @@
INSERT INTO migration.functions (filename, definition, up, down, version)
VALUES (:filename, :definition, :up, :down, :version);
INSERT INTO migration.functions as f (filename, definition, executed_at, up, down, version)
VALUES (?, ?, now(), ?, ?, ?)
RETURNING to_json(f);

View File

@@ -1,2 +1,3 @@
INSERT INTO migration.history (filename, up, down, version)
VALUES (:filename, :up, :down, :version);
INSERT INTO migration.history as h (filename, executed_at, up, down, version)
VALUES (?, now(), ?, ?, nextval('migration.version_seq'))
RETURNING to_json(h);