WIP: implement better sql schema - towards #77

This commit is contained in:
Luck 2016-12-24 20:30:35 +00:00
parent d34013396a
commit 6874cfd5b1
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
15 changed files with 1307 additions and 364 deletions

View File

@ -74,6 +74,13 @@
<version>1.4.192</version>
<scope>provided</scope>
</dependency>
<!-- PostgreSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1212</version>
<scope>provided</scope>
</dependency>
<!-- MongoDB -->
<dependency>
<groupId>org.mongodb</groupId>

View File

@ -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"),

View File

@ -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();

View File

@ -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:

View File

@ -34,6 +34,7 @@ public enum StorageType {
YAML("yaml", "yml"),
MONGODB("mongodb"),
MYSQL("mysql"),
POSTGRESQL("postgresql"),
SQLITE("sqlite"),
H2("h2");

View File

@ -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<Map<String, Boolean>>() {}.getType();
private static final Type T_TYPE = new TypeToken<List<String>>() {}.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<List<String>>() {}.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<Class<? extends SQLProvider>, String[]> INIT_QUERIES = ImmutableMap.<Class<? extends SQLProvider>, 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<String, String> 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<NodeDataHolder> data = new ArrayList<>();
AtomicReference<String> primaryGroup = new AtomicReference<>(null);
AtomicReference<String> 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<String, Boolean> nodes = gson.fromJson(perms[0], NM_TYPE);
if (!data.isEmpty()) {
Set<Node> 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<NodeDataHolder> 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<UUID> getUniqueUsers() {
Set<UUID> 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<String, Boolean> 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<String, Boolean> nodes = gson.fromJson(perms[0], NM_TYPE);
group.setNodes(nodes);
return true;
} finally {
group.getIoLock().unlock();
}
return false;
}
@Override
public boolean loadAllGroups() {
List<String> 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<String> 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<String> 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<String> 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;
}
}

View File

@ -0,0 +1,685 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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<Map<String, Boolean>>() {}.getType();
private static final Type T_TYPE = new TypeToken<List<String>>() {}.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<Class<? extends SQLProvider>, String[]> INIT_QUERIES = ImmutableMap.<Class<? extends SQLProvider>, 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<String, Boolean> 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<UUID> getUniqueUsers() {
Set<UUID> 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<String, Boolean> 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<String, Boolean> nodes = gson.fromJson(perms[0], NM_TYPE);
group.setNodes(nodes);
return true;
} finally {
group.getIoLock().unlock();
}
}
@Override
public boolean loadAllGroups() {
List<String> 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<String> 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;
}
}

View File

@ -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);

View File

@ -0,0 +1,144 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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;
}
}

View File

@ -64,7 +64,6 @@ public abstract class SQLProvider {
return runQuery(query, EMPTY_PS, queryRS);
}
@FunctionalInterface
public interface QueryPS {
void onRun(PreparedStatement preparedStatement) throws SQLException;

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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;
}
}

View File

@ -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`)
);

View File

@ -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;

View File

@ -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
);

View File

@ -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`)
);