Skip to content

Commit fcfff1a

Browse files
authored
Improve splitting of SQL scripts into statements (#1627)
Fixes #1547
1 parent 64dcf7a commit fcfff1a

File tree

5 files changed

+337
-103
lines changed

5 files changed

+337
-103
lines changed

modules/database-commons/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ description = "Testcontainers :: Database-Commons"
22

33
dependencies {
44
compile project(':testcontainers')
5+
6+
testCompile 'org.assertj:assertj-core:3.12.2'
57
}

modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,21 +163,18 @@ public static void splitSqlScript(String resource, String script, String separat
163163
}
164164
final boolean inComment = inLineComment || inBlockComment;
165165

166-
if (!inLiteral && !inComment && containsSubstringAtOffset(lowerCaseScriptContent, "BEGIN", i)) {
166+
if (!inLiteral && !inComment && containsKeywordsAtOffset(lowerCaseScriptContent, "BEGIN", i, separator, commentPrefix, blockCommentStartDelimiter)) {
167167
compoundStatementDepth++;
168168
}
169-
if (!inLiteral && !inComment && containsSubstringAtOffset(lowerCaseScriptContent, "END", i)) {
169+
if (!inLiteral && !inComment && containsKeywordsAtOffset(lowerCaseScriptContent, "END", i, separator, commentPrefix, blockCommentStartDelimiter)) {
170170
compoundStatementDepth--;
171171
}
172172
final boolean inCompoundStatement = compoundStatementDepth != 0;
173173

174174
if (!inLiteral && !inCompoundStatement) {
175175
if (script.startsWith(separator, i)) {
176176
// we've reached the end of the current statement
177-
if (sb.length() > 0) {
178-
statements.add(sb.toString());
179-
sb = new StringBuilder();
180-
}
177+
sb = flushStringBuilder(sb, statements);
181178
i += separator.length() - 1;
182179
continue;
183180
}
@@ -199,6 +196,7 @@ else if (script.startsWith(blockCommentStartDelimiter, i)) {
199196
int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i);
200197
if (indexOfCommentEnd > i) {
201198
i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1;
199+
inBlockComment = false;
202200
continue;
203201
}
204202
else {
@@ -218,24 +216,57 @@ else if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
218216
}
219217
sb.append(c);
220218
}
221-
if (StringUtils.isNotEmpty(sb.toString())) {
222-
statements.add(sb.toString());
219+
flushStringBuilder(sb, statements);
220+
}
221+
222+
private static StringBuilder flushStringBuilder(StringBuilder sb, List<String> statements) {
223+
if (sb.length() == 0) {
224+
return sb;
223225
}
226+
227+
final String s = sb.toString().trim();
228+
if (StringUtils.isNotEmpty(s)) {
229+
statements.add(s);
230+
}
231+
232+
return new StringBuilder();
224233
}
225234

235+
private static boolean isSeperator(char c, String separator, String commentPrefix,
236+
String blockCommentStartDelimiter) {
237+
return c == ' ' || c == '\r' || c == '\n' || c == '\t' ||
238+
c == separator.charAt(0) || c == separator.charAt(separator.length() - 1) ||
239+
c == commentPrefix.charAt(0) || c == blockCommentStartDelimiter.charAt(0) ||
240+
c == blockCommentStartDelimiter.charAt(blockCommentStartDelimiter.length() - 1);
241+
}
242+
226243
private static boolean containsSubstringAtOffset(String lowercaseString, String substring, int offset) {
227244
String lowercaseSubstring = substring.toLowerCase();
228245

229246
return lowercaseString.startsWith(lowercaseSubstring, offset);
230247
}
231248

249+
private static boolean containsKeywordsAtOffset(String lowercaseString, String keywords, int offset,
250+
String separator, String commentPrefix,
251+
String blockCommentStartDelimiter) {
252+
String lowercaseKeywords = keywords.toLowerCase();
253+
254+
boolean backSeperated = (offset == 0) || isSeperator(lowercaseString.charAt(offset - 1),
255+
separator, commentPrefix, blockCommentStartDelimiter);
256+
boolean frontSeperated = (offset >= (lowercaseString.length() - keywords.length())) ||
257+
isSeperator(lowercaseString.charAt(offset + keywords.length()),
258+
separator, commentPrefix, blockCommentStartDelimiter);
259+
260+
return backSeperated && frontSeperated && lowercaseString.startsWith(lowercaseKeywords, offset);
261+
}
262+
232263
private static void checkArgument(boolean expression, String errorMessage) {
233264
if (!expression) {
234265
throw new IllegalArgumentException(errorMessage);
235266
}
236267
}
237268

238-
/**
269+
/**
239270
* Does the provided SQL script contain the specified delimiter?
240271
* @param script the SQL script
241272
* @param delim String delimiting each statement - typically a ';' character
@@ -356,7 +387,7 @@ public ScriptLoadException(String message, Throwable cause) {
356387
}
357388
}
358389

359-
private static class ScriptParseException extends RuntimeException {
390+
public static class ScriptParseException extends RuntimeException {
360391
public ScriptParseException(String format, String scriptPath) {
361392
super(String.format(format, scriptPath));
362393
}

0 commit comments

Comments
 (0)