diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/storage/StorageType.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/storage/StorageType.java index 7eb05f95..20a24e94 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/storage/StorageType.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/storage/StorageType.java @@ -31,7 +31,7 @@ import de.bluecolored.bluemap.core.storage.sql.SQLStorage; public enum StorageType { FILE (FileConfig.class, FileStorage::new), - SQL (SQLConfig.class, SQLStorage::new); + SQL (SQLConfig.class, SQLStorage::create); private final Class configType; private final StorageFactory storageFactory; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/MySQLStorage.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/MySQLStorage.java new file mode 100644 index 00000000..1f99693e --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/MySQLStorage.java @@ -0,0 +1,13 @@ +package de.bluecolored.bluemap.core.storage.sql; + +import de.bluecolored.bluemap.core.storage.sql.dialect.MySQLDialect; + +import java.net.MalformedURLException; + +public class MySQLStorage extends SQLStorage{ + + public MySQLStorage(SQLStorageSettings config) throws MalformedURLException, SQLDriverException { + super(MySQLDialect.INSTANCE, config); + } + +} 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 new file mode 100644 index 00000000..e6ef360a --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java @@ -0,0 +1,126 @@ +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.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; + +public class PostgreSQLStorage extends SQLStorage { + + public PostgreSQLStorage(SQLStorageSettings config) throws MalformedURLException, SQLDriverException { + super(PostgresDialect.INSTANCE, config); + } + + @Override + public OutputStream writeMapTile(String mapId, int lod, Vector2i tile) throws IOException { + Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE; + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + return new WrappedOutputStream(compression.compress(byteOut), () -> { + int mapFK = getMapFK(mapId); + 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(); + }, 2); + }); + } + + @Override + public OutputStream writeMeta(String mapId, String name) { + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + 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(); + }, 2); + }); + } + + @Override + public Optional readMapTile(String mapId, int lod, Vector2i tile) throws IOException { + Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE; + + try { + byte[] data = recoveringConnection(connection -> { + ResultSet result = executeQuery(connection, + this.dialect.readMapTile(), + mapId, + lod, + tile.getX(), + tile.getY(), + compression.getTypeId() + ); + + if (result.next()) { + return result.getBytes(1); + } else { + return null; + } + }, 2); + + if (data == null) { + return Optional.empty(); + } + + InputStream inputStream = new ByteArrayInputStream(data); + return Optional.of(new CompressedInputStream(inputStream, compression)); + } catch (SQLException ex) { + throw new IOException(ex); + } + } + + @Override + public Optional readMeta(String mapId, String name) throws IOException { + try { + byte[] data = recoveringConnection(connection -> { + ResultSet result = executeQuery(connection, + this.dialect.readMeta(), + mapId, + escapeMetaName(name) + ); + if (result.next()) { + return result.getBytes(1); + } else { + return null; + } + }, 2); + + if (data == null) { + return Optional.empty(); + } + + InputStream inputStream = new ByteArrayInputStream(data); + return Optional.of(inputStream); + } catch (SQLException ex) { + throw new IOException(ex); + } + } + +} 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 2ec91c36..d633d73f 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 @@ -30,6 +30,8 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.storage.*; +import de.bluecolored.bluemap.core.storage.sql.dialect.DialectType; +import de.bluecolored.bluemap.core.storage.sql.dialect.SQLQueryDialect; import de.bluecolored.bluemap.core.util.WrappedOutputStream; import org.apache.commons.dbcp2.*; import org.apache.commons.pool2.ObjectPool; @@ -48,10 +50,12 @@ import java.util.*; import java.util.concurrent.CompletionException; import java.util.function.Function; -public class SQLStorage extends Storage { +public abstract class SQLStorage extends Storage { private final DataSource dataSource; - private final Compression hiresCompression; + + protected final SQLQueryDialect dialect; + protected final Compression hiresCompression; private final LoadingCache mapFKs = Caffeine.newBuilder() .executor(BlueMap.THREAD_POOL) @@ -62,9 +66,9 @@ public class SQLStorage extends Storage { private volatile boolean closed; - public SQLStorage(SQLStorageSettings config) throws MalformedURLException, SQLDriverException { + public SQLStorage(SQLQueryDialect dialect, SQLStorageSettings config) throws MalformedURLException, SQLDriverException { + this.dialect = dialect; this.closed = false; - try { if (config.getDriverClass().isPresent()) { if (config.getDriverJar().isPresent()) { @@ -115,9 +119,7 @@ public class SQLStorage extends Storage { byteOut.writeTo(blobOut); } - executeUpdate(connection, - "REPLACE INTO `bluemap_map_tile` (`map`, `lod`, `x`, `z`, `compression`, `data`) " + - "VALUES (?, ?, ?, ?, ?, ?)", + executeUpdate(connection,this.dialect.writeMapTile(), mapFK, lod, tile.getX(), @@ -139,17 +141,7 @@ public class SQLStorage extends Storage { try { byte[] data = recoveringConnection(connection -> { ResultSet result = executeQuery(connection, - "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` = ?", + this.dialect.readMapTile(), mapId, lod, tile.getX(), @@ -179,17 +171,7 @@ public class SQLStorage extends Storage { try { TileInfo tileInfo = recoveringConnection(connection -> { ResultSet result = executeQuery(connection, - "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` = ?", + this.dialect.readMapTileInfo(), mapId, lod, tile.getX(), @@ -238,15 +220,7 @@ public class SQLStorage extends Storage { public void deleteMapTile(String mapId, int lod, Vector2i tile) throws IOException { try { recoveringConnection(connection -> - executeUpdate(connection, - "DELETE t " + - "FROM `bluemap_map_tile` t " + - " INNER JOIN `bluemap_map` m " + - " ON t.`map` = m.`id` " + - "WHERE m.`map_id` = ? " + - "AND t.`lod` = ? " + - "AND t.`x` = ? " + - "AND t.`z` = ?", + executeUpdate(connection,this.dialect.deleteMapTile(), mapId, lod, tile.getX(), @@ -271,8 +245,7 @@ public class SQLStorage extends Storage { } executeUpdate(connection, - "REPLACE INTO `bluemap_map_meta` (`map`, `key`, `value`) " + - "VALUES (?, ?, ?)", + this.dialect.writeMeta(), mapFK, escapeMetaName(name), dataBlob @@ -289,12 +262,7 @@ public class SQLStorage extends Storage { try { byte[] data = recoveringConnection(connection -> { ResultSet result = executeQuery(connection, - "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` = ?", + this.dialect.readMeta(), mapId, escapeMetaName(name) ); @@ -319,12 +287,7 @@ public class SQLStorage extends Storage { try { MetaInfo tileInfo = recoveringConnection(connection -> { ResultSet result = executeQuery(connection, - "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` = ?", + this.dialect.readMetaSize(), mapId, escapeMetaName(name) ); @@ -361,12 +324,7 @@ public class SQLStorage extends Storage { try { recoveringConnection(connection -> executeUpdate(connection, - "DELETE t " + - "FROM `bluemap_map_meta` t " + - " INNER JOIN `bluemap_map` m " + - " ON t.`map` = m.`id` " + - "WHERE m.`map_id` = ? " + - "AND t.`key` = ?", + this.dialect.purgeMeta(), mapId, escapeMetaName(name) ), 2); @@ -381,28 +339,18 @@ public class SQLStorage extends Storage { try { recoveringConnection(connection -> { executeUpdate(connection, - "DELETE t " + - "FROM `bluemap_map_tile` t " + - " INNER JOIN `bluemap_map` m " + - " ON t.`map` = m.`id` " + - "WHERE m.`map_id` = ?", + this.dialect.purgeMapTile(), mapId ); executeUpdate(connection, - "DELETE t " + - "FROM `bluemap_map_meta` t " + - " INNER JOIN `bluemap_map` m " + - " ON t.`map` = m.`id` " + - "WHERE m.`map_id` = ?", + this.dialect.purgeMapMeta(), mapId ); executeUpdate(connection, - "DELETE " + - "FROM `bluemap_map` " + - "WHERE `map_id` = ?", + this.dialect.purgeMap(), mapId ); }, 2); @@ -420,7 +368,7 @@ public class SQLStorage extends Storage { try { return recoveringConnection(connection -> { ResultSet result = executeQuery(connection, - "SELECT `map_id` FROM `bluemap_map`" + this.dialect.selectMapIds() ); Collection mapIds = new ArrayList<>(); while (result.next()) { @@ -440,15 +388,10 @@ public class SQLStorage extends Storage { // initialize and get schema-version String schemaVersionString = recoveringConnection(connection -> { connection.createStatement().executeUpdate( - "CREATE TABLE IF NOT EXISTS `bluemap_storage_meta` (" + - "`key` varchar(255) NOT NULL, " + - "`value` varchar(255) DEFAULT NULL, " + - "PRIMARY KEY (`key`)" + - ")"); + this.dialect.initializeStorageMeta()); ResultSet result = executeQuery(connection, - "SELECT `value` FROM `bluemap_storage_meta` " + - "WHERE `key` = ?", + this.dialect.selectStorageMeta(), "schema_version" ); @@ -456,8 +399,7 @@ public class SQLStorage extends Storage { return result.getString("value"); } else { executeUpdate(connection, - "INSERT INTO `bluemap_storage_meta` (`key`, `value`) " + - "VALUES (?, ?)", + this.dialect.insertStorageMeta(), "schema_version", "0" ); return "0"; @@ -482,51 +424,22 @@ public class SQLStorage extends Storage { recoveringConnection(connection -> { connection.createStatement().executeUpdate( - "CREATE TABLE `bluemap_map` (" + - "`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT," + - "`map_id` VARCHAR(255) NOT NULL," + - "PRIMARY KEY (`id`)," + - "UNIQUE INDEX `map_id` (`map_id`)" + - ");" + this.dialect.initializeMap() ); connection.createStatement().executeUpdate( - "CREATE TABLE `bluemap_map_tile_compression` (" + - "`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT," + - "`compression` VARCHAR(255) NOT NULL," + - "PRIMARY KEY (`id`)," + - "UNIQUE INDEX `compression` (`compression`)" + - ");" + this.dialect.initializeMapTileCompression() ); connection.createStatement().executeUpdate( - "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" + - ")"); + this.dialect.initializeMapMeta()); connection.createStatement().executeUpdate( - "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 ON UPDATE 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" + - ");" + this.dialect.initializeMapTile() ); executeUpdate(connection, - "UPDATE `bluemap_storage_meta` " + - "SET `value` = ? " + - "WHERE `key` = ?", + this.dialect.updateStorageMeta(), "3", "schema_version" ); }, 2); @@ -544,36 +457,27 @@ public class SQLStorage extends Storage { // delete potential files that are already in the new format to avoid constraint-issues executeUpdate(connection, - "DELETE FROM `bluemap_map_meta`" + - "WHERE `key` IN (?, ?, ?)", + this.dialect.deleteMapMeta(), "settings.json", "textures.json", ".rstate" ); // rename files executeUpdate(connection, - "UPDATE `bluemap_map_meta` " + - "SET `key` = ? " + - "WHERE `key` = ?", + this.dialect.updateMapMeta(), "settings.json", "settings" ); executeUpdate(connection, - "UPDATE `bluemap_map_meta` " + - "SET `key` = ? " + - "WHERE `key` = ?", + this.dialect.updateMapMeta(), "textures.json", "textures" ); executeUpdate(connection, - "UPDATE `bluemap_map_meta` " + - "SET `key` = ? " + - "WHERE `key` = ?", + this.dialect.updateMapMeta(), ".rstate", "render_state" ); // update schemaVersion executeUpdate(connection, - "UPDATE `bluemap_storage_meta` " + - "SET `value` = ? " + - "WHERE `key` = ?", + this.dialect.updateStorageMeta(), "3", "schema_version" ); }, 2); @@ -603,7 +507,7 @@ public class SQLStorage extends Storage { } } - private ResultSet executeQuery(Connection connection, @Language("sql") String sql, Object... parameters) throws SQLException { + 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++) { @@ -613,7 +517,7 @@ public class SQLStorage extends Storage { } @SuppressWarnings("UnusedReturnValue") - private int executeUpdate(Connection connection, @Language("sql") String sql, Object... parameters) throws SQLException { + protected int executeUpdate(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++) { @@ -623,12 +527,12 @@ public class SQLStorage extends Storage { } @SuppressWarnings("SameParameterValue") - private void recoveringConnection(ConnectionConsumer action, int tries) throws SQLException, IOException { + protected void recoveringConnection(ConnectionConsumer action, int tries) throws SQLException, IOException { recoveringConnection((ConnectionFunction) action, tries); } @SuppressWarnings("SameParameterValue") - private R recoveringConnection(ConnectionFunction action, int tries) throws SQLException, IOException { + protected R recoveringConnection(ConnectionFunction action, int tries) throws SQLException, IOException { SQLException sqlException = null; try { @@ -657,7 +561,7 @@ public class SQLStorage extends Storage { throw sqlException; } - private int getMapFK(String mapId) throws SQLException { + protected int getMapFK(String mapId) throws SQLException { try { return Objects.requireNonNull(mapFKs.get(mapId)); } catch (CompletionException ex) { @@ -670,7 +574,7 @@ public class SQLStorage extends Storage { } } - private int getMapTileCompressionFK(Compression compression) throws SQLException { + int getMapTileCompressionFK(Compression compression) throws SQLException { try { return Objects.requireNonNull(mapTileCompressionFKs.get(compression)); } catch (CompletionException ex) { @@ -698,9 +602,7 @@ public class SQLStorage extends Storage { return recoveringConnection(connection -> { int key; ResultSet result = executeQuery(connection, - //language=SQL - "SELECT `" + idField + "` FROM `" + table + "` " + - "WHERE `" + valueField + "` = ?", + this.dialect.lookupFK(table,idField,valueField), value ); @@ -708,8 +610,7 @@ public class SQLStorage extends Storage { key = result.getInt("id"); } else { PreparedStatement statement = connection.prepareStatement( - "INSERT INTO `" + table + "` (`" + valueField + "`) " + - "VALUES (?)", + this.dialect.insertFK(table,valueField), Statement.RETURN_GENERATED_KEYS ); statement.setString(1, value); @@ -774,6 +675,12 @@ public class SQLStorage extends Storage { return new PoolingDataSource<>(connectionPool); } + public static SQLStorage create(SQLStorageSettings settings) throws Exception { + String dbUrl = settings.getConnectionUrl(); + String provider = dbUrl.strip().split(":", 3)[1]; + return DialectType.getStorage(provider,settings); + } + @FunctionalInterface public interface ConnectionConsumer extends ConnectionFunction { 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 new file mode 100644 index 00000000..25fff0df --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/DialectType.java @@ -0,0 +1,40 @@ +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; + +public enum DialectType { + + MYSQL (MySQLStorage::new, "mysql"), + MARIADB (MySQLStorage::new, "mariadb"), + POSTGRESQL (PostgreSQLStorage::new,"postgresql"); + + private final SQLStorageFactory storageFactory; + private final String dialectName; + + DialectType(SQLStorageFactory storageFactory, String dialectName) { + this.storageFactory = storageFactory; + this.dialectName = dialectName; + } + public String getDialectName() { + return dialectName; + } + + public static SQLStorage getStorage(String dialectName, SQLStorageSettings settings) throws Exception { + for (DialectType dialect : values()) { + if (dialect.getDialectName().equals(dialectName)) { + return dialect.storageFactory.provide(settings); + } + } + return null; + } + + @FunctionalInterface + public interface SQLStorageFactory { + SQLStorage provide(SQLStorageSettings config) throws Exception; + + } + +} 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 new file mode 100644 index 00000000..c7fd5d5f --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/MySQLDialect.java @@ -0,0 +1,250 @@ +package de.bluecolored.bluemap.core.storage.sql.dialect; + +import org.intellij.lang.annotations.Language; + +public class MySQLDialect implements SQLQueryDialect { + + public static final MySQLDialect INSTANCE = new MySQLDialect(); + + private MySQLDialect() {}; + + @Override + @Language("MySQL") + public String writeMapTile() { + return "REPLACE INTO `bluemap_map_tile` (`map`, `lod`, `x`, `z`, `compression`, `data`) " + + "VALUES (?, ?, ?, ?, ?, ?)"; + } + + @Override + @Language("MySQL") + 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("MySQL") + 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("MySQL") + public String deleteMapTile() { + return "DELETE t " + + "FROM `bluemap_map_tile` t " + + " INNER JOIN `bluemap_map` m " + + " ON t.`map` = m.`id` " + + "WHERE m.`map_id` = ? " + + "AND t.`lod` = ? " + + "AND t.`x` = ? " + + "AND t.`z` = ?"; + } + + @Override + @Language("MySQL") + public String writeMeta() { + return "REPLACE INTO `bluemap_map_meta` (`map`, `key`, `value`) " + + "VALUES (?, ?, ?)"; + } + + @Override + @Language("MySQL") + 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("MySQL") + 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("MySQL") + public String purgeMeta() { + return "DELETE t " + + "FROM `bluemap_map_meta` t " + + " INNER JOIN `bluemap_map` m " + + " ON t.`map` = m.`id` " + + "WHERE m.`map_id` = ? " + + "AND t.`key` = ?"; + } + + @Override + @Language("MySQL") + public String purgeMapTile() { + return "DELETE t " + + "FROM `bluemap_map_tile` t " + + " INNER JOIN `bluemap_map` m " + + " ON t.`map` = m.`id` " + + "WHERE m.`map_id` = ?"; + } + + @Override + @Language("MySQL") + public String purgeMapMeta() { + return "DELETE t " + + "FROM `bluemap_map_meta` t " + + " INNER JOIN `bluemap_map` m " + + " ON t.`map` = m.`id` " + + "WHERE m.`map_id` = ?"; + } + + @Override + @Language("MySQL") + public String purgeMap() { + return "DELETE " + + "FROM `bluemap_map` " + + "WHERE `map_id` = ?"; + } + + @Override + @Language("MySQL") + public String selectMapIds() { + return "SELECT `map_id` FROM `bluemap_map`"; + } + + @Override + @Language("MySQL") + 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("MySQL") + public String selectStorageMeta() { + return "SELECT `value` FROM `bluemap_storage_meta` " + + "WHERE `key` = ?"; + } + + @Override + @Language("MySQL") + public String insertStorageMeta() { + return "INSERT INTO `bluemap_storage_meta` (`key`, `value`) " + + "VALUES (?, ?)"; + } + + @Override + @Language("MySQL") + public String initializeMap() { + return "CREATE TABLE `bluemap_map` (" + + "`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT," + + "`map_id` VARCHAR(255) NOT NULL," + + "PRIMARY KEY (`id`)," + + "UNIQUE INDEX `map_id` (`map_id`)" + + ");"; + } + + @Override + @Language("MySQL") + public String initializeMapTileCompression() { + return "CREATE TABLE `bluemap_map_tile_compression` (" + + "`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT," + + "`compression` VARCHAR(255) NOT NULL," + + "PRIMARY KEY (`id`)," + + "UNIQUE INDEX `compression` (`compression`)" + + ");"; + } + + @Override + @Language("MySQL") + 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("MySQL") + 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 ON UPDATE 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("MySQL") + public String updateStorageMeta() { + return "UPDATE `bluemap_storage_meta` " + + "SET `value` = ? " + + "WHERE `key` = ?"; + } + + @Override + @Language("MySQL") + public String deleteMapMeta() { + return "DELETE FROM `bluemap_map_meta`" + + "WHERE `key` IN (?, ?, ?)"; + } + + @Override + @Language("MySQL") + public String updateMapMeta() { + return "UPDATE `bluemap_map_meta` " + + "SET `key` = ? " + + "WHERE `key` = ?"; + } + + @Override + @Language("MySQL") + public String lookupFK(String table, String idField, String valueField) { + return "SELECT `" + idField + "` FROM `" + table + "` " + + "WHERE `" + valueField + "` = ?"; + } + + @Override + @Language("MySQL") + public String insertFK(String table, String valueField) { + return "INSERT INTO `" + table + "` (`" + valueField + "`) " + + "VALUES (?)"; + } + + +} 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 new file mode 100644 index 00000000..6f5a930e --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/PostgresDialect.java @@ -0,0 +1,240 @@ +package de.bluecolored.bluemap.core.storage.sql.dialect; + +import org.intellij.lang.annotations.Language; + +public class PostgresDialect implements SQLQueryDialect { + + public static final PostgresDialect INSTANCE = new PostgresDialect(); + + private PostgresDialect() {}; + + @Override + @Language("PostgreSQL") + public String writeMapTile() { + return "INSERT INTO bluemap_map_tile (map, lod, x, z, compression, data) " + + "VALUES (?, ?, ?, ?, ?, ?) " + + "ON CONFLICT (map, lod, x, z) DO UPDATE SET compression = EXCLUDED.compression, data = EXCLUDED.data"; + } + + @Override + @Language("PostgreSQL") + 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("PostgreSQL") + public String readMapTileInfo() { + return "SELECT t.changed, OCTET_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("PostgreSQL") + public String deleteMapTile() { + return "DELETE FROM bluemap_map_tile t " + + "USING bluemap_map m " + + "WHERE t.map = m.id " + + "AND m.map_id = ? " + + "AND t.lod = ? " + + "AND t.x = ? " + + "AND t.z = ?"; + } + + @Override + @Language("PostgreSQL") + public String writeMeta() { + return "INSERT INTO bluemap_map_meta (map, key, value) " + + "VALUES (?, ?, ?) " + + "ON CONFLICT (map, key) DO UPDATE SET value = EXCLUDED.value"; + } + + @Override + @Language("PostgreSQL") + 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("PostgreSQL") + public String readMetaSize() { + return "SELECT OCTET_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("PostgreSQL") + public String purgeMeta() { + return "DELETE FROM bluemap_map_meta t " + + "USING bluemap_map m " + + "WHERE t.map = m.id " + + "AND m.map_id = ? " + + "AND t.key = ?"; + } + + @Override + @Language("PostgreSQL") + public String purgeMapTile() { + return "DELETE FROM bluemap_map_tile t " + + "USING bluemap_map m " + + "WHERE t.map = m.id " + + "AND m.map_id = ?"; + } + + @Override + @Language("PostgreSQL") + public String purgeMapMeta() { + return "DELETE FROM bluemap_map_meta t " + + "USING bluemap_map m " + + "WHERE t.map = m.id " + + "AND m.map_id = ?"; + } + + @Override + @Language("PostgreSQL") + public String purgeMap() { + return "DELETE FROM bluemap_map " + + "WHERE map_id = ?"; + } + + @Override + @Language("PostgreSQL") + public String selectMapIds() { + return "SELECT map_id FROM bluemap_map"; + } + + @Override + @Language("PostgreSQL") + public String initializeStorageMeta() { + return "CREATE TABLE IF NOT EXISTS bluemap_storage_meta (" + + "key varchar(255) PRIMARY KEY, " + + "value varchar(255)" + + ")"; + } + + @Override + @Language("PostgreSQL") + public String selectStorageMeta() { + return "SELECT value FROM bluemap_storage_meta " + + "WHERE key = ?"; + } + + @Override + @Language("PostgreSQL") + public String insertStorageMeta() { + return "INSERT INTO bluemap_storage_meta (key, value) " + + "VALUES (?, ?) " + + "ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value"; + } + + @Override + @Language("PostgreSQL") + public String initializeMap() { + return "CREATE TABLE IF NOT EXISTS bluemap_map (" + + "id SERIAL PRIMARY KEY, " + + "map_id VARCHAR(255) UNIQUE NOT NULL" + + ")"; + } + + @Override + @Language("PostgreSQL") + public String initializeMapTileCompression() { + return "CREATE TABLE IF NOT EXISTS bluemap_map_tile_compression (" + + "id SERIAL PRIMARY KEY, " + + "compression VARCHAR(255) UNIQUE NOT NULL" + + ")"; + } + + @Override + @Language("PostgreSQL") + public String initializeMapMeta() { + return "CREATE TABLE IF NOT EXISTS bluemap_map_meta (" + + "map SMALLINT REFERENCES bluemap_map(id) ON UPDATE RESTRICT ON DELETE RESTRICT, " + + "key varchar(255) NOT NULL, " + + "value BYTEA NOT NULL, " + + "PRIMARY KEY (map, key)" + + ")"; + } + + @Override + @Language("PostgreSQL") + public String initializeMapTile() { + return "CREATE TABLE IF NOT EXISTS bluemap_map_tile (" + + "map SMALLINT REFERENCES bluemap_map(id) ON UPDATE RESTRICT ON DELETE RESTRICT, " + + "lod SMALLINT NOT NULL, " + + "x INT NOT NULL, " + + "z INT NOT NULL, " + + "compression SMALLINT REFERENCES bluemap_map_tile_compression(id) ON UPDATE RESTRICT ON DELETE RESTRICT, " + + "changed TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, " + + "data BYTEA NOT NULL, " + + "PRIMARY KEY (map, lod, x, z)" + + ")"; + } + + @Override + @Language("PostgreSQL") + public String updateStorageMeta() { + return "UPDATE bluemap_storage_meta " + + "SET value = ? " + + "WHERE key = ?"; + } + + @Override + @Language("PostgreSQL") + public String deleteMapMeta() { + return "DELETE FROM bluemap_map_meta " + + "WHERE key IN (?, ?, ?)"; + } + + @Override + @Language("PostgreSQL") + public String updateMapMeta() { + return "UPDATE bluemap_map_meta " + + "SET key = ? " + + "WHERE key = ?"; + } + + @Override + @Language("PostgreSQL") + public String lookupFK(String table, String idField, String valueField) { + return "SELECT " + idField + " FROM " + table + + " WHERE " + valueField + " = ?"; + } + + @Override + @Language("PostgreSQL") + public String insertFK(String table, String valueField) { + return "INSERT INTO " + table + " (" + valueField + ") " + + "VALUES (?)"; + } + + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/SQLQueryDialect.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/SQLQueryDialect.java new file mode 100644 index 00000000..da0d3a50 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/dialect/SQLQueryDialect.java @@ -0,0 +1,78 @@ +package de.bluecolored.bluemap.core.storage.sql.dialect; + +import org.intellij.lang.annotations.Language; + +public interface SQLQueryDialect { + @Language("sql") + String writeMapTile(); + + @Language("sql") + String readMapTile(); + + @Language("sql") + String readMapTileInfo(); + + @Language("sql") + String deleteMapTile(); + + @Language("sql") + String writeMeta(); + + @Language("sql") + String readMeta(); + + @Language("sql") + String readMetaSize(); + + @Language("sql") + String purgeMeta(); + + @Language("sql") + String purgeMapTile(); + + @Language("sql") + String purgeMapMeta(); + + @Language("sql") + String purgeMap(); + + @Language("sql") + String selectMapIds(); + + @Language("sql") + String initializeStorageMeta(); + + @Language("sql") + String selectStorageMeta(); + + @Language("sql") + String insertStorageMeta(); + + @Language("sql") + String initializeMap(); + + @Language("sql") + String initializeMapTileCompression(); + + @Language("sql") + String initializeMapMeta(); + + @Language("sql") + String initializeMapTile(); + + @Language("sql") + String updateStorageMeta(); // can be use twice in init + + @Language("sql") + String deleteMapMeta(); + + @Language("sql") + String updateMapMeta(); // can be used twice in init + + @Language("sql") + String lookupFK(String table, String idField, String valueField); + + @Language("sql") + String insertFK(String table, String valueField); + +}