-
Notifications
You must be signed in to change notification settings - Fork 45
/
Copy pathScriptRunner.java
317 lines (281 loc) · 10.6 KB
/
ScriptRunner.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
/*
* Slightly modified version of the com.ibatis.common.jdbc.ScriptRunner class
* from the iBATIS Apache project. Only removed dependency on Resource class
* and a constructor
* GPSHansl, 06.08.2015: regex for delimiter, rearrange comment/delimiter detection, remove some ide warnings.
*/
/*
* Copyright 2004 Clinton Begin
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
import java.io.*;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Tool to run database scripts
*/
public class ScriptRunner {
private static final String DEFAULT_DELIMITER = ";";
private static final Pattern SOURCE_COMMAND = Pattern.compile("^\\s*SOURCE\\s+(.*?)\\s*$", Pattern.CASE_INSENSITIVE);
/**
* regex to detect delimiter.
* ignores spaces, allows delimiter in comment, allows an equals-sign
*/
public static final Pattern delimP = Pattern.compile("^\\s*(--)?\\s*delimiter\\s*=?\\s*([^\\s]+)+\\s*.*$", Pattern.CASE_INSENSITIVE);
private final Connection connection;
private final boolean stopOnError;
private final boolean autoCommit;
@SuppressWarnings("UseOfSystemOutOrSystemErr")
private PrintWriter logWriter = null;
@SuppressWarnings("UseOfSystemOutOrSystemErr")
private PrintWriter errorLogWriter = null;
private String delimiter = DEFAULT_DELIMITER;
private boolean fullLineDelimiter = false;
private String userDirectory = System.getProperty("user.dir");
/**
* Default constructor
*/
public ScriptRunner(Connection connection, boolean autoCommit,
boolean stopOnError) {
this.connection = connection;
this.autoCommit = autoCommit;
this.stopOnError = stopOnError;
File logFile = new File("create_db.log");
File errorLogFile = new File("create_db_error.log");
try {
if (logFile.exists()) {
logWriter = new PrintWriter(new FileWriter(logFile, true));
} else {
logWriter = new PrintWriter(new FileWriter(logFile, false));
}
} catch(IOException e){
System.err.println("Unable to access or create the db_create log");
}
try {
if (errorLogFile.exists()) {
errorLogWriter = new PrintWriter(new FileWriter(errorLogFile, true));
} else {
errorLogWriter = new PrintWriter(new FileWriter(errorLogFile, false));
}
} catch(IOException e){
System.err.println("Unable to access or create the db_create error log");
}
String timeStamp = new SimpleDateFormat("dd/mm/yyyy HH:mm:ss").format(new java.util.Date());
println("\n-------\n" + timeStamp + "\n-------\n");
printlnError("\n-------\n" + timeStamp + "\n-------\n");
}
public void setDelimiter(String delimiter, boolean fullLineDelimiter) {
this.delimiter = delimiter;
this.fullLineDelimiter = fullLineDelimiter;
}
/**
* Setter for logWriter property
*
* @param logWriter - the new value of the logWriter property
*/
public void setLogWriter(PrintWriter logWriter) {
this.logWriter = logWriter;
}
/**
* Setter for errorLogWriter property
*
* @param errorLogWriter - the new value of the errorLogWriter property
*/
public void setErrorLogWriter(PrintWriter errorLogWriter) {
this.errorLogWriter = errorLogWriter;
}
/**
* Set the current working directory. Source commands will be relative to this.
*/
public void setUserDirectory(String userDirectory) {
this.userDirectory = userDirectory;
}
/**
* Runs an SQL script (read in using the Reader parameter)
*
* @param filepath - the filepath of the script to run. May be relative to the userDirectory.
*/
public void runScript(String filepath) throws IOException, SQLException {
File file = new File(userDirectory, filepath);
this.runScript(new BufferedReader(new FileReader(file)));
}
/**
* Runs an SQL script (read in using the Reader parameter)
*
* @param reader - the source of the script
*/
public void runScript(Reader reader) throws IOException, SQLException {
try {
boolean originalAutoCommit = connection.getAutoCommit();
try {
if (originalAutoCommit != this.autoCommit) {
connection.setAutoCommit(this.autoCommit);
}
runScript(connection, reader);
} finally {
connection.setAutoCommit(originalAutoCommit);
}
} catch (IOException | SQLException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Error running script. Cause: " + e, e);
}
}
/**
* Runs an SQL script (read in using the Reader parameter) using the
* connection passed in
*
* @param conn - the connection to use for the script
* @param reader - the source of the script
* @throws SQLException if any SQL errors occur
* @throws IOException if there is an error reading from the Reader
*/
private void runScript(Connection conn, Reader reader) throws IOException,
SQLException {
StringBuffer command = null;
try {
LineNumberReader lineReader = new LineNumberReader(reader);
String line;
while ((line = lineReader.readLine()) != null) {
if (command == null) {
command = new StringBuffer();
}
String trimmedLine = line.trim();
final Matcher delimMatch = delimP.matcher(trimmedLine);
if (trimmedLine.length() < 1
|| trimmedLine.startsWith("//")) {
// Do nothing
} else if (delimMatch.matches()) {
setDelimiter(delimMatch.group(2), false);
} else if (trimmedLine.startsWith("--")) {
println(trimmedLine);
} else if (trimmedLine.length() < 1
|| trimmedLine.startsWith("--")) {
// Do nothing
} else if (!fullLineDelimiter
&& trimmedLine.endsWith(getDelimiter())
|| fullLineDelimiter
&& trimmedLine.equals(getDelimiter())) {
command.append(line.substring(0, line
.lastIndexOf(getDelimiter())));
command.append(" ");
this.execCommand(conn, command, lineReader);
command = null;
} else {
command.append(line);
command.append("\n");
}
}
if (command != null) {
this.execCommand(conn, command, lineReader);
}
if (!autoCommit) {
conn.commit();
}
}
catch (IOException e) {
throw new IOException(String.format("Error executing '%s': %s", command, e.getMessage()), e);
} finally {
conn.rollback();
flush();
}
}
private void execCommand(Connection conn, StringBuffer command,
LineNumberReader lineReader) throws IOException, SQLException {
if (command.length() == 0) {
return;
}
Matcher sourceCommandMatcher = SOURCE_COMMAND.matcher(command);
if (sourceCommandMatcher.matches()) {
this.runScriptFile(conn, sourceCommandMatcher.group(1));
return;
}
this.execSqlCommand(conn, command, lineReader);
}
private void runScriptFile(Connection conn, String filepath) throws IOException, SQLException {
File file = new File(userDirectory, filepath);
this.runScript(conn, new BufferedReader(new FileReader(file)));
}
private void execSqlCommand(Connection conn, StringBuffer command,
LineNumberReader lineReader) throws SQLException {
Statement statement = conn.createStatement();
println(command);
boolean hasResults = false;
try {
hasResults = statement.execute(command.toString());
} catch (SQLException e) {
final String errText = String.format("Error executing '%s' (line %d): %s",
command, lineReader.getLineNumber(), e.getMessage());
printlnError(errText);
System.err.println(errText);
if (stopOnError) {
throw new SQLException(errText, e);
}
}
if (autoCommit && !conn.getAutoCommit()) {
conn.commit();
}
ResultSet rs = statement.getResultSet();
if (hasResults && rs != null) {
ResultSetMetaData md = rs.getMetaData();
int cols = md.getColumnCount();
for (int i = 1; i <= cols; i++) {
String name = md.getColumnLabel(i);
print(name + "\t");
}
println("");
while (rs.next()) {
for (int i = 1; i <= cols; i++) {
String value = rs.getString(i);
print(value + "\t");
}
println("");
}
}
try {
statement.close();
} catch (Exception e) {
// Ignore to workaround a bug in Jakarta DBCP
}
}
private String getDelimiter() {
return delimiter;
}
@SuppressWarnings("UseOfSystemOutOrSystemErr")
private void print(Object o) {
if (logWriter != null) {
logWriter.print(o);
}
}
private void println(Object o) {
if (logWriter != null) {
logWriter.println(o);
}
}
private void printlnError(Object o) {
if (errorLogWriter != null) {
errorLogWriter.println(o);
}
}
private void flush() {
if (logWriter != null) {
logWriter.flush();
}
if (errorLogWriter != null) {
errorLogWriter.flush();
}
}
}