diff --git a/liquibase-core/src/main/java/liquibase/util/StringUtil.java b/liquibase-core/src/main/java/liquibase/util/StringUtil.java index bce27c06d77..ed7f1bfd2be 100644 --- a/liquibase-core/src/main/java/liquibase/util/StringUtil.java +++ b/liquibase-core/src/main/java/liquibase/util/StringUtil.java @@ -69,8 +69,25 @@ public static String[] processMultiLineSQL(String multiLineSQL, boolean stripCom String previousPiece = null; boolean previousDelimiter = false; List parsedArray = Arrays.asList(parsed.toArray(true)); - for (Object piece : mergeTokens(parsedArray, endDelimiter)) { - if (splitStatements && (piece instanceof String) && isDelimiter((String) piece, previousPiece, endDelimiter)) { + int isInClause = 0; + List tokens = mergeTokens(parsedArray, endDelimiter); + for (int i = 0; i < tokens.size(); i++) { + Object piece = tokens.get(i); + String nextPiece = null; + int nextIndex = i + 1; + while (nextPiece == null && nextIndex < tokens.size()) { + nextPiece = StringUtil.trimToNull(String.valueOf(tokens.get(nextIndex))); + nextIndex++; + } + + if (piece instanceof String && ((String) piece).equalsIgnoreCase("BEGIN") && (!"transaction".equalsIgnoreCase(nextPiece) && !"trans".equalsIgnoreCase(nextPiece))) { + isInClause++; + } + if (piece instanceof String && ((String) piece).equalsIgnoreCase("END") && isInClause > 0 && (!"transaction".equalsIgnoreCase(nextPiece) && !"trans".equalsIgnoreCase(nextPiece))) { + isInClause--; + } + + if (isInClause == 0 && splitStatements && (piece instanceof String) && isDelimiter((String) piece, previousPiece, endDelimiter)) { String trimmedString = StringUtil.trimToNull(currentString.toString()); if (trimmedString != null) { returnArray.add(trimmedString); @@ -920,7 +937,7 @@ public static String stripSqlCommentsAndWhitespacesFromTheEnd(String sqlString) return trimRight(str.toString()); } - + /** * Concatenates the addition string to the baseString string, adjusting the case of "addition" to match the base string. * If the string is all caps, append addition in all caps. If all lower case, append in all lower case. If baseString is mixed case, make no changes to addition. @@ -959,7 +976,8 @@ public static boolean equalsWordNull(String value) { *

Splits a camel-case string into words based on the came casing. *

* This code originated from the StringUtils class of https://github.com/apache/commons-lang - * @param str the String to split, may be {@code null} + * + * @param str the String to split, may be {@code null} * @return an array of parsed Strings, {@code null} if null String input */ public static String[] splitCamelCase(final String str) { @@ -1027,7 +1045,7 @@ public static boolean isNumeric(CharSequence cs) { } else { int sz = cs.length(); - for(int i = 0; i < sz; ++i) { + for (int i = 0; i < sz; ++i) { if (!Character.isDigit(cs.charAt(i))) { return false; } diff --git a/liquibase-core/src/test/groovy/liquibase/util/StringUtilTest.groovy b/liquibase-core/src/test/groovy/liquibase/util/StringUtilTest.groovy index d05db806738..22516717f35 100644 --- a/liquibase-core/src/test/groovy/liquibase/util/StringUtilTest.groovy +++ b/liquibase-core/src/test/groovy/liquibase/util/StringUtilTest.groovy @@ -15,7 +15,7 @@ class StringUtilTest extends Specification { that Arrays.asList(StringUtil.processMultiLineSQL(rawString, stripComments, splitStatements, endDelimiter)), Matchers.contains(expected.toArray()) where: - stripComments | splitStatements | endDelimiter | rawString | expected + stripComments | splitStatements | endDelimiter | rawString | expected true | true | null | "/**\nSome comments go here\n**/\ncreate table sqlfilerollback (id int);\n\n/**\nSome morecomments go here\n**/\ncreate table sqlfilerollback2 (id int);" | ["create table sqlfilerollback (id int)", "create table sqlfilerollback2 (id int)"] true | true | null | "/*\nThis is a test comment of MS-SQL script\n*/\n\nSelect * from Test;\nUpdate Test set field = 1" | ["Select * from Test", "Update Test set field = 1"] true | true | null | "some sql/*Some text\nmore text*/more sql" | ["some sqlmore sql"] @@ -32,6 +32,9 @@ class StringUtilTest extends Specification { true | true | null | "statement 1;\nstatement 2;\nGO\n\nstatement 3; statement 4;" | ["statement 1", "statement 2", "statement 3", "statement 4"] true | true | "\\nGO" | "statement 1 \nGO\nstatement 2" | ["statement 1", "statement 2"] true | true | "\\nGO" | "CREATE OR REPLACE PACKAGE emp_actions AS -- spec\nTYPE EmpRecTyp IS RECORD (emp_id INT, salary REAL);\nCURSOR desc_salary RETURN EmpRecTyp);\nEND emp_actions;\nGO\nanother statement;here\nGO\n" | ["CREATE OR REPLACE PACKAGE emp_actions AS \nTYPE EmpRecTyp IS RECORD (emp_id INT, salary REAL);\nCURSOR desc_salary RETURN EmpRecTyp);\nEND emp_actions;", "another statement;here"] + true | true | null | "CREATE OR REPLACE PACKAGE emp_actions AS BEGIN\n statement 1;\nanother statement;here; END;" | ["CREATE OR REPLACE PACKAGE emp_actions AS BEGIN\n statement 1;\nanother statement;here; END"] + true | true | null | "CREATE OR REPLACE PACKAGE emp_actions AS BEGIN\n statement 1;\nBEGIN a nested statement;here; END; END;" | ["CREATE OR REPLACE PACKAGE emp_actions AS BEGIN\n statement 1;\nBEGIN a nested statement;here; END; END"] + true | true | null | "BEGIN TRANSACTION; statement 1; end transaction;" | ["BEGIN TRANSACTION", "statement 1", "end transaction"] }