Skip to content

Commit

Permalink
Improve splitting of SQL scripts into statements (#1627)
Browse files Browse the repository at this point in the history
Fixes #1547
  • Loading branch information
rnorth committed Jul 23, 2019
1 parent 64dcf7a commit fcfff1a
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 103 deletions.
2 changes: 2 additions & 0 deletions modules/database-commons/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ description = "Testcontainers :: Database-Commons"

dependencies {
compile project(':testcontainers')

testCompile 'org.assertj:assertj-core:3.12.2'
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,21 +163,18 @@ public static void splitSqlScript(String resource, String script, String separat
}
final boolean inComment = inLineComment || inBlockComment;

if (!inLiteral && !inComment && containsSubstringAtOffset(lowerCaseScriptContent, "BEGIN", i)) {
if (!inLiteral && !inComment && containsKeywordsAtOffset(lowerCaseScriptContent, "BEGIN", i, separator, commentPrefix, blockCommentStartDelimiter)) {
compoundStatementDepth++;
}
if (!inLiteral && !inComment && containsSubstringAtOffset(lowerCaseScriptContent, "END", i)) {
if (!inLiteral && !inComment && containsKeywordsAtOffset(lowerCaseScriptContent, "END", i, separator, commentPrefix, blockCommentStartDelimiter)) {
compoundStatementDepth--;
}
final boolean inCompoundStatement = compoundStatementDepth != 0;

if (!inLiteral && !inCompoundStatement) {
if (script.startsWith(separator, i)) {
// we've reached the end of the current statement
if (sb.length() > 0) {
statements.add(sb.toString());
sb = new StringBuilder();
}
sb = flushStringBuilder(sb, statements);
i += separator.length() - 1;
continue;
}
Expand All @@ -199,6 +196,7 @@ else if (script.startsWith(blockCommentStartDelimiter, i)) {
int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i);
if (indexOfCommentEnd > i) {
i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1;
inBlockComment = false;
continue;
}
else {
Expand All @@ -218,24 +216,57 @@ else if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
}
sb.append(c);
}
if (StringUtils.isNotEmpty(sb.toString())) {
statements.add(sb.toString());
flushStringBuilder(sb, statements);
}

private static StringBuilder flushStringBuilder(StringBuilder sb, List<String> statements) {
if (sb.length() == 0) {
return sb;
}

final String s = sb.toString().trim();
if (StringUtils.isNotEmpty(s)) {
statements.add(s);
}

return new StringBuilder();
}

private static boolean isSeperator(char c, String separator, String commentPrefix,
String blockCommentStartDelimiter) {
return c == ' ' || c == '\r' || c == '\n' || c == '\t' ||
c == separator.charAt(0) || c == separator.charAt(separator.length() - 1) ||
c == commentPrefix.charAt(0) || c == blockCommentStartDelimiter.charAt(0) ||
c == blockCommentStartDelimiter.charAt(blockCommentStartDelimiter.length() - 1);
}

private static boolean containsSubstringAtOffset(String lowercaseString, String substring, int offset) {
String lowercaseSubstring = substring.toLowerCase();

return lowercaseString.startsWith(lowercaseSubstring, offset);
}

private static boolean containsKeywordsAtOffset(String lowercaseString, String keywords, int offset,
String separator, String commentPrefix,
String blockCommentStartDelimiter) {
String lowercaseKeywords = keywords.toLowerCase();

boolean backSeperated = (offset == 0) || isSeperator(lowercaseString.charAt(offset - 1),
separator, commentPrefix, blockCommentStartDelimiter);
boolean frontSeperated = (offset >= (lowercaseString.length() - keywords.length())) ||
isSeperator(lowercaseString.charAt(offset + keywords.length()),
separator, commentPrefix, blockCommentStartDelimiter);

return backSeperated && frontSeperated && lowercaseString.startsWith(lowercaseKeywords, offset);
}

private static void checkArgument(boolean expression, String errorMessage) {
if (!expression) {
throw new IllegalArgumentException(errorMessage);
}
}

/**
/**
* Does the provided SQL script contain the specified delimiter?
* @param script the SQL script
* @param delim String delimiting each statement - typically a ';' character
Expand Down Expand Up @@ -356,7 +387,7 @@ public ScriptLoadException(String message, Throwable cause) {
}
}

private static class ScriptParseException extends RuntimeException {
public static class ScriptParseException extends RuntimeException {
public ScriptParseException(String format, String scriptPath) {
super(String.format(format, scriptPath));
}
Expand Down

0 comments on commit fcfff1a

Please sign in to comment.