Upgrade h2 database engine to v2 (#3458)

This commit is contained in:
Luck 2022-10-16 14:28:59 +01:00
parent 495d951556
commit e35f46798b
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
4 changed files with 96 additions and 5 deletions

View File

@ -160,7 +160,7 @@ public enum Dependency {
"IydH+gkk2Iom36QrgSi2+hFAgC2AQSWJFZboyl8pEyI=", "IydH+gkk2Iom36QrgSi2+hFAgC2AQSWJFZboyl8pEyI=",
Relocation.of("postgresql", "org{}postgresql") Relocation.of("postgresql", "org{}postgresql")
), ),
H2_DRIVER( H2_DRIVER_LEGACY(
"com.h2database", "com.h2database",
"h2", "h2",
// seems to be a compat bug in 1.4.200 with older dbs // seems to be a compat bug in 1.4.200 with older dbs
@ -170,6 +170,14 @@ public enum Dependency {
// we don't apply relocations to h2 - it gets loaded via // we don't apply relocations to h2 - it gets loaded via
// an isolated classloader // an isolated classloader
), ),
H2_DRIVER(
"com.h2database",
"h2",
"2.1.214",
"1iPNwPYdIYz1SajQnxw5H/kQlhFrIuJHVHX85PvnK9A="
// we don't apply relocations to h2 - it gets loaded via
// an isolated classloader
),
SQLITE_DRIVER( SQLITE_DRIVER(
"org.xerial", "org.xerial",
"sqlite-jdbc", "sqlite-jdbc",

View File

@ -128,6 +128,7 @@ public class DependencyRegistry {
case ASM_COMMONS: case ASM_COMMONS:
case JAR_RELOCATOR: case JAR_RELOCATOR:
case H2_DRIVER: case H2_DRIVER:
case H2_DRIVER_LEGACY:
case SQLITE_DRIVER: case SQLITE_DRIVER:
return false; return false;
default: default:

View File

@ -114,7 +114,7 @@ public class StorageFactory {
case H2: case H2:
return new SqlStorage( return new SqlStorage(
this.plugin, this.plugin,
new H2ConnectionFactory(this.plugin.getBootstrap().getDataDirectory().resolve("luckperms-h2")), new H2ConnectionFactory(this.plugin.getBootstrap().getDataDirectory().resolve("luckperms-h2-v2")),
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX) this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
); );
case POSTGRESQL: case POSTGRESQL:

View File

@ -30,9 +30,12 @@ import me.lucko.luckperms.common.dependencies.classloader.IsolatedClassLoader;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Properties; import java.util.Properties;
import java.util.function.Function; import java.util.function.Function;
@ -56,16 +59,22 @@ public class H2ConnectionFactory extends FlatfileConnectionFactory {
IsolatedClassLoader classLoader = plugin.getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.H2_DRIVER)); IsolatedClassLoader classLoader = plugin.getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.H2_DRIVER));
try { try {
Class<?> connectionClass = classLoader.loadClass("org.h2.jdbc.JdbcConnection"); Class<?> connectionClass = classLoader.loadClass("org.h2.jdbc.JdbcConnection");
this.connectionConstructor = connectionClass.getConstructor(String.class, Properties.class); this.connectionConstructor = connectionClass.getConstructor(String.class, Properties.class, String.class, Object.class, boolean.class);
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
try {
new MigrateH2ToVersion2(plugin, super.getWriteFile().getParent()).run(this);
} catch (Exception e) {
plugin.getLogger().warn("Something went wrong whilst upgrading the LuckPerms database. Please report this on GitHub.", e);
}
} }
@Override @Override
protected Connection createConnection(Path file) throws SQLException { protected Connection createConnection(Path file) throws SQLException {
try { try {
return (Connection) this.connectionConstructor.newInstance("jdbc:h2:" + file.toString(), new Properties()); return (Connection) this.connectionConstructor.newInstance("jdbc:h2:" + file.toString(), new Properties(), null, null, false);
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
if (e.getCause() instanceof SQLException) { if (e.getCause() instanceof SQLException) {
throw (SQLException) e.getCause(); throw (SQLException) e.getCause();
@ -83,6 +92,79 @@ public class H2ConnectionFactory extends FlatfileConnectionFactory {
@Override @Override
public Function<String, String> getStatementProcessor() { public Function<String, String> getStatementProcessor() {
return s -> s.replace('\'', '`').replace("LIKE", "ILIKE"); return s -> s.replace('\'', '`')
.replace("LIKE", "ILIKE")
.replace("value", "`value`")
.replace("``value``", "`value`");
}
/**
* Migrates the old (version 1) H2 database to version 2.
*
* See <a href="http://www.h2database.com/html/migration-to-v2.html">here</a> for more info.
*/
private static final class MigrateH2ToVersion2 {
private final LuckPermsPlugin plugin;
private final Path directory;
MigrateH2ToVersion2(LuckPermsPlugin plugin, Path directory) {
this.plugin = plugin;
this.directory = directory;
}
public void run(H2ConnectionFactory newFactory) throws Exception {
Path oldDatabase = this.directory.resolve("luckperms-h2");
Path oldDatabaseWriteFile = this.directory.resolve("luckperms-h2.mv.db");
if (!Files.exists(oldDatabaseWriteFile)) {
return;
}
Path tempMigrationFile = this.directory.resolve("luckperms-h2-migration.sql");
this.plugin.getLogger().warn("[DB Upgrade] Found an old (v1) H2 database file. LuckPerms will now attempt to upgrade it to v2 (this is a one time operation).");
this.plugin.getLogger().info("[DB Upgrade] Stage 1: Exporting the old database to an intermediary file...");
Constructor<?> constructor = getConnectionConstructor();
try (Connection c = getConnection(constructor, oldDatabase)) {
try (Statement stmt = c.createStatement()) {
stmt.execute(String.format("SCRIPT TO '%s'", tempMigrationFile));
} }
} }
this.plugin.getLogger().info("[DB Upgrade] Stage 2: Importing the intermediary file into the new database...");
try (Connection c = newFactory.getConnection()) {
try (Statement stmt = c.createStatement()) {
stmt.execute(String.format("RUNSCRIPT FROM '%s'", tempMigrationFile));
}
}
this.plugin.getLogger().info("[DB Upgrade] Stage 3: Tidying up...");
Files.deleteIfExists(tempMigrationFile);
Files.move(oldDatabaseWriteFile, this.directory.resolve("luckperms-h2-v1-backup.mv.db"));
this.plugin.getLogger().info("[DB Upgrade] All done!");
}
private Constructor<?> getConnectionConstructor() {
this.plugin.getDependencyManager().loadDependencies(Collections.singleton(Dependency.H2_DRIVER_LEGACY));
IsolatedClassLoader classLoader = this.plugin.getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.H2_DRIVER_LEGACY));
try {
Class<?> connectionClass = classLoader.loadClass("org.h2.jdbc.JdbcConnection");
return connectionClass.getConstructor(String.class, Properties.class);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
private Connection getConnection(Constructor<?> constructor, Path file) {
try {
return (Connection) constructor.newInstance("jdbc:h2:" + file.toString(), new Properties());
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
}
}