|
|
|
|
@ -35,7 +35,7 @@ import org.springframework.util.Assert;
@@ -35,7 +35,7 @@ import org.springframework.util.Assert;
|
|
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Generic utility methods for working with SQL scripts. |
|
|
|
|
* Generic utility methods for working with SQL scripts in conjunction with JDBC. |
|
|
|
|
* |
|
|
|
|
* <p>Mainly for internal use within the framework. |
|
|
|
|
* |
|
|
|
|
@ -50,6 +50,7 @@ import org.springframework.util.StringUtils;
@@ -50,6 +50,7 @@ import org.springframework.util.StringUtils;
|
|
|
|
|
* @author Nicolas Debeissat |
|
|
|
|
* @author Phillip Webb |
|
|
|
|
* @since 4.0.3 |
|
|
|
|
* @see org.springframework.r2dbc.connection.init.ScriptUtils |
|
|
|
|
*/ |
|
|
|
|
public abstract class ScriptUtils { |
|
|
|
|
|
|
|
|
|
@ -102,197 +103,207 @@ public abstract class ScriptUtils {
@@ -102,197 +103,207 @@ public abstract class ScriptUtils {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Split an SQL script into separate statements delimited by the provided |
|
|
|
|
* separator character. Each individual statement will be added to the |
|
|
|
|
* provided {@code List}. |
|
|
|
|
* <p>Within the script, {@value #DEFAULT_COMMENT_PREFIX} 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 output. Similarly, |
|
|
|
|
* {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and |
|
|
|
|
* {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as the |
|
|
|
|
* <em>start</em> and <em>end</em> block comment delimiters: any text enclosed |
|
|
|
|
* in a block comment will be omitted from the output. In addition, multiple |
|
|
|
|
* adjacent whitespace characters will be collapsed into a single space. |
|
|
|
|
* @param script the SQL script |
|
|
|
|
* @param separator character separating each statement (typically a ';') |
|
|
|
|
* @param statements the list that will contain the individual statements |
|
|
|
|
* @throws ScriptException if an error occurred while splitting the SQL script |
|
|
|
|
* @see #splitSqlScript(String, String, List) |
|
|
|
|
* @see #splitSqlScript(EncodedResource, String, String, String, String, String, List) |
|
|
|
|
* Execute the given SQL script using default settings for statement |
|
|
|
|
* separators, comment delimiters, and exception handling flags. |
|
|
|
|
* <p>Statement separators and comments will be removed before executing |
|
|
|
|
* individual statements within the supplied script. |
|
|
|
|
* <p><strong>Warning</strong>: this method does <em>not</em> release the |
|
|
|
|
* provided {@link Connection}. |
|
|
|
|
* @param connection the JDBC connection to use to execute the script; already |
|
|
|
|
* configured and ready to use |
|
|
|
|
* @param resource the resource to load the SQL script from; encoded with the |
|
|
|
|
* current platform's default encoding |
|
|
|
|
* @throws ScriptException if an error occurred while executing the SQL script |
|
|
|
|
* @see #executeSqlScript(Connection, EncodedResource, boolean, boolean, String, String, String, String) |
|
|
|
|
* @see #DEFAULT_STATEMENT_SEPARATOR |
|
|
|
|
* @see #DEFAULT_COMMENT_PREFIX |
|
|
|
|
* @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER |
|
|
|
|
* @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection |
|
|
|
|
*/ |
|
|
|
|
public static void splitSqlScript(String script, char separator, List<String> statements) throws ScriptException { |
|
|
|
|
splitSqlScript(script, String.valueOf(separator), statements); |
|
|
|
|
public static void executeSqlScript(Connection connection, Resource resource) throws ScriptException { |
|
|
|
|
executeSqlScript(connection, new EncodedResource(resource)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Split an SQL script into separate statements delimited by the provided |
|
|
|
|
* separator string. Each individual statement will be added to the |
|
|
|
|
* provided {@code List}. |
|
|
|
|
* <p>Within the script, {@value #DEFAULT_COMMENT_PREFIX} 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 output. Similarly, |
|
|
|
|
* {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and |
|
|
|
|
* {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as the |
|
|
|
|
* <em>start</em> and <em>end</em> block comment delimiters: any text enclosed |
|
|
|
|
* in a block comment will be omitted from the output. In addition, multiple |
|
|
|
|
* adjacent whitespace characters will be collapsed into a single space. |
|
|
|
|
* @param script the SQL script |
|
|
|
|
* @param separator text separating each statement |
|
|
|
|
* (typically a ';' or newline character) |
|
|
|
|
* @param statements the list that will contain the individual statements |
|
|
|
|
* @throws ScriptException if an error occurred while splitting the SQL script |
|
|
|
|
* @see #splitSqlScript(String, char, List) |
|
|
|
|
* @see #splitSqlScript(EncodedResource, String, String, String, String, String, List) |
|
|
|
|
* Execute the given SQL script using default settings for statement |
|
|
|
|
* separators, comment delimiters, and exception handling flags. |
|
|
|
|
* <p>Statement separators and comments will be removed before executing |
|
|
|
|
* individual statements within the supplied script. |
|
|
|
|
* <p><strong>Warning</strong>: this method does <em>not</em> release the |
|
|
|
|
* provided {@link Connection}. |
|
|
|
|
* @param connection the JDBC connection to use to execute the script; already |
|
|
|
|
* configured and ready to use |
|
|
|
|
* @param resource the resource (potentially associated with a specific encoding) |
|
|
|
|
* to load the SQL script from |
|
|
|
|
* @throws ScriptException if an error occurred while executing the SQL script |
|
|
|
|
* @see #executeSqlScript(Connection, EncodedResource, boolean, boolean, String, String, String, String) |
|
|
|
|
* @see #DEFAULT_STATEMENT_SEPARATOR |
|
|
|
|
* @see #DEFAULT_COMMENT_PREFIX |
|
|
|
|
* @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER |
|
|
|
|
* @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection |
|
|
|
|
*/ |
|
|
|
|
public static void splitSqlScript(String script, String separator, List<String> statements) throws ScriptException { |
|
|
|
|
splitSqlScript(null, script, separator, DEFAULT_COMMENT_PREFIX, DEFAULT_BLOCK_COMMENT_START_DELIMITER, |
|
|
|
|
DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements); |
|
|
|
|
public static void executeSqlScript(Connection connection, EncodedResource resource) throws ScriptException { |
|
|
|
|
executeSqlScript(connection, resource, false, false, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, |
|
|
|
|
DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Split an SQL script into separate statements delimited by the provided |
|
|
|
|
* separator string. Each individual statement will be added to the provided |
|
|
|
|
* {@code List}. |
|
|
|
|
* <p>Within the script, 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 output. Similarly, the provided |
|
|
|
|
* {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter} |
|
|
|
|
* delimiters will be honored: any text enclosed in a block comment will be |
|
|
|
|
* omitted from the output. In addition, multiple adjacent whitespace characters |
|
|
|
|
* will be collapsed into a single space. |
|
|
|
|
* @param resource the resource from which the script was read |
|
|
|
|
* @param script the SQL script |
|
|
|
|
* @param separator text separating each statement |
|
|
|
|
* (typically a ';' or newline character) |
|
|
|
|
* @param commentPrefix the prefix that identifies SQL line comments |
|
|
|
|
* (typically "--") |
|
|
|
|
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter; |
|
|
|
|
* never {@code null} or empty |
|
|
|
|
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter; |
|
|
|
|
* never {@code null} or empty |
|
|
|
|
* @param statements the list that will contain the individual statements |
|
|
|
|
* @throws ScriptException if an error occurred while splitting the SQL script |
|
|
|
|
* Execute the given SQL script. |
|
|
|
|
* <p>Statement separators and comments will be removed before executing |
|
|
|
|
* individual statements within the supplied script. |
|
|
|
|
* <p><strong>Warning</strong>: this method does <em>not</em> release the |
|
|
|
|
* provided {@link Connection}. |
|
|
|
|
* @param connection the JDBC connection to use to execute the script; already |
|
|
|
|
* configured and ready to use |
|
|
|
|
* @param resource the resource (potentially associated with a specific encoding) |
|
|
|
|
* to load the SQL script from |
|
|
|
|
* @param continueOnError whether or not to continue without throwing an exception |
|
|
|
|
* in the event of an error |
|
|
|
|
* @param ignoreFailedDrops whether or not to continue in the event of specifically |
|
|
|
|
* an error on a {@code DROP} statement |
|
|
|
|
* @param commentPrefix the prefix that identifies single-line comments in the |
|
|
|
|
* SQL script (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; may be set to |
|
|
|
|
* {@value #EOF_STATEMENT_SEPARATOR} to signal that the script contains a |
|
|
|
|
* single statement without a separator |
|
|
|
|
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter |
|
|
|
|
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter |
|
|
|
|
* @throws ScriptException if an error occurred while executing the SQL script |
|
|
|
|
* @see #DEFAULT_STATEMENT_SEPARATOR |
|
|
|
|
* @see #FALLBACK_STATEMENT_SEPARATOR |
|
|
|
|
* @see #EOF_STATEMENT_SEPARATOR |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection |
|
|
|
|
*/ |
|
|
|
|
public static void splitSqlScript(@Nullable EncodedResource resource, String script, |
|
|
|
|
String separator, String commentPrefix, String blockCommentStartDelimiter, |
|
|
|
|
String blockCommentEndDelimiter, List<String> statements) throws ScriptException { |
|
|
|
|
public static void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError, |
|
|
|
|
boolean ignoreFailedDrops, String commentPrefix, @Nullable String separator, |
|
|
|
|
String blockCommentStartDelimiter, String blockCommentEndDelimiter) throws ScriptException { |
|
|
|
|
|
|
|
|
|
Assert.hasText(commentPrefix, "'commentPrefix' must not be null or empty"); |
|
|
|
|
splitSqlScript(resource, script, separator, new String[] { commentPrefix }, |
|
|
|
|
blockCommentStartDelimiter, blockCommentEndDelimiter, statements); |
|
|
|
|
executeSqlScript(connection, resource, continueOnError, ignoreFailedDrops, |
|
|
|
|
new String[] { commentPrefix }, separator, blockCommentStartDelimiter, |
|
|
|
|
blockCommentEndDelimiter); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Split an SQL script into separate statements delimited by the provided |
|
|
|
|
* separator string. Each individual statement will be added to the provided |
|
|
|
|
* {@code List}. |
|
|
|
|
* <p>Within the script, the provided {@code commentPrefixes} will be honored: |
|
|
|
|
* any text beginning with one of the comment prefixes and extending to the |
|
|
|
|
* end of the line will be omitted from the output. Similarly, the provided |
|
|
|
|
* {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter} |
|
|
|
|
* delimiters will be honored: any text enclosed in a block comment will be |
|
|
|
|
* omitted from the output. In addition, multiple adjacent whitespace characters |
|
|
|
|
* will be collapsed into a single space. |
|
|
|
|
* @param resource the resource from which the script was read |
|
|
|
|
* @param script the SQL script |
|
|
|
|
* @param separator text separating each statement |
|
|
|
|
* (typically a ';' or newline character) |
|
|
|
|
* @param commentPrefixes the prefixes that identify SQL line comments |
|
|
|
|
* (typically "--") |
|
|
|
|
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter; |
|
|
|
|
* never {@code null} or empty |
|
|
|
|
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter; |
|
|
|
|
* never {@code null} or empty |
|
|
|
|
* @param statements the list that will contain the individual statements |
|
|
|
|
* @throws ScriptException if an error occurred while splitting the SQL script |
|
|
|
|
* Execute the given SQL script. |
|
|
|
|
* <p>Statement separators and comments will be removed before executing |
|
|
|
|
* individual statements within the supplied script. |
|
|
|
|
* <p><strong>Warning</strong>: this method does <em>not</em> release the |
|
|
|
|
* provided {@link Connection}. |
|
|
|
|
* @param connection the JDBC connection to use to execute the script; already |
|
|
|
|
* configured and ready to use |
|
|
|
|
* @param resource the resource (potentially associated with a specific encoding) |
|
|
|
|
* to load the SQL script from |
|
|
|
|
* @param continueOnError whether or not to continue without throwing an exception |
|
|
|
|
* in the event of an error |
|
|
|
|
* @param ignoreFailedDrops whether or not to continue in the event of specifically |
|
|
|
|
* an error on a {@code DROP} statement |
|
|
|
|
* @param commentPrefixes the prefixes that identify single-line comments in the |
|
|
|
|
* SQL script (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; may be set to |
|
|
|
|
* {@value #EOF_STATEMENT_SEPARATOR} to signal that the script contains a |
|
|
|
|
* single statement without a separator |
|
|
|
|
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter |
|
|
|
|
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter |
|
|
|
|
* @throws ScriptException if an error occurred while executing the SQL script |
|
|
|
|
* @since 5.2 |
|
|
|
|
* @see #DEFAULT_STATEMENT_SEPARATOR |
|
|
|
|
* @see #FALLBACK_STATEMENT_SEPARATOR |
|
|
|
|
* @see #EOF_STATEMENT_SEPARATOR |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection |
|
|
|
|
*/ |
|
|
|
|
public static void splitSqlScript(@Nullable EncodedResource resource, String script, |
|
|
|
|
String separator, String[] commentPrefixes, String blockCommentStartDelimiter, |
|
|
|
|
String blockCommentEndDelimiter, List<String> statements) throws ScriptException { |
|
|
|
|
|
|
|
|
|
Assert.hasText(script, "'script' must not be null or empty"); |
|
|
|
|
Assert.notNull(separator, "'separator' must not be null"); |
|
|
|
|
Assert.notEmpty(commentPrefixes, "'commentPrefixes' must not be null or empty"); |
|
|
|
|
for (String commentPrefix : commentPrefixes) { |
|
|
|
|
Assert.hasText(commentPrefix, "'commentPrefixes' must not contain null or empty elements"); |
|
|
|
|
} |
|
|
|
|
Assert.hasText(blockCommentStartDelimiter, "'blockCommentStartDelimiter' must not be null or empty"); |
|
|
|
|
Assert.hasText(blockCommentEndDelimiter, "'blockCommentEndDelimiter' must not be null or empty"); |
|
|
|
|
public static void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError, |
|
|
|
|
boolean ignoreFailedDrops, String[] commentPrefixes, @Nullable String separator, |
|
|
|
|
String blockCommentStartDelimiter, String blockCommentEndDelimiter) throws ScriptException { |
|
|
|
|
|
|
|
|
|
StringBuilder sb = new StringBuilder(); |
|
|
|
|
boolean inSingleQuote = false; |
|
|
|
|
boolean inDoubleQuote = false; |
|
|
|
|
boolean inEscape = false; |
|
|
|
|
try { |
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
logger.debug("Executing SQL script from " + resource); |
|
|
|
|
} |
|
|
|
|
long startTime = System.currentTimeMillis(); |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < script.length(); i++) { |
|
|
|
|
char c = script.charAt(i); |
|
|
|
|
if (inEscape) { |
|
|
|
|
inEscape = false; |
|
|
|
|
sb.append(c); |
|
|
|
|
continue; |
|
|
|
|
String script; |
|
|
|
|
try { |
|
|
|
|
script = readScript(resource, separator, commentPrefixes, blockCommentEndDelimiter); |
|
|
|
|
} |
|
|
|
|
// MySQL style escapes
|
|
|
|
|
if (c == '\\') { |
|
|
|
|
inEscape = true; |
|
|
|
|
sb.append(c); |
|
|
|
|
continue; |
|
|
|
|
catch (IOException ex) { |
|
|
|
|
throw new CannotReadScriptException(resource, ex); |
|
|
|
|
} |
|
|
|
|
if (!inDoubleQuote && (c == '\'')) { |
|
|
|
|
inSingleQuote = !inSingleQuote; |
|
|
|
|
|
|
|
|
|
if (separator == null) { |
|
|
|
|
separator = DEFAULT_STATEMENT_SEPARATOR; |
|
|
|
|
} |
|
|
|
|
else if (!inSingleQuote && (c == '"')) { |
|
|
|
|
inDoubleQuote = !inDoubleQuote; |
|
|
|
|
if (!EOF_STATEMENT_SEPARATOR.equals(separator) && |
|
|
|
|
!containsStatementSeparator(resource, script, separator, commentPrefixes, |
|
|
|
|
blockCommentStartDelimiter, blockCommentEndDelimiter)) { |
|
|
|
|
separator = FALLBACK_STATEMENT_SEPARATOR; |
|
|
|
|
} |
|
|
|
|
if (!inSingleQuote && !inDoubleQuote) { |
|
|
|
|
if (script.startsWith(separator, i)) { |
|
|
|
|
// We've reached the end of the current statement
|
|
|
|
|
if (sb.length() > 0) { |
|
|
|
|
statements.add(sb.toString()); |
|
|
|
|
sb = new StringBuilder(); |
|
|
|
|
} |
|
|
|
|
i += separator.length() - 1; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
else if (startsWithAny(script, commentPrefixes, 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; |
|
|
|
|
|
|
|
|
|
List<String> statements = new ArrayList<>(); |
|
|
|
|
splitSqlScript(resource, script, separator, commentPrefixes, blockCommentStartDelimiter, |
|
|
|
|
blockCommentEndDelimiter, statements); |
|
|
|
|
|
|
|
|
|
int stmtNumber = 0; |
|
|
|
|
Statement stmt = connection.createStatement(); |
|
|
|
|
try { |
|
|
|
|
for (String statement : statements) { |
|
|
|
|
stmtNumber++; |
|
|
|
|
try { |
|
|
|
|
stmt.execute(statement); |
|
|
|
|
int rowsAffected = stmt.getUpdateCount(); |
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
logger.debug(rowsAffected + " returned as update count for SQL: " + statement); |
|
|
|
|
SQLWarning warningToLog = stmt.getWarnings(); |
|
|
|
|
while (warningToLog != null) { |
|
|
|
|
logger.debug("SQLWarning ignored: SQL state '" + warningToLog.getSQLState() + |
|
|
|
|
"', error code '" + warningToLog.getErrorCode() + |
|
|
|
|
"', message [" + warningToLog.getMessage() + "]"); |
|
|
|
|
warningToLog = warningToLog.getNextWarning(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// If there's no EOL, we must be at the end of the script, so stop here.
|
|
|
|
|
break; |
|
|
|
|
catch (SQLException ex) { |
|
|
|
|
boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop"); |
|
|
|
|
if (continueOnError || (dropStatement && ignoreFailedDrops)) { |
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
logger.debug(ScriptStatementFailedException.buildErrorMessage(statement, stmtNumber, resource), ex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
throw new ScriptStatementFailedException(statement, stmtNumber, resource, ex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (script.startsWith(blockCommentStartDelimiter, i)) { |
|
|
|
|
// Skip over any block comments
|
|
|
|
|
int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i); |
|
|
|
|
if (indexOfCommentEnd > i) { |
|
|
|
|
i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
throw new ScriptParseException( |
|
|
|
|
"Missing block comment end delimiter: " + blockCommentEndDelimiter, resource); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
finally { |
|
|
|
|
try { |
|
|
|
|
stmt.close(); |
|
|
|
|
} |
|
|
|
|
else if (c == ' ' || c == '\r' || c == '\n' || c == '\t') { |
|
|
|
|
// Avoid multiple adjacent whitespace characters
|
|
|
|
|
if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') { |
|
|
|
|
c = ' '; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
catch (Throwable ex) { |
|
|
|
|
logger.trace("Could not close JDBC Statement", ex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
sb.append(c); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (StringUtils.hasText(sb)) { |
|
|
|
|
statements.add(sb.toString()); |
|
|
|
|
long elapsedTime = System.currentTimeMillis() - startTime; |
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
logger.debug("Executed SQL script from " + resource + " in " + elapsedTime + " ms."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) { |
|
|
|
|
if (ex instanceof ScriptException) { |
|
|
|
|
throw (ScriptException) ex; |
|
|
|
|
} |
|
|
|
|
throw new UncategorizedScriptException( |
|
|
|
|
"Failed to execute database script from resource [" + resource + "]", ex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -312,7 +323,7 @@ public abstract class ScriptUtils {
@@ -312,7 +323,7 @@ public abstract class ScriptUtils {
|
|
|
|
|
* @throws IOException in case of I/O errors |
|
|
|
|
*/ |
|
|
|
|
static String readScript(EncodedResource resource, @Nullable String separator, |
|
|
|
|
@Nullable String[] commentPrefixes, @Nullable String blockCommentEndDelimiter) throws IOException { |
|
|
|
|
String[] commentPrefixes, String blockCommentEndDelimiter) throws IOException { |
|
|
|
|
|
|
|
|
|
try (LineNumberReader lnr = new LineNumberReader(resource.getReader())) { |
|
|
|
|
return readScript(lnr, commentPrefixes, separator, blockCommentEndDelimiter); |
|
|
|
|
@ -393,15 +404,6 @@ public abstract class ScriptUtils {
@@ -393,15 +404,6 @@ public abstract class ScriptUtils {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static boolean startsWithAny(String script, String[] prefixes, int offset) { |
|
|
|
|
for (String prefix : prefixes) { |
|
|
|
|
if (script.startsWith(prefix, offset)) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Determine if the provided SQL script contains the specified delimiter. |
|
|
|
|
* <p>This method is intended to be used to find the string delimiting each |
|
|
|
|
@ -441,10 +443,185 @@ public abstract class ScriptUtils {
@@ -441,10 +443,185 @@ public abstract class ScriptUtils {
|
|
|
|
|
* (typically <code>"*/"</code>) |
|
|
|
|
* @since 5.2.16 |
|
|
|
|
*/ |
|
|
|
|
private static boolean containsStatementSeparator(@Nullable EncodedResource resource, String script, |
|
|
|
|
private static boolean containsStatementSeparator(@Nullable EncodedResource resource, String script, |
|
|
|
|
String separator, String[] commentPrefixes, String blockCommentStartDelimiter, |
|
|
|
|
String blockCommentEndDelimiter) throws ScriptException { |
|
|
|
|
|
|
|
|
|
boolean inSingleQuote = false; |
|
|
|
|
boolean inDoubleQuote = false; |
|
|
|
|
boolean inEscape = false; |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < script.length(); i++) { |
|
|
|
|
char c = script.charAt(i); |
|
|
|
|
if (inEscape) { |
|
|
|
|
inEscape = false; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
// MySQL style escapes
|
|
|
|
|
if (c == '\\') { |
|
|
|
|
inEscape = true; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
if (!inDoubleQuote && (c == '\'')) { |
|
|
|
|
inSingleQuote = !inSingleQuote; |
|
|
|
|
} |
|
|
|
|
else if (!inSingleQuote && (c == '"')) { |
|
|
|
|
inDoubleQuote = !inDoubleQuote; |
|
|
|
|
} |
|
|
|
|
if (!inSingleQuote && !inDoubleQuote) { |
|
|
|
|
if (script.startsWith(separator, i)) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
else if (startsWithAny(script, commentPrefixes, 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 EOL, we must be at the end of the script, so stop here.
|
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (script.startsWith(blockCommentStartDelimiter, i)) { |
|
|
|
|
// Skip over any block comments
|
|
|
|
|
int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i); |
|
|
|
|
if (indexOfCommentEnd > i) { |
|
|
|
|
i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
throw new ScriptParseException( |
|
|
|
|
"Missing block comment end delimiter: " + blockCommentEndDelimiter, resource); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Split an SQL script into separate statements delimited by the provided |
|
|
|
|
* separator character. Each individual statement will be added to the |
|
|
|
|
* provided {@code List}. |
|
|
|
|
* <p>Within the script, {@value #DEFAULT_COMMENT_PREFIX} 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 output. Similarly, |
|
|
|
|
* {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and |
|
|
|
|
* {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as the |
|
|
|
|
* <em>start</em> and <em>end</em> block comment delimiters: any text enclosed |
|
|
|
|
* in a block comment will be omitted from the output. In addition, multiple |
|
|
|
|
* adjacent whitespace characters will be collapsed into a single space. |
|
|
|
|
* @param script the SQL script |
|
|
|
|
* @param separator character separating each statement (typically a ';') |
|
|
|
|
* @param statements the list that will contain the individual statements |
|
|
|
|
* @throws ScriptException if an error occurred while splitting the SQL script |
|
|
|
|
* @see #splitSqlScript(String, String, List) |
|
|
|
|
* @see #splitSqlScript(EncodedResource, String, String, String, String, String, List) |
|
|
|
|
*/ |
|
|
|
|
public static void splitSqlScript(String script, char separator, List<String> statements) throws ScriptException { |
|
|
|
|
splitSqlScript(script, String.valueOf(separator), statements); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Split an SQL script into separate statements delimited by the provided |
|
|
|
|
* separator string. Each individual statement will be added to the |
|
|
|
|
* provided {@code List}. |
|
|
|
|
* <p>Within the script, {@value #DEFAULT_COMMENT_PREFIX} 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 output. Similarly, |
|
|
|
|
* {@value #DEFAULT_BLOCK_COMMENT_START_DELIMITER} and |
|
|
|
|
* {@value #DEFAULT_BLOCK_COMMENT_END_DELIMITER} will be used as the |
|
|
|
|
* <em>start</em> and <em>end</em> block comment delimiters: any text enclosed |
|
|
|
|
* in a block comment will be omitted from the output. In addition, multiple |
|
|
|
|
* adjacent whitespace characters will be collapsed into a single space. |
|
|
|
|
* @param script the SQL script |
|
|
|
|
* @param separator text separating each statement |
|
|
|
|
* (typically a ';' or newline character) |
|
|
|
|
* @param statements the list that will contain the individual statements |
|
|
|
|
* @throws ScriptException if an error occurred while splitting the SQL script |
|
|
|
|
* @see #splitSqlScript(String, char, List) |
|
|
|
|
* @see #splitSqlScript(EncodedResource, String, String, String, String, String, List) |
|
|
|
|
*/ |
|
|
|
|
public static void splitSqlScript(String script, String separator, List<String> statements) throws ScriptException { |
|
|
|
|
splitSqlScript(null, script, separator, DEFAULT_COMMENT_PREFIX, DEFAULT_BLOCK_COMMENT_START_DELIMITER, |
|
|
|
|
DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Split an SQL script into separate statements delimited by the provided |
|
|
|
|
* separator string. Each individual statement will be added to the provided |
|
|
|
|
* {@code List}. |
|
|
|
|
* <p>Within the script, 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 output. Similarly, the provided |
|
|
|
|
* {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter} |
|
|
|
|
* delimiters will be honored: any text enclosed in a block comment will be |
|
|
|
|
* omitted from the output. In addition, multiple adjacent whitespace characters |
|
|
|
|
* will be collapsed into a single space. |
|
|
|
|
* @param resource the resource from which the script was read |
|
|
|
|
* @param script the SQL script |
|
|
|
|
* @param separator text separating each statement |
|
|
|
|
* (typically a ';' or newline character) |
|
|
|
|
* @param commentPrefix the prefix that identifies SQL line comments |
|
|
|
|
* (typically "--") |
|
|
|
|
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter; |
|
|
|
|
* never {@code null} or empty |
|
|
|
|
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter; |
|
|
|
|
* never {@code null} or empty |
|
|
|
|
* @param statements the list that will contain the individual statements |
|
|
|
|
* @throws ScriptException if an error occurred while splitting the SQL script |
|
|
|
|
*/ |
|
|
|
|
public static void splitSqlScript(@Nullable EncodedResource resource, String script, |
|
|
|
|
String separator, String commentPrefix, String blockCommentStartDelimiter, |
|
|
|
|
String blockCommentEndDelimiter, List<String> statements) throws ScriptException { |
|
|
|
|
|
|
|
|
|
Assert.hasText(commentPrefix, "'commentPrefix' must not be null or empty"); |
|
|
|
|
splitSqlScript(resource, script, separator, new String[] { commentPrefix }, |
|
|
|
|
blockCommentStartDelimiter, blockCommentEndDelimiter, statements); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Split an SQL script into separate statements delimited by the provided |
|
|
|
|
* separator string. Each individual statement will be added to the provided |
|
|
|
|
* {@code List}. |
|
|
|
|
* <p>Within the script, the provided {@code commentPrefixes} will be honored: |
|
|
|
|
* any text beginning with one of the comment prefixes and extending to the |
|
|
|
|
* end of the line will be omitted from the output. Similarly, the provided |
|
|
|
|
* {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter} |
|
|
|
|
* delimiters will be honored: any text enclosed in a block comment will be |
|
|
|
|
* omitted from the output. In addition, multiple adjacent whitespace characters |
|
|
|
|
* will be collapsed into a single space. |
|
|
|
|
* @param resource the resource from which the script was read |
|
|
|
|
* @param script the SQL script |
|
|
|
|
* @param separator text separating each statement |
|
|
|
|
* (typically a ';' or newline character) |
|
|
|
|
* @param commentPrefixes the prefixes that identify SQL line comments |
|
|
|
|
* (typically "--") |
|
|
|
|
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter; |
|
|
|
|
* never {@code null} or empty |
|
|
|
|
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter; |
|
|
|
|
* never {@code null} or empty |
|
|
|
|
* @param statements the list that will contain the individual statements |
|
|
|
|
* @throws ScriptException if an error occurred while splitting the SQL script |
|
|
|
|
* @since 5.2 |
|
|
|
|
*/ |
|
|
|
|
public static void splitSqlScript(@Nullable EncodedResource resource, String script, |
|
|
|
|
String separator, String[] commentPrefixes, String blockCommentStartDelimiter, |
|
|
|
|
String blockCommentEndDelimiter) throws ScriptException { |
|
|
|
|
String blockCommentEndDelimiter, List<String> statements) throws ScriptException { |
|
|
|
|
|
|
|
|
|
Assert.hasText(script, "'script' must not be null or empty"); |
|
|
|
|
Assert.notNull(separator, "'separator' must not be null"); |
|
|
|
|
Assert.notEmpty(commentPrefixes, "'commentPrefixes' must not be null or empty"); |
|
|
|
|
for (String commentPrefix : commentPrefixes) { |
|
|
|
|
Assert.hasText(commentPrefix, "'commentPrefixes' must not contain null or empty elements"); |
|
|
|
|
} |
|
|
|
|
Assert.hasText(blockCommentStartDelimiter, "'blockCommentStartDelimiter' must not be null or empty"); |
|
|
|
|
Assert.hasText(blockCommentEndDelimiter, "'blockCommentEndDelimiter' must not be null or empty"); |
|
|
|
|
|
|
|
|
|
StringBuilder sb = new StringBuilder(); |
|
|
|
|
boolean inSingleQuote = false; |
|
|
|
|
boolean inDoubleQuote = false; |
|
|
|
|
boolean inEscape = false; |
|
|
|
|
@ -453,11 +630,13 @@ public abstract class ScriptUtils {
@@ -453,11 +630,13 @@ public abstract class ScriptUtils {
|
|
|
|
|
char c = script.charAt(i); |
|
|
|
|
if (inEscape) { |
|
|
|
|
inEscape = false; |
|
|
|
|
sb.append(c); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
// MySQL style escapes
|
|
|
|
|
if (c == '\\') { |
|
|
|
|
inEscape = true; |
|
|
|
|
sb.append(c); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
if (!inDoubleQuote && (c == '\'')) { |
|
|
|
|
@ -468,7 +647,13 @@ public abstract class ScriptUtils {
@@ -468,7 +647,13 @@ public abstract class ScriptUtils {
|
|
|
|
|
} |
|
|
|
|
if (!inSingleQuote && !inDoubleQuote) { |
|
|
|
|
if (script.startsWith(separator, i)) { |
|
|
|
|
return true; |
|
|
|
|
// We've reached the end of the current statement
|
|
|
|
|
if (sb.length() > 0) { |
|
|
|
|
statements.add(sb.toString()); |
|
|
|
|
sb = new StringBuilder(); |
|
|
|
|
} |
|
|
|
|
i += separator.length() - 1; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
else if (startsWithAny(script, commentPrefixes, i)) { |
|
|
|
|
// Skip over any content from the start of the comment to the EOL
|
|
|
|
|
@ -494,215 +679,31 @@ public abstract class ScriptUtils {
@@ -494,215 +679,31 @@ public abstract class ScriptUtils {
|
|
|
|
|
"Missing block comment end delimiter: " + blockCommentEndDelimiter, resource); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Execute the given SQL script using default settings for statement |
|
|
|
|
* separators, comment delimiters, and exception handling flags. |
|
|
|
|
* <p>Statement separators and comments will be removed before executing |
|
|
|
|
* individual statements within the supplied script. |
|
|
|
|
* <p><strong>Warning</strong>: this method does <em>not</em> release the |
|
|
|
|
* provided {@link Connection}. |
|
|
|
|
* @param connection the JDBC connection to use to execute the script; already |
|
|
|
|
* configured and ready to use |
|
|
|
|
* @param resource the resource to load the SQL script from; encoded with the |
|
|
|
|
* current platform's default encoding |
|
|
|
|
* @throws ScriptException if an error occurred while executing the SQL script |
|
|
|
|
* @see #executeSqlScript(Connection, EncodedResource, boolean, boolean, String, String, String, String) |
|
|
|
|
* @see #DEFAULT_STATEMENT_SEPARATOR |
|
|
|
|
* @see #DEFAULT_COMMENT_PREFIX |
|
|
|
|
* @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER |
|
|
|
|
* @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection |
|
|
|
|
*/ |
|
|
|
|
public static void executeSqlScript(Connection connection, Resource resource) throws ScriptException { |
|
|
|
|
executeSqlScript(connection, new EncodedResource(resource)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Execute the given SQL script using default settings for statement |
|
|
|
|
* separators, comment delimiters, and exception handling flags. |
|
|
|
|
* <p>Statement separators and comments will be removed before executing |
|
|
|
|
* individual statements within the supplied script. |
|
|
|
|
* <p><strong>Warning</strong>: this method does <em>not</em> release the |
|
|
|
|
* provided {@link Connection}. |
|
|
|
|
* @param connection the JDBC connection to use to execute the script; already |
|
|
|
|
* configured and ready to use |
|
|
|
|
* @param resource the resource (potentially associated with a specific encoding) |
|
|
|
|
* to load the SQL script from |
|
|
|
|
* @throws ScriptException if an error occurred while executing the SQL script |
|
|
|
|
* @see #executeSqlScript(Connection, EncodedResource, boolean, boolean, String, String, String, String) |
|
|
|
|
* @see #DEFAULT_STATEMENT_SEPARATOR |
|
|
|
|
* @see #DEFAULT_COMMENT_PREFIX |
|
|
|
|
* @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER |
|
|
|
|
* @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection |
|
|
|
|
*/ |
|
|
|
|
public static void executeSqlScript(Connection connection, EncodedResource resource) throws ScriptException { |
|
|
|
|
executeSqlScript(connection, resource, false, false, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, |
|
|
|
|
DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Execute the given SQL script. |
|
|
|
|
* <p>Statement separators and comments will be removed before executing |
|
|
|
|
* individual statements within the supplied script. |
|
|
|
|
* <p><strong>Warning</strong>: this method does <em>not</em> release the |
|
|
|
|
* provided {@link Connection}. |
|
|
|
|
* @param connection the JDBC connection to use to execute the script; already |
|
|
|
|
* configured and ready to use |
|
|
|
|
* @param resource the resource (potentially associated with a specific encoding) |
|
|
|
|
* to load the SQL script from |
|
|
|
|
* @param continueOnError whether or not to continue without throwing an exception |
|
|
|
|
* in the event of an error |
|
|
|
|
* @param ignoreFailedDrops whether or not to continue in the event of specifically |
|
|
|
|
* an error on a {@code DROP} statement |
|
|
|
|
* @param commentPrefix the prefix that identifies single-line comments in the |
|
|
|
|
* SQL script (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; may be set to |
|
|
|
|
* {@value #EOF_STATEMENT_SEPARATOR} to signal that the script contains a |
|
|
|
|
* single statement without a separator |
|
|
|
|
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter |
|
|
|
|
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter |
|
|
|
|
* @throws ScriptException if an error occurred while executing the SQL script |
|
|
|
|
* @see #DEFAULT_STATEMENT_SEPARATOR |
|
|
|
|
* @see #FALLBACK_STATEMENT_SEPARATOR |
|
|
|
|
* @see #EOF_STATEMENT_SEPARATOR |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection |
|
|
|
|
*/ |
|
|
|
|
public static void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError, |
|
|
|
|
boolean ignoreFailedDrops, String commentPrefix, @Nullable String separator, |
|
|
|
|
String blockCommentStartDelimiter, String blockCommentEndDelimiter) throws ScriptException { |
|
|
|
|
|
|
|
|
|
executeSqlScript(connection, resource, continueOnError, ignoreFailedDrops, |
|
|
|
|
new String[] { commentPrefix }, separator, blockCommentStartDelimiter, |
|
|
|
|
blockCommentEndDelimiter); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Execute the given SQL script. |
|
|
|
|
* <p>Statement separators and comments will be removed before executing |
|
|
|
|
* individual statements within the supplied script. |
|
|
|
|
* <p><strong>Warning</strong>: this method does <em>not</em> release the |
|
|
|
|
* provided {@link Connection}. |
|
|
|
|
* @param connection the JDBC connection to use to execute the script; already |
|
|
|
|
* configured and ready to use |
|
|
|
|
* @param resource the resource (potentially associated with a specific encoding) |
|
|
|
|
* to load the SQL script from |
|
|
|
|
* @param continueOnError whether or not to continue without throwing an exception |
|
|
|
|
* in the event of an error |
|
|
|
|
* @param ignoreFailedDrops whether or not to continue in the event of specifically |
|
|
|
|
* an error on a {@code DROP} statement |
|
|
|
|
* @param commentPrefixes the prefixes that identify single-line comments in the |
|
|
|
|
* SQL script (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; may be set to |
|
|
|
|
* {@value #EOF_STATEMENT_SEPARATOR} to signal that the script contains a |
|
|
|
|
* single statement without a separator |
|
|
|
|
* @param blockCommentStartDelimiter the <em>start</em> block comment delimiter |
|
|
|
|
* @param blockCommentEndDelimiter the <em>end</em> block comment delimiter |
|
|
|
|
* @throws ScriptException if an error occurred while executing the SQL script |
|
|
|
|
* @since 5.2 |
|
|
|
|
* @see #DEFAULT_STATEMENT_SEPARATOR |
|
|
|
|
* @see #FALLBACK_STATEMENT_SEPARATOR |
|
|
|
|
* @see #EOF_STATEMENT_SEPARATOR |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection |
|
|
|
|
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection |
|
|
|
|
*/ |
|
|
|
|
public static void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError, |
|
|
|
|
boolean ignoreFailedDrops, String[] commentPrefixes, @Nullable String separator, |
|
|
|
|
String blockCommentStartDelimiter, String blockCommentEndDelimiter) throws ScriptException { |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
logger.debug("Executing SQL script from " + resource); |
|
|
|
|
} |
|
|
|
|
long startTime = System.currentTimeMillis(); |
|
|
|
|
|
|
|
|
|
String script; |
|
|
|
|
try { |
|
|
|
|
script = readScript(resource, separator, commentPrefixes, blockCommentEndDelimiter); |
|
|
|
|
} |
|
|
|
|
catch (IOException ex) { |
|
|
|
|
throw new CannotReadScriptException(resource, ex); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (separator == null) { |
|
|
|
|
separator = DEFAULT_STATEMENT_SEPARATOR; |
|
|
|
|
} |
|
|
|
|
if (!EOF_STATEMENT_SEPARATOR.equals(separator) && |
|
|
|
|
!containsStatementSeparator(resource, script, separator, commentPrefixes, |
|
|
|
|
blockCommentStartDelimiter, blockCommentEndDelimiter)) { |
|
|
|
|
separator = FALLBACK_STATEMENT_SEPARATOR; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
List<String> statements = new ArrayList<>(); |
|
|
|
|
splitSqlScript(resource, script, separator, commentPrefixes, blockCommentStartDelimiter, |
|
|
|
|
blockCommentEndDelimiter, statements); |
|
|
|
|
|
|
|
|
|
int stmtNumber = 0; |
|
|
|
|
Statement stmt = connection.createStatement(); |
|
|
|
|
try { |
|
|
|
|
for (String statement : statements) { |
|
|
|
|
stmtNumber++; |
|
|
|
|
try { |
|
|
|
|
stmt.execute(statement); |
|
|
|
|
int rowsAffected = stmt.getUpdateCount(); |
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
logger.debug(rowsAffected + " returned as update count for SQL: " + statement); |
|
|
|
|
SQLWarning warningToLog = stmt.getWarnings(); |
|
|
|
|
while (warningToLog != null) { |
|
|
|
|
logger.debug("SQLWarning ignored: SQL state '" + warningToLog.getSQLState() + |
|
|
|
|
"', error code '" + warningToLog.getErrorCode() + |
|
|
|
|
"', message [" + warningToLog.getMessage() + "]"); |
|
|
|
|
warningToLog = warningToLog.getNextWarning(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (c == ' ' || c == '\r' || c == '\n' || c == '\t') { |
|
|
|
|
// Avoid multiple adjacent whitespace characters
|
|
|
|
|
if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') { |
|
|
|
|
c = ' '; |
|
|
|
|
} |
|
|
|
|
catch (SQLException ex) { |
|
|
|
|
boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop"); |
|
|
|
|
if (continueOnError || (dropStatement && ignoreFailedDrops)) { |
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
logger.debug(ScriptStatementFailedException.buildErrorMessage(statement, stmtNumber, resource), ex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
throw new ScriptStatementFailedException(statement, stmtNumber, resource, ex); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
finally { |
|
|
|
|
try { |
|
|
|
|
stmt.close(); |
|
|
|
|
} |
|
|
|
|
catch (Throwable ex) { |
|
|
|
|
logger.trace("Could not close JDBC Statement", ex); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
sb.append(c); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
long elapsedTime = System.currentTimeMillis() - startTime; |
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
|
logger.debug("Executed SQL script from " + resource + " in " + elapsedTime + " ms."); |
|
|
|
|
} |
|
|
|
|
if (StringUtils.hasText(sb)) { |
|
|
|
|
statements.add(sb.toString()); |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) { |
|
|
|
|
if (ex instanceof ScriptException) { |
|
|
|
|
throw (ScriptException) ex; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static boolean startsWithAny(String script, String[] prefixes, int offset) { |
|
|
|
|
for (String prefix : prefixes) { |
|
|
|
|
if (script.startsWith(prefix, offset)) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
throw new UncategorizedScriptException( |
|
|
|
|
"Failed to execute database script from resource [" + resource + "]", ex); |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|