diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java index e6ef360a..85825337 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java @@ -3,12 +3,12 @@ package de.bluecolored.bluemap.core.storage.sql; import com.flowpowered.math.vector.Vector2i; import de.bluecolored.bluemap.core.storage.CompressedInputStream; import de.bluecolored.bluemap.core.storage.Compression; +import de.bluecolored.bluemap.core.storage.sql.dialect.Dialect; import de.bluecolored.bluemap.core.storage.sql.dialect.PostgresDialect; import de.bluecolored.bluemap.core.util.WrappedOutputStream; import java.io.*; import java.net.MalformedURLException; -import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Optional; @@ -19,6 +19,10 @@ public class PostgreSQLStorage extends SQLStorage { super(PostgresDialect.INSTANCE, config); } + public PostgreSQLStorage(Dialect dialect, SQLStorageSettings config) throws MalformedURLException, SQLDriverException { + super(dialect, config); + } + @Override public OutputStream writeMapTile(String mapId, int lod, Vector2i tile) throws IOException { Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE; @@ -28,18 +32,14 @@ public class PostgreSQLStorage extends SQLStorage { int tileCompressionFK = getMapTileCompressionFK(compression); recoveringConnection(connection -> { - byte[] byteData = byteOut.toByteArray(); - ByteArrayInputStream inputStream = new ByteArrayInputStream(byteData); - - PreparedStatement statement = connection.prepareStatement(this.dialect.writeMapTile()); - statement.setInt(1, mapFK); - statement.setInt(2, lod); - statement.setInt(3, tile.getX()); - statement.setInt(4, tile.getY()); - statement.setInt(5, tileCompressionFK); - statement.setBinaryStream(6, inputStream); - - statement.executeUpdate(); + executeUpdate(connection, this.dialect.writeMapTile(), + mapFK, + lod, + tile.getX(), + tile.getY(), + tileCompressionFK, + byteOut.toByteArray() + ); }, 2); }); } @@ -50,15 +50,11 @@ public class PostgreSQLStorage extends SQLStorage { return new WrappedOutputStream(byteOut, () -> { int mapFK = getMapFK(mapId); recoveringConnection(connection -> { - byte[] byteData = byteOut.toByteArray(); - ByteArrayInputStream inputStream = new ByteArrayInputStream(byteData); - - PreparedStatement statement = connection.prepareStatement(this.dialect.writeMeta()); - statement.setInt(1, mapFK); - statement.setString(2, name); - statement.setBinaryStream(3, inputStream); - - statement.executeUpdate(); + executeUpdate(connection, this.dialect.writeMeta(), + mapFK, + name, + byteOut.toByteArray() + ); }, 2); }); } @@ -69,8 +65,7 @@ public class PostgreSQLStorage extends SQLStorage { try { byte[] data = recoveringConnection(connection -> { - ResultSet result = executeQuery(connection, - this.dialect.readMapTile(), + ResultSet result = executeQuery(connection, this.dialect.readMapTile(), mapId, lod, tile.getX(), @@ -100,8 +95,7 @@ public class PostgreSQLStorage extends SQLStorage { public Optional readMeta(String mapId, String name) throws IOException { try { byte[] data = recoveringConnection(connection -> { - ResultSet result = executeQuery(connection, - this.dialect.readMeta(), + ResultSet result = executeQuery(connection, this.dialect.readMeta(), mapId, escapeMetaName(name) ); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java index 8ea329f6..3af604ac 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java @@ -119,7 +119,7 @@ public abstract class SQLStorage extends Storage { byteOut.writeTo(blobOut); } - executeUpdate(connection,this.dialect.writeMapTile(), + executeUpdate(connection, this.dialect.writeMapTile(), mapFK, lod, tile.getX(), @@ -324,7 +324,7 @@ public abstract class SQLStorage extends Storage { try { recoveringConnection(connection -> executeUpdate(connection, - this.dialect.purgeMeta(), + this.dialect.deleteMeta(), mapId, escapeMetaName(name) ), 2); @@ -508,22 +508,21 @@ public abstract class SQLStorage extends Storage { } protected ResultSet executeQuery(Connection connection, @Language("sql") String sql, Object... parameters) throws SQLException { - // we only use this prepared statement once, but the DB-Driver caches those and reuses them - PreparedStatement statement = connection.prepareStatement(sql); - for (int i = 0; i < parameters.length; i++) { - statement.setObject(i+1, parameters[i]); - } - return statement.executeQuery(); + return prepareStatement(connection, sql, parameters).executeQuery(); } @SuppressWarnings("UnusedReturnValue") protected int executeUpdate(Connection connection, @Language("sql") String sql, Object... parameters) throws SQLException { + return prepareStatement(connection, sql, parameters).executeUpdate(); + } + + private PreparedStatement prepareStatement(Connection connection, @Language("sql") String sql, Object... parameters) throws SQLException { // we only use this prepared statement once, but the DB-Driver caches those and reuses them PreparedStatement statement = connection.prepareStatement(sql); for (int i = 0; i < parameters.length; i++) { - statement.setObject(i+1, parameters[i]); + statement.setObject(i + 1, parameters[i]); } - return statement.executeUpdate(); + return statement; } @SuppressWarnings("SameParameterValue") diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLiteStorage.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLiteStorage.java new file mode 100644 index 00000000..4c41217a --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLiteStorage.java @@ -0,0 +1,13 @@ +package de.bluecolored.bluemap.core.storage.sql; + +import de.bluecolored.bluemap.core.storage.sql.dialect.SqliteDialect; + +import java.net.MalformedURLException; + +public class SQLiteStorage extends PostgreSQLStorage { + + public SQLiteStorage(SQLStorageSettings config) throws MalformedURLException, SQLDriverException { + super(SqliteDialect.INSTANCE, config); + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/Dialect.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/Dialect.java index 4406e7d2..bf2b1c04 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/Dialect.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/Dialect.java @@ -25,7 +25,7 @@ public interface Dialect { String readMetaSize(); @Language("sql") - String purgeMeta(); + String deleteMeta(); @Language("sql") String purgeMapTile(); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/DialectType.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/DialectType.java index da50aa1f..d0394bdd 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/DialectType.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/DialectType.java @@ -1,15 +1,13 @@ package de.bluecolored.bluemap.core.storage.sql.dialect; -import de.bluecolored.bluemap.core.storage.sql.MySQLStorage; -import de.bluecolored.bluemap.core.storage.sql.PostgreSQLStorage; -import de.bluecolored.bluemap.core.storage.sql.SQLStorage; -import de.bluecolored.bluemap.core.storage.sql.SQLStorageSettings; +import de.bluecolored.bluemap.core.storage.sql.*; public enum DialectType { MYSQL (MySQLStorage::new, "mysql"), MARIADB (MySQLStorage::new, "mariadb"), - POSTGRESQL (PostgreSQLStorage::new,"postgresql"); + POSTGRESQL (PostgreSQLStorage::new, "postgresql"), + SQLITE (SQLiteStorage::new, "sqlite"); private static final DialectType FALLBACK = MYSQL; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/MySQLDialect.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/MySQLDialect.java index 1d2f49f9..bed99c34 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/MySQLDialect.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/MySQLDialect.java @@ -91,7 +91,7 @@ public class MySQLDialect implements Dialect { @Override @Language("MySQL") - public String purgeMeta() { + public String deleteMeta() { return "DELETE t " + "FROM `bluemap_map_meta` t " + " INNER JOIN `bluemap_map` m " + diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/PostgresDialect.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/PostgresDialect.java index 8f4b69c8..0332ef8d 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/PostgresDialect.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/PostgresDialect.java @@ -92,7 +92,7 @@ public class PostgresDialect implements Dialect { @Override @Language("PostgreSQL") - public String purgeMeta() { + public String deleteMeta() { return "DELETE FROM bluemap_map_meta t " + "USING bluemap_map m " + "WHERE t.map = m.id " + diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/SqliteDialect.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/SqliteDialect.java new file mode 100644 index 00000000..80157aa0 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/SqliteDialect.java @@ -0,0 +1,256 @@ +package de.bluecolored.bluemap.core.storage.sql.dialect; + +import org.intellij.lang.annotations.Language; + +public class SqliteDialect implements Dialect { + + public static final SqliteDialect INSTANCE = new SqliteDialect(); + + private SqliteDialect() {} + + @Override + @Language("sqlite") + public String writeMapTile() { + return "REPLACE INTO `bluemap_map_tile` (`map`, `lod`, `x`, `z`, `compression`, `data`) " + + "VALUES (?, ?, ?, ?, ?, ?)"; + } + + @Override + @Language("sqlite") + public String readMapTile() { + return "SELECT t.`data` " + + "FROM `bluemap_map_tile` t " + + " INNER JOIN `bluemap_map` m " + + " ON t.`map` = m.`id` " + + " INNER JOIN `bluemap_map_tile_compression` c " + + " ON t.`compression` = c.`id` " + + "WHERE m.`map_id` = ? " + + "AND t.`lod` = ? " + + "AND t.`x` = ? " + + "AND t.`z` = ? " + + "AND c.`compression` = ?"; + } + + @Override + @Language("sqlite") + public String readMapTileInfo() { + return "SELECT t.`changed`, LENGTH(t.`data`) as 'size' " + + "FROM `bluemap_map_tile` t " + + " INNER JOIN `bluemap_map` m " + + " ON t.`map` = m.`id` " + + " INNER JOIN `bluemap_map_tile_compression` c " + + " ON t.`compression` = c.`id` " + + "WHERE m.`map_id` = ? " + + "AND t.`lod` = ? " + + "AND t.`x` = ? " + + "AND t.`z` = ? " + + "AND c.`compression` = ?"; + } + + @Override + @Language("sqlite") + public String deleteMapTile() { + return "DELETE FROM `bluemap_map_tile` " + + "WHERE `map` IN( " + + " SELECT `id` " + + " FROM `bluemap_map` " + + " WHERE `map_id` = ? " + + " LIMIT 1 " + + ") " + + "AND `lod` = ? " + + "AND `x` = ? " + + "AND `z` = ? "; + } + + @Override + @Language("sqlite") + public String writeMeta() { + return "REPLACE INTO `bluemap_map_meta` (`map`, `key`, `value`) " + + "VALUES (?, ?, ?)"; + } + + @Override + @Language("sqlite") + public String readMeta() { + return "SELECT t.`value` " + + "FROM `bluemap_map_meta` t " + + " INNER JOIN `bluemap_map` m " + + " ON t.`map` = m.`id` " + + "WHERE m.`map_id` = ? " + + "AND t.`key` = ?"; + } + + @Override + @Language("sqlite") + public String readMetaSize() { + return "SELECT LENGTH(t.`value`) as 'size' " + + "FROM `bluemap_map_meta` t " + + " INNER JOIN `bluemap_map` m " + + " ON t.`map` = m.`id` " + + "WHERE m.`map_id` = ? " + + "AND t.`key` = ?"; + } + + @Override + @Language("sqlite") + public String deleteMeta() { + return "DELETE FROM `bluemap_map_meta` " + + "WHERE `map` IN( " + + " SELECT `id` " + + " FROM `bluemap_map` " + + " WHERE `map_id` = ? " + + " LIMIT 1 " + + ") " + + "AND `key` = ?"; + } + + @Override + @Language("sqlite") + public String purgeMapTile() { + return "DELETE FROM `bluemap_map_tile` " + + "WHERE `map` IN( " + + " SELECT `id` " + + " FROM `bluemap_map` " + + " WHERE `map_id` = ? " + + " LIMIT 1 " + + ")"; + } + + @Override + @Language("sqlite") + public String purgeMapMeta() { + return "DELETE FROM `bluemap_map_meta` " + + "WHERE `map` IN( " + + " SELECT `id` " + + " FROM `bluemap_map` " + + " WHERE `map_id` = ? " + + " LIMIT 1 " + + ")"; + } + + @Override + @Language("sqlite") + public String purgeMap() { + return "DELETE " + + "FROM `bluemap_map` " + + "WHERE `map_id` = ?"; + } + + @Override + @Language("sqlite") + public String selectMapIds() { + return "SELECT `map_id` FROM `bluemap_map`"; + } + + @Override + @Language("sqlite") + public String initializeStorageMeta() { + return "CREATE TABLE IF NOT EXISTS `bluemap_storage_meta` (" + + "`key` varchar(255) NOT NULL, " + + "`value` varchar(255) DEFAULT NULL, " + + "PRIMARY KEY (`key`)" + + ")"; + } + + @Override + @Language("sqlite") + public String selectStorageMeta() { + return "SELECT `value` FROM `bluemap_storage_meta` " + + "WHERE `key` = ?"; + } + + @Override + @Language("sqlite") + public String insertStorageMeta() { + return "INSERT INTO `bluemap_storage_meta` (`key`, `value`) " + + "VALUES (?, ?)"; + } + + @Override + @Language("sqlite") + public String initializeMap() { + return "CREATE TABLE `bluemap_map` (" + + "`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," + + "`map_id` VARCHAR(255) NOT NULL," + + "UNIQUE (`map_id`)" + + ");"; + } + + @Override + @Language("sqlite") + public String initializeMapTileCompression() { + return "CREATE TABLE `bluemap_map_tile_compression` (" + + "`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," + + "`compression` VARCHAR(255) NOT NULL," + + "UNIQUE (`compression`)" + + ");"; + } + + @Override + @Language("sqlite") + public String initializeMapMeta() { + return "CREATE TABLE `bluemap_map_meta` (" + + "`map` SMALLINT UNSIGNED NOT NULL," + + "`key` varchar(255) NOT NULL," + + "`value` LONGBLOB NOT NULL," + + "PRIMARY KEY (`map`, `key`)," + + "CONSTRAINT `fk_bluemap_map_meta_map` FOREIGN KEY (`map`) REFERENCES `bluemap_map` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT" + + ")"; + } + + @Override + @Language("sqlite") + public String initializeMapTile() { + return "CREATE TABLE `bluemap_map_tile` (" + + "`map` SMALLINT UNSIGNED NOT NULL," + + "`lod` SMALLINT UNSIGNED NOT NULL," + + "`x` INT NOT NULL," + + "`z` INT NOT NULL," + + "`compression` SMALLINT UNSIGNED NOT NULL," + + "`changed` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP," + + "`data` LONGBLOB NOT NULL," + + "PRIMARY KEY (`map`, `lod`, `x`, `z`)," + + "CONSTRAINT `fk_bluemap_map_tile_map` FOREIGN KEY (`map`) REFERENCES `bluemap_map` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT," + + "CONSTRAINT `fk_bluemap_map_tile_compression` FOREIGN KEY (`compression`) REFERENCES `bluemap_map_tile_compression` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT" + + ");"; + } + + @Override + @Language("sqlite") + public String updateStorageMeta() { + return "UPDATE `bluemap_storage_meta` " + + "SET `value` = ? " + + "WHERE `key` = ?"; + } + + @Override + @Language("sqlite") + public String deleteMapMeta() { + return "DELETE FROM `bluemap_map_meta`" + + "WHERE `key` IN (?, ?, ?)"; + } + + @Override + @Language("sqlite") + public String updateMapMeta() { + return "UPDATE `bluemap_map_meta` " + + "SET `key` = ? " + + "WHERE `key` = ?"; + } + + @Override + @Language("sqlite") + public String lookupFK(String table, String idField, String valueField) { + return "SELECT `" + idField + "` FROM `" + table + "` " + + "WHERE `" + valueField + "` = ?"; + } + + @Override + @Language("sqlite") + public String insertFK(String table, String valueField) { + return "INSERT INTO `" + table + "` (`" + valueField + "`) " + + "VALUES (?)"; + } + + +}