diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/DatabaseUpdater.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/DatabaseUpdater.java index 98c14c3aa..9b6b4f526 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/DatabaseUpdater.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/DatabaseUpdater.java @@ -20,19 +20,17 @@ package com.seibel.distanthorizons.core.sql; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.util.ResourceUtil; import org.apache.logging.log4j.Logger; -import java.io.IOException; -import java.net.URISyntaxException; -import java.sql.BatchUpdateException; +import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Map; -import java.util.Stack; +import java.util.Scanner; +/** Handles both initial setup and updating of the sql databases. */ public class DatabaseUpdater { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -41,22 +39,24 @@ public class DatabaseUpdater /** Since java can only run one sql query at a time this string is used to split up our scripts into individual queries. */ public static final String UPDATE_SCRIPT_BATCH_SEPARATOR = "--batch--"; + private static final String SQL_SCRIPT_RESOURCE_FOLDER = "sqlScripts/"; + /** + * Unfortunately dynamically pulling in resource files is very unstable in Java so we need a file that lists all the scripts to expect.
+ * (If anyone has a good way to automatically pull all resource files ending in `.sql` instead, please replace the existing code.) + */ + private static final String SQL_SCRIPT_LIST_FILE = SQL_SCRIPT_RESOURCE_FOLDER+"scriptList.txt"; - /** Handles both initial setup and */ + + //================// + // script running // + //================// + public static void runAutoUpdateScripts(AbstractDhRepo repo) throws SQLException { // get the resource scripts - ArrayList sqlScripts; - try - { - sqlScripts = ResourceUtil.getFilesInFolder("sqlScripts", ".sql"); - } - catch (URISyntaxException | IOException e) - { - // shouldn't normally happen, but just in case - throw new RuntimeException(e); - } + ArrayList scriptList = getAutoUpdateScripts(); + // create the base update table if necessary @@ -75,7 +75,7 @@ public class DatabaseUpdater // attempt to run any new update scripts - for (ResourceUtil.ResourceFile resource : sqlScripts) + for (SqlScript resource : scriptList) { Map scriptAlreadyRunResult = repo.queryDictionaryFirst("SELECT EXISTS(SELECT 1 FROM "+SCHEMA_TABLE_NAME+" WHERE ScriptName='"+resource.name+"') as 'existingCount';"); if (scriptAlreadyRunResult != null && (int) scriptAlreadyRunResult.get("existingCount") == 0) @@ -87,7 +87,7 @@ public class DatabaseUpdater try { // split up each individual statement so Java can handle the script as a whole - String[] fileUpdateSqlArray = resource.content.split(UPDATE_SCRIPT_BATCH_SEPARATOR); + String[] fileUpdateSqlArray = resource.queryString.split(UPDATE_SCRIPT_BATCH_SEPARATOR); Connection connection = repo.getConnection(); try (Statement statement = connection.createStatement()) @@ -137,7 +137,7 @@ public class DatabaseUpdater // updating needs to stop to prevent data corruption LOGGER.error("Unexpected error running database update script ["+resource.name+"] on database ["+repo.databaseLocation+"], stopping database update. Database reading/writing may fail if you continue. \n" + "Error: ["+e.getMessage()+"]. \n" + - "Sql Script:["+resource.content+"]", e); + "Sql Script:["+resource.queryString+"]", e); throw e; } @@ -148,4 +148,73 @@ public class DatabaseUpdater } } + + + //===============// + // file handling // + //===============// + + /** @throws NullPointerException if any of the script files failed to be read. */ + private static ArrayList getAutoUpdateScripts() throws NullPointerException + { + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + + + // get the script list + InputStream scriptListInputStream = loader.getResourceAsStream(SQL_SCRIPT_LIST_FILE); + if (scriptListInputStream == null) + { + throw new NullPointerException("Failed to find the SQL Script list file ["+SQL_SCRIPT_LIST_FILE+"], no auto update scripts can be run."); + } + Scanner scanner = new Scanner(scriptListInputStream).useDelimiter("\\A"); + String result = scanner.hasNext() ? scanner.next() : ""; + + + + // get each script + ArrayList scriptList = new ArrayList<>(); + String[] sqlScriptNames = result.split("\n"); + for (String scriptName : sqlScriptNames) + { + scriptName = scriptName.trim(); + if (scriptName.isEmpty()) + { + // ignore any empty lines + continue; + } + scriptName = SQL_SCRIPT_RESOURCE_FOLDER + scriptName.trim(); + + // get the script's content + InputStream scriptInputStream = loader.getResourceAsStream(scriptName); + if (scriptInputStream == null) + { + throw new NullPointerException("Failed to find the SQL Script file ["+scriptName+"], no auto update scripts can be run."); + } + scanner = new Scanner(scriptInputStream).useDelimiter("\\A"); + result = scanner.hasNext() ? scanner.next() : ""; + + scriptList.add(new SqlScript(scriptName, result)); + } + + return scriptList; + } + + + + //================// + // helper classes // + //================// + + private static class SqlScript + { + public String name; + public String queryString; + + public SqlScript(String name, String queryString) + { + this.name = name; + this.queryString = queryString; + } + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ResourceUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ResourceUtil.java deleted file mode 100644 index 275708dfd..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ResourceUtil.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.util; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLDecoder; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -public class ResourceUtil -{ - /** - * Returns all files in the "resources" folders. getFilesInFolder(String directoryName, String fileExtension) throws URISyntaxException, IOException - { - ArrayList resourceFiles = new ArrayList<>(); - - URL url = Thread.currentThread().getContextClassLoader().getResource(directoryName); - if (url != null) - { - if (url.getProtocol().equals("file")) - { - File rootFile = Paths.get(url.toURI()).toFile(); - File[] files = rootFile.listFiles(); - if (files != null) - { - for (File file : files) - { - if (file.getName().endsWith(fileExtension)) - { - String content = FileUtil.readFile(file, Charset.defaultCharset()); - ResourceFile resourceFile = new ResourceFile(file.getName(), content); - resourceFiles.add(resourceFile); - } - } - } - } - else if (url.getProtocol().equals("jar")) - { - String dirname = directoryName + "/"; - String path = url.getPath(); - String jarPath = path.substring(5, path.indexOf("!")); - try (JarFile jar = new JarFile(URLDecoder.decode(jarPath, StandardCharsets.UTF_8.name()))) - { - Enumeration entries = jar.entries(); - while (entries.hasMoreElements()) - { - JarEntry entry = entries.nextElement(); - String fileName = entry.getName(); - if (fileName.startsWith(dirname) && !dirname.equals(fileName)) - { - if (fileName.endsWith(fileExtension)) - { - URL resource = Thread.currentThread().getContextClassLoader().getResource(fileName); - File file = new File(resource.getFile()); - String content = FileUtil.readFile(file, Charset.defaultCharset()); - - ResourceFile resourceFile = new ResourceFile(file.getName(), content); - resourceFiles.add(resourceFile); - } - } - } - } - } - } - - return resourceFiles; - } - - - - //==============// - // helper class // - //==============// - - public static class ResourceFile - { - public final String name; - public final String content; - - public ResourceFile(String name, String content) - { - this.name = name; - this.content = content; - } - } - -} diff --git a/core/src/main/resources/sqlScripts/0010-sqlite-createFullDataTable.sql b/core/src/main/resources/sqlScripts/0010-sqlite-createInitialDataTables.sql similarity index 100% rename from core/src/main/resources/sqlScripts/0010-sqlite-createFullDataTable.sql rename to core/src/main/resources/sqlScripts/0010-sqlite-createInitialDataTables.sql diff --git a/core/src/main/resources/sqlScripts/readme.md b/core/src/main/resources/sqlScripts/readme.md index 4dea66089..18fdb2456 100644 --- a/core/src/main/resources/sqlScripts/readme.md +++ b/core/src/main/resources/sqlScripts/readme.md @@ -6,6 +6,12 @@ https://dbup.readthedocs.io/en/latest/philosophy-behind-dbup/
+### Adding New Scripts: +New scripts must be added to the "scriptList.txt" file, otherwise they will not be run.
+(If anyone has a good way to automatically pull all resource files ending in `.sql` instead, please let us know.) + +
+ ### File Naming: - The first 3 numbers are major scripts. - The 4th number is for minor/related scripts or if a bug fix needs to be applied between scripts. @@ -31,3 +37,6 @@ CREATE TABLE TableTwo( ,Data BLOB NULL ); ``` + + + diff --git a/core/src/main/resources/sqlScripts/scriptList.txt b/core/src/main/resources/sqlScripts/scriptList.txt new file mode 100644 index 000000000..47c749378 --- /dev/null +++ b/core/src/main/resources/sqlScripts/scriptList.txt @@ -0,0 +1,2 @@ + +0010-sqlite-createInitialDataTables.sql