1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.jdbc;
17
18 import java.io.BufferedReader;
19 import java.io.PrintWriter;
20 import java.io.Reader;
21 import java.sql.Connection;
22 import java.sql.ResultSet;
23 import java.sql.ResultSetMetaData;
24 import java.sql.SQLException;
25 import java.sql.SQLWarning;
26 import java.sql.Statement;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29
30
31
32
33
34
35
36
37
38 public class ScriptRunner {
39
40 private static final String LINE_SEPARATOR = System.lineSeparator();
41
42 private static final String DEFAULT_DELIMITER = ";";
43
44 private static final Pattern DELIMITER_PATTERN = Pattern.compile("^\\s*((--)|(//))?\\s*(//)?\\s*@DELIMITER\\s+([^\\s]+)", Pattern.CASE_INSENSITIVE);
45
46 private final Connection connection;
47
48 private boolean stopOnError;
49 private boolean throwWarning;
50 private boolean autoCommit;
51 private boolean sendFullScript;
52 private boolean removeCRs;
53 private boolean escapeProcessing = true;
54
55 private PrintWriter logWriter = new PrintWriter(System.out);
56 private PrintWriter errorLogWriter = new PrintWriter(System.err);
57
58 private String delimiter = DEFAULT_DELIMITER;
59 private boolean fullLineDelimiter;
60
61 public ScriptRunner(Connection connection) {
62 this.connection = connection;
63 }
64
65 public void setStopOnError(boolean stopOnError) {
66 this.stopOnError = stopOnError;
67 }
68
69 public void setThrowWarning(boolean throwWarning) {
70 this.throwWarning = throwWarning;
71 }
72
73 public void setAutoCommit(boolean autoCommit) {
74 this.autoCommit = autoCommit;
75 }
76
77 public void setSendFullScript(boolean sendFullScript) {
78 this.sendFullScript = sendFullScript;
79 }
80
81 public void setRemoveCRs(boolean removeCRs) {
82 this.removeCRs = removeCRs;
83 }
84
85
86
87
88
89
90
91
92 public void setEscapeProcessing(boolean escapeProcessing) {
93 this.escapeProcessing = escapeProcessing;
94 }
95
96 public void setLogWriter(PrintWriter logWriter) {
97 this.logWriter = logWriter;
98 }
99
100 public void setErrorLogWriter(PrintWriter errorLogWriter) {
101 this.errorLogWriter = errorLogWriter;
102 }
103
104 public void setDelimiter(String delimiter) {
105 this.delimiter = delimiter;
106 }
107
108 public void setFullLineDelimiter(boolean fullLineDelimiter) {
109 this.fullLineDelimiter = fullLineDelimiter;
110 }
111
112 public void runScript(Reader reader) {
113 setAutoCommit();
114
115 try {
116 if (sendFullScript) {
117 executeFullScript(reader);
118 } else {
119 executeLineByLine(reader);
120 }
121 } finally {
122 rollbackConnection();
123 }
124 }
125
126 private void executeFullScript(Reader reader) {
127 StringBuilder script = new StringBuilder();
128 try {
129 BufferedReader lineReader = new BufferedReader(reader);
130 String line;
131 while ((line = lineReader.readLine()) != null) {
132 script.append(line);
133 script.append(LINE_SEPARATOR);
134 }
135 String command = script.toString();
136 println(command);
137 executeStatement(command);
138 commitConnection();
139 } catch (Exception e) {
140 String message = "Error executing: " + script + ". Cause: " + e;
141 printlnError(message);
142 throw new RuntimeSqlException(message, e);
143 }
144 }
145
146 private void executeLineByLine(Reader reader) {
147 StringBuilder command = new StringBuilder();
148 try {
149 BufferedReader lineReader = new BufferedReader(reader);
150 String line;
151 while ((line = lineReader.readLine()) != null) {
152 handleLine(command, line);
153 }
154 commitConnection();
155 checkForMissingLineTerminator(command);
156 } catch (Exception e) {
157 String message = "Error executing: " + command + ". Cause: " + e;
158 printlnError(message);
159 throw new RuntimeSqlException(message, e);
160 }
161 }
162
163
164
165
166 @Deprecated
167 public void closeConnection() {
168 try {
169 connection.close();
170 } catch (Exception e) {
171
172 }
173 }
174
175 private void setAutoCommit() {
176 try {
177 if (autoCommit != connection.getAutoCommit()) {
178 connection.setAutoCommit(autoCommit);
179 }
180 } catch (Throwable t) {
181 throw new RuntimeSqlException("Could not set AutoCommit to " + autoCommit + ". Cause: " + t, t);
182 }
183 }
184
185 private void commitConnection() {
186 try {
187 if (!connection.getAutoCommit()) {
188 connection.commit();
189 }
190 } catch (Throwable t) {
191 throw new RuntimeSqlException("Could not commit transaction. Cause: " + t, t);
192 }
193 }
194
195 private void rollbackConnection() {
196 try {
197 if (!connection.getAutoCommit()) {
198 connection.rollback();
199 }
200 } catch (Throwable t) {
201
202 }
203 }
204
205 private void checkForMissingLineTerminator(StringBuilder command) {
206 if (command != null && command.toString().trim().length() > 0) {
207 throw new RuntimeSqlException("Line missing end-of-line terminator (" + delimiter + ") => " + command);
208 }
209 }
210
211 private void handleLine(StringBuilder command, String line) throws SQLException {
212 String trimmedLine = line.trim();
213 if (lineIsComment(trimmedLine)) {
214 Matcher matcher = DELIMITER_PATTERN.matcher(trimmedLine);
215 if (matcher.find()) {
216 delimiter = matcher.group(5);
217 }
218 println(trimmedLine);
219 } else if (commandReadyToExecute(trimmedLine)) {
220 command.append(line, 0, line.lastIndexOf(delimiter));
221 command.append(LINE_SEPARATOR);
222 println(command);
223 executeStatement(command.toString());
224 command.setLength(0);
225 } else if (trimmedLine.length() > 0) {
226 command.append(line);
227 command.append(LINE_SEPARATOR);
228 }
229 }
230
231 private boolean lineIsComment(String trimmedLine) {
232 return trimmedLine.startsWith("//") || trimmedLine.startsWith("--");
233 }
234
235 private boolean commandReadyToExecute(String trimmedLine) {
236
237 return !fullLineDelimiter && trimmedLine.contains(delimiter) || fullLineDelimiter && trimmedLine.equals(delimiter);
238 }
239
240 private void executeStatement(String command) throws SQLException {
241 try (Statement statement = connection.createStatement()) {
242 statement.setEscapeProcessing(escapeProcessing);
243 String sql = command;
244 if (removeCRs) {
245 sql = sql.replace("\r\n", "\n");
246 }
247 try {
248 boolean hasResults = statement.execute(sql);
249 while (!(!hasResults && statement.getUpdateCount() == -1)) {
250 checkWarnings(statement);
251 printResults(statement, hasResults);
252 hasResults = statement.getMoreResults();
253 }
254 } catch (SQLWarning e) {
255 throw e;
256 } catch (SQLException e) {
257 if (stopOnError) {
258 throw e;
259 } else {
260 String message = "Error executing: " + command + ". Cause: " + e;
261 printlnError(message);
262 }
263 }
264 }
265 }
266
267 private void checkWarnings(Statement statement) throws SQLException {
268 if (!throwWarning) {
269 return;
270 }
271
272
273 SQLWarning warning = statement.getWarnings();
274 if (warning != null) {
275 throw warning;
276 }
277 }
278
279 private void printResults(Statement statement, boolean hasResults) {
280 if (!hasResults) {
281 return;
282 }
283 try (ResultSet rs = statement.getResultSet()) {
284 ResultSetMetaData md = rs.getMetaData();
285 int cols = md.getColumnCount();
286 for (int i = 0; i < cols; i++) {
287 String name = md.getColumnLabel(i + 1);
288 print(name + "\t");
289 }
290 println("");
291 while (rs.next()) {
292 for (int i = 0; i < cols; i++) {
293 String value = rs.getString(i + 1);
294 print(value + "\t");
295 }
296 println("");
297 }
298 } catch (SQLException e) {
299 printlnError("Error printing results: " + e.getMessage());
300 }
301 }
302
303 private void print(Object o) {
304 if (logWriter != null) {
305 logWriter.print(o);
306 logWriter.flush();
307 }
308 }
309
310 private void println(Object o) {
311 if (logWriter != null) {
312 logWriter.println(o);
313 logWriter.flush();
314 }
315 }
316
317 private void printlnError(Object o) {
318 if (errorLogWriter != null) {
319 errorLogWriter.println(o);
320 errorLogWriter.flush();
321 }
322 }
323
324 }