From 960c2291b6eaca340513388287e99373ed5461a0 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 8 Jan 2017 19:33:27 +0000 Subject: [PATCH] Fully implement new SQL schema & add migration code --- bukkit/src/main/resources/config.yml | 4 + bungee/src/main/resources/config.yml | 4 + .../common/config/AbstractConfiguration.java | 2 + .../common/config/LPConfiguration.java | 2 + .../common/storage/StorageFactory.java | 11 +- .../common/storage/backing/JSONBacking.java | 2 +- .../storage/backing/MongoDBBacking.java | 2 +- .../common/storage/backing/SQLBacking.java | 37 +- .../storage/backing/SQLLegacyBacking.java | 685 ------------------ .../common/storage/backing/YAMLBacking.java | 2 +- .../backing/utils/LegacySchemaMigration.java | 277 +++++++ .../storage/backing/utils/NodeDataHolder.java | 2 + sponge/src/main/resources/luckperms.conf | 4 + 13 files changed, 338 insertions(+), 696 deletions(-) delete mode 100644 common/src/main/java/me/lucko/luckperms/common/storage/backing/SQLLegacyBacking.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/storage/backing/utils/LegacySchemaMigration.java diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index 944bc1e73..acd710218 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -173,6 +173,10 @@ data: password: '' pool-size: 10 # The size of the MySQL connection pool. + # The prefix for all LuckPerms tables. Change this is you want to use different tables for different servers. + # This should *not* be set to "lp_" if you have previously ran LuckPerms v2.16.81 or earlier with this database. + table_prefix: 'luckperms_' + # Set to -1 to disable. If this is the only instance accessing the datastore, you can disable syncing. # e.g. if you're using sqlite or flatfile, this can be set to -1 to save resources. sync-minutes: 3 diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index 7eafac6ac..673b21573 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -115,6 +115,10 @@ data: password: '' pool-size: 10 # The size of the MySQL connection pool. + # The prefix for all LuckPerms tables. Change this is you want to use different tables for different servers. + # This should *not* be set to "lp_" if you have previously ran LuckPerms v2.16.81 or earlier with this database. + table_prefix: 'luckperms_' + # Set to -1 to disable. If this is the only instance accessing the datastore, you can disable syncing. # e.g. if you're using sqlite or flatfile, this can be set to -1 to save resources. sync-minutes: 3 diff --git a/common/src/main/java/me/lucko/luckperms/common/config/AbstractConfiguration.java b/common/src/main/java/me/lucko/luckperms/common/config/AbstractConfiguration.java index 4c6205b66..37597cbad 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/AbstractConfiguration.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/AbstractConfiguration.java @@ -79,6 +79,7 @@ public abstract class AbstractConfiguration implement private Map groupNameRewrites; private List defaultAssignments; private DatastoreConfiguration databaseValues; + private String sqlTablePrefix; private String storageMethod; private boolean splitStorage; private Map splitStorageOptions; @@ -163,6 +164,7 @@ public abstract class AbstractConfiguration implement getString("data.password", null), getInt("data.pool-size", 10) ); + sqlTablePrefix = getString("data.table_prefix", "luckperms_"); storageMethod = getString("storage-method", defaultStorage); splitStorage = getBoolean("split-storage.enabled", false); splitStorageOptions = ImmutableMap.builder() diff --git a/common/src/main/java/me/lucko/luckperms/common/config/LPConfiguration.java b/common/src/main/java/me/lucko/luckperms/common/config/LPConfiguration.java index d05449b3f..6e62483cb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/LPConfiguration.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/LPConfiguration.java @@ -98,6 +98,8 @@ public interface LPConfiguration { DatastoreConfiguration getDatabaseValues(); + String getSqlTablePrefix(); + String getStorageMethod(); boolean isSplitStorage(); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/StorageFactory.java b/common/src/main/java/me/lucko/luckperms/common/storage/StorageFactory.java index 32979dcbb..1de1be413 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/StorageFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/StorageFactory.java @@ -30,10 +30,11 @@ import me.lucko.luckperms.common.LuckPermsPlugin; import me.lucko.luckperms.common.storage.backing.AbstractBacking; import me.lucko.luckperms.common.storage.backing.JSONBacking; import me.lucko.luckperms.common.storage.backing.MongoDBBacking; -import me.lucko.luckperms.common.storage.backing.SQLLegacyBacking; +import me.lucko.luckperms.common.storage.backing.SQLBacking; import me.lucko.luckperms.common.storage.backing.YAMLBacking; import me.lucko.luckperms.common.storage.backing.sqlprovider.H2Provider; import me.lucko.luckperms.common.storage.backing.sqlprovider.MySQLProvider; +import me.lucko.luckperms.common.storage.backing.sqlprovider.PostgreSQLProvider; import me.lucko.luckperms.common.storage.backing.sqlprovider.SQLiteProvider; import me.lucko.luckperms.common.utils.ImmutableCollectors; @@ -120,11 +121,13 @@ public class StorageFactory { private static AbstractBacking makeBacking(StorageType method, LuckPermsPlugin plugin) { switch (method) { case MYSQL: - return new SQLLegacyBacking(plugin, new MySQLProvider(plugin.getConfiguration().getDatabaseValues())); + return new SQLBacking(plugin, new MySQLProvider(plugin.getConfiguration().getDatabaseValues()), plugin.getConfiguration().getSqlTablePrefix()); case SQLITE: - return new SQLLegacyBacking(plugin, new SQLiteProvider(new File(plugin.getDataFolder(), "luckperms.sqlite"))); + return new SQLBacking(plugin, new SQLiteProvider(new File(plugin.getDataFolder(), "luckperms.sqlite")), plugin.getConfiguration().getSqlTablePrefix()); case H2: - return new SQLLegacyBacking(plugin, new H2Provider(new File(plugin.getDataFolder(), "luckperms.db"))); + return new SQLBacking(plugin, new H2Provider(new File(plugin.getDataFolder(), "luckperms.db")), plugin.getConfiguration().getSqlTablePrefix()); + case POSTGRESQL: + return new SQLBacking(plugin, new PostgreSQLProvider(plugin.getConfiguration().getDatabaseValues()), plugin.getConfiguration().getSqlTablePrefix()); case MONGODB: return new MongoDBBacking(plugin, plugin.getConfiguration().getDatabaseValues()); case YAML: diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/JSONBacking.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/JSONBacking.java index e142d8c7f..caf45a1fd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/JSONBacking.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/JSONBacking.java @@ -130,7 +130,7 @@ public class JSONBacking extends FlatfileBacking { if (user.getName() == null || user.getName().equalsIgnoreCase("null")) { user.setName(name1); } else { - if (!name1.equals(user.getName())) { + if (!name1.equalsIgnoreCase(user.getName())) { save = true; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/MongoDBBacking.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/MongoDBBacking.java index 25f80344b..20cc7479a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/MongoDBBacking.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/MongoDBBacking.java @@ -235,7 +235,7 @@ public class MongoDBBacking extends AbstractBacking { if (user.getName() == null || user.getName().equalsIgnoreCase("null")) { user.setName(d.getString("name")); } else { - if (!d.getString("name").equals(user.getName())) { + if (!d.getString("name").equalsIgnoreCase(user.getName())) { save = true; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/SQLBacking.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/SQLBacking.java index 999aa08a5..4730538f5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/SQLBacking.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/SQLBacking.java @@ -22,6 +22,8 @@ package me.lucko.luckperms.common.storage.backing; +import lombok.Getter; + import com.google.common.collect.Maps; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -38,6 +40,7 @@ import me.lucko.luckperms.common.managers.GroupManager; import me.lucko.luckperms.common.managers.TrackManager; import me.lucko.luckperms.common.managers.impl.GenericUserManager; import me.lucko.luckperms.common.storage.backing.sqlprovider.SQLProvider; +import me.lucko.luckperms.common.storage.backing.utils.LegacySchemaMigration; import me.lucko.luckperms.common.storage.backing.utils.NodeDataHolder; import java.io.BufferedReader; @@ -65,7 +68,7 @@ public class SQLBacking extends AbstractBacking { private static final Type LIST_STRING_TYPE = new TypeToken>(){}.getType(); private static final String USER_PERMISSIONS_SELECT = "SELECT permission, value, server, world, expiry, contexts FROM {prefix}user_permissions WHERE uuid=?"; - private static final String USER_PERMISSIONS_DELETE_SPECIFIC = "DELETE FROM {prefix}user_permissions WHERE uuid=?, permission=?, value=?, server=?, world=?, expiry=?, contexts=?"; + private static final String USER_PERMISSIONS_DELETE_SPECIFIC = "DELETE FROM {prefix}user_permissions WHERE uuid=? AND permission=? AND value=? AND server=? AND world=? AND expiry=? AND contexts=?"; private static final String USER_PERMISSIONS_DELETE = "DELETE FROM {prefix}user_permissions WHERE uuid=?"; private static final String USER_PERMISSIONS_INSERT = "INSERT INTO {prefix}user_permissions(uuid, permission, value, server, world, expiry, contexts) VALUES(?, ?, ?, ?, ?, ?, ?)"; private static final String USER_PERMISSIONS_SELECT_DISTINCT = "SELECT DISTINCT uuid FROM {prefix}user_permissions"; @@ -76,10 +79,11 @@ public class SQLBacking extends AbstractBacking { private static final String PLAYER_INSERT = "INSERT INTO {prefix}players VALUES(?, ?, ?)"; private static final String PLAYER_UPDATE = "UPDATE {prefix}players SET username=? WHERE uuid=?"; private static final String PLAYER_UPDATE_FULL = "UPDATE {prefix}players SET username=?, primary_group=? WHERE uuid=?"; + private static final String PLAYER_UPDATE_PRIMARY_GROUP = "UPDATE {prefix}players SET primary_group=? WHERE uuid=?"; private static final String GROUP_PERMISSIONS_SELECT = "SELECT permission, value, server, world, expiry, contexts FROM {prefix}group_permissions WHERE name=?"; private static final String GROUP_PERMISSIONS_DELETE = "DELETE FROM {prefix}group_permissions WHERE name=?"; - private static final String GROUP_PERMISSIONS_DELETE_SPECIFIC = "DELETE FROM {prefix}group_permissions WHERE name=?, permission=?, value=?, server=?, world=?, expiry=?, contexts=?"; + private static final String GROUP_PERMISSIONS_DELETE_SPECIFIC = "DELETE FROM {prefix}group_permissions WHERE name=? AND permission=? AND value=? AND server=? AND world=? AND expiry=? AND contexts=?"; private static final String GROUP_PERMISSIONS_INSERT = "INSERT INTO {prefix}group_permissions(name, permission, value, server, world, expiry, contexts) VALUES(?, ?, ?, ?, ?, ?, ?)"; private static final String GROUP_SELECT_ALL = "SELECT name FROM {prefix}groups"; @@ -96,8 +100,12 @@ public class SQLBacking extends AbstractBacking { private static final String ACTION_SELECT_ALL = "SELECT * FROM {prefix}actions"; + @Getter private final Gson gson; + + @Getter private final SQLProvider provider; + @Getter private final Function prefix; public SQLBacking(LuckPermsPlugin plugin, SQLProvider provider, String prefix) { @@ -109,7 +117,14 @@ public class SQLBacking extends AbstractBacking { private boolean tableExists(String table) throws SQLException { try (Connection connection = provider.getConnection()) { - return connection.getMetaData().getTables(null, null, table.toUpperCase(), null).next(); + try (ResultSet rs = connection.getMetaData().getTables(null, null, "%", null)) { + while (rs.next()) { + if (rs.getString(3).equalsIgnoreCase(table)) { + return true; + } + } + return false; + } } } @@ -152,6 +167,15 @@ public class SQLBacking extends AbstractBacking { } } } + + // Try migration from legacy backing + if (tableExists("lp_users")) { + plugin.getLog().severe("===== Legacy Schema Migration ====="); + plugin.getLog().severe("Starting migration from legacy schema. This could take a while...."); + plugin.getLog().severe("Please do not stop your server while the migration takes place."); + + new LegacySchemaMigration(this).run(); + } } setAcceptingLogins(true); @@ -289,7 +313,7 @@ public class SQLBacking extends AbstractBacking { user.setName(name); } else { // The name in storage is not the same as their actual name. - if (!name.equals(user.getName())) { + if (!name.equalsIgnoreCase(user.getName())) { save = true; } } @@ -327,6 +351,11 @@ public class SQLBacking extends AbstractBacking { ps.setString(1, user.getUuid().toString()); ps.execute(); } + try (PreparedStatement ps = c.prepareStatement(prefix.apply(PLAYER_UPDATE_PRIMARY_GROUP))) { + ps.setString(1, "default"); + ps.setString(2, user.getUuid().toString()); + ps.execute(); + } } catch (SQLException e) { e.printStackTrace(); return false; diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/SQLLegacyBacking.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/SQLLegacyBacking.java deleted file mode 100644 index 98c5cd8b0..000000000 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/SQLLegacyBacking.java +++ /dev/null @@ -1,685 +0,0 @@ -/* - * Copyright (c) 2016 Lucko (Luck) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package me.lucko.luckperms.common.storage.backing; - -import com.google.common.collect.ImmutableMap; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; - -import me.lucko.luckperms.api.LogEntry; -import me.lucko.luckperms.common.LuckPermsPlugin; -import me.lucko.luckperms.common.core.UserIdentifier; -import me.lucko.luckperms.common.core.model.Group; -import me.lucko.luckperms.common.core.model.Track; -import me.lucko.luckperms.common.core.model.User; -import me.lucko.luckperms.common.data.Log; -import me.lucko.luckperms.common.managers.GroupManager; -import me.lucko.luckperms.common.managers.TrackManager; -import me.lucko.luckperms.common.managers.impl.GenericUserManager; -import me.lucko.luckperms.common.storage.backing.sqlprovider.H2Provider; -import me.lucko.luckperms.common.storage.backing.sqlprovider.MySQLProvider; -import me.lucko.luckperms.common.storage.backing.sqlprovider.SQLProvider; -import me.lucko.luckperms.common.storage.backing.sqlprovider.SQLiteProvider; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - -import static me.lucko.luckperms.common.core.model.PermissionHolder.exportToLegacy; -import static me.lucko.luckperms.common.storage.backing.sqlprovider.SQLProvider.QueryPS; -import static me.lucko.luckperms.common.storage.backing.sqlprovider.SQLProvider.QueryRS; - -public class SQLLegacyBacking extends AbstractBacking { - private static final Type NM_TYPE = new TypeToken>() {}.getType(); - private static final Type T_TYPE = new TypeToken>() {}.getType(); - - private static final String MYSQL_CREATETABLE_UUID = "CREATE TABLE IF NOT EXISTS `lp_uuid` (`name` VARCHAR(16) NOT NULL, `uuid` VARCHAR(36) NOT NULL, PRIMARY KEY (`name`)) DEFAULT CHARSET=utf8;"; - private static final String MYSQL_CREATETABLE_USERS = "CREATE TABLE IF NOT EXISTS `lp_users` (`uuid` VARCHAR(36) NOT NULL, `name` VARCHAR(16) NOT NULL, `primary_group` VARCHAR(36) NOT NULL, `perms` TEXT NOT NULL, PRIMARY KEY (`uuid`)) DEFAULT CHARSET=utf8;"; - private static final String MYSQL_CREATETABLE_GROUPS = "CREATE TABLE IF NOT EXISTS `lp_groups` (`name` VARCHAR(36) NOT NULL, `perms` TEXT NULL, PRIMARY KEY (`name`)) DEFAULT CHARSET=utf8;"; - private static final String MYSQL_CREATETABLE_TRACKS = "CREATE TABLE IF NOT EXISTS `lp_tracks` (`name` VARCHAR(36) NOT NULL, `groups` TEXT NULL, PRIMARY KEY (`name`)) DEFAULT CHARSET=utf8;"; - private static final String MYSQL_CREATETABLE_ACTION = "CREATE TABLE IF NOT EXISTS `lp_actions` (`id` INT AUTO_INCREMENT NOT NULL, `time` BIGINT NOT NULL, `actor_uuid` VARCHAR(36) NOT NULL, `actor_name` VARCHAR(16) NOT NULL, `type` CHAR(1) NOT NULL, `acted_uuid` VARCHAR(36) NOT NULL, `acted_name` VARCHAR(36) NOT NULL, `action` VARCHAR(256) NOT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"; - - private static final String H2_CREATETABLE_UUID = "CREATE TABLE IF NOT EXISTS `lp_uuid` (`name` VARCHAR(16) NOT NULL, `uuid` VARCHAR(36) NOT NULL, PRIMARY KEY (`name`)) DEFAULT CHARSET=utf8;"; - private static final String H2_CREATETABLE_USERS = "CREATE TABLE IF NOT EXISTS `lp_users` (`uuid` VARCHAR(36) NOT NULL, `name` VARCHAR(16) NOT NULL, `primary_group` VARCHAR(36) NOT NULL, `perms` TEXT NOT NULL, PRIMARY KEY (`uuid`)) DEFAULT CHARSET=utf8;"; - private static final String H2_CREATETABLE_GROUPS = "CREATE TABLE IF NOT EXISTS `lp_groups` (`name` VARCHAR(36) NOT NULL, `perms` TEXT NULL, PRIMARY KEY (`name`)) DEFAULT CHARSET=utf8;"; - private static final String H2_CREATETABLE_TRACKS = "CREATE TABLE IF NOT EXISTS `lp_tracks` (`name` VARCHAR(36) NOT NULL, `groups` TEXT NULL, PRIMARY KEY (`name`)) DEFAULT CHARSET=utf8;"; - private static final String H2_CREATETABLE_ACTION = "CREATE TABLE IF NOT EXISTS `lp_actions` (`id` INT AUTO_INCREMENT NOT NULL, `time` BIGINT NOT NULL, `actor_uuid` VARCHAR(36) NOT NULL, `actor_name` VARCHAR(16) NOT NULL, `type` CHAR(1) NOT NULL, `acted_uuid` VARCHAR(36) NOT NULL, `acted_name` VARCHAR(36) NOT NULL, `action` VARCHAR(256) NOT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"; - - private static final String SQLITE_CREATETABLE_UUID = "CREATE TABLE IF NOT EXISTS `lp_uuid` (`name` VARCHAR(16) NOT NULL, `uuid` VARCHAR(36) NOT NULL, PRIMARY KEY (`name`));"; - private static final String SQLITE_CREATETABLE_USERS = "CREATE TABLE IF NOT EXISTS `lp_users` (`uuid` VARCHAR(36) NOT NULL, `name` VARCHAR(16) NOT NULL, `primary_group` VARCHAR(36) NOT NULL, `perms` TEXT NOT NULL, PRIMARY KEY (`uuid`));"; - private static final String SQLITE_CREATETABLE_GROUPS = "CREATE TABLE IF NOT EXISTS `lp_groups` (`name` VARCHAR(36) NOT NULL, `perms` TEXT NULL, PRIMARY KEY (`name`));"; - private static final String SQLITE_CREATETABLE_TRACKS = "CREATE TABLE IF NOT EXISTS `lp_tracks` (`name` VARCHAR(36) NOT NULL, `groups` TEXT NULL, PRIMARY KEY (`name`));"; - private static final String SQLITE_CREATETABLE_ACTION = "CREATE TABLE IF NOT EXISTS `lp_actions` (`id` INTEGER PRIMARY KEY NOT NULL, `time` BIG INT NOT NULL, `actor_uuid` VARCHAR(36) NOT NULL, `actor_name` VARCHAR(16) NOT NULL, `type` CHAR(1) NOT NULL, `acted_uuid` VARCHAR(36) NOT NULL, `acted_name` VARCHAR(36) NOT NULL, `action` VARCHAR(256) NOT NULL);"; - - private static final Map, String[]> INIT_QUERIES = ImmutableMap., String[]>builder() - .put(MySQLProvider.class, new String[]{MYSQL_CREATETABLE_UUID, MYSQL_CREATETABLE_USERS, MYSQL_CREATETABLE_GROUPS, MYSQL_CREATETABLE_TRACKS, MYSQL_CREATETABLE_ACTION}) - .put(H2Provider.class, new String[]{H2_CREATETABLE_UUID, H2_CREATETABLE_USERS, H2_CREATETABLE_GROUPS, H2_CREATETABLE_TRACKS, H2_CREATETABLE_ACTION}) - .put(SQLiteProvider.class, new String[]{SQLITE_CREATETABLE_UUID, SQLITE_CREATETABLE_USERS, SQLITE_CREATETABLE_GROUPS, SQLITE_CREATETABLE_TRACKS, SQLITE_CREATETABLE_ACTION}) - .build(); - - private static final String USER_INSERT = "INSERT INTO lp_users VALUES(?, ?, ?, ?)"; - private static final String USER_SELECT = "SELECT * FROM lp_users WHERE uuid=?"; - private static final String USER_SELECT_ALL = "SELECT uuid FROM lp_users"; - private static final String USER_UPDATE = "UPDATE lp_users SET name=?, primary_group = ?, perms=? WHERE uuid=?"; - private static final String USER_DELETE = "DELETE FROM lp_users WHERE uuid=?"; - private static final String USER_DELETE_ALL = "DELETE FROM lp_users WHERE perms=?"; - - private static final String GROUP_INSERT = "INSERT INTO lp_groups VALUES(?, ?)"; - private static final String GROUP_SELECT = "SELECT perms FROM lp_groups WHERE name=?"; - private static final String GROUP_SELECT_ALL = "SELECT * FROM lp_groups"; - private static final String GROUP_UPDATE = "UPDATE lp_groups SET perms=? WHERE name=?"; - private static final String GROUP_DELETE = "DELETE FROM lp_groups WHERE name=?"; - - private static final String TRACK_INSERT = "INSERT INTO lp_tracks VALUES(?, ?)"; - private static final String TRACK_SELECT = "SELECT groups FROM lp_tracks WHERE name=?"; - private static final String TRACK_SELECT_ALL = "SELECT * FROM lp_tracks"; - private static final String TRACK_UPDATE = "UPDATE lp_tracks SET groups=? WHERE name=?"; - private static final String TRACK_DELETE = "DELETE FROM lp_tracks WHERE name=?"; - - private static final String UUIDCACHE_INSERT = "INSERT INTO lp_uuid VALUES(?, ?)"; - private static final String UUIDCACHE_SELECT = "SELECT uuid FROM lp_uuid WHERE name=?"; - private static final String UUIDCACHE_SELECT_NAME = "SELECT name FROM lp_uuid WHERE uuid=?"; - private static final String UUIDCACHE_UPDATE = "UPDATE lp_uuid SET uuid=? WHERE name=?"; - - private static final String ACTION_INSERT = "INSERT INTO lp_actions(`time`, `actor_uuid`, `actor_name`, `type`, `acted_uuid`, `acted_name`, `action`) VALUES(?, ?, ?, ?, ?, ?, ?)"; - private static final String ACTION_SELECT_ALL = "SELECT * FROM lp_actions"; - - private final Gson gson; - private final SQLProvider provider; - - public SQLLegacyBacking(LuckPermsPlugin plugin, SQLProvider provider) { - super(plugin, provider.getName()); - this.provider = provider; - gson = new Gson(); - } - - private boolean runQuery(String query, QueryPS queryPS) { - return provider.runQuery(query, queryPS); - } - - private boolean runQuery(String query, QueryPS queryPS, QueryRS queryRS) { - return provider.runQuery(query, queryPS, queryRS); - } - - private boolean runQuery(String query) { - return provider.runQuery(query); - } - - private boolean runQuery(String query, QueryRS queryRS) { - return provider.runQuery(query, queryRS); - } - - private boolean setupTables(String[] tableQueries) { - boolean success = true; - for (String q : tableQueries) { - if (!runQuery(q)) success = false; - } - - return success && cleanupUsers(); - } - - @Override - public void init() { - try { - provider.init(); - - if (!setupTables(INIT_QUERIES.get(provider.getClass()))) { - plugin.getLog().severe("Error occurred whilst initialising the database."); - shutdown(); - } else { - setAcceptingLogins(true); - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void shutdown() { - try { - provider.shutdown(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public boolean logAction(LogEntry entry) { - return runQuery(ACTION_INSERT, preparedStatement -> { - preparedStatement.setLong(1, entry.getTimestamp()); - preparedStatement.setString(2, entry.getActor().toString()); - preparedStatement.setString(3, entry.getActorName()); - preparedStatement.setString(4, Character.toString(entry.getType())); - preparedStatement.setString(5, entry.getActed() == null ? "null" : entry.getActed().toString()); - preparedStatement.setString(6, entry.getActedName()); - preparedStatement.setString(7, entry.getAction()); - }); - } - - @Override - public Log getLog() { - final Log.Builder log = Log.builder(); - boolean success = runQuery(ACTION_SELECT_ALL, resultSet -> { - while (resultSet.next()) { - final String actedUuid = resultSet.getString("acted_uuid"); - LogEntry e = new LogEntry( - resultSet.getLong("time"), - UUID.fromString(resultSet.getString("actor_uuid")), - resultSet.getString("actor_name"), - resultSet.getString("type").toCharArray()[0], - actedUuid.equals("null") ? null : UUID.fromString(actedUuid), - resultSet.getString("acted_name"), - resultSet.getString("action") - ); - log.add(e); - } - return true; - }); - return success ? log.build() : null; - } - - @Override - public boolean loadUser(UUID uuid, String username) { - User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username)); - user.getIoLock().lock(); - try { - // screw "effectively final" - final String[] perms = new String[1]; - final String[] pg = new String[1]; - final String[] name = new String[1]; - final boolean[] exists = {false}; - - boolean s = runQuery(USER_SELECT, - preparedStatement -> preparedStatement.setString(1, user.getUuid().toString()), - resultSet -> { - if (resultSet.next()) { - // User exists. - exists[0] = true; - perms[0] = resultSet.getString("perms"); - pg[0] = resultSet.getString("primary_group"); - name[0] = resultSet.getString("name"); - } - return true; - } - ); - - if (!s) { - return false; - } - - if (exists[0]) { - // User exists, let's load. - Map nodes = gson.fromJson(perms[0], NM_TYPE); - - user.setNodes(nodes); - user.setPrimaryGroup(pg[0]); - - boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false); - - if (user.getName() == null || user.getName().equalsIgnoreCase("null")) { - user.setName(name[0]); - } else { - if (!name[0].equals(user.getName())) { - save = true; - } - } - - if (save) { - String json = gson.toJson(exportToLegacy(user.getNodes())); - runQuery(USER_UPDATE, preparedStatement -> { - preparedStatement.setString(1, user.getName()); - preparedStatement.setString(2, user.getPrimaryGroup()); - preparedStatement.setString(3, json); - preparedStatement.setString(4, user.getUuid().toString()); - }); - } - - } else { - if (GenericUserManager.shouldSave(user)) { - user.clearNodes(); - user.setPrimaryGroup(null); - plugin.getUserManager().giveDefaultIfNeeded(user, false); - } - } - - return true; - } finally { - user.getIoLock().unlock(); - user.getRefreshBuffer().requestDirectly(); - } - } - - @Override - public boolean saveUser(User user) { - if (!GenericUserManager.shouldSave(user)) { - user.getIoLock().lock(); - try { - return runQuery(USER_DELETE, preparedStatement -> { - preparedStatement.setString(1, user.getUuid().toString()); - }); - } finally { - user.getIoLock().unlock(); - } - // return true above ^^^^^ - } - - user.getIoLock().lock(); - try { - final boolean[] exists = {false}; - boolean success = runQuery(USER_SELECT, - preparedStatement -> preparedStatement.setString(1, user.getUuid().toString()), - resultSet -> { - if (resultSet.next()) { - exists[0] = true; - } - return true; - } - ); - - if (!success) { - return false; - } - - final String s = gson.toJson(exportToLegacy(user.getNodes())); - - if (exists[0]) { - // User exists, let's update. - return runQuery(USER_UPDATE, preparedStatement -> { - preparedStatement.setString(1, user.getName()); - preparedStatement.setString(2, user.getPrimaryGroup()); - preparedStatement.setString(3, s); - preparedStatement.setString(4, user.getUuid().toString()); - }); - } else { - // Doesn't already exist, let's insert. - return runQuery(USER_INSERT, preparedStatement -> { - preparedStatement.setString(1, user.getUuid().toString()); - preparedStatement.setString(2, user.getName()); - preparedStatement.setString(3, user.getPrimaryGroup()); - preparedStatement.setString(4, s); - }); - } - - - } finally { - user.getIoLock().unlock(); - } - } - - @Override - public boolean cleanupUsers() { - return runQuery(USER_DELETE_ALL, preparedStatement -> { - preparedStatement.setString(1, "{\"group.default\":true}"); - }); - } - - @Override - public Set getUniqueUsers() { - Set uuids = new HashSet<>(); - - boolean success = runQuery(USER_SELECT_ALL, resultSet -> { - while (resultSet.next()) { - String uuid = resultSet.getString("uuid"); - uuids.add(UUID.fromString(uuid)); - } - return true; - }); - - return success ? uuids : null; - } - - @Override - public boolean createAndLoadGroup(String name) { - Group group = plugin.getGroupManager().getOrMake(name); - group.getIoLock().lock(); - try { - final boolean[] exists = {false}; - final String[] perms = new String[1]; - - boolean s = runQuery(GROUP_SELECT, - preparedStatement -> preparedStatement.setString(1, group.getName()), - resultSet -> { - if (resultSet.next()) { - exists[0] = true; - perms[0] = resultSet.getString("perms"); - } - return true; - } - ); - - if (!s) { - return false; - } - - if (exists[0]) { - // Group exists, let's load. - Map nodes = gson.fromJson(perms[0], NM_TYPE); - group.setNodes(nodes); - return true; - } else { - String json = gson.toJson(exportToLegacy(group.getNodes())); - return runQuery(GROUP_INSERT, preparedStatement -> { - preparedStatement.setString(1, group.getName()); - preparedStatement.setString(2, json); - }); - } - - } finally { - group.getIoLock().unlock(); - } - } - - @Override - public boolean loadGroup(String name) { - Group group = plugin.getGroupManager().getOrMake(name); - group.getIoLock().lock(); - try { - final String[] perms = new String[1]; - boolean s = runQuery(GROUP_SELECT, - preparedStatement -> preparedStatement.setString(1, name), - resultSet -> { - if (resultSet.next()) { - perms[0] = resultSet.getString("perms"); - return true; - } - return false; - } - ); - - if (!s) { - return false; - } - - // Group exists, let's load. - Map nodes = gson.fromJson(perms[0], NM_TYPE); - group.setNodes(nodes); - return true; - - } finally { - group.getIoLock().unlock(); - } - } - - @Override - public boolean loadAllGroups() { - List groups = new ArrayList<>(); - boolean b = runQuery(GROUP_SELECT_ALL, resultSet -> { - while (resultSet.next()) { - String name = resultSet.getString("name"); - groups.add(name); - } - return true; - }); - - if (!b) { - return false; - } - - for (String g : groups) { - if (!loadGroup(g)) { - b = false; - } - } - - if (b) { - GroupManager gm = plugin.getGroupManager(); - gm.getAll().values().stream() - .filter(g -> !groups.contains(g.getName())) - .forEach(gm::unload); - } - return b; - } - - @Override - public boolean saveGroup(Group group) { - group.getIoLock().lock(); - try { - String json = gson.toJson(exportToLegacy(group.getNodes())); - return runQuery(GROUP_UPDATE, preparedStatement -> { - preparedStatement.setString(1, json); - preparedStatement.setString(2, group.getName()); - }); - } finally { - group.getIoLock().unlock(); - } - } - - @Override - public boolean deleteGroup(Group group) { - group.getIoLock().lock(); - boolean success; - try { - success = runQuery(GROUP_DELETE, preparedStatement -> { - preparedStatement.setString(1, group.getName()); - }); - } finally { - group.getIoLock().unlock(); - } - - if (success) plugin.getGroupManager().unload(group); - return success; - } - - @Override - public boolean createAndLoadTrack(String name) { - Track track = plugin.getTrackManager().getOrMake(name); - track.getIoLock().lock(); - try { - final boolean[] exists = {false}; - final String[] groups = new String[1]; - - boolean s = runQuery(TRACK_SELECT, - preparedStatement -> preparedStatement.setString(1, track.getName()), - resultSet -> { - if (resultSet.next()) { - exists[0] = true; - groups[0] = resultSet.getString("groups"); - } - return true; - } - ); - - if (!s) { - return false; - } - - if (exists[0]) { - // Track exists, let's load. - track.setGroups(gson.fromJson(groups[0], T_TYPE)); - return true; - } else { - String json = gson.toJson(track.getGroups()); - return runQuery(TRACK_INSERT, preparedStatement -> { - preparedStatement.setString(1, track.getName()); - preparedStatement.setString(2, json); - }); - } - - } finally { - track.getIoLock().unlock(); - } - } - - @Override - public boolean loadTrack(String name) { - Track track = plugin.getTrackManager().getOrMake(name); - track.getIoLock().lock(); - try { - final String[] groups = {null}; - boolean s = runQuery(TRACK_SELECT, - preparedStatement -> preparedStatement.setString(1, name), - resultSet -> { - if (resultSet.next()) { - groups[0] = resultSet.getString("groups"); - return true; - } - return false; - } - ); - - if (!s) { - return false; - } - - track.setGroups(gson.fromJson(groups[0], T_TYPE)); - return true; - - } finally { - track.getIoLock().unlock(); - } - } - - @Override - public boolean loadAllTracks() { - List tracks = new ArrayList<>(); - boolean b = runQuery(TRACK_SELECT_ALL, resultSet -> { - while (resultSet.next()) { - String name = resultSet.getString("name"); - tracks.add(name); - } - return true; - }); - - if (!b) { - return false; - } - - for (String t : tracks) { - if (!loadTrack(t)) { - b = false; - } - } - - if (b) { - TrackManager tm = plugin.getTrackManager(); - tm.getAll().values().stream() - .filter(t -> !tracks.contains(t.getName())) - .forEach(tm::unload); - } - return b; - } - - @Override - public boolean saveTrack(Track track) { - track.getIoLock().lock(); - try { - String s = gson.toJson(track.getGroups()); - return runQuery(TRACK_UPDATE, preparedStatement -> { - preparedStatement.setString(1, s); - preparedStatement.setString(2, track.getName()); - }); - } finally { - track.getIoLock().unlock(); - } - } - - @Override - public boolean deleteTrack(Track track) { - track.getIoLock().lock(); - boolean success; - try { - success = runQuery(TRACK_DELETE, preparedStatement -> { - preparedStatement.setString(1, track.getName()); - }); - } finally { - track.getIoLock().unlock(); - } - - if (success) plugin.getTrackManager().unload(track); - return success; - } - - @Override - public boolean saveUUIDData(String username, UUID uuid) { - final String u = username.toLowerCase(); - final boolean[] update = {false}; - boolean s = runQuery(UUIDCACHE_SELECT, - preparedStatement -> preparedStatement.setString(1, u), - resultSet -> { - if (resultSet.next()) { - update[0] = true; - } - return true; - } - ); - - if (!s) { - return false; - } - - if (update[0]) { - return runQuery(UUIDCACHE_UPDATE, preparedStatement -> { - preparedStatement.setString(1, uuid.toString()); - preparedStatement.setString(2, u); - }); - } else { - return runQuery(UUIDCACHE_INSERT, preparedStatement -> { - preparedStatement.setString(1, u); - preparedStatement.setString(2, uuid.toString()); - }); - } - } - - @Override - public UUID getUUID(String username) { - final String u = username.toLowerCase(); - final UUID[] uuid = {null}; - - boolean success = runQuery(UUIDCACHE_SELECT, - preparedStatement -> preparedStatement.setString(1, u), - resultSet -> { - if (resultSet.next()) { - uuid[0] = UUID.fromString(resultSet.getString("uuid")); - return true; - } - return false; - } - ); - - return success ? uuid[0] : null; - } - - @Override - public String getName(UUID uuid) { - final String u = uuid.toString(); - final String[] name = {null}; - - boolean success = runQuery(UUIDCACHE_SELECT_NAME, - preparedStatement -> preparedStatement.setString(1, u), - resultSet -> { - if (resultSet.next()) { - name[0] = resultSet.getString("name"); - return true; - } - return false; - } - ); - - return success ? name[0] : null; - } -} diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/YAMLBacking.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/YAMLBacking.java index 78b72179b..ed59526e2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/YAMLBacking.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/YAMLBacking.java @@ -119,7 +119,7 @@ public class YAMLBacking extends FlatfileBacking { if (user.getName() == null || user.getName().equalsIgnoreCase("null")) { user.setName(name); } else { - if (!name.equals(user.getName())) { + if (!name.equalsIgnoreCase(user.getName())) { save = true; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/utils/LegacySchemaMigration.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/utils/LegacySchemaMigration.java new file mode 100644 index 000000000..76a283d06 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/utils/LegacySchemaMigration.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.storage.backing.utils; + +import lombok.RequiredArgsConstructor; + +import com.google.common.collect.Lists; +import com.google.gson.reflect.TypeToken; + +import me.lucko.luckperms.common.core.NodeFactory; +import me.lucko.luckperms.common.storage.backing.SQLBacking; + +import java.lang.reflect.Type; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +public class LegacySchemaMigration implements Runnable { + private static final Type NODE_MAP_TYPE = new TypeToken>() {}.getType(); + private final SQLBacking backing; + + @Override + public void run() { + backing.getPlugin().getLog().info("Collecting UUID data from the old tables."); + + Map uuidData = new HashMap<>(); + try (Connection c = backing.getProvider().getConnection()) { + try (PreparedStatement ps = c.prepareStatement("SELECT uuid, name FROM lp_uuid")) { + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + try { + uuidData.put(UUID.fromString(rs.getString("uuid")), rs.getString("name")); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + } + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + + backing.getPlugin().getLog().info("Found " + uuidData.size() + " uuid data entries. Copying to new tables..."); + + List> uuidEntries = uuidData.entrySet().stream().collect(Collectors.toList()); + List>> partitionedUuidEntries = Lists.partition(uuidEntries, 100); + + for (List> l : partitionedUuidEntries) { + try (Connection c = backing.getProvider().getConnection()) { + try (PreparedStatement ps = c.prepareStatement(backing.getPrefix().apply("INSERT INTO {prefix}players VALUES(?, ?, ?)"))) { + for (Map.Entry e : l) { + ps.setString(1, e.getKey().toString()); + ps.setString(2, e.getValue().toLowerCase()); + ps.setString(3, "default"); + ps.addBatch(); + } + ps.executeBatch(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + uuidData.clear(); + uuidEntries.clear(); + partitionedUuidEntries.clear(); + + backing.getPlugin().getLog().info("Migrated all uuid data."); + backing.getPlugin().getLog().info("Starting user data migration."); + + Set users = new HashSet<>(); + try (Connection c = backing.getProvider().getConnection()) { + try (PreparedStatement ps = c.prepareStatement("SELECT uuid FROM lp_users")) { + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + try { + users.add(UUID.fromString(rs.getString("uuid"))); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + } + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + + backing.getPlugin().getLog().info("Found " + users.size() + " user data entries. Copying to new tables..."); + + AtomicInteger userCounter = new AtomicInteger(0); + for (UUID uuid : users) { + String permsJson = null; + String primaryGroup = null; + + try (Connection c = backing.getProvider().getConnection()) { + try (PreparedStatement ps = c.prepareStatement("SELECT primary_group, perms FROM lp_users WHERE uuid=?")) { + ps.setString(1, uuid.toString()); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + permsJson = rs.getString("perms"); + primaryGroup = rs.getString("primary_group"); + } + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + + if (permsJson == null || primaryGroup == null) { + new Throwable().printStackTrace(); + continue; + } + + Map convertedPerms = backing.getGson().fromJson(permsJson, NODE_MAP_TYPE); + if (convertedPerms == null) { + new Throwable().printStackTrace(); + continue; + } + + Set nodes = convertedPerms.entrySet().stream() + .map(e -> NodeFactory.fromSerialisedNode(e.getKey(), e.getValue())) + .map(NodeDataHolder::fromNode) + .collect(Collectors.toSet()); + + try (Connection c = backing.getProvider().getConnection()) { + try (PreparedStatement ps = c.prepareStatement(backing.getPrefix().apply("INSERT INTO {prefix}user_permissions(uuid, permission, value, server, world, expiry, contexts) VALUES(?, ?, ?, ?, ?, ?, ?)"))) { + for (NodeDataHolder nd : nodes) { + ps.setString(1, uuid.toString()); + ps.setString(2, nd.getPermission()); + ps.setBoolean(3, nd.isValue()); + ps.setString(4, nd.getServer()); + ps.setString(5, nd.getWorld()); + ps.setLong(6, nd.getExpiry()); + ps.setString(7, nd.getContexts()); + ps.addBatch(); + } + ps.executeBatch(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + if (!primaryGroup.equalsIgnoreCase("default")) { + try (Connection c = backing.getProvider().getConnection()) { + try (PreparedStatement ps = c.prepareStatement(backing.getPrefix().apply("UPDATE {prefix}players SET primary_group=? WHERE uuid=?"))) { + ps.setString(1, primaryGroup); + ps.setString(2, uuid.toString()); + ps.execute(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + int i = userCounter.incrementAndGet(); + if (i % 100 == 0) { + backing.getPlugin().getLog().info("Migrated " + i + " users so far..."); + } + } + + users.clear(); + + backing.getPlugin().getLog().info("Migrated all user data."); + backing.getPlugin().getLog().info("Starting group data migration."); + + Map groupData = new HashMap<>(); + try (Connection c = backing.getProvider().getConnection()) { + try (PreparedStatement ps = c.prepareStatement("SELECT name, perms FROM lp_groups")) { + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + groupData.put(rs.getString("name"), rs.getString("perms")); + } + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + + backing.getPlugin().getLog().info("Found " + groupData.size() + " group data entries. Copying to new tables..."); + for (Map.Entry e : groupData.entrySet()) { + String name = e.getKey(); + String permsJson = e.getValue(); + + try (Connection c = backing.getProvider().getConnection()) { + try (PreparedStatement ps = c.prepareStatement(backing.getPrefix().apply("INSERT INTO {prefix}groups VALUES(?)"))) { + ps.setString(1, name); + ps.execute(); + } + } catch (SQLException ex) { + ex.printStackTrace(); + } + + Map convertedPerms = backing.getGson().fromJson(permsJson, NODE_MAP_TYPE); + if (convertedPerms == null) { + new Throwable().printStackTrace(); + continue; + } + + Set nodes = convertedPerms.entrySet().stream() + .map(ent -> NodeFactory.fromSerialisedNode(ent.getKey(), ent.getValue())) + .map(NodeDataHolder::fromNode) + .collect(Collectors.toSet()); + + try (Connection c = backing.getProvider().getConnection()) { + try (PreparedStatement ps = c.prepareStatement(backing.getPrefix().apply("INSERT INTO {prefix}group_permissions(name, permission, value, server, world, expiry, contexts) VALUES(?, ?, ?, ?, ?, ?, ?)"))) { + for (NodeDataHolder nd : nodes) { + ps.setString(1, name); + ps.setString(2, nd.getPermission()); + ps.setBoolean(3, nd.isValue()); + ps.setString(4, nd.getServer()); + ps.setString(5, nd.getWorld()); + ps.setLong(6, nd.getExpiry()); + ps.setString(7, nd.getContexts()); + ps.addBatch(); + } + ps.executeBatch(); + } + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + + groupData.clear(); + backing.getPlugin().getLog().info("Migrated all group data."); + + backing.getPlugin().getLog().info("Renaming action and track tables."); + try (Connection c = backing.getProvider().getConnection()) { + try (PreparedStatement ps = c.prepareStatement(backing.getPrefix().apply("DROP TABLE {prefix}actions"))) { + ps.execute(); + } + try (PreparedStatement ps = c.prepareStatement(backing.getPrefix().apply("ALTER TABLE lp_actions RENAME TO {prefix}actions"))) { + ps.execute(); + } + + try (PreparedStatement ps = c.prepareStatement(backing.getPrefix().apply("DROP TABLE {prefix}tracks"))) { + ps.execute(); + } + try (PreparedStatement ps = c.prepareStatement(backing.getPrefix().apply("ALTER TABLE lp_tracks RENAME TO {prefix}tracks"))) { + ps.execute(); + } + } catch (SQLException ex) { + ex.printStackTrace(); + } + + backing.getPlugin().getLog().info("Legacy schema migration complete."); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/utils/NodeDataHolder.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/utils/NodeDataHolder.java index 9cf9b7124..9a53edd65 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/utils/NodeDataHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/utils/NodeDataHolder.java @@ -23,6 +23,7 @@ package me.lucko.luckperms.common.storage.backing.utils; import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; @@ -38,6 +39,7 @@ import java.util.Map; @Getter @ToString +@EqualsAndHashCode @AllArgsConstructor(staticName = "of") public class NodeDataHolder { private static final Gson GSON = new Gson(); diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index 9526c7a7a..870a16910 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -116,6 +116,10 @@ data { password="" pool-size=10 # The size of the MySQL connection pool. + # The prefix for all LuckPerms tables. Change this is you want to use different tables for different servers. + # This should *not* be set to "lp_" if you have previously ran LuckPerms v2.16.81 or earlier with this database. + table_prefix="luckperms_" + # Set to -1 to disable. If this is the only instance accessing the datastore, you can disable syncing. # e.g. if you're using sqlite or flatfile, this can be set to -1 to save resources. sync-minutes=3