ScriptUtils.java 20.9 KB
Newer Older
1
/*
2
 * Copyright 2002-2016 the original author or authors.
3 4 5 6 7
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9 10 11 12 13 14 15 16 17 18 19 20
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.jdbc.datasource.init;

import java.io.IOException;
import java.io.LineNumberReader;
S
Sam Brannen 已提交
21
import java.sql.Connection;
22
import java.sql.SQLException;
23
import java.sql.SQLWarning;
S
Sam Brannen 已提交
24
import java.sql.Statement;
25 26 27 28 29
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
30

31
import org.springframework.core.io.Resource;
32
import org.springframework.core.io.support.EncodedResource;
S
Sam Brannen 已提交
33
import org.springframework.util.Assert;
34 35 36
import org.springframework.util.StringUtils;

/**
37 38 39
 * Generic utility methods for working with SQL scripts.
 *
 * <p>Mainly for internal use within the framework.
S
Sam Brannen 已提交
40 41 42 43 44 45 46 47
 *
 * @author Thomas Risberg
 * @author Sam Brannen
 * @author Juergen Hoeller
 * @author Keith Donald
 * @author Dave Syer
 * @author Chris Beams
 * @author Oliver Gierke
48
 * @author Chris Baldwin
49
 * @author Nicolas Debeissat
50 51 52 53
 * @since 4.0.3
 */
public abstract class ScriptUtils {

S
Sam Brannen 已提交
54
	/**
55
	 * Default statement separator within SQL scripts: {@code ";"}.
S
Sam Brannen 已提交
56 57 58
	 */
	public static final String DEFAULT_STATEMENT_SEPARATOR = ";";

59
	/**
60 61
	 * Fallback statement separator within SQL scripts: {@code "\n"}.
	 * <p>Used if neither a custom separator nor the
62 63 64 65
	 * {@link #DEFAULT_STATEMENT_SEPARATOR} is present in a given script.
	 */
	public static final String FALLBACK_STATEMENT_SEPARATOR = "\n";

66
	/**
67
	 * End of file (EOF) SQL statement separator: {@code "^^^ END OF SCRIPT ^^^"}.
68 69 70 71 72 73 74
	 * <p>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
	 * <em>virtual</em> statement separator.
	 */
75
	public static final String EOF_STATEMENT_SEPARATOR = "^^^ END OF SCRIPT ^^^";
76

S
Sam Brannen 已提交
77
	/**
78
	 * Default prefix for single-line comments within SQL scripts: {@code "--"}.
S
Sam Brannen 已提交
79
	 */
80 81
	public static final String DEFAULT_COMMENT_PREFIX = "--";

S
Sam Brannen 已提交
82
	/**
83
	 * Default start delimiter for block comments within SQL scripts: {@code "/*"}.
S
Sam Brannen 已提交
84
	 */
85
	public static final String DEFAULT_BLOCK_COMMENT_START_DELIMITER = "/*";
S
Sam Brannen 已提交
86 87

	/**
88
	 * Default end delimiter for block comments within SQL scripts: <code>"*&#47;"</code>.
S
Sam Brannen 已提交
89
	 */
90 91
	public static final String DEFAULT_BLOCK_COMMENT_END_DELIMITER = "*/";

S
Sam Brannen 已提交
92

93 94
	private static final Log logger = LogFactory.getLog(ScriptUtils.class);

S
Sam Brannen 已提交
95

96 97
	/**
	 * Split an SQL script into separate statements delimited by the provided
98 99 100 101 102 103 104 105 106 107 108 109 110
	 * 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 &mdash; typically a ';'
	 * @param statements the list that will contain the individual statements
111
	 * @throws ScriptException if an error occurred while splitting the SQL script
112 113 114 115 116 117 118 119 120 121
	 * @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
122
	 * provided {@code List}.
S
Sam Brannen 已提交
123 124 125 126 127 128 129 130
	 * <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.
131
	 * @param script the SQL script
132
	 * @param separator text separating each statement &mdash; typically a ';' or newline character
133
	 * @param statements the list that will contain the individual statements
134
	 * @throws ScriptException if an error occurred while splitting the SQL script
135
	 * @see #splitSqlScript(String, char, List)
S
Sam Brannen 已提交
136
	 * @see #splitSqlScript(EncodedResource, String, String, String, String, String, List)
137
	 */
138 139
	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,
140
				DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements);
