From 9230b38aea41316d78dc2f1bb206ce7a92b9ca56 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 11 Apr 2014 17:44:27 +0200 Subject: [PATCH] Support EOF as statement separator in SQL scripts Prior to Spring Framework 4.0.3, it was possible to supply a bogus statement separator (i.e., a separator string that does not exist in the configured SQL scripts) to ResourceDatabasePopulator with the side effect that the entire contents of a script file would be interpreted as a single SQL statement. This undocumented feature was never intentional; however, some developers came to rely on it. Unfortunately, changes made in conjunction with SPR-9531 and SPR-11560 caused such scenarios to no longer work. This commit introduces first-class support for executing scripts which contain a single statement that spans multiple lines but is not followed by an explicit statement separator. Specifically, ScriptUtils.EOF_STATEMENT_SEPARATOR may now be specified as a 'virtual' statement separator to denote that a script contains a single statement and no actual separator. Issue: SPR-11687 (cherry picked from commit cc0ae3a881dde5910ecc340fa20c2365dd7474fa) --- .../embedded/EmbeddedDatabaseBuilder.java | 11 ++++++---- .../init/ResourceDatabasePopulator.java | 14 +++++++------ .../jdbc/datasource/init/ScriptUtils.java | 20 +++++++++++++++++-- .../init/AbstractDatabasePopulatorTests.java | 11 ++++++++++ .../datasource/init/drop-users-schema.sql | 1 + .../init/users-data-without-separator.sql | 3 +++ .../init/users-schema-without-separator.sql | 5 +++++ 7 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/drop-users-schema.sql create mode 100644 spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-data-without-separator.sql create mode 100644 spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-schema-without-separator.sql diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.java index f9aa4fc5724..08ee1687559 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.java @@ -21,6 +21,7 @@ import javax.sql.DataSource; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; +import org.springframework.jdbc.datasource.init.ScriptUtils; import org.springframework.util.Assert; /** @@ -161,7 +162,9 @@ public class EmbeddedDatabaseBuilder { /** * Specify the statement separator used in all SQL scripts, if a custom one. - *

Default is ";". + *

Defaults to {@code ";"} if not specified and falls back to {@code "\n"} + * as a last resort; may be set to {@link ScriptUtils#EOF_STATEMENT_SEPARATOR} + * to signal that each script contains a single statement without a separator. * @param separator the statement separator * @return {@code this}, to facilitate method chaining * @since 4.0.3 @@ -173,7 +176,7 @@ public class EmbeddedDatabaseBuilder { /** * Specify the single-line comment prefix used in all SQL scripts. - *

Default is "--". + *

Defaults to {@code "--"}. * @param commentPrefix the prefix for single-line comments * @return {@code this}, to facilitate method chaining * @since 4.0.3 @@ -185,7 +188,7 @@ public class EmbeddedDatabaseBuilder { /** * Specify the start delimiter for block comments in all SQL scripts. - *

Default is "/*". + *

Defaults to {@code "/*"}. * @param blockCommentStartDelimiter the start delimiter for block comments * @return {@code this}, to facilitate method chaining * @since 4.0.3 @@ -199,7 +202,7 @@ public class EmbeddedDatabaseBuilder { /** * Specify the end delimiter for block comments in all SQL scripts. - *

Default is "*/". + *

