aboutsummaryrefslogtreecommitdiff
path: root/database
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2025-01-16 21:49:20 +0100
committerLinnea Gräf <nea@nea.moe>2025-01-16 21:49:20 +0100
commit305178f5511bbf7b4499d7cf4142a067ab42e2fc (patch)
tree845b3cca6490fb9c1252215aa310eec108316bf1 /database
parent40ed3a1f667d58501fc43fb45f53585315c013d1 (diff)
downloadLocalTransactionLedger-305178f5511bbf7b4499d7cf4142a067ab42e2fc.tar.gz
LocalTransactionLedger-305178f5511bbf7b4499d7cf4142a067ab42e2fc.tar.bz2
LocalTransactionLedger-305178f5511bbf7b4499d7cf4142a067ab42e2fc.zip
refactor: Extract database models to own modules
Diffstat (limited to 'database')
-rw-r--r--database/impl/build.gradle.kts12
-rw-r--r--database/impl/src/main/kotlin/moe/nea/ledger/database/DBLogEntry.kt32
-rw-r--r--database/impl/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt68
-rw-r--r--database/impl/src/main/kotlin/moe/nea/ledger/database/Database.kt57
-rw-r--r--database/impl/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt20
-rw-r--r--database/impl/src/main/kotlin/moe/nea/ledger/database/schema.dot23
6 files changed, 212 insertions, 0 deletions
diff --git a/database/impl/build.gradle.kts b/database/impl/build.gradle.kts
new file mode 100644
index 0000000..17a7a5a
--- /dev/null
+++ b/database/impl/build.gradle.kts
@@ -0,0 +1,12 @@
+plugins {
+ `java-library`
+ kotlin("jvm")
+}
+
+dependencies {
+ api(project(":database:core"))
+}
+
+java {
+ toolchain.languageVersion.set(JavaLanguageVersion.of(8))
+}
diff --git a/database/impl/src/main/kotlin/moe/nea/ledger/database/DBLogEntry.kt b/database/impl/src/main/kotlin/moe/nea/ledger/database/DBLogEntry.kt
new file mode 100644
index 0000000..e2530cc
--- /dev/null
+++ b/database/impl/src/main/kotlin/moe/nea/ledger/database/DBLogEntry.kt
@@ -0,0 +1,32 @@
+package moe.nea.ledger.database
+
+import moe.nea.ledger.ItemChange
+import moe.nea.ledger.ItemId
+import moe.nea.ledger.TransactionType
+import moe.nea.ledger.database.columns.DBDouble
+import moe.nea.ledger.database.columns.DBEnum
+import moe.nea.ledger.database.columns.DBString
+import moe.nea.ledger.database.columns.DBUlid
+import moe.nea.ledger.database.columns.DBUuid
+
+object DBLogEntry : Table("LogEntry") {
+ val transactionId = column("transactionId", DBUlid)
+ val type = column("type", DBEnum<TransactionType>())
+ val profileId = column("profileId", DBUuid)
+ val playerId = column("playerId", DBUuid)
+}
+
+object DBItemEntry : Table("ItemEntry") {
+ val transactionId = column("transactionId", DBUlid) // TODO: add foreign keys
+ val mode = column("mode", DBEnum<ItemChange.ChangeDirection>())
+ val itemId = column("item", DBString.mapped(ItemId::string, ::ItemId))
+ val size = column("size", DBDouble)
+
+ fun objMap(result: ResultRow): ItemChange {
+ return ItemChange(
+ result[itemId],
+ result[size],
+ result[mode],
+ )
+ }
+}
diff --git a/database/impl/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt b/database/impl/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt
new file mode 100644
index 0000000..7d1782a
--- /dev/null
+++ b/database/impl/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt
@@ -0,0 +1,68 @@
+package moe.nea.ledger.database
+
+import java.sql.Connection
+
+interface DBUpgrade {
+ val toVersion: Long
+ val fromVersion get() = toVersion - 1
+ fun performUpgrade(connection: Connection)
+
+ companion object {
+
+ fun performUpgrades(
+ connection: Connection,
+ upgrades: Iterable<DBUpgrade>,
+ ) {
+ for (upgrade in upgrades) {
+ upgrade.performUpgrade(connection)
+ }
+ }
+
+ fun performUpgradeChain(
+ connection: Connection,
+ from: Long, to: Long,
+ upgrades: Iterable<DBUpgrade>,
+ afterEach: (newVersion: Long) -> Unit,
+ ) {
+ val table = buildLookup(upgrades)
+ for (version in (from + 1)..(to)) {
+ val currentUpgrades = table[version] ?: listOf()
+ println("Scheduled ${currentUpgrades.size} upgrades to reach DB version $version")
+ performUpgrades(connection, currentUpgrades)
+ afterEach(version)
+ }
+ }
+
+ fun buildLookup(upgrades: Iterable<DBUpgrade>): Map<Long, List<DBUpgrade>> {
+ return upgrades.groupBy { it.toVersion }
+ }
+
+ fun createTable(to: Long, table: Table, vararg columns: Column<*>): DBUpgrade {
+ require(columns.all { it in table.columns })
+ return of("Create table ${table}", to) {
+ table.createIfNotExists(it, columns.toList())
+ }
+ }
+
+ fun addColumns(to: Long, table: Table, vararg columns: Column<*>): DBUpgrade {
+ return of("Add columns to table $table", to) {
+ table.alterTableAddColumns(it, columns.toList())
+ }
+ }
+
+ fun of(name: String, to: Long, block: (Connection) -> Unit): DBUpgrade {
+ return object : DBUpgrade {
+ override val toVersion: Long
+ get() = to
+
+ override fun performUpgrade(connection: Connection) {
+ block(connection)
+ }
+
+ override fun toString(): String {
+ return name
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/database/impl/src/main/kotlin/moe/nea/ledger/database/Database.kt b/database/impl/src/main/kotlin/moe/nea/ledger/database/Database.kt
new file mode 100644
index 0000000..e4b34c8
--- /dev/null
+++ b/database/impl/src/main/kotlin/moe/nea/ledger/database/Database.kt
@@ -0,0 +1,57 @@
+package moe.nea.ledger.database
+
+import moe.nea.ledger.database.columns.DBString
+import java.io.File
+import java.sql.Connection
+import java.sql.DriverManager
+
+class Database(val dataFolder: File) {
+ lateinit var connection: Connection
+
+ object MetaTable : Table("LedgerMeta") {
+ val key = column("key", DBString)
+ val value = column("value", DBString)
+
+ init {
+ unique(key)
+ }
+ }
+
+ data class MetaKey(val name: String) {
+ companion object {
+ val DATABASE_VERSION = MetaKey("databaseVersion")
+ val LAST_LAUNCH = MetaKey("lastLaunch")
+ }
+ }
+
+ fun setMetaKey(key: MetaKey, value: String) {
+ MetaTable.insert(connection, Table.OnConflict.REPLACE) {
+ it[MetaTable.key] = key.name
+ it[MetaTable.value] = value
+ }
+ }
+
+ val databaseVersion: Long = 1
+
+ fun loadAndUpgrade() {
+ connection = DriverManager.getConnection("jdbc:sqlite:${dataFolder.resolve("database.db")}")
+ MetaTable.createIfNotExists(connection)
+ val meta = MetaTable.selectAll(connection).associate { MetaKey(it[MetaTable.key]) to it[MetaTable.value] }
+ val lastLaunch = meta[MetaKey.LAST_LAUNCH]?.toLong() ?: 0L
+ println("Last launch $lastLaunch")
+ setMetaKey(MetaKey.LAST_LAUNCH, System.currentTimeMillis().toString())
+
+ val oldVersion = meta[MetaKey.DATABASE_VERSION]?.toLong() ?: -1
+ println("Old Database Version: $oldVersion; Current version: $databaseVersion")
+ if (oldVersion > databaseVersion)
+ error("Outdated software. Database is newer than me!")
+ // TODO: create a backup if there is a db version upgrade happening
+ DBUpgrade.performUpgradeChain(
+ connection, oldVersion, databaseVersion,
+ Upgrades().upgrades
+ ) { version ->
+ setMetaKey(MetaKey.DATABASE_VERSION, version.toString())
+ }
+ }
+
+} \ No newline at end of file
diff --git a/database/impl/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt b/database/impl/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt
new file mode 100644
index 0000000..e83abe7
--- /dev/null
+++ b/database/impl/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt
@@ -0,0 +1,20 @@
+package moe.nea.ledger.database
+
+class Upgrades {
+ val upgrades = mutableListOf<DBUpgrade>()
+
+ fun add(upgrade: DBUpgrade) = upgrades.add(upgrade)
+
+ init {
+ add(DBUpgrade.createTable(
+ 0, DBLogEntry,
+ DBLogEntry.type, DBLogEntry.playerId, DBLogEntry.profileId,
+ DBLogEntry.transactionId))
+ add(DBUpgrade.createTable(
+ 0, DBItemEntry,
+ DBItemEntry.itemId, DBItemEntry.size, DBItemEntry.mode, DBItemEntry.transactionId
+ ))
+ }
+
+
+} \ No newline at end of file
diff --git a/database/impl/src/main/kotlin/moe/nea/ledger/database/schema.dot b/database/impl/src/main/kotlin/moe/nea/ledger/database/schema.dot
new file mode 100644
index 0000000..d932f6a
--- /dev/null
+++ b/database/impl/src/main/kotlin/moe/nea/ledger/database/schema.dot
@@ -0,0 +1,23 @@
+digraph {
+ node [shape=plain];
+ rankdir=LR;
+ entry [label=<
+ <table border="0" cellborder="1" cellspacing="0">
+ <tr><td>Log Entry</td></tr>
+ <tr><td port="player">playerId</td></tr>
+ <tr><td port="profile">profileId</td></tr>
+ <tr><td port="date">timestamp</td></tr>
+ <tr><td port="type">Type</td></tr>
+ </table>
+ >];
+ item [label=<
+ <table border="0" cellborder="1" cellspacing="0">
+ <tr><td>Item Stack</td><tr>
+ <tr><td port="transaction">Transaction</td></tr>
+ <tr><td port="id">Item ID</td></tr>
+ <tr><td port="count">Count</td></tr>
+ <tr><td port="direction">Transfer Direction</td></tr>
+ </table>
+ >];
+// item:transaction -> entry;
+} \ No newline at end of file