-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
467 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
language: java | ||
jdk: | ||
- openjdk8 | ||
- openjdk10 | ||
- openjdk11 | ||
- openjdk12 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
name: DatabaseLib | ||
author: Exlll | ||
|
||
version: 3.1.0 | ||
version: 3.2.0 | ||
main: de.exlll.databaselib.DatabaseLib | ||
|
||
depend: ['ConfigLib'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
name: DatabaseLib | ||
author: Exlll | ||
|
||
version: 3.1.0 | ||
version: 3.2.0 | ||
main: de.exlll.databaselib.DatabaseLib | ||
|
||
depends: ['ConfigLib'] |
67 changes: 67 additions & 0 deletions
67
DatabaseLib-Core/src/main/java/de/exlll/databaselib/sql/util/QueryReader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package de.exlll.databaselib.sql.util; | ||
|
||
import java.io.IOException; | ||
import java.io.Reader; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
final class QueryReader { | ||
private final Reader reader; | ||
private final char delimiter; | ||
|
||
QueryReader(Reader reader, char delimiter) { | ||
this.reader = reader; | ||
this.delimiter = delimiter; | ||
} | ||
|
||
public List<String> readQueries() throws IOException { | ||
List<String> queries = new ArrayList<>(); | ||
|
||
String query; | ||
while (!(query = readQuery()).isEmpty()) { | ||
queries.add(query.trim()); | ||
} | ||
|
||
return queries; | ||
} | ||
|
||
private String readQuery() throws IOException { | ||
StringBuilder builder = new StringBuilder(); | ||
|
||
int value; | ||
while ((value = reader.read()) != -1) { | ||
char c = (char) value; | ||
|
||
if (c == '"') { | ||
readToClosingQuote(builder, '"'); | ||
} else if (c == '\'') { | ||
readToClosingQuote(builder, '\''); | ||
} else if (c == delimiter) { | ||
return builder.toString(); | ||
} else { | ||
builder.append((c == '\n') ? ' ' : c); | ||
} | ||
} | ||
|
||
return builder.toString(); | ||
} | ||
|
||
private void readToClosingQuote(StringBuilder builder, char quoteChar) | ||
throws IOException { | ||
builder.append(quoteChar); | ||
|
||
int value; | ||
boolean escaped = false; | ||
while ((value = reader.read()) != -1) { | ||
char c = (char) value; | ||
|
||
if (c == quoteChar && !escaped) { | ||
builder.append(quoteChar); | ||
return; | ||
} | ||
|
||
escaped = (c == '\\' && !escaped); | ||
builder.append(c); | ||
} | ||
} | ||
} |
111 changes: 111 additions & 0 deletions
111
DatabaseLib-Core/src/main/java/de/exlll/databaselib/sql/util/ScriptRunner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
package de.exlll.databaselib.sql.util; | ||
|
||
import java.io.IOException; | ||
import java.io.Reader; | ||
import java.sql.Connection; | ||
import java.sql.SQLException; | ||
import java.sql.Statement; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.logging.Logger; | ||
|
||
/** | ||
* A {@code ScriptRunner} is a utility class to execute SQL scripts. | ||
* <p> | ||
* An SQL script is a file that contains SQL queries delimited by ';' (semicolon). | ||
* You can create a {@code ScriptRunner} by passing an instance of a {@link Reader} | ||
* and a {@link Connection} to its constructor. A {@code ScriptRunner} has the ability | ||
* to replace any part of a query prior to executing it. | ||
*/ | ||
public final class ScriptRunner { | ||
private static final Logger queryLogger = Logger.getLogger( | ||
ScriptRunner.class.getName() | ||
); | ||
private final QueryReader queryReader; | ||
private final Connection connection; | ||
private Map<String, ?> replacements = Collections.emptyMap(); | ||
private boolean logQueries; | ||
|
||
/** | ||
* Creates a new {@code ScriptRunner} that executes queries read from the | ||
* given {@code Reader} using the given {@code Connection}. | ||
* | ||
* @param reader {@code Reader} queries are read from | ||
* @param connection {@code Connection} used to execute queries | ||
* @throws NullPointerException if any argument is null | ||
*/ | ||
public ScriptRunner(Reader reader, Connection connection) { | ||
this.queryReader = new QueryReader( | ||
Objects.requireNonNull(reader), ';' | ||
); | ||
this.connection = Objects.requireNonNull(connection); | ||
} | ||
|
||
/** | ||
* Executes all queries from the given {@code Reader}. | ||
* | ||
* @throws IOException if an I/O error occurs while reading from the {@code Reader} | ||
* @throws SQLException if a database access error occurred or | ||
* if at least on of the queries failed to execute | ||
*/ | ||
public void runScript() throws IOException, SQLException { | ||
List<String> queries = queryReader.readQueries(); | ||
try (Statement stmt = connection.createStatement()) { | ||
for (String query : queries) { | ||
executeQuery(stmt, query); | ||
} | ||
} | ||
} | ||
|
||
private void executeQuery(Statement stmt, String query) throws SQLException { | ||
query = preProcess(query); | ||
if (logQueries) { | ||
queryLogger.info(query); | ||
} | ||
stmt.execute(query); | ||
} | ||
|
||
private String preProcess(String query) { | ||
for (Map.Entry<String, ?> entry : replacements.entrySet()) { | ||
String key = entry.getKey(); | ||
String replacement = (entry.getValue() == null) | ||
? "null" | ||
: entry.getValue().toString(); | ||
query = query.replace(key, replacement); | ||
} | ||
return query; | ||
} | ||
|
||
/** | ||
* Sets the query replacements. Defaults to an empty map. | ||
* <p> | ||
* Before a query is executed, all parts of the query that | ||
* match a key of the map are replaced with the value to | ||
* which the key is mapped. | ||
* | ||
* @param replacements the query replacements | ||
* @return this {@code ScriptRunner} | ||
* @throws NullPointerException if {@code replacements} or any of its keys is null | ||
*/ | ||
public ScriptRunner setReplacements(Map<String, ?> replacements) { | ||
for (String key : replacements.keySet()) { | ||
Objects.requireNonNull(key, "Map must not contain null keys."); | ||
} | ||
this.replacements = replacements; | ||
return this; | ||
} | ||
|
||
/** | ||
* Enables or disables query logging. Default value is false. | ||
* Each query is logged right before it is executed. | ||
* | ||
* @param logQueries true if queries should be logged, false otherwise | ||
* @return this {@code ScriptRunner} | ||
*/ | ||
public ScriptRunner setLogQueries(boolean logQueries) { | ||
this.logQueries = logQueries; | ||
return this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
DatabaseLib-Core/src/test/java/de/exlll/databaselib/sql/util/QueryReaderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package de.exlll.databaselib.sql.util; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import java.io.IOException; | ||
import java.io.StringReader; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
import static org.hamcrest.Matchers.is; | ||
import static org.junit.Assert.assertThat; | ||
|
||
class QueryReaderTest { | ||
public static final String SQL_INPUT = "CREATE TABLE IF NOT EXISTS `test`\n" + | ||
"(\n" + | ||
" `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,\n" + | ||
" `name` VARCHAR(32)\n" + | ||
") DEFAULT CHARACTER SET utf8;\n" + | ||
"\n" + | ||
"SELECT * FROM `test` WHERE `id` = 10;\n" + | ||
"SELECT * FROM `test` WHERE `name` = \";\";\n" + | ||
"SELECT * FROM `test` WHERE `name` = \"\\\"\";\n" + | ||
"SELECT * FROM `test` WHERE `name` = \"\\\\\";\n" + | ||
"SELECT * FROM `test` WHERE `name` = \"\\\\\\\"\";\n" + | ||
"SELECT * FROM `test` WHERE `name` = ';';\n" + | ||
"SELECT * FROM `test` WHERE `name` = '\\'';\n" + | ||
"SELECT * FROM `test` WHERE `name` = '\\\\';\n" + | ||
"SELECT * FROM `test` WHERE `name` = '\\\\\\'';"; | ||
private static final String SQL_INPUT_PIPE_DELIM = "CREATE TABLE IF NOT EXISTS `test`\n" + | ||
"(\n" + | ||
" `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,\n" + | ||
" `name` VARCHAR(32)\n" + | ||
") DEFAULT CHARACTER SET utf8|\n" + | ||
"\n" + | ||
"SELECT * FROM `test` WHERE `id` = 10|\n" + | ||
"SELECT * FROM `test` WHERE `name` = \"|\"|\n" + | ||
"SELECT * FROM `test` WHERE `name` = \"\\\"\"|\n" + | ||
"SELECT * FROM `test` WHERE `name` = \"\\\\\"|\n" + | ||
"SELECT * FROM `test` WHERE `name` = \"\\\\\\\"\"|\n" + | ||
"SELECT * FROM `test` WHERE `name` = '|'|\n" + | ||
"SELECT * FROM `test` WHERE `name` = '\\''|\n" + | ||
"SELECT * FROM `test` WHERE `name` = '\\\\'|\n" + | ||
"SELECT * FROM `test` WHERE `name` = '\\\\\\''|"; | ||
public static final List<String> QUERIES = Arrays.asList( | ||
"CREATE TABLE IF NOT EXISTS `test` ( " + | ||
" `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, " + | ||
" `name` VARCHAR(32) ) " + | ||
"DEFAULT CHARACTER SET utf8", | ||
"SELECT * FROM `test` WHERE `id` = 10", | ||
"SELECT * FROM `test` WHERE `name` = \";\"", | ||
"SELECT * FROM `test` WHERE `name` = \"\\\"\"", | ||
"SELECT * FROM `test` WHERE `name` = \"\\\\\"", | ||
"SELECT * FROM `test` WHERE `name` = \"\\\\\\\"\"", | ||
"SELECT * FROM `test` WHERE `name` = ';'", | ||
"SELECT * FROM `test` WHERE `name` = '\\''", | ||
"SELECT * FROM `test` WHERE `name` = '\\\\'", | ||
"SELECT * FROM `test` WHERE `name` = '\\\\\\''" | ||
); | ||
|
||
|
||
@Test | ||
void readQueriesReadsAllQueries() throws IOException { | ||
QueryReader reader = new QueryReader( | ||
new StringReader(SQL_INPUT), ';' | ||
); | ||
assertThat(reader.readQueries(), is(QUERIES)); | ||
} | ||
|
||
@Test | ||
void readQueriesUsesDelimiter() throws IOException { | ||
QueryReader reader = new QueryReader( | ||
new StringReader(SQL_INPUT_PIPE_DELIM), '|' | ||
); | ||
List<String> queries = new ArrayList<>(QueryReaderTest.QUERIES); | ||
queries.replaceAll(s -> s.replace(';', '|')); | ||
assertThat(reader.readQueries(), is(queries)); | ||
} | ||
} |
Oops, something went wrong.