From d0f687f028eef881cf034fa8741a96a5239ef193 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 5 Dec 2012 16:58:26 +0100 Subject: [PATCH] Support comments in statements in JdbcTestUtils Prior to this commit, executing an SQL script with JdbcTestUtils would fail if a statement in the script contained a line comment within the statement. This commit ensures that standard SQL comments (i.e., any text beginning with two hyphens and extending to the end of the line) are properly omitted from the statement before executing it. In addition, multiple adjacent whitespace characters within a statement but outside a literal are now collapsed into a single space. Issue: SPR-9982 --- .../test/jdbc/JdbcTestUtils.java | 76 ++++++++++++------- .../test/jdbc/JdbcTestUtilsTests.java | 10 ++- .../test/jdbc/test-data-with-comments.sql | 12 ++- 3 files changed, 65 insertions(+), 33 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java b/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java index 87d33221bf7..8c2370f7a7e 100644 --- a/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java +++ b/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java @@ -120,7 +120,7 @@ public class JdbcTestUtils { /** * Execute the given SQL script. *

The script will typically be loaded from the classpath. There should - * be one statement per line. Any semicolons will be removed. + * be one statement per line. Any semicolons and line comments will be removed. *

Do not use this method to execute DDL if you expect rollback. * @param jdbcTemplate the JdbcTemplate with which to perform JDBC operations * @param resourceLoader the resource loader with which to load the SQL script @@ -130,6 +130,7 @@ public class JdbcTestUtils { * @throws DataAccessException if there is an error executing a statement * and {@code continueOnError} is {@code false} * @see ResourceDatabasePopulator + * @see #executeSqlScript(JdbcTemplate, Resource, boolean) */ public static void executeSqlScript(JdbcTemplate jdbcTemplate, ResourceLoader resourceLoader, String sqlResourcePath, boolean continueOnError) throws DataAccessException { @@ -142,7 +143,8 @@ public class JdbcTestUtils { *

The script will typically be loaded from the classpath. Statements * should be delimited with a semicolon. If statements are not delimited with * a semicolon then there should be one statement per line. Statements are - * allowed to span lines only if they are delimited with a semicolon. + * allowed to span lines only if they are delimited with a semicolon. Any + * line comments will be removed. *

Do not use this method to execute DDL if you expect rollback. * @param jdbcTemplate the JdbcTemplate with which to perform JDBC operations * @param resource the resource to load the SQL script from @@ -151,6 +153,7 @@ public class JdbcTestUtils { * @throws DataAccessException if there is an error executing a statement * and {@code continueOnError} is {@code false} * @see ResourceDatabasePopulator + * @see #executeSqlScript(JdbcTemplate, EncodedResource, boolean) */ public static void executeSqlScript(JdbcTemplate jdbcTemplate, Resource resource, boolean continueOnError) throws DataAccessException { @@ -160,7 +163,7 @@ public class JdbcTestUtils { /** * Execute the given SQL script. *

The script will typically be loaded from the classpath. There should - * be one statement per line. Any semicolons will be removed. + * be one statement per line. Any semicolons and line comments will be removed. *

Do not use this method to execute DDL if you expect rollback. * @param jdbcTemplate the JdbcTemplate with which to perform JDBC operations * @param resource the resource (potentially associated with a specific encoding) @@ -245,9 +248,12 @@ public class JdbcTestUtils { /** * Read a script from the provided {@code LineNumberReader}, using the supplied * comment prefix, and build a {@code String} containing the lines. + *

Lines beginning with the comment prefix are excluded from the + * results; however, line comments anywhere else — for example, within + * a statement — will be included in the results. * @param lineNumberReader the {@code LineNumberReader} containing the script * to be processed - * @param commentPrefix the line prefix that identifies comments in the SQL script + * @param commentPrefix the prefix that identifies comments in the SQL script — typically "--" * @return a {@code String} containing the script lines */ public static String readScript(LineNumberReader lineNumberReader, String commentPrefix) throws IOException { @@ -287,25 +293,35 @@ public class JdbcTestUtils { } /** - * Split an SQL script into separate statements delimited with the provided + * Split an SQL script into separate statements delimited by the provided * delimiter character. Each individual statement will be added to the * provided List. + *

Within a statement, "{@code --}" will be used as the comment prefix; + * any text beginning with the comment prefix and extending to the end of + * the line will be omitted from the statement. In addition, multiple adjacent + * whitespace characters will be collapsed into a single space. * @param script the SQL script * @param delim character delimiting each statement — typically a ';' character * @param statements the list that will contain the individual statements */ public static void splitSqlScript(String script, char delim, List statements) { - splitSqlScript(script, "" + delim, statements); + splitSqlScript(script, "" + delim, DEFAULT_COMMENT_PREFIX, statements); } /** - * Split an SQL script into separate statements delimited with the provided delimiter - * character. Each individual statement will be added to the provided {@code List}. + * Split an SQL script into separate statements delimited by the provided + * delimiter string. Each individual statement will be added to the provided + * {@code List}. + *

Within a statement, the provided {@code commentPrefix} will be honored; + * any text beginning with the comment prefix and extending to the end of the + * line will be omitted from the statement. In addition, multiple adjacent + * whitespace characters will be collapsed into a single space. * @param script the SQL script * @param delim character delimiting each statement — typically a ';' character + * @param commentPrefix the prefix that identifies line comments in the SQL script — typically "--" * @param statements the List that will contain the individual statements */ - private static void splitSqlScript(String script, String delim, List statements) { + private static void splitSqlScript(String script, String delim, String commentPrefix, List statements) { StringBuilder sb = new StringBuilder(); boolean inLiteral = false; boolean inEscape = false; @@ -327,7 +343,8 @@ public class JdbcTestUtils { inLiteral = !inLiteral; } if (!inLiteral) { - if (startsWithDelimiter(script, i, delim)) { + if (script.startsWith(delim, i)) { + // we've reached the end of the current statement if (sb.length() > 0) { statements.add(sb.toString()); sb = new StringBuilder(); @@ -335,8 +352,27 @@ public class JdbcTestUtils { i += delim.length() - 1; continue; } - else if (c == '\n' || c == '\t') { - c = ' '; + else if (script.startsWith(commentPrefix, i)) { + // skip over any content from the start of the comment to the EOL + int indexOfNextNewline = script.indexOf("\n", i); + if (indexOfNextNewline > i) { + i = indexOfNextNewline; + continue; + } + else { + // if there's no newline after the comment, we must be at the end + // of the script, so stop here. + break; + } + } + else if (c == ' ' || c == '\n' || c == '\t') { + // avoid multiple adjacent whitespace characters + if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') { + c = ' '; + } + else { + continue; + } } } sb.append(c); @@ -346,20 +382,4 @@ public class JdbcTestUtils { } } - /** - * Return whether the substring of a given source {@link String} starting at the - * given index starts with the given delimiter. - * @param source the source {@link String} to inspect - * @param startIndex the index to look for the delimiter - * @param delim the delimiter to look for - */ - private static boolean startsWithDelimiter(String source, int startIndex, String delim) { - int endIndex = startIndex + delim.length(); - if (source.length() < endIndex) { - // String is too short to contain the delimiter - return false; - } - return source.substring(startIndex, endIndex).equals(delim); - } - } diff --git a/spring-test/src/test/java/org/springframework/test/jdbc/JdbcTestUtilsTests.java b/spring-test/src/test/java/org/springframework/test/jdbc/JdbcTestUtilsTests.java index f688be143ad..9651d2bcdef 100644 --- a/spring-test/src/test/java/org/springframework/test/jdbc/JdbcTestUtilsTests.java +++ b/spring-test/src/test/java/org/springframework/test/jdbc/JdbcTestUtilsTests.java @@ -86,20 +86,22 @@ public class JdbcTestUtilsTests { LineNumberReader lineNumberReader = new LineNumberReader(resource.getReader()); String script = JdbcTestUtils.readScript(lineNumberReader); - assertFalse("script should not contain --", script.contains("--")); char delim = ';'; List statements = new ArrayList(); JdbcTestUtils.splitSqlScript(script, delim, statements); String statement1 = "insert into customer (id, name) values (1, 'Rod; Johnson'), (2, 'Adrian Collier')"; - String statement2 = " insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)"; - String statement3 = " insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)"; + String statement2 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)"; + String statement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)"; + // Statement 4 addresses the error described in SPR-9982. + String statement4 = "INSERT INTO persons( person_id , name) VALUES( 1 , 'Name' )"; - assertEquals("wrong number of statements", 3, statements.size()); + assertEquals("wrong number of statements", 4, statements.size()); assertEquals("statement 1 not split correctly", statement1, statements.get(0)); assertEquals("statement 2 not split correctly", statement2, statements.get(1)); assertEquals("statement 3 not split correctly", statement3, statements.get(2)); + assertEquals("statement 4 not split correctly", statement4, statements.get(3)); } } diff --git a/spring-test/src/test/resources/org/springframework/test/jdbc/test-data-with-comments.sql b/spring-test/src/test/resources/org/springframework/test/jdbc/test-data-with-comments.sql index 50dfb419a05..82483ca4d36 100644 --- a/spring-test/src/test/resources/org/springframework/test/jdbc/test-data-with-comments.sql +++ b/spring-test/src/test/resources/org/springframework/test/jdbc/test-data-with-comments.sql @@ -1,6 +1,16 @@ +-- The next comment line has no text after the '--' prefix. +-- +-- The next comment line starts with a space. + -- x, y, z... + insert into customer (id, name) values (1, 'Rod; Johnson'), (2, 'Adrian Collier'); -- This is also a comment. insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2); -insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2); \ No newline at end of file +insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2); +INSERT INTO persons( person_id-- + , name) +VALUES( 1 -- person_id + , 'Name' --name +);-- \ No newline at end of file