141 142 143 144
	}

	/**
	 * Split an SQL script into separate statements delimited by the provided
145
	 * separator string. Each individual statement will be added to the provided
146
	 * {@code List}.
S
Sam Brannen 已提交
147
	 * <p>Within the script, the provided {@code commentPrefix} will be honored:
148
	 * any text beginning with the comment prefix and extending to the end of the
S
Sam Brannen 已提交
149 150 151 152 153 154 155
	 * 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; never {@code null} or empty
156 157
	 * @param separator text separating each statement &mdash; typically a ';' or
	 * newline character; never {@code null}
S
Sam Brannen 已提交
158 159 160 161 162 163 164
	 * @param commentPrefix the prefix that identifies SQL line comments &mdash;
	 * typically "--"; never {@code null} or empty
	 * @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
165
	 * @throws ScriptException if an error occurred while splitting the SQL script
166
	 */
167
	public static void splitSqlScript(EncodedResource resource, String script, String separator, String commentPrefix,
S
Sam Brannen 已提交
168 169 170 171
			String blockCommentStartDelimiter, String blockCommentEndDelimiter, List<String> statements)
			throws ScriptException {

		Assert.hasText(script, "script must not be null or empty");
172
		Assert.notNull(separator, "separator must not be null");
S
Sam Brannen 已提交
173 174 175 176
		Assert.hasText(commentPrefix, "commentPrefix must not be null or empty");
		Assert.hasText(blockCommentStartDelimiter, "blockCommentStartDelimiter must not be null or empty");
		Assert.hasText(blockCommentEndDelimiter, "blockCommentEndDelimiter must not be null or empty");

177
		StringBuilder sb = new StringBuilder();
178 179
		boolean inSingleQuote = false;
		boolean inDoubleQuote = false;
180 181
		boolean inEscape = false;
		for (int i = 0; i < script.length(); i++) {
182
			char c = script.charAt(i);
183 184 185 186 187 188 189 190 191 192 193
			if (inEscape) {
				inEscape = false;
				sb.append(c);
				continue;
			}
			// MySQL style escapes
			if (c == '\\') {
				inEscape = true;
				sb.append(c);
				continue;
			}
194 195
			if (!inDoubleQuote && (c == '\'')) {
				inSingleQuote = !inSingleQuote;
196
			}
197 198 199 200
			else if (!inSingleQuote && (c == '"')) {
				inDoubleQuote = !inDoubleQuote;
			}
			if (!inSingleQuote && !inDoubleQuote) {
201
				if (script.startsWith(separator, i)) {
202 203 204 205 206
					// we've reached the end of the current statement
					if (sb.length() > 0) {
						statements.add(sb.toString());
						sb = new StringBuilder();
					}
207
					i += separator.length() - 1;
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
					continue;
				}
				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 EOL, we must be at the end
						// of the script, so stop here.
						break;
					}
				}
S
Sam Brannen 已提交
223
				else if (script.startsWith(blockCommentStartDelimiter, i)) {
224
					// skip over any block comments
S
Sam Brannen 已提交
225 226 227
					int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i);
					if (indexOfCommentEnd > i) {
						i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1;
228 229 230
						continue;
					}
					else {
S
Sam Brannen 已提交
231 232
						throw new ScriptParseException(String.format("Missing block comment end delimiter [%s].",
							blockCommentEndDelimiter), resource);
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
					}
				}
				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);
		}
		if (StringUtils.hasText(sb)) {
			statements.add(sb.toString());
		}
	}

	/**
S
Sam Brannen 已提交
253 254
	 * Read a script from the given resource, using "{@code --}" as the comment prefix
	 * and "{@code ;}" as the statement separator, and build a String containing the lines.
255 256 257 258
	 * @param resource the {@code EncodedResource} to be read
	 * @return {@code String} containing the script lines
	 * @throws IOException in case of I/O errors
	 */
S
Sam Brannen 已提交
259 260
	static String readScript(EncodedResource resource) throws IOException {
		return readScript(resource, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR);
261
	}
S
Sam Brannen 已提交
262

263
	/**
264 265
	 * Read a script from the provided resource, using the supplied comment prefix
	 * and statement separator, and build a {@code String} containing the lines.
266 267 268 269 270
	 * <p>Lines <em>beginning</em> with the comment prefix are excluded from the
	 * results; however, line comments anywhere else &mdash; for example, within
	 * a statement &mdash; will be included in the results.
	 * @param resource the {@code EncodedResource} containing the script
	 * to be processed
S
Sam Brannen 已提交
271 272
	 * @param commentPrefix the prefix that identifies comments in the SQL script &mdash;
	 * typically "--"
273 274
	 * @param separator the statement separator in the SQL script &mdash; typically ";"
	 * @return a {@code String} containing the script lines
275
	 * @throws IOException in case of I/O errors
276
	 */
