Skip to content

Commit e434b6e

Browse files
authored
Added Database version migration (#29)
Signed-off-by: Arnau Mora <[email protected]>
1 parent a0a1b75 commit e434b6e

File tree

9 files changed

+123
-7
lines changed

9 files changed

+123
-7
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ plugins {
99
}
1010

1111
group = "com.arnyminerz.escalaralcoiaicomtat.backend"
12-
version = "1.0.20"
12+
version = "1.0.21"
1313

1414
repositories {
1515
mavenCentral()

src/main/kotlin/Main.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import com.arnyminerz.escalaralcoiaicomtat.backend.Logger
22
import com.arnyminerz.escalaralcoiaicomtat.backend.ServerDatabase
3+
import com.arnyminerz.escalaralcoiaicomtat.backend.database.migration.Migrations
34
import com.arnyminerz.escalaralcoiaicomtat.backend.localization.Localization
45
import com.arnyminerz.escalaralcoiaicomtat.backend.server.plugins.configureEndpoints
56
import com.arnyminerz.escalaralcoiaicomtat.backend.server.plugins.installPlugins
@@ -24,6 +25,11 @@ fun main() {
2425
Logger.info("Connecting to the database, and creating tables...")
2526
ServerDatabase.instance
2627

28+
Logger.info("Running database migration if required...")
29+
runBlocking {
30+
Migrations.runMigrations()
31+
}
32+
2733
Logger.info("Initializing Crowdin connection...")
2834
Localization.init()
2935

src/main/kotlin/com/arnyminerz/escalaralcoiaicomtat/backend/ServerDatabase.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.arnyminerz.escalaralcoiaicomtat.backend
22

3+
import com.arnyminerz.escalaralcoiaicomtat.backend.database.entity.info.DatabaseVersion
4+
import com.arnyminerz.escalaralcoiaicomtat.backend.database.migration.Migrations
35
import com.arnyminerz.escalaralcoiaicomtat.backend.database.table.Areas
46
import com.arnyminerz.escalaralcoiaicomtat.backend.database.table.BlockingTable
57
import com.arnyminerz.escalaralcoiaicomtat.backend.database.table.InfoTable
@@ -11,6 +13,7 @@ import kotlinx.coroutines.Dispatchers
1113
import org.jetbrains.exposed.sql.Database
1214
import org.jetbrains.exposed.sql.SchemaUtils
1315
import org.jetbrains.exposed.sql.SqlLogger
16+
import org.jetbrains.exposed.sql.Transaction
1417
import org.jetbrains.exposed.sql.addLogger
1518
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
1619

@@ -74,9 +77,15 @@ class ServerDatabase private constructor() {
7477
Database.connect(url, driver, username, password)
7578
}
7679

77-
suspend fun <T> query(block: suspend () -> T): T = newSuspendedTransaction(Dispatchers.IO, database) {
80+
suspend fun <T> query(block: suspend Transaction.() -> T): T = newSuspendedTransaction(Dispatchers.IO, database) {
81+
val isFirstRun = SchemaUtils.listDatabases().isEmpty()
82+
7883
SchemaUtils.create(Areas, Zones, Sectors, Paths, BlockingTable, InfoTable)
7984

85+
if (isFirstRun) {
86+
DatabaseVersion.update(Migrations.DATABASE_VERSION)
87+
}
88+
8089
logger?.let { addLogger(it) }
8190

8291
block()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.arnyminerz.escalaralcoiaicomtat.backend.database.entity.info
2+
3+
import com.arnyminerz.escalaralcoiaicomtat.backend.database.table.InfoTable
4+
import org.jetbrains.exposed.dao.EntityClass
5+
import org.jetbrains.exposed.dao.id.EntityID
6+
import org.jetbrains.exposed.sql.Transaction
7+
8+
class DatabaseVersion(id: EntityID<String>): InfoEntry(id) {
9+
companion object: EntityClass<String, LastUpdate>(InfoTable), InfoEntryCompanion<Int> {
10+
private const val ID = "database_version"
11+
12+
context(Transaction)
13+
override fun get(): Int? = findById(ID)?.value?.toIntOrNull()
14+
15+
context(Transaction)
16+
override fun update(value: Int) {
17+
val entry = findById(ID)
18+
if (entry != null) {
19+
// Already exists, update
20+
entry.value = value.toString()
21+
} else {
22+
new(ID) {
23+
this.value = value.toString()
24+
}
25+
}
26+
}
27+
}
28+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.arnyminerz.escalaralcoiaicomtat.backend.database.entity.info
2+
3+
import org.jetbrains.exposed.sql.Transaction
4+
5+
interface InfoEntryCompanion<Type: Any> {
6+
context(Transaction)
7+
fun get(): Type?
8+
9+
context(Transaction)
10+
fun update(value: Type)
11+
}

src/main/kotlin/com/arnyminerz/escalaralcoiaicomtat/backend/database/entity/info/LastUpdate.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,30 @@ import com.arnyminerz.escalaralcoiaicomtat.backend.database.table.InfoTable
44
import java.time.Instant
55
import org.jetbrains.exposed.dao.EntityClass
66
import org.jetbrains.exposed.dao.id.EntityID
7+
import org.jetbrains.exposed.sql.Transaction
78

89
class LastUpdate(id: EntityID<String>): InfoEntry(id) {
910

10-
companion object: EntityClass<String, LastUpdate>(InfoTable) {
11+
companion object: EntityClass<String, LastUpdate>(InfoTable), InfoEntryCompanion<Instant> {
1112
private const val ID = "last_update"
1213

13-
fun get(): Instant? = findById(ID)?.value?.toLong()?.let(Instant::ofEpochMilli)
14+
context(Transaction)
15+
override fun get(): Instant? = findById(ID)?.value?.toLong()?.let(Instant::ofEpochMilli)
1416

15-
fun set(timestamp: Instant = Instant.now()) {
17+
context(Transaction)
18+
override fun update(value: Instant) {
1619
val entry = findById(ID)
1720
if (entry != null) {
1821
// Already exists, update
19-
entry.value = timestamp.toEpochMilli().toString()
22+
entry.value = value.toEpochMilli().toString()
2023
} else {
2124
LastUpdate.new(ID) {
22-
value = timestamp.toEpochMilli().toString()
25+
this.value = value.toEpochMilli().toString()
2326
}
2427
}
2528
}
29+
30+
context(Transaction)
31+
fun set(value: Instant = Instant.now()) = update(value)
2632
}
2733
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.arnyminerz.escalaralcoiaicomtat.backend.database.migration
2+
3+
import com.arnyminerz.escalaralcoiaicomtat.backend.database.entity.info.DatabaseVersion
4+
import org.jetbrains.exposed.sql.Transaction
5+
6+
abstract class Migration(
7+
val from: Int?,
8+
val to: Int
9+
) {
10+
context (Transaction)
11+
suspend fun performMigration() {
12+
migrate()
13+
14+
DatabaseVersion.update(to)
15+
}
16+
17+
protected abstract suspend fun Transaction.migrate()
18+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.arnyminerz.escalaralcoiaicomtat.backend.database.migration
2+
3+
import com.arnyminerz.escalaralcoiaicomtat.backend.Logger
4+
import com.arnyminerz.escalaralcoiaicomtat.backend.ServerDatabase
5+
import com.arnyminerz.escalaralcoiaicomtat.backend.database.entity.info.DatabaseVersion
6+
7+
object Migrations {
8+
const val DATABASE_VERSION = 1
9+
10+
private val values = arrayOf<Migration>(To1)
11+
12+
suspend fun runMigrations() {
13+
ServerDatabase.instance.query {
14+
var currentVersion = DatabaseVersion.get()
15+
while (currentVersion != DATABASE_VERSION) {
16+
val migration = values.find { it.from == currentVersion }
17+
require(migration != null) {
18+
"Could not find migration from version $currentVersion. Database version: $DATABASE_VERSION"
19+
}
20+
migration.performMigration()
21+
22+
currentVersion = DatabaseVersion.get()
23+
}
24+
Logger.info("Database is up to date.")
25+
}
26+
}
27+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.arnyminerz.escalaralcoiaicomtat.backend.database.migration
2+
3+
import com.arnyminerz.escalaralcoiaicomtat.backend.database.SqlConsts
4+
import com.arnyminerz.escalaralcoiaicomtat.backend.database.table.Paths
5+
import org.jetbrains.exposed.sql.Transaction
6+
7+
object To1: Migration(null, 1) {
8+
override suspend fun Transaction.migrate() {
9+
this.exec("ALTER TABLE Paths ADD images varchar(${SqlConsts.FILE_LENGTH * Paths.MAX_IMAGES});")
10+
}
11+
}

0 commit comments

Comments
 (0)