Fix DatabaseUpdater on Forge
This commit is contained in:
@@ -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. <br>
|
||||
* (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 <TDTO extends IBaseDTO> void runAutoUpdateScripts(AbstractDhRepo<TDTO> repo) throws SQLException
|
||||
{
|
||||
// get the resource scripts
|
||||
ArrayList<ResourceUtil.ResourceFile> sqlScripts;
|
||||
try
|
||||
{
|
||||
sqlScripts = ResourceUtil.getFilesInFolder("sqlScripts", ".sql");
|
||||
}
|
||||
catch (URISyntaxException | IOException e)
|
||||
{
|
||||
// shouldn't normally happen, but just in case
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
ArrayList<SqlScript> 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<String, Object> 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<SqlScript> 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<SqlScript> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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. <br.
|
||||
* Source: https://stackoverflow.com/a/48190582
|
||||
*/
|
||||
public static ArrayList<ResourceFile> getFilesInFolder(String directoryName, String fileExtension) throws URISyntaxException, IOException
|
||||
{
|
||||
ArrayList<ResourceFile> 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<JarEntry> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,12 @@ https://dbup.readthedocs.io/en/latest/philosophy-behind-dbup/
|
||||
|
||||
<br>
|
||||
|
||||
### Adding New Scripts:
|
||||
New scripts must be added to the "scriptList.txt" file, otherwise they will not be run. <br>
|
||||
(If anyone has a good way to automatically pull all resource files ending in `.sql` instead, please let us know.)
|
||||
|
||||
<br>
|
||||
|
||||
### 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
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
0010-sqlite-createInitialDataTables.sql
|
||||
Reference in New Issue
Block a user