S
Sam Brannen 已提交
277 278
	private static String readScript(EncodedResource resource, String commentPrefix, String separator)
			throws IOException {
279

280 281 282 283 284 285 286 287
		LineNumberReader lnr = new LineNumberReader(resource.getReader());
		try {
			return readScript(lnr, commentPrefix, separator);
		}
		finally {
			lnr.close();
		}
	}
S
Sam Brannen 已提交
288

289 290
	/**
	 * Read a script from the provided {@code LineNumberReader}, using the supplied
S
Sam Brannen 已提交
291 292
	 * comment prefix and statement separator, and build a {@code String} containing
	 * the lines.
293 294 295 296 297
	 * <p>Lines <em>beginning</em> with the comment prefix are excluded from the
	 * results; however, line comments anywhere else &mdash; for example, within
	 * a statement &mdash; will be included in the results.
	 * @param lineNumberReader the {@code LineNumberReader} containing the script
	 * to be processed
S
Sam Brannen 已提交
298 299
	 * @param commentPrefix the prefix that identifies comments in the SQL script &mdash;
	 * typically "--"
300 301
	 * @param separator the statement separator in the SQL script &mdash; typically ";"
	 * @return a {@code String} containing the script lines
302
	 * @throws IOException in case of I/O errors
303
	 */
S
Sam Brannen 已提交
304 305
	public static String readScript(LineNumberReader lineNumberReader, String commentPrefix, String separator)
			throws IOException {
306

307 308 309
		String currentStatement = lineNumberReader.readLine();
		StringBuilder scriptBuilder = new StringBuilder();
		while (currentStatement != null) {
310
			if (commentPrefix != null && !currentStatement.startsWith(commentPrefix)) {
311 312 313 314 315 316 317
				if (scriptBuilder.length() > 0) {
					scriptBuilder.append('\n');
				}
				scriptBuilder.append(currentStatement);
			}
			currentStatement = lineNumberReader.readLine();
		}
318
		appendSeparatorToScriptIfNecessary(scriptBuilder, separator);
319 320 321
		return scriptBuilder.toString();
	}

322
	private static void appendSeparatorToScriptIfNecessary(StringBuilder scriptBuilder, String separator) {
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
		if (separator == null) {
			return;
		}
		String trimmed = separator.trim();
		if (trimmed.length() == separator.length()) {
			return;
		}
		// 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(separator.substring(trimmed.length()));
		}
	}

	/**
	 * Does the provided SQL script contain the specified delimiter?
	 * @param script the SQL script
	 * @param delim String delimiting each statement - typically a ';' character
	 */
	public static boolean containsSqlScriptDelimiters(String script, String delim) {
		boolean inLiteral = false;
		for (int i = 0; i < script.length(); i++) {
345
			if (script.charAt(i) == '\'') {
346 347 348 349 350 351 352 353 354
				inLiteral = !inLiteral;
			}
			if (!inLiteral && script.startsWith(delim, i)) {
				return true;
			}
		}
		return false;
	}

355
	/**
356 357
	 * Execute the given SQL script using default settings for statement
	 * separators, comment delimiters, and exception handling flags.
358 359
	 * <p>Statement separators and comments will be removed before executing
	 * individual statements within the supplied script.
360 361
	 * <p><strong>Warning</strong>: this method does <em>not</em> release the
	 * provided {@link Connection}.
362 363 364 365
	 * @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
366
	 * @throws ScriptException if an error occurred while executing the SQL script
367 368
	 * @see #executeSqlScript(Connection, EncodedResource, boolean, boolean, String, String, String, String)
	 * @see #DEFAULT_STATEMENT_SEPARATOR
369
	 * @see #DEFAULT_COMMENT_PREFIX
370 371
	 * @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER
	 * @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER
372 373
	 * @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
	 * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
374
	 */
375
	public static void executeSqlScript(Connection connection, Resource resource) throws ScriptException {
376 377 378 379
		executeSqlScript(connection, new EncodedResource(resource));
	}

	/**
380 381
	 * Execute the given SQL script using default settings for statement
	 * separators, comment delimiters, and exception handling flags.
382 383
	 * <p>Statement separators and comments will be removed before executing
	 * individual statements within the supplied script.
384 385
	 * <p><strong>Warning</strong>: this method does <em>not</em> release the
	 * provided {@link Connection}.
386 387 388 389
	 * @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
390
	 * @throws ScriptException if an error occurred while executing the SQL script
391 392
	 * @see #executeSqlScript(Connection, EncodedResource, boolean, boolean, String, String, String, String)
	 * @see #DEFAULT_STATEMENT_SEPARATOR
393
	 * @see #DEFAULT_COMMENT_PREFIX
394 395
	 * @see #DEFAULT_BLOCK_COMMENT_START_DELIMITER
	 * @see #DEFAULT_BLOCK_COMMENT_END_DELIMITER
396 397
	 * @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
	 * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
398
	 */
