diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java index 65f52a35af9..5c8bf6d9bb3 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java @@ -44,6 +44,7 @@ import org.springframework.util.StringUtils; * @author Juergen Hoeller * @author Chris Beams * @author Oliver Gierke + * @author Sam Brannen * @since 3.0 */ public class ResourceDatabasePopulator implements DatabasePopulator { @@ -54,7 +55,6 @@ public class ResourceDatabasePopulator implements DatabasePopulator { private static final Log logger = LogFactory.getLog(ResourceDatabasePopulator.class); - private List scripts = new ArrayList(); private String sqlScriptEncoding; @@ -127,7 +127,6 @@ public class ResourceDatabasePopulator implements DatabasePopulator { this.ignoreFailedDrops = ignoreFailedDrops; } - public void populate(Connection connection) throws SQLException { for (Resource script : this.scripts) { executeSqlScript(connection, applyEncodingIfNecessary(script), this.continueOnError, this.ignoreFailedDrops); @@ -153,8 +152,8 @@ public class ResourceDatabasePopulator implements DatabasePopulator { * @param continueOnError whether or not to continue without throwing an exception in the event of an error * @param ignoreFailedDrops whether of not to continue in the event of specifically an error on a {@code DROP} */ - private void executeSqlScript(Connection connection, EncodedResource resource, - boolean continueOnError, boolean ignoreFailedDrops) throws SQLException { + private void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError, + boolean ignoreFailedDrops) throws SQLException { if (logger.isInfoEnabled()) { logger.info("Executing SQL script from " + resource); @@ -175,7 +174,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator { delimiter = "\n"; } } - splitSqlScript(script, delimiter, statements); + splitSqlScript(script, delimiter, this.commentPrefix, statements); int lineNumber = 0; Statement stmt = connection.createStatement(); try { @@ -192,8 +191,8 @@ public class ResourceDatabasePopulator implements DatabasePopulator { boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop"); if (continueOnError || (dropStatement && ignoreFailedDrops)) { if (logger.isDebugEnabled()) { - logger.debug("Failed to execute SQL script statement at line " + lineNumber + - " of resource " + resource + ": " + statement, ex); + logger.debug("Failed to execute SQL script statement at line " + lineNumber + + " of resource " + resource + ": " + statement, ex); } } else { @@ -239,7 +238,8 @@ public class ResourceDatabasePopulator implements DatabasePopulator { } maybeAddSeparatorToScript(scriptBuilder); return scriptBuilder.toString(); - } finally { + } + finally { lnr.close(); } } @@ -252,7 +252,8 @@ public class ResourceDatabasePopulator implements DatabasePopulator { if (trimmed.length() == this.separator.length()) { return; } - // separator ends in whitespace, so we might want to see if the script is trying to end the same way + // separator ends in whitespace, so we might want to see if the script is trying + // to end the same way if (scriptBuilder.lastIndexOf(trimmed) == scriptBuilder.length() - trimmed.length()) { scriptBuilder.append(this.separator.substring(trimmed.length())); } @@ -270,7 +271,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator { if (content[i] == '\'') { inLiteral = !inLiteral; } - if (!inLiteral && startsWithDelimiter(script, i, delim)) { + if (!inLiteral && script.startsWith(delim, i)) { return true; } } @@ -278,29 +279,18 @@ public class ResourceDatabasePopulator implements DatabasePopulator { } /** - * 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 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); - } - - /** - * 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 void splitSqlScript(String script, String delim, List statements) { + private void splitSqlScript(String script, String delim, String commentPrefix, List statements) { StringBuilder sb = new StringBuilder(); boolean inLiteral = false; boolean inEscape = false; @@ -322,7 +312,8 @@ public class ResourceDatabasePopulator implements DatabasePopulator { 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(); @@ -330,8 +321,27 @@ public class ResourceDatabasePopulator implements DatabasePopulator { 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); diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/DatabasePopulatorTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/DatabasePopulatorTests.java index 6535eeb426c..e5dd4b0712d 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/DatabasePopulatorTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/DatabasePopulatorTests.java @@ -59,9 +59,11 @@ public class DatabasePopulatorTests { assertEquals(name, jdbcTemplate.queryForObject("select NAME from T_TEST", String.class)); } - private void assertUsersDatabaseCreated() { - assertEquals("Sam", jdbcTemplate.queryForObject("select first_name from users where last_name = 'Brannen'", - String.class)); + private void assertUsersDatabaseCreated(String... lastNames) { + for (String lastName : lastNames) { + assertEquals("Did not find user with last name [" + lastName + "].", 1, + jdbcTemplate.queryForInt("select count(0) from users where last_name = ?", lastName)); + } } @After @@ -73,7 +75,6 @@ public class DatabasePopulatorTests { db.shutdown(); } - @Test public void testBuildWithCommentsAndFailedDrop() throws Exception { databasePopulator.addScript(resourceLoader.getResource("db-schema-failed-drop-comments.sql")); @@ -215,7 +216,22 @@ public class DatabasePopulatorTests { connection.close(); } - assertUsersDatabaseCreated(); + assertUsersDatabaseCreated("Brannen"); + } + + @Test + public void scriptWithCommentsWithinStatements() throws Exception { + databasePopulator.addScript(resourceLoader.getResource("users-schema.sql")); + databasePopulator.addScript(resourceLoader.getResource("users-data-with-comments.sql")); + Connection connection = db.getConnection(); + try { + databasePopulator.populate(connection); + } + finally { + connection.close(); + } + + assertUsersDatabaseCreated("Brannen", "Hoeller"); } @Test diff --git a/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-data-with-comments.sql b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-data-with-comments.sql new file mode 100644 index 00000000000..8e7f6442112 --- /dev/null +++ b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-data-with-comments.sql @@ -0,0 +1,12 @@ +-- The next comment line has no text after the '--' prefix. +-- +-- The next comment line starts with a space. + -- x, y, z... + +INSERT INTO users(first_name, last_name) VALUES('Juergen', 'Hoeller'); +-- This is also a comment. +INSERT INTO +users(first_name, last_name) +VALUES( 'Sam' -- first_name + , 'Brannen' -- last_name +);-- \ No newline at end of file