package com.nicewuerfel.blockown.database; import com.nicewuerfel.blockown.Ownable; import com.nicewuerfel.blockown.OwnedBlock; import com.nicewuerfel.blockown.OwnedEntity; import com.nicewuerfel.blockown.User; import com.nicewuerfel.blockown.output.Output; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; import java.io.File; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class SqliteDatabase extends CachedDatabase { private static final String FILENAME = "world.db"; private static final String CREATE_BLOCK_TABLE_QUERY = String.format( "CREATE TABLE IF NOT EXISTS %1$s (%2$s VARCHAR(50), %3$s INT, %4$s INT, %5$s INT, %6$s CHAR(36) NOT NULL, PRIMARY KEY (%2$s, %3$s, %4$s, %5$s));", Database.BLOCK_TABLE, Database.WORLD_COLUMN, Database.X_COLUMN, Database.Y_COLUMN, Database.Z_COLUMN, Database.PLAYER_ID_COLUMN); private static final String CREATE_ENTITY_TABLE_QUERY = String.format( "CREATE TABLE IF NOT EXISTS %1$s (%2$s VARCHAR(50) NOT NULL, %3$s CHAR(36), %4$s CHAR(36) NOT NULL, PRIMARY KEY (%3$s));", Database.ENTITY_TABLE, Database.WORLD_COLUMN, Database.ENTITY_ID_COLUMN, Database.PLAYER_ID_COLUMN); private static final String GET_BLOCK_OWNER_QUERY = String.format("SELECT %1$s FROM %2$s WHERE %3$s=? AND %4$s=? AND %5$s=? AND %6$s=?;", Database.PLAYER_ID_COLUMN, Database.BLOCK_TABLE, Database.WORLD_COLUMN, Database.X_COLUMN, Database.Y_COLUMN, Database.Z_COLUMN); private static final String GET_ENTITY_OWNER_QUERY = String.format("SELECT %1$s FROM %2$s WHERE %3$s=? AND %4$s=?;", Database.PLAYER_ID_COLUMN, Database.ENTITY_TABLE, Database.WORLD_COLUMN, Database.ENTITY_ID_COLUMN); private static final String UNOWN_BLOCK_QUERY = String.format( "DELETE FROM %1$s WHERE %2$s=? AND %3$s=? AND %4$s=? AND %5$s=?;", Database.BLOCK_TABLE, Database.WORLD_COLUMN, Database.X_COLUMN, Database.Y_COLUMN, Database.Z_COLUMN); private static final String UNOWN_ENTITY_QUERY = String.format("DELETE FROM %1$s WHERE %2$s=? AND %3$s=?;", Database.ENTITY_TABLE, Database.WORLD_COLUMN, Database.ENTITY_ID_COLUMN); private static final String OWN_BLOCK_QUERY = String.format( "INSERT OR REPLACE INTO %1$s(%2$s, %3$s, %4$s, %5$s, %6$s) VALUES(?, ?, ?, ?, ?);", Database.BLOCK_TABLE, Database.WORLD_COLUMN, Database.X_COLUMN, Database.Y_COLUMN, Database.Z_COLUMN, Database.PLAYER_ID_COLUMN); private static final String OWN_ENTITY_QUERY = String.format( "INSERT OR REPLACE INTO %1$s(%2$s, %3$s, %4$s) VALUES(?, ?, ?);", Database.ENTITY_TABLE, Database.WORLD_COLUMN, Database.ENTITY_ID_COLUMN, Database.PLAYER_ID_COLUMN); private static final String DROP_USER_BLOCK_QUERY = String .format("DELETE FROM %1$s WHERE %2$s=?;", Database.BLOCK_TABLE, Database.PLAYER_ID_COLUMN); private static final String DROP_USER_ENTITY_QUERY = String .format("DELETE FROM %1$s WHERE %2$s=?;", Database.ENTITY_TABLE, Database.PLAYER_ID_COLUMN); private final File pluginDataFolder; public SqliteDatabase(Output output, File pluginFolder) throws SQLException, ClassNotFoundException { super(output, pluginFolder); this.pluginDataFolder = pluginFolder.getAbsoluteFile(); HikariConfig config = new HikariConfig(); config.setDataSourceClassName("org.sqlite.SQLiteDataSource"); File file = new File(pluginDataFolder, FILENAME); config.addDataSourceProperty("url", "jdbc:sqlite:" + file.getPath()); // Since Spigot/CraftBukkit use a very old version of the SQLite JDBC, JDBC4 operations like // Connection.isValid() are not supported config.setConnectionTestQuery("SELECT 1;"); config.setMaximumPoolSize(1); // Possible fix for ClassLoader error try { Plugin plugin = Bukkit.getPluginManager().getPlugin("BlockOwn"); plugin.getPluginLoader().getClass().getClassLoader().loadClass("org.sqlite.SQLiteDataSource"); } catch (NullPointerException ignored) { // Should only happen while testing } connectionPool = new HikariDataSource(config); createTables(); } @Override PreparedStatement[] createCreateTablesStatements() throws SQLException { Connection connection = getConnection(); PreparedStatement[] stmnts = {connection.prepareStatement(CREATE_BLOCK_TABLE_QUERY), connection.prepareStatement(CREATE_ENTITY_TABLE_QUERY)}; return stmnts; } @Override PreparedStatement createGetOwnerStatement(Ownable ownable) throws SQLException { PreparedStatement stmnt; if (ownable instanceof OwnedBlock) { OwnedBlock block = (OwnedBlock) ownable; stmnt = getConnection().prepareStatement(GET_BLOCK_OWNER_QUERY); stmnt.setString(1, block.getWorldName()); stmnt.setInt(2, block.getX()); stmnt.setInt(3, block.getY()); stmnt.setInt(4, block.getZ()); } else if (ownable instanceof OwnedEntity) { OwnedEntity entity = (OwnedEntity) ownable; stmnt = getConnection().prepareStatement(GET_ENTITY_OWNER_QUERY); stmnt.setString(1, entity.getWorldName()); stmnt.setString(2, entity.getUniqueId().toString()); } else { throw new IllegalArgumentException("Invalid Ownable type"); } return stmnt; } @Override PreparedStatement createSetOwnerStatement(DatabaseAction databaseAction) throws SQLException { PreparedStatement stmnt; if (databaseAction.getActionType() == DatabaseAction.Type.UNOWN) { if (databaseAction.getOwnable() instanceof OwnedBlock) { OwnedBlock block = (OwnedBlock) databaseAction.getOwnable(); stmnt = getConnection().prepareStatement(UNOWN_BLOCK_QUERY); stmnt.setString(1, block.getWorldName()); stmnt.setInt(2, block.getX()); stmnt.setInt(3, block.getY()); stmnt.setInt(4, block.getZ()); } else if (databaseAction.getOwnable() instanceof OwnedEntity) { OwnedEntity entity = (OwnedEntity) databaseAction.getOwnable(); stmnt = getConnection().prepareStatement(UNOWN_ENTITY_QUERY); stmnt.setString(1, entity.getWorldName()); stmnt.setString(2, entity.getUniqueId().toString()); } else { throw new IllegalArgumentException("Invalid Ownable type"); } } else if (databaseAction.getActionType() == DatabaseAction.Type.OWN) { if (databaseAction.getOwnable() instanceof OwnedBlock) { OwnedBlock block = (OwnedBlock) databaseAction.getOwnable(); stmnt = getConnection().prepareStatement(OWN_BLOCK_QUERY); stmnt.setString(1, block.getWorldName()); stmnt.setInt(2, block.getX()); stmnt.setInt(3, block.getY()); stmnt.setInt(4, block.getZ()); stmnt.setString(5, databaseAction.getUser().getUniqueId().toString()); } else if (databaseAction.getOwnable() instanceof OwnedEntity) { OwnedEntity entity = (OwnedEntity) databaseAction.getOwnable(); stmnt = getConnection().prepareStatement(OWN_ENTITY_QUERY); stmnt.setString(1, entity.getWorldName()); stmnt.setString(2, entity.getUniqueId().toString()); stmnt.setString(3, databaseAction.getUser().getUniqueId().toString()); } else { throw new IllegalArgumentException("Invalid Ownable type"); } } else { // Should never happen getOutput().printException(new IllegalArgumentException("Invalid DatabaseActionType")); return null; } return stmnt; } @Override PreparedStatement[] createDropUserStatements(User user) throws SQLException { Connection connection = getConnection(); PreparedStatement stmnt1 = connection.prepareStatement(DROP_USER_BLOCK_QUERY); stmnt1.setString(1, user.getUniqueId().toString()); PreparedStatement stmnt2 = connection.prepareStatement(DROP_USER_ENTITY_QUERY); stmnt2.setString(1, user.getUniqueId().toString()); return new PreparedStatement[]{stmnt1, stmnt2}; } }