399
	public static void executeSqlScript(Connection connection, EncodedResource resource) throws ScriptException {
400
		executeSqlScript(connection, resource, false, false, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR,
401
				DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER);
402 403
	}

404 405
	/**
	 * Execute the given SQL script.
S
Sam Brannen 已提交
406 407
	 * <p>Statement separators and comments will be removed before executing
	 * individual statements within the supplied script.
408 409
	 * <p><strong>Warning</strong>: this method does <em>not</em> release the
	 * provided {@link Connection}.
S
Sam Brannen 已提交
410 411 412 413 414 415 416 417
	 * @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
418 419
	 * @param commentPrefix the prefix that identifies single-line comments in the
	 * SQL script &mdash; typically "--"
S
Sam Brannen 已提交
420
	 * @param separator the script statement separator; defaults to
421
	 * {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified and falls back to
422 423 424
	 * {@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
S
Sam Brannen 已提交
425 426 427 428
	 * @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
429
	 * @throws ScriptException if an error occurred while executing the SQL script
430 431 432
	 * @see #DEFAULT_STATEMENT_SEPARATOR
	 * @see #FALLBACK_STATEMENT_SEPARATOR
	 * @see #EOF_STATEMENT_SEPARATOR
433 434
	 * @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
	 * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
435
	 */
S
Sam Brannen 已提交
436 437
	public static void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError,
			boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter,
438
			String blockCommentEndDelimiter) throws ScriptException {
439 440

		try {
441 442 443 444
			if (logger.isInfoEnabled()) {
				logger.info("Executing SQL script from " + resource);
			}
			long startTime = System.currentTimeMillis();
445

446 447 448 449 450 451 452
			String script;
			try {
				script = readScript(resource, commentPrefix, separator);
			}
			catch (IOException ex) {
				throw new CannotReadScriptException(resource, ex);
			}
S
Sam Brannen 已提交
453

454 455 456
			if (separator == null) {
				separator = DEFAULT_STATEMENT_SEPARATOR;
			}
457
			if (!EOF_STATEMENT_SEPARATOR.equals(separator) && !containsSqlScriptDelimiters(script, separator)) {
458 459 460
				separator = FALLBACK_STATEMENT_SEPARATOR;
			}

461
			List<String> statements = new LinkedList<>();
462
			splitSqlScript(resource, script, separator, commentPrefix, blockCommentStartDelimiter,
463 464 465
					blockCommentEndDelimiter, statements);

			int stmtNumber = 0;
466 467 468
			Statement stmt = connection.createStatement();
			try {
				for (String statement : statements) {
469
					stmtNumber++;
470 471 472
					try {
						stmt.execute(statement);
						int rowsAffected = stmt.getUpdateCount();
S
Sam Brannen 已提交
473
						if (logger.isDebugEnabled()) {
474 475 476 477 478 479 480 481
							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();
							}
S
Sam Brannen 已提交
482 483
						}
					}
484 485 486 487
					catch (SQLException ex) {
						boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop");
						if (continueOnError || (dropStatement && ignoreFailedDrops)) {
							if (logger.isDebugEnabled()) {
488
								logger.debug(ScriptStatementFailedException.buildErrorMessage(statement, stmtNumber, resource), ex);
489 490 491
							}
						}
						else {
492
							throw new ScriptStatementFailedException(statement, stmtNumber, resource, ex);
493
						}
S
Sam Brannen 已提交
494
					}
495 496
				}
			}
497 498 499 500 501 502 503
			finally {
				try {
					stmt.close();
				}
				catch (Throwable ex) {
					logger.debug("Could not close JDBC Statement", ex);
				}
S
Sam Brannen 已提交
504
			}
505 506 507 508

			long elapsedTime = System.currentTimeMillis() - startTime;
			if (logger.isInfoEnabled()) {
				logger.info("Executed SQL script from " + resource + " in " + elapsedTime + " ms.");
S
Sam Brannen 已提交
509 510
			}
		}
511 512 513 514 515 516
		catch (Exception ex) {
			if (ex instanceof ScriptException) {
				throw (ScriptException) ex;
			}
			throw new UncategorizedScriptException(
				"Failed to execute database script from resource [" + resource + "]", ex);
517 518 519 520
		}
	}

}