From 6874cfd5b1d51c5a1d05eb5f5589072a82211d56 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 24 Dec 2016 20:30:35 +0000 Subject: [PATCH] WIP: implement better sql schema - towards #77 --- common/pom.xml | 7 + .../common/dependencies/Dependency.java | 1 + .../dependencies/DependencyManager.java | 1 + .../common/storage/StorageFactory.java | 8 +- .../luckperms/common/storage/StorageType.java | 1 + .../common/storage/backing/SQLBacking.java | 550 +++++--------- .../storage/backing/SQLLegacyBacking.java | 685 ++++++++++++++++++ .../backing/sqlprovider/MySQLProvider.java | 1 + .../sqlprovider/PostgreSQLProvider.java | 144 ++++ .../backing/sqlprovider/SQLProvider.java | 1 - .../storage/backing/utils/NodeDataHolder.java | 52 ++ .../src/main/resources/sql/lp-schema-h2.sql | 55 ++ .../main/resources/sql/lp-schema-mysql.sql | 61 ++ .../resources/sql/lp-schema-postgresql.sql | 50 ++ .../main/resources/sql/lp-schema-sqlite.sql | 54 ++ 15 files changed, 1307 insertions(+), 364 deletions(-) create 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/sqlprovider/PostgreSQLProvider.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/storage/backing/utils/NodeDataHolder.java create mode 100644 common/src/main/resources/sql/lp-schema-h2.sql create mode 100644 common/src/main/resources/sql/lp-schema-mysql.sql create mode 100644 common/src/main/resources/sql/lp-schema-postgresql.sql create mode 100644 common/src/main/resources/sql/lp-schema-sqlite.sql diff --git a/common/pom.xml b/common/pom.xml index 695c5eab..41863dbd 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -74,6 +74,13 @@ 1.4.192 provided + + + org.postgresql + postgresql + 9.4.1212 + provided + org.mongodb diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java index f902fac5..f406d673 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/Dependency.java @@ -30,6 +30,7 @@ import lombok.Getter; public enum Dependency { MYSQL_DRIVER("https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.6/mysql-connector-java-5.1.6.jar", "5.1.6", "com.mysql.jdbc.jdbc2.optional.MysqlDataSource"), + POSTGRESQL_DRIVER("https://repo1.maven.org/maven2/org/postgresql/postgresql/9.4.1212/postgresql-9.4.1212.jar", "9.4.1212", "org.postgresql.ds.PGSimpleDataSource"), H2_DRIVER("https://repo1.maven.org/maven2/com/h2database/h2/1.4.193/h2-1.4.193.jar", "1.4.193", "org.h2.Driver"), SQLITE_DRIVER("https://repo1.maven.org/maven2/org/xerial/sqlite-jdbc/3.15.1/sqlite-jdbc-3.15.1.jar", "3.15.1", "org.sqlite.JDBC"), HIKARI("https://repo1.maven.org/maven2/com/zaxxer/HikariCP/2.5.1/HikariCP-2.5.1.jar", "2.5.1", "com.zaxxer.hikari.HikariDataSource"), diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyManager.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyManager.java index 067ef19e..fa5a3fe9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyManager.java @@ -60,6 +60,7 @@ public class DependencyManager { .put(StorageType.YAML, ImmutableList.of()) .put(StorageType.MONGODB, ImmutableList.of(Dependency.MONGODB_DRIVER)) .put(StorageType.MYSQL, ImmutableList.of(Dependency.MYSQL_DRIVER, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI)) + .put(StorageType.POSTGRESQL, ImmutableList.of(Dependency.POSTGRESQL_DRIVER, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI)) .put(StorageType.SQLITE, ImmutableList.of(Dependency.SQLITE_DRIVER)) .put(StorageType.H2, ImmutableList.of(Dependency.H2_DRIVER)) .build(); 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 d0d7438a..32979dcb 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,7 +30,7 @@ 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.SQLBacking; +import me.lucko.luckperms.common.storage.backing.SQLLegacyBacking; 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; @@ -120,11 +120,11 @@ public class StorageFactory { private static AbstractBacking makeBacking(StorageType method, LuckPermsPlugin plugin) { switch (method) { case MYSQL: - return new SQLBacking(plugin, new MySQLProvider(plugin.getConfiguration().getDatabaseValues())); + return new SQLLegacyBacking(plugin, new MySQLProvider(plugin.getConfiguration().getDatabaseValues())); case SQLITE: - return new SQLBacking(plugin, new SQLiteProvider(new File(plugin.getDataFolder(), "luckperms.sqlite"))); + return new SQLLegacyBacking(plugin, new SQLiteProvider(new File(plugin.getDataFolder(), "luckperms.sqlite"))); case H2: - return new SQLBacking(plugin, new H2Provider(new File(plugin.getDataFolder(), "luckperms.db"))); + return new SQLLegacyBacking(plugin, new H2Provider(new File(plugin.getDataFolder(), "luckperms.db"))); case MONGODB: return new MongoDBBacking(plugin, plugin.getConfiguration().getDatabaseValues()); case YAML: diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/StorageType.java b/common/src/main/java/me/lucko/luckperms/common/storage/StorageType.java index 7249d068..4ca8b083 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/StorageType.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/StorageType.java @@ -34,6 +34,7 @@ public enum StorageType { YAML("yaml", "yml"), MONGODB("mongodb"), MYSQL("mysql"), + POSTGRESQL("postgresql"), SQLITE("sqlite"), H2("h2"); 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 8eaf7007..066caa3a 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,106 +22,76 @@ 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.api.Node; 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 me.lucko.luckperms.common.storage.backing.utils.NodeDataHolder; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; 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; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.stream.Collectors; public class SQLBacking 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 Type LIST_STRING_TYPE = new TypeToken>() {}.getType(); - 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 USER_PERMISSIONS_SELECT = "SELECT permission, value, server, world, expiry, contexts FROM {prefix}user_permissions WHERE uuid=?"; + 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 VALUES(?, ?, ?, ?, ?, ?, ?)"; + private static final String USER_PERMISSIONS_SELECT_DISTINCT = "SELECT DISTINCT uuid FROM {prefix}user_permissions"; - 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 PLAYER_SELECT = "SELECT username, primary_group FROM {prefix}players WHERE uuid=?"; - 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 {prefix}tracks VALUES(?, ?)"; + private static final String TRACK_SELECT = "SELECT groups FROM {prefix}tracks WHERE name=?"; + private static final String TRACK_SELECT_ALL = "SELECT * FROM {prefix}tracks"; + private static final String TRACK_UPDATE = "UPDATE {prefix}tracks SET groups=? WHERE name=?"; + private static final String TRACK_DELETE = "DELETE FROM {prefix}tracks 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 ACTION_INSERT = "INSERT INTO {prefix}actions(`time`, `actor_uuid`, `actor_name`, `type`, `acted_uuid`, `acted_name`, `action`) VALUES(?, ?, ?, ?, ?, ?, ?)"; + private static final String ACTION_SELECT_ALL = "SELECT * FROM {prefix}actions"; - 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; + private final Function prefix; - public SQLBacking(LuckPermsPlugin plugin, SQLProvider provider) { + public SQLBacking(LuckPermsPlugin plugin, SQLProvider provider, String prefix) { super(plugin, provider.getName()); this.provider = provider; + this.prefix = s -> s.replace("{prefix}", prefix); gson = new Gson(); } - private boolean runQuery(String query, QueryPS queryPS) { + private boolean runQuery(String query, SQLProvider.QueryPS queryPS) { return provider.runQuery(query, queryPS); } - private boolean runQuery(String query, QueryPS queryPS, QueryRS queryRS) { + private boolean runQuery(String query, SQLProvider.QueryPS queryPS, SQLProvider.QueryRS queryRS) { return provider.runQuery(query, queryPS, queryRS); } @@ -129,17 +99,12 @@ public class SQLBacking extends AbstractBacking { return provider.runQuery(query); } - private boolean runQuery(String query, QueryRS queryRS) { + private boolean runQuery(String query, SQLProvider.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(); + public boolean tableExists(String table) throws SQLException { + return provider.getConnection().getMetaData().getTables(null, null, table.toUpperCase(), null).next(); } @Override @@ -147,15 +112,45 @@ public class SQLBacking extends AbstractBacking { try { provider.init(); - if (!setupTables(INIT_QUERIES.get(provider.getClass()))) { - plugin.getLog().severe("Error occurred whilst initialising the database."); - shutdown(); - } else { - setAcceptingLogins(true); + // Init tables + if (!tableExists(prefix + "user_permissions")) { + String schemaFileName = "lp-schema-" + provider.getName().toLowerCase() + ".sql"; + try (InputStream is = plugin.getClass().getResourceAsStream("sql/" + schemaFileName)) { + if (is == null) { + throw new Exception("Couldn't locate schema file for " + provider.getName()); + } + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + try (Statement s = provider.getConnection().createStatement()) { + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith("--") || line.startsWith("#")) continue; + + sb.append(line); + + // check for end of declaration + if (line.endsWith(";")) { + sb.deleteCharAt(sb.length() - 1); + + String result = prefix.apply(sb.toString().trim()); + if (!result.isEmpty()) s.addBatch(result); + + // reset + sb = new StringBuilder(); + } + } + s.executeBatch(); + } + } + } } - + + setAcceptingLogins(true); } catch (Exception e) { e.printStackTrace(); + plugin.getLog().severe("Error occurred whilst initialising the database."); + shutdown(); } } @@ -170,7 +165,7 @@ public class SQLBacking extends AbstractBacking { @Override public boolean logAction(LogEntry entry) { - return runQuery(ACTION_INSERT, preparedStatement -> { + return runQuery(prefix.apply(ACTION_INSERT), preparedStatement -> { preparedStatement.setLong(1, entry.getTimestamp()); preparedStatement.setString(2, entry.getActor().toString()); preparedStatement.setString(3, entry.getActorName()); @@ -184,7 +179,7 @@ public class SQLBacking extends AbstractBacking { @Override public Log getLog() { final Log.Builder log = Log.builder(); - boolean success = runQuery(ACTION_SELECT_ALL, resultSet -> { + boolean success = runQuery(prefix.apply(ACTION_SELECT_ALL), resultSet -> { while (resultSet.next()) { final String actedUuid = resultSet.getString("acted_uuid"); LogEntry e = new LogEntry( @@ -208,56 +203,61 @@ public class SQLBacking extends AbstractBacking { 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"); + List data = new ArrayList<>(); + AtomicReference primaryGroup = new AtomicReference<>(null); + AtomicReference userName = new AtomicReference<>(); + boolean s = runQuery( + prefix.apply(USER_PERMISSIONS_SELECT), + ps -> ps.setString(1, user.getUuid().toString()), + rs -> { + while (rs.next()) { + String permission = rs.getString("permission"); + boolean value = rs.getBoolean("value"); + String server = rs.getString("server"); + String world = rs.getString("world"); + long expiry = rs.getLong("expiry"); + String contexts = rs.getString("contexts"); + data.add(NodeDataHolder.of(permission, value, server, world, expiry, contexts)); } return true; } ); - if (!s) { + boolean s2 = runQuery( + prefix.apply(PLAYER_SELECT), + ps -> ps.setString(1, user.getUuid().toString()), + rs -> { + if (rs.next()) { + userName.set(rs.getString("username")); + primaryGroup.set(rs.getString("primary_group")); + } + return true; + } + ); + + if (!s || !s2) { return false; } - if (exists[0]) { - // User exists, let's load. - Map nodes = gson.fromJson(perms[0], NM_TYPE); - + if (!data.isEmpty()) { + Set nodes = data.stream().map(NodeDataHolder::toNode).collect(Collectors.toSet()); user.setNodes(nodes); - user.setPrimaryGroup(pg[0]); - boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false); + String pg = primaryGroup.get(); + if (pg == null) { + pg = "default"; + } + + String name = userName.get(); + if (name == null) { + name = "null"; + } if (user.getName() == null || user.getName().equalsIgnoreCase("null")) { - user.setName(name[0]); - } else { - if (!name[0].equals(user.getName())) { - save = true; - } + user.setName(name); } - 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()); - }); - } + user.setPrimaryGroup(pg); } else { if (GenericUserManager.shouldSave(user)) { @@ -276,55 +276,39 @@ public class SQLBacking extends AbstractBacking { @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(); - } - } + boolean shouldSave = GenericUserManager.shouldSave(user); 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) { + boolean s = runQuery(prefix.apply(USER_PERMISSIONS_DELETE), preparedStatement -> { + preparedStatement.setString(1, user.getUuid().toString()); + }); + if (!s) { 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); - }); + if (!shouldSave) { + return true; } + List data = user.getNodes().stream().map(NodeDataHolder::fromNode).collect(Collectors.toList()); + try (PreparedStatement ps = provider.getConnection().prepareStatement(prefix.apply(USER_PERMISSIONS_INSERT))) { + for (NodeDataHolder nd : data) { + ps.setString(1, user.getUuid().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(); + } + return true; } finally { user.getIoLock().unlock(); } @@ -332,18 +316,16 @@ public class SQLBacking extends AbstractBacking { @Override public boolean cleanupUsers() { - return runQuery(USER_DELETE_ALL, preparedStatement -> { - preparedStatement.setString(1, "{\"group.default\":true}"); - }); + return true; // TODO } @Override public Set getUniqueUsers() { Set uuids = new HashSet<>(); - boolean success = runQuery(USER_SELECT_ALL, resultSet -> { - while (resultSet.next()) { - String uuid = resultSet.getString("uuid"); + boolean success = runQuery(prefix.apply(USER_PERMISSIONS_SELECT_DISTINCT), rs -> { + while (rs.next()) { + String uuid = rs.getString("uuid"); uuids.add(UUID.fromString(uuid)); } return true; @@ -354,134 +336,27 @@ public class SQLBacking extends AbstractBacking { @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(); - } + return false; } @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(); - } + return false; } @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; + return false; } @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(); - } + return false; } @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; + return false; } @Override @@ -489,15 +364,16 @@ public class SQLBacking extends AbstractBacking { Track track = plugin.getTrackManager().getOrMake(name); track.getIoLock().lock(); try { - final boolean[] exists = {false}; - final String[] groups = new String[1]; + AtomicBoolean exists = new AtomicBoolean(false); + AtomicReference groups = new AtomicReference<>(null); - boolean s = runQuery(TRACK_SELECT, - preparedStatement -> preparedStatement.setString(1, track.getName()), - resultSet -> { - if (resultSet.next()) { - exists[0] = true; - groups[0] = resultSet.getString("groups"); + boolean s = runQuery( + prefix.apply(TRACK_SELECT), + ps -> ps.setString(1, track.getName()), + rs -> { + if (rs.next()) { + exists.set(true); + groups.set(rs.getString("groups")); } return true; } @@ -507,9 +383,9 @@ public class SQLBacking extends AbstractBacking { return false; } - if (exists[0]) { + if (exists.get()) { // Track exists, let's load. - track.setGroups(gson.fromJson(groups[0], T_TYPE)); + track.setGroups(gson.fromJson(groups.get(), LIST_STRING_TYPE)); return true; } else { String json = gson.toJson(track.getGroups()); @@ -529,12 +405,13 @@ public class SQLBacking extends AbstractBacking { 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"); + AtomicReference groups = new AtomicReference<>(null); + boolean s = runQuery( + TRACK_SELECT, + ps -> ps.setString(1, name), + rs -> { + if (rs.next()) { + groups.set(rs.getString("groups")); return true; } return false; @@ -545,7 +422,7 @@ public class SQLBacking extends AbstractBacking { return false; } - track.setGroups(gson.fromJson(groups[0], T_TYPE)); + track.setGroups(gson.fromJson(groups.get(), LIST_STRING_TYPE)); return true; } finally { @@ -556,13 +433,16 @@ public class SQLBacking extends AbstractBacking { @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; - }); + boolean b = runQuery( + prefix.apply(TRACK_SELECT_ALL), + rs -> { + while (rs.next()) { + String name = rs.getString("name"); + tracks.add(name); + } + return true; + } + ); if (!b) { return false; @@ -588,10 +468,13 @@ public class SQLBacking extends AbstractBacking { track.getIoLock().lock(); try { String s = gson.toJson(track.getGroups()); - return runQuery(TRACK_UPDATE, preparedStatement -> { - preparedStatement.setString(1, s); - preparedStatement.setString(2, track.getName()); - }); + return runQuery( + prefix.apply(TRACK_UPDATE), + ps -> { + ps.setString(1, s); + ps.setString(2, track.getName()); + } + ); } finally { track.getIoLock().unlock(); } @@ -602,9 +485,12 @@ public class SQLBacking extends AbstractBacking { track.getIoLock().lock(); boolean success; try { - success = runQuery(TRACK_DELETE, preparedStatement -> { - preparedStatement.setString(1, track.getName()); - }); + success = runQuery( + prefix.apply(TRACK_DELETE), + ps -> { + ps.setString(1, track.getName()); + } + ); } finally { track.getIoLock().unlock(); } @@ -615,70 +501,16 @@ public class SQLBacking extends AbstractBacking { @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()); - }); - } + return false; } @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; + return 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; + return null; } } 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 new file mode 100644 index 00000000..98c5cd8b --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/SQLLegacyBacking.java @@ -0,0 +1,685 @@ +/* + * 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/sqlprovider/MySQLProvider.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/sqlprovider/MySQLProvider.java index a1ca22d8..b37f98ce 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/sqlprovider/MySQLProvider.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/sqlprovider/MySQLProvider.java @@ -77,6 +77,7 @@ public class MySQLProvider extends SQLProvider { config.setConnectionTimeout(TimeUnit.SECONDS.toMillis(10)); // 10000 config.setLeakDetectionThreshold(TimeUnit.SECONDS.toMillis(5)); // 5000 config.setValidationTimeout(TimeUnit.SECONDS.toMillis(3)); // 3000 + config.setInitializationFailFast(true); config.setConnectionTestQuery("/* LuckPerms ping */ SELECT 1"); hikari = new HikariDataSource(config); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/sqlprovider/PostgreSQLProvider.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/sqlprovider/PostgreSQLProvider.java new file mode 100644 index 00000000..6fbcf9cc --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/sqlprovider/PostgreSQLProvider.java @@ -0,0 +1,144 @@ +/* + * 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.sqlprovider; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +import me.lucko.luckperms.common.storage.DatastoreConfiguration; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.TimeUnit; + +public class PostgreSQLProvider extends SQLProvider { + + private final DatastoreConfiguration configuration; + private HikariDataSource hikari; + + public PostgreSQLProvider(DatastoreConfiguration configuration) { + super("PostgreSQL"); + this.configuration = configuration; + } + + @Override + public void init() throws Exception { + HikariConfig config = new HikariConfig(); + + String address = configuration.getAddress(); + String[] addressSplit = address.split(":"); + address = addressSplit[0]; + String port = addressSplit.length > 1 ? addressSplit[1] : "3306"; + + String database = configuration.getDatabase(); + String username = configuration.getUsername(); + String password = configuration.getPassword(); + + config.setMaximumPoolSize(configuration.getPoolSize()); + + config.setPoolName("luckperms"); + config.setDataSourceClassName("org.postgresql.ds.PGSimpleDataSource"); + config.addDataSourceProperty("serverName", address); + config.addDataSourceProperty("portNumber", port); + config.addDataSourceProperty("databaseName", database); + config.addDataSourceProperty("user", username); + config.addDataSourceProperty("password", password); + config.setConnectionTimeout(TimeUnit.SECONDS.toMillis(10)); // 10000 + config.setLeakDetectionThreshold(TimeUnit.SECONDS.toMillis(5)); // 5000 + config.setValidationTimeout(TimeUnit.SECONDS.toMillis(3)); // 3000 + config.setInitializationFailFast(true); + config.setConnectionTestQuery("/* LuckPerms ping */ SELECT 1"); + + hikari = new HikariDataSource(config); + } + + @Override + public void shutdown() throws Exception { + if (hikari != null) { + hikari.close(); + } + } + + @Override + public Connection getConnection() throws SQLException { + return hikari.getConnection(); + } + + @Override + public boolean runQuery(String query, QueryPS queryPS) { + boolean success = false; + + Connection connection = null; + PreparedStatement preparedStatement = null; + + try { + connection = getConnection(); + if (connection == null || connection.isClosed()) { + throw new IllegalStateException("SQL connection is null"); + } + + preparedStatement = connection.prepareStatement(query); + queryPS.onRun(preparedStatement); + + preparedStatement.execute(); + success = true; + } catch (SQLException e) { + e.printStackTrace(); + } finally { + close(preparedStatement); + close(connection); + } + return success; + } + + @Override + public boolean runQuery(String query, QueryPS queryPS, QueryRS queryRS) { + boolean success = false; + + Connection connection = null; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + try { + connection = getConnection(); + if (connection == null || connection.isClosed()) { + throw new IllegalStateException("SQL connection is null"); + } + + preparedStatement = connection.prepareStatement(query); + queryPS.onRun(preparedStatement); + + resultSet = preparedStatement.executeQuery(); + success = queryRS.onResult(resultSet); + } catch (SQLException e) { + e.printStackTrace(); + } finally { + close(resultSet); + close(preparedStatement); + close(connection); + } + return success; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/sqlprovider/SQLProvider.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/sqlprovider/SQLProvider.java index f1659c61..a214ba43 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/sqlprovider/SQLProvider.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/sqlprovider/SQLProvider.java @@ -64,7 +64,6 @@ public abstract class SQLProvider { return runQuery(query, EMPTY_PS, queryRS); } - @FunctionalInterface public interface QueryPS { void onRun(PreparedStatement preparedStatement) throws SQLException; 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 new file mode 100644 index 00000000..5c5fa3aa --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/utils/NodeDataHolder.java @@ -0,0 +1,52 @@ +/* + * 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.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +import me.lucko.luckperms.api.Node; + +@Getter +@ToString +@AllArgsConstructor(staticName = "of") +public class NodeDataHolder { + public static NodeDataHolder fromNode(Node node) { + // TODO + return null; + } + + private final String permission; + private final boolean value; + private final String server; + private final String world; + private final long expiry; + private final String contexts; + + public Node toNode() { + // TODO + return null; + } + +} diff --git a/common/src/main/resources/sql/lp-schema-h2.sql b/common/src/main/resources/sql/lp-schema-h2.sql new file mode 100644 index 00000000..a37c0495 --- /dev/null +++ b/common/src/main/resources/sql/lp-schema-h2.sql @@ -0,0 +1,55 @@ +-- LuckPerms H2 Schema. + +CREATE TABLE '{prefix}user_permissions' ( + 'id' INT AUTO_INCREMENT NOT NULL, + 'uuid' VARCHAR(36) NOT NULL, + 'permission' VARCHAR(200) NOT NULL, + 'value' BOOL NOT NULL, + 'server' VARCHAR(36) NULL, + 'world' VARCHAR(36) NULL, + 'expiry' INT(11) NOT NULL, + 'contexts' VARCHAR(200) NULL, + PRIMARY KEY ('id') +); +CREATE INDEX ON '{prefix}user_permissions' ('uuid'); +CREATE INDEX ON '{prefix}user_permissions' ('permission'); + +CREATE TABLE '{prefix}group_permissions' ( + 'id' INT AUTO_INCREMENT NOT NULL, + 'name' VARCHAR(36) NOT NULL, + 'permission' VARCHAR(200) NOT NULL, + 'value' BOOL NOT NULL, + 'server' VARCHAR(36) NOT NULL, + 'world' VARCHAR(36) NOT NULL, + 'expiry' INT(11) NOT NULL, + 'contexts' VARCHAR(200) NOT NULL, + PRIMARY KEY ('id') +); +CREATE INDEX ON '{prefix}group_permissions' ('name'); +CREATE INDEX ON '{prefix}group_permissions' ('permission'); + +CREATE TABLE `{prefix}players` ( + `uuid` VARCHAR(36) NOT NULL, + `username` VARCHAR(16) NOT NULL, + `primary_group` VARCHAR(36) NOT NULL, + PRIMARY KEY (`uuid`) +); +CREATE INDEX ON '{prefix}players' ('username'); + +CREATE TABLE `{prefix}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`) +); + +CREATE TABLE `{prefix}tracks` ( + `name` VARCHAR(36) NOT NULL, + `groups` TEXT NOT NULL, + PRIMARY KEY (`name`) +); \ No newline at end of file diff --git a/common/src/main/resources/sql/lp-schema-mysql.sql b/common/src/main/resources/sql/lp-schema-mysql.sql new file mode 100644 index 00000000..a8c3d8ee --- /dev/null +++ b/common/src/main/resources/sql/lp-schema-mysql.sql @@ -0,0 +1,61 @@ +-- LuckPerms MySQL Schema + +CREATE TABLE '{prefix}user_permissions' ( + 'id' INT AUTO_INCREMENT NOT NULL, + 'uuid' VARCHAR(36) NOT NULL, + 'permission' VARCHAR(200) NOT NULL, + 'value' BOOL NOT NULL, + 'server' VARCHAR(36) NOT NULL, + 'world' VARCHAR(36) NOT NULL, + 'expiry' INT(11) NOT NULL, + 'contexts' VARCHAR(200) NOT NULL, + PRIMARY KEY ('id') +) DEFAULT CHARSET = utf8; +CREATE INDEX '{prefix}user_permissions_uuid' ON '{prefix}user_permissions' ('uuid'); +CREATE INDEX '{prefix}user_permissions_permission' ON '{prefix}user_permissions' ('permission'); + +CREATE TABLE '{prefix}group_permissions' ( + 'id' INT AUTO_INCREMENT NOT NULL, + 'name' VARCHAR(36) NOT NULL, + 'permission' VARCHAR(200) NOT NULL, + 'value' BOOL NOT NULL, + 'server' VARCHAR(36) NULL, + 'world' VARCHAR(36) NULL, + 'expiry' INT(11) NOT NULL, + 'contexts' VARCHAR(200) NULL, + PRIMARY KEY ('id') +) DEFAULT CHARSET = utf8; +CREATE INDEX '{prefix}group_permissions_name' ON '{prefix}group_permissions' ('name'); +CREATE INDEX '{prefix}group_permissions_permission' ON '{prefix}group_permissions' ('permission'); + +CREATE TABLE `{prefix}players` ( + `uuid` VARCHAR(36) NOT NULL, + `username` VARCHAR(16) NOT NULL, + `primary_group` VARCHAR(36) NOT NULL, + PRIMARY KEY (`uuid`) +) DEFAULT CHARSET = utf8; +CREATE INDEX '{prefix}players_username' ON '{prefix}players' ('username'); + +CREATE TABLE `{prefix}groups` ( + `id` INT AUTO_INCREMENT NOT NULL, + `name` VARCHAR(36) NOT NULL, + PRIMARY KEY (`id`) +); + +CREATE TABLE `{prefix}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; + +CREATE TABLE `{prefix}tracks` ( + `name` VARCHAR(36) NOT NULL, + `groups` TEXT NOT NULL, + PRIMARY KEY (`name`) +) DEFAULT CHARSET = utf8; \ No newline at end of file diff --git a/common/src/main/resources/sql/lp-schema-postgresql.sql b/common/src/main/resources/sql/lp-schema-postgresql.sql new file mode 100644 index 00000000..51ad3c00 --- /dev/null +++ b/common/src/main/resources/sql/lp-schema-postgresql.sql @@ -0,0 +1,50 @@ +-- LuckPerms PostgreSQL Schema + +CREATE TABLE "{prefix}user_permissions" ( + "id" SERIAL PRIMARY KEY NOT NULL, + "uuid" VARCHAR(36) NOT NULL, + "permission" VARCHAR(200) NOT NULL, + "value" BOOL NOT NULL, + "server" VARCHAR(36) NOT NULL, + "world" VARCHAR(36) NOT NULL, + "expiry" INT NOT NULL, + "contexts" VARCHAR(200) NOT NULL +); +CREATE INDEX "{prefix}user_permissions_uuid" ON "{prefix}user_permissions" ("uuid"); +CREATE INDEX "{prefix}user_permissions_permission" ON "{prefix}user_permissions" ("permission"); + +CREATE TABLE "{prefix}group_permissions" ( + "id" SERIAL PRIMARY KEY NOT NULL, + "name" VARCHAR(36) NOT NULL, + "permission" VARCHAR(200) NOT NULL, + "value" BOOL NOT NULL, + "server" VARCHAR(36) NULL, + "world" VARCHAR(36) NULL, + "expiry" INT NOT NULL, + "contexts" VARCHAR(200) NULL +); +CREATE INDEX "{prefix}group_permissions_name" ON "{prefix}group_permissions" ("name"); +CREATE INDEX "{prefix}group_permissions_permission" ON "{prefix}group_permissions" ("permission"); + +CREATE TABLE "{prefix}players" ( + "uuid" VARCHAR(36) PRIMARY KEY NOT NULL, + "username" VARCHAR(16) NOT NULL, + "primary_group" VARCHAR(36) NOT NULL +); +CREATE INDEX "{prefix}players_username" ON "{prefix}players" ("username"); + +CREATE TABLE "{prefix}actions" ( + "id" SERIAL PRIMARY KEY 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 +); + +CREATE TABLE "{prefix}tracks" ( + "name" VARCHAR(36) PRIMARY KEY NOT NULL, + "groups" TEXT NOT NULL +); \ No newline at end of file diff --git a/common/src/main/resources/sql/lp-schema-sqlite.sql b/common/src/main/resources/sql/lp-schema-sqlite.sql new file mode 100644 index 00000000..2255230b --- /dev/null +++ b/common/src/main/resources/sql/lp-schema-sqlite.sql @@ -0,0 +1,54 @@ +-- LuckPerms SQLite Schema + +CREATE TABLE '{prefix}user_permissions' ( + 'id' INTEGER PRIMARY KEY NOT NULL, + 'uuid' VARCHAR(36) NOT NULL, + 'permission' VARCHAR(200) NOT NULL, + 'value' BOOL NOT NULL, + 'server' VARCHAR(36) NOT NULL, + 'world' VARCHAR(36) NOT NULL, + 'expiry' INT(11) NOT NULL, + 'contexts' VARCHAR(200) NOT NULL, + PRIMARY KEY ('id') +); +CREATE INDEX '{prefix}user_permissions_uuid' ON '{prefix}user_permissions' ('uuid'); +CREATE INDEX '{prefix}user_permissions_permission' ON '{prefix}user_permissions' ('permission'); + +CREATE TABLE '{prefix}group_permissions' ( + 'id' INTEGER PRIMARY KEY NOT NULL, + 'name' VARCHAR(36) NOT NULL, + 'permission' VARCHAR(200) NOT NULL, + 'value' BOOL NOT NULL, + 'server' VARCHAR(36) NULL, + 'world' VARCHAR(36) NULL, + 'expiry' INT(11) NOT NULL, + 'contexts' VARCHAR(200) NULL, + PRIMARY KEY ('id') +); +CREATE INDEX '{prefix}group_permissions_name' ON '{prefix}group_permissions' ('name'); +CREATE INDEX '{prefix}group_permissions_permission' ON '{prefix}group_permissions' ('permission'); + +CREATE TABLE `{prefix}players` ( + `uuid` VARCHAR(36) NOT NULL, + `username` VARCHAR(16) NOT NULL, + `primary_group` VARCHAR(36) NOT NULL, + PRIMARY KEY (`uuid`) +); +CREATE INDEX '{prefix}players_username' ON '{prefix}players' ('username'); + +CREATE TABLE `{prefix}actions` ( + `id` INTEGER PRIMARY KEY 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 +); + +CREATE TABLE `{prefix}tracks` ( + `name` VARCHAR(36) NOT NULL, + `groups` TEXT NOT NULL, + PRIMARY KEY (`name`) +); \ No newline at end of file