From 2e6783acc65b3d4c52402ae2c19dab2f4e42309c Mon Sep 17 00:00:00 2001 From: SamB440 Date: Sun, 23 Apr 2023 17:20:07 +0100 Subject: [PATCH] Use flyway for database migrations, bump region varchar length to 64 --- rpgregions/build.gradle.kts | 2 + .../managers/data/SQLCommonStorage.java | 60 ++++++++++++++++++- .../managers/data/sql/SqlStorage.java | 15 ++--- .../managers/data/sqlite/SqliteStorage.java | 12 +--- .../V1__Create_Discoveries_Table.sql | 1 + .../V2__Update_Region_Column_Length.sql | 7 +++ rpgregions/src/main/resources/plugin.yml | 3 + 7 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 rpgregions/src/main/resources/db/migration/V1__Create_Discoveries_Table.sql create mode 100644 rpgregions/src/main/resources/db/migration/V2__Update_Region_Column_Length.sql diff --git a/rpgregions/build.gradle.kts b/rpgregions/build.gradle.kts index f9744ad..d322147 100644 --- a/rpgregions/build.gradle.kts +++ b/rpgregions/build.gradle.kts @@ -40,6 +40,8 @@ dependencies { exclude("org.bukkit") exclude("org.spigotmc") } + compileOnly("org.flywaydb:flyway-core:9.16.2") // IMPLEMENTED VIA LIBRARIES - db migration + compileOnly("org.flywaydb:flyway-mysql:9.16.3") // IMPLEMENTED VIA LIBRARIES //compileOnly 'com.zaxxer:HikariCP:2.4.1' // IMPLEMENTED VIA LIBRARIES - database compileOnly("me.clip:placeholderapi:2.10.4") // PAPI compileOnly("com.github.MilkBowl:VaultAPI:1.7") { // vault diff --git a/rpgregions/src/main/java/net/islandearth/rpgregions/managers/data/SQLCommonStorage.java b/rpgregions/src/main/java/net/islandearth/rpgregions/managers/data/SQLCommonStorage.java index 7bcdfcb..cb68bea 100644 --- a/rpgregions/src/main/java/net/islandearth/rpgregions/managers/data/SQLCommonStorage.java +++ b/rpgregions/src/main/java/net/islandearth/rpgregions/managers/data/SQLCommonStorage.java @@ -1,6 +1,7 @@ package net.islandearth.rpgregions.managers.data; import co.aikar.idb.DB; +import co.aikar.idb.DatabaseOptions; import co.aikar.idb.DbRow; import com.github.benmanes.caffeine.cache.AsyncCache; import com.github.benmanes.caffeine.cache.Caffeine; @@ -10,6 +11,10 @@ import net.islandearth.rpgregions.RPGRegions; import net.islandearth.rpgregions.managers.data.account.RPGRegionsAccount; import net.islandearth.rpgregions.managers.data.region.Discovery; import net.islandearth.rpgregions.managers.data.region.WorldDiscovery; +import org.bukkit.Bukkit; +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.output.ValidateOutput; +import org.flywaydb.core.api.output.ValidateResult; import org.intellij.lang.annotations.Language; import java.sql.SQLException; @@ -23,7 +28,6 @@ import java.util.concurrent.TimeUnit; public abstract class SQLCommonStorage implements IStorageManager { - protected static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS rpgregions_discoveries (uuid varchar(32) NOT NULL, region varchar(36) NOT NULL, time varchar(64) NOT NULL, PRIMARY KEY(uuid, region))"; protected static final String SELECT_REGION = "SELECT * FROM rpgregions_discoveries WHERE uuid = ?"; protected static final String INSERT_DISCOVERY = "INSERT INTO rpgregions_discoveries (uuid, region, time) VALUES (?, ?, ?)"; protected static final String DELETE_DISCOVERIES = "DELETE * FROM rpgregions_discoveries WHERE uuid = ?"; @@ -32,9 +36,14 @@ public abstract class SQLCommonStorage implements IStorageManager { private final AsyncCache cachedAccounts; private final RPGRegions plugin; + private final DatabaseOptions options; - public SQLCommonStorage(RPGRegions plugin) { + public SQLCommonStorage(RPGRegions plugin, DatabaseOptions options) { + this.options = options; this.plugin = plugin; + + migrate(); + this.cachedAccounts = Caffeine.newBuilder() .maximumSize(1_000) // Realistically no server can support higher than this, even Folia .scheduler(Scheduler.systemScheduler()) @@ -48,6 +57,53 @@ public abstract class SQLCommonStorage implements IStorageManager { .buildAsync((key, executor) -> getAccount(key)); } + private void migrate() { + // Time to migrate! + // Create the Flyway instance and point it to the database + Flyway flyway = Flyway.configure(plugin.getClass().getClassLoader()) + .baselineOnMigrate(true) + .dataSource("jdbc:" + options.getDsn(), options.getUser(), options.getPass()).load(); + // Start the migration + flyway.migrate(); + final ValidateResult result = flyway.validateWithResult(); + if (!result.validationSuccessful) { + panic(result); + throw new IllegalStateException("Could not migrate the database!"); + } + } + + private void panic(ValidateResult result) { + plugin.getLogger().severe("=== UNABLE TO MIGRATE DATABASE, ERROR AS FOLLOWS ==="); + plugin.getLogger().severe("=== BASIC INFO ==="); + plugin.getLogger().severe("Flyway Version: " + result.flywayVersion); + plugin.getLogger().severe("Plugin Version: " + plugin.getDescription().getVersion()); + plugin.getLogger().severe("Server Version: " + Bukkit.getServer().getVersion()); + plugin.getLogger().severe("=== MIGRATION INFO ==="); + plugin.getLogger().severe("Operation: " + result.operation); + plugin.getLogger().severe("Error messages (combined): " + result.getAllErrorMessages()); + plugin.getLogger().severe("Error code: " + result.errorDetails.errorCode); + plugin.getLogger().severe("Error message: " + result.errorDetails.errorMessage); + plugin.getLogger().severe("=== INVALID MIGRATIONS ==="); + for (ValidateOutput invalidMigration : result.invalidMigrations) { + plugin.getLogger().severe("-"); + plugin.getLogger().severe("Error code: " + invalidMigration.errorDetails.errorCode); + plugin.getLogger().severe("Error message: " + invalidMigration.errorDetails.errorMessage); + plugin.getLogger().severe("Description: " + invalidMigration.description); + plugin.getLogger().severe("Version: " + invalidMigration.version); + plugin.getLogger().severe("Path: " + invalidMigration.filepath); + plugin.getLogger().severe("-"); + } + plugin.getLogger().severe("=== MIGRATION WARNINGS ==="); + for (String warning : result.warnings) { + plugin.getLogger().warning(warning); + } + plugin.getLogger().severe("=== END ERROR, EXITING ==="); + } + + protected DatabaseOptions getDatabaseOptions() { + return options; + } + @Override public CompletableFuture getAccount(UUID uuid) { // Check if cached diff --git a/rpgregions/src/main/java/net/islandearth/rpgregions/managers/data/sql/SqlStorage.java b/rpgregions/src/main/java/net/islandearth/rpgregions/managers/data/sql/SqlStorage.java index a3b3686..57a43c2 100644 --- a/rpgregions/src/main/java/net/islandearth/rpgregions/managers/data/sql/SqlStorage.java +++ b/rpgregions/src/main/java/net/islandearth/rpgregions/managers/data/sql/SqlStorage.java @@ -7,22 +7,15 @@ import co.aikar.idb.PooledDatabaseOptions; import net.islandearth.rpgregions.RPGRegions; import net.islandearth.rpgregions.managers.data.SQLCommonStorage; -import java.sql.SQLException; - public class SqlStorage extends SQLCommonStorage { public SqlStorage(RPGRegions plugin) { - super(plugin); - DatabaseOptions options = DatabaseOptions.builder().mysql(plugin.getConfig().getString("settings.sql.user"), + super(plugin, + DatabaseOptions.builder().mysql(plugin.getConfig().getString("settings.sql.user"), plugin.getConfig().getString("settings.sql.pass"), plugin.getConfig().getString("settings.sql.db"), - plugin.getConfig().getString("settings.sql.host") + ":" + plugin.getConfig().getString("settings.sql.port")).build(); - Database db = PooledDatabaseOptions.builder().options(options).createHikariDatabase(); + plugin.getConfig().getString("settings.sql.host") + ":" + plugin.getConfig().getString("settings.sql.port")).build()); + Database db = PooledDatabaseOptions.builder().options(getDatabaseOptions()).createHikariDatabase(); DB.setGlobalDatabase(db); - try { - db.executeUpdate(CREATE_TABLE); - } catch (SQLException e) { - e.printStackTrace(); - } } } diff --git a/rpgregions/src/main/java/net/islandearth/rpgregions/managers/data/sqlite/SqliteStorage.java b/rpgregions/src/main/java/net/islandearth/rpgregions/managers/data/sqlite/SqliteStorage.java index 031a987..a06cbff 100644 --- a/rpgregions/src/main/java/net/islandearth/rpgregions/managers/data/sqlite/SqliteStorage.java +++ b/rpgregions/src/main/java/net/islandearth/rpgregions/managers/data/sqlite/SqliteStorage.java @@ -7,19 +7,11 @@ import co.aikar.idb.PooledDatabaseOptions; import net.islandearth.rpgregions.RPGRegions; import net.islandearth.rpgregions.managers.data.SQLCommonStorage; -import java.sql.SQLException; - public class SqliteStorage extends SQLCommonStorage { public SqliteStorage(RPGRegions plugin) { - super(plugin); - DatabaseOptions options = DatabaseOptions.builder().sqlite(plugin.getDataFolder() + "/regions.sqlite").build(); - Database db = PooledDatabaseOptions.builder().options(options).createHikariDatabase(); + super(plugin, DatabaseOptions.builder().sqlite(plugin.getDataFolder() + "/regions.sqlite").build()); + Database db = PooledDatabaseOptions.builder().options(getDatabaseOptions()).createHikariDatabase(); DB.setGlobalDatabase(db); - try { - db.executeUpdate(CREATE_TABLE); - } catch (SQLException e) { - e.printStackTrace(); - } } } diff --git a/rpgregions/src/main/resources/db/migration/V1__Create_Discoveries_Table.sql b/rpgregions/src/main/resources/db/migration/V1__Create_Discoveries_Table.sql new file mode 100644 index 0000000..0d26ea3 --- /dev/null +++ b/rpgregions/src/main/resources/db/migration/V1__Create_Discoveries_Table.sql @@ -0,0 +1 @@ +CREATE TABLE IF NOT EXISTS rpgregions_discoveries (uuid varchar(32) NOT NULL, region varchar(32) NOT NULL, time varchar(64) NOT NULL, PRIMARY KEY(uuid, region)) diff --git a/rpgregions/src/main/resources/db/migration/V2__Update_Region_Column_Length.sql b/rpgregions/src/main/resources/db/migration/V2__Update_Region_Column_Length.sql new file mode 100644 index 0000000..2a89796 --- /dev/null +++ b/rpgregions/src/main/resources/db/migration/V2__Update_Region_Column_Length.sql @@ -0,0 +1,7 @@ +-- Ok, so sqlite does not support altering columns, so we have to do this hacky stuff. +-- I make a new temporary table with the new region varchar(64), copy all the data from the old table into the temp one, +-- then drop the old table and rename the new one to the old one. +CREATE TABLE IF NOT EXISTS migrate_rpgregions_discoveries (uuid varchar(32) NOT NULL, region varchar(64) NOT NULL, time varchar(64) NOT NULL, PRIMARY KEY(uuid, region)); +INSERT INTO migrate_rpgregions_discoveries SELECT * FROM rpgregions_discoveries; +DROP TABLE rpgregions_discoveries; +ALTER TABLE migrate_rpgregions_discoveries RENAME TO rpgregions_discoveries; \ No newline at end of file diff --git a/rpgregions/src/main/resources/plugin.yml b/rpgregions/src/main/resources/plugin.yml index d8ce450..d433a35 100644 --- a/rpgregions/src/main/resources/plugin.yml +++ b/rpgregions/src/main/resources/plugin.yml @@ -7,6 +7,9 @@ libraries: - "net.kyori:adventure-platform-bukkit:4.3.0" - "net.kyori:adventure-text-minimessage:4.13.0" - "com.github.ben-manes.caffeine:caffeine:3.1.5" + - "org.xerial:sqlite-jdbc:3.30.1" + - "org.flywaydb:flyway-core:9.16.2" # db migration + - "org.flywaydb:flyway-mysql:9.16.3" softdepend: [Hyperverse, Multiverse, UltraRegions, WorldGuard, PlaceholderAPI, HeadDatabase, Residence, Plan, GriefPrevention, GriefDefender, Vault, MythicMobs, AlonsoLevels, dynmap, ProtocolLib, Quests, BetonQuest, Lands, MMOCore, CustomStructures] authors: [SamB440] description: Discoverable regions