Defaults to "*/". * @param blockCommentEndDelimiter the end delimiter for block comments * @return {@code this}, to facilitate method chaining * @since 4.0.3 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 700f9e2a3e4..23082390354 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 @@ -136,16 +136,18 @@ public class ResourceDatabasePopulator implements DatabasePopulator { /** * Specify the statement separator, if a custom one. - *

Default is ";". - * @param separator the statement separator + *

Defaults to {@code ";"} if not specified and falls back to {@code "\n"} + * as a last resort; may be set to {@link ScriptUtils#EOF_STATEMENT_SEPARATOR} + * to signal that each script contains a single statement without a separator. + * @param separator the script statement separator */ public void setSeparator(String separator) { this.separator = separator; } /** - * Set the prefix that identifies line comments within the SQL scripts. - *

Default is "--". + * Set the prefix that identifies single-line comments within the SQL scripts. + *

Defaults to {@code "--"}. * @param commentPrefix the prefix for single-line comments */ public void setCommentPrefix(String commentPrefix) { @@ -155,7 +157,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator { /** * Set the start delimiter that identifies block comments within the SQL * scripts. - *

Default is "/*". + *

Defaults to {@code "/*"}. * @param blockCommentStartDelimiter the start delimiter for block comments * @since 4.0.3 * @see #setBlockCommentEndDelimiter @@ -167,7 +169,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator { /** * Set the end delimiter that identifies block comments within the SQL * scripts. - *

Default is "*/". + *

Defaults to "*/". * @param blockCommentEndDelimiter the end delimiter for block comments * @since 4.0.3 * @see #setBlockCommentStartDelimiter diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java index 364bfe71363..025e6f3d346 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java @@ -61,6 +61,17 @@ public abstract class ScriptUtils { */ public static final String FALLBACK_STATEMENT_SEPARATOR = "\n"; + /** + * End of file (EOF) SQL statement separator. + *

This value may be supplied as the {@code separator} to {@link + * #executeSqlScript(Connection, EncodedResource, boolean, boolean, String, String, String, String)} + * to denote that an SQL script contains a single statement (potentially + * spanning multiple lines) with no explicit statement separator. Note that + * such a script should not actually contain this value; it is merely a + * virtual statement separator. + */ + public static final String EOF_STATEMENT_SEPARATOR = "<<< END OF SCRIPT >>>"; + /** * Default prefix for line comments within SQL scripts. */ @@ -399,12 +410,17 @@ public abstract class ScriptUtils { * typically "--" * @param separator the script statement separator; defaults to * {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified and falls back to - * {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort + * {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort; may be set to + * {@value #EOF_STATEMENT_SEPARATOR} to signal that the script contains a + * single statement without a separator * @param blockCommentStartDelimiter the start block comment delimiter; never * {@code null} or empty * @param blockCommentEndDelimiter the end block comment delimiter; never * {@code null} or empty * @throws ScriptException if an error occurred while executing the SQL script + * @see #DEFAULT_STATEMENT_SEPARATOR + * @see #FALLBACK_STATEMENT_SEPARATOR + * @see #EOF_STATEMENT_SEPARATOR */ public static void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError, boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter, @@ -428,7 +444,7 @@ public abstract class ScriptUtils { if (separator == null) { separator = DEFAULT_STATEMENT_SEPARATOR; } - if (!containsSqlScriptDelimiters(script, separator)) { + if (!EOF_STATEMENT_SEPARATOR.equals(separator) && !containsSqlScriptDelimiters(script, separator)) { separator = FALLBACK_STATEMENT_SEPARATOR; } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/AbstractDatabasePopulatorTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/AbstractDatabasePopulatorTests.java index 63aea669d6d..82d8acfedc2 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/AbstractDatabasePopulatorTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/AbstractDatabasePopulatorTests.java @@ -139,6 +139,17 @@ public abstract class AbstractDatabasePopulatorTests extends AbstractDatabaseIni assertUsersDatabaseCreated("Brannen", "Hoeller"); } + @Test + public void scriptWithoutStatementSeparator() throws Exception { + databasePopulator.setSeparator(ScriptUtils.EOF_STATEMENT_SEPARATOR); + databasePopulator.addScript(resource("drop-users-schema.sql")); + databasePopulator.addScript(resource("users-schema-without-separator.sql")); + databasePopulator.addScript(resource("users-data.sql")); + DatabasePopulatorUtils.execute(databasePopulator, db); + + assertUsersDatabaseCreated("Brannen"); + } + @Test public void constructorWithMultipleScriptResources() throws Exception { final ResourceDatabasePopulator populator = new ResourceDatabasePopulator(usersSchema(), diff --git a/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/drop-users-schema.sql b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/drop-users-schema.sql new file mode 100644 index 00000000000..cf1647ac58f --- /dev/null +++ b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/drop-users-schema.sql @@ -0,0 +1 @@ +DROP TABLE users IF EXISTS \ No newline at end of file diff --git a/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-data-without-separator.sql b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-data-without-separator.sql new file mode 100644 index 00000000000..33bf3b41b3b --- /dev/null +++ b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-data-without-separator.sql @@ -0,0 +1,3 @@ +INSERT INTO +users(first_name, last_name) +values('Sam', 'Brannen') \ No newline at end of file diff --git a/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-schema-without-separator.sql b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-schema-without-separator.sql new file mode 100644 index 00000000000..4a4dc97f2fb --- /dev/null +++ b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-schema-without-separator.sql @@ -0,0 +1,5 @@ +CREATE TABLE users ( + id INTEGER NOT NULL IDENTITY, + first_name VARCHAR(50) NOT NULL, + last_name VARCHAR(50) NOT NULL +) \ No newline at end of file