• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / migrations / #457

pending completion
#457

Pull #307

github

web-flow
Merge 3c965b414 into 114baa00b
Pull Request #307: Another set of propery closing resources

11 of 11 new or added lines in 6 files covered. (100.0%)

1774 of 2207 relevant lines covered (80.38%)

0.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.17
/src/main/java/org/apache/ibatis/migration/operations/ScriptRunner.java
1
/*
2
 *    Copyright 2010-2023 the original author or authors.
3
 *
4
 *    Licensed under the Apache License, Version 2.0 (the "License");
5
 *    you may not use this file except in compliance with the License.
6
 *    You may obtain a copy of the License at
7
 *
8
 *       https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *    Unless required by applicable law or agreed to in writing, software
11
 *    distributed under the License is distributed on an "AS IS" BASIS,
12
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *    See the License for the specific language governing permissions and
14
 *    limitations under the License.
15
 */
16
package org.apache.ibatis.migration.operations;
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
 * @author Clinton Begin
32
 */
33
public class ScriptRunner {
34

35
  private static final String LINE_SEPARATOR = System.lineSeparator();
1✔
36

37
  private static final String DEFAULT_DELIMITER = ";";
38

39
  private static final Pattern DELIMITER_PATTERN = Pattern
1✔
40
      .compile("^\\s*((--)|(//))?\\s*(//)?\\s*@DELIMITER\\s+([^\\s]+)", Pattern.CASE_INSENSITIVE);
1✔
41

42
  private final Connection connection;
43

44
  private boolean stopOnError;
45
  private boolean throwWarning;
46
  private boolean autoCommit;
47
  private boolean sendFullScript;
48
  private boolean removeCRs;
49
  private boolean escapeProcessing = true;
1✔
50

51
  private PrintWriter logWriter = new PrintWriter(System.out);
1✔
52
  private PrintWriter errorLogWriter = new PrintWriter(System.err);
1✔
53

54
  private String delimiter = DEFAULT_DELIMITER;
1✔
55
  private boolean fullLineDelimiter;
56

57
  public ScriptRunner(Connection connection) {
1✔
58
    this.connection = connection;
1✔
59
  }
1✔
60

61
  public void setStopOnError(boolean stopOnError) {
62
    this.stopOnError = stopOnError;
1✔
63
  }
1✔
64

65
  public void setThrowWarning(boolean throwWarning) {
66
    this.throwWarning = throwWarning;
1✔
67
  }
1✔
68

69
  public void setAutoCommit(boolean autoCommit) {
70
    this.autoCommit = autoCommit;
1✔
71
  }
1✔
72

73
  public void setSendFullScript(boolean sendFullScript) {
74
    this.sendFullScript = sendFullScript;
1✔
75
  }
1✔
76

77
  public void setRemoveCRs(boolean removeCRs) {
78
    this.removeCRs = removeCRs;
1✔
79
  }
1✔
80

81
  /**
82
   * Sets the escape processing.
83
   *
84
   * @param escapeProcessing
85
   *          the new escape processing
86
   *
87
   * @since 3.1.1
88
   */
89
  public void setEscapeProcessing(boolean escapeProcessing) {
90
    this.escapeProcessing = escapeProcessing;
1✔
91
  }
1✔
92

93
  public void setLogWriter(PrintWriter logWriter) {
94
    this.logWriter = logWriter;
1✔
95
  }
1✔
96

97
  public void setErrorLogWriter(PrintWriter errorLogWriter) {
98
    this.errorLogWriter = errorLogWriter;
1✔
99
  }
1✔
100

101
  public void setDelimiter(String delimiter) {
102
    this.delimiter = delimiter;
1✔
103
  }
1✔
104

105
  public void setFullLineDelimiter(boolean fullLineDelimiter) {
106
    this.fullLineDelimiter = fullLineDelimiter;
1✔
107
  }
1✔
108

109
  public void runScript(Reader reader) {
110
    setAutoCommit();
1✔
111

112
    try {
113
      if (sendFullScript) {
1✔
114
        executeFullScript(reader);
×
115
      } else {
116
        executeLineByLine(reader);
1✔
117
      }
118
    } finally {
119
      rollbackConnection();
1✔
120
    }
121
  }
1✔
122

123
  private void executeFullScript(Reader reader) {
124
    StringBuilder script = new StringBuilder();
×
125
    try (BufferedReader lineReader = new BufferedReader(reader)) {
×
126
      String line;
127
      while ((line = lineReader.readLine()) != null) {
×
128
        script.append(line);
×
129
        script.append(LINE_SEPARATOR);
×
130
      }
131
      String command = script.toString();
×
132
      println(command);
×
133
      executeStatement(command);
×
134
      commitConnection();
×
135
    } catch (Exception e) {
×
136
      String message = "Error executing: " + script + ".  Cause: " + e;
×
137
      printlnError(message);
×
138
      throw new RuntimeException(message, e);
×
139
    }
×
140
  }
×
141

142
  private void executeLineByLine(Reader reader) {
143
    StringBuilder command = new StringBuilder();
1✔
144
    try {
145
      BufferedReader lineReader = new BufferedReader(reader);
1✔
146
      String line;
147
      while ((line = lineReader.readLine()) != null) {
1✔
148
        handleLine(command, line);
1✔
149
      }
150
      commitConnection();
1✔
151
      checkForMissingLineTerminator(command);
1✔
152
    } catch (Exception e) {
×
153
      String message = "Error executing: " + command + ".  Cause: " + e;
×
154
      printlnError(message);
×
155
      throw new RuntimeException(message, e);
×
156
    }
1✔
157
  }
1✔
158

159
  /**
160
   * @deprecated Since 3.5.4, this method is deprecated. Please close the {@link Connection} outside of this class.
161
   */
162
  @Deprecated
163
  public void closeConnection() {
164
    try {
165
      connection.close();
×
166
    } catch (Exception e) {
×
167
      // ignore
168
    }
×
169
  }
×
170

171
  private void setAutoCommit() {
172
    try {
173
      if (autoCommit != connection.getAutoCommit()) {
1✔
174
        connection.setAutoCommit(autoCommit);
1✔
175
      }
176
    } catch (Throwable t) {
×
177
      throw new RuntimeException("Could not set AutoCommit to " + autoCommit + ". Cause: " + t, t);
×
178
    }
1✔
179
  }
1✔
180

181
  private void commitConnection() {
182
    try {
183
      if (!connection.getAutoCommit()) {
1✔
184
        connection.commit();
1✔
185
      }
186
    } catch (Throwable t) {
×
187
      throw new RuntimeException("Could not commit transaction. Cause: " + t, t);
×
188
    }
1✔
189
  }
1✔
190

191
  private void rollbackConnection() {
192
    try {
193
      if (!connection.getAutoCommit()) {
1✔
194
        connection.rollback();
1✔
195
      }
196
    } catch (Throwable t) {
×
197
      // ignore
198
    }
1✔
199
  }
1✔
200

201
  private void checkForMissingLineTerminator(StringBuilder command) {
202
    if (command != null && command.toString().trim().length() > 0) {
1✔
203
      throw new RuntimeException("Line missing end-of-line terminator (" + delimiter + ") => " + command);
×
204
    }
205
  }
1✔
206

207
  private void handleLine(StringBuilder command, String line) throws SQLException {
208
    String trimmedLine = line.trim();
1✔
209
    if (lineIsComment(trimmedLine)) {
1✔
210
      Matcher matcher = DELIMITER_PATTERN.matcher(trimmedLine);
1✔
211
      if (matcher.find()) {
1✔
212
        delimiter = matcher.group(5);
×
213
      }
214
      println(trimmedLine);
1✔
215
    } else if (commandReadyToExecute(trimmedLine)) {
1✔
216
      command.append(line, 0, line.lastIndexOf(delimiter));
1✔
217
      command.append(LINE_SEPARATOR);
1✔
218
      println(command);
1✔
219
      executeStatement(command.toString());
1✔
220
      command.setLength(0);
1✔
221
    } else if (trimmedLine.length() > 0) {
1✔
222
      command.append(line);
1✔
223
      command.append(LINE_SEPARATOR);
1✔
224
    }
225
  }
1✔
226

227
  private boolean lineIsComment(String trimmedLine) {
228
    return trimmedLine.startsWith("//") || trimmedLine.startsWith("--");
1✔
229
  }
230

231
  private boolean commandReadyToExecute(String trimmedLine) {
232
    // issue #561 remove anything after the delimiter
233
    return !fullLineDelimiter && trimmedLine.contains(delimiter) || fullLineDelimiter && trimmedLine.equals(delimiter);
1✔
234
  }
235

236
  private void executeStatement(String command) throws SQLException {
237
    Statement statement = connection.createStatement();
1✔
238
    try {
239
      statement.setEscapeProcessing(escapeProcessing);
1✔
240
      String sql = command;
1✔
241
      if (removeCRs) {
1✔
242
        sql = sql.replace("\r\n", "\n");
×
243
      }
244
      try {
245
        boolean hasResults = statement.execute(sql);
1✔
246
        while (hasResults || statement.getUpdateCount() != -1) {
1✔
247
          checkWarnings(statement);
1✔
248
          printResults(statement, hasResults);
1✔
249
          hasResults = statement.getMoreResults();
1✔
250
        }
251
      } catch (SQLWarning e) {
×
252
        throw e;
×
253
      } catch (SQLException e) {
×
254
        if (stopOnError) {
×
255
          throw e;
×
256
        }
257
        String message = "Error executing: " + command + ".  Cause: " + e;
×
258
        printlnError(message);
×
259
      }
1✔
260
    } finally {
261
      try {
262
        statement.close();
1✔
263
      } catch (Exception ignored) {
×
264
        // Ignore to workaround a bug in some connection pools
265
        // (Does anyone know the details of the bug?)
266
      }
1✔
267
    }
268
  }
1✔
269

270
  private void checkWarnings(Statement statement) throws SQLException {
271
    if (!throwWarning) {
1✔
272
      return;
1✔
273
    }
274
    // In Oracle, CREATE PROCEDURE, FUNCTION, etc. returns warning
275
    // instead of throwing exception if there is compilation error.
276
    SQLWarning warning = statement.getWarnings();
1✔
277
    if (warning != null) {
1✔
278
      throw warning;
×
279
    }
280
  }
1✔
281

282
  private void printResults(Statement statement, boolean hasResults) {
283
    if (!hasResults) {
1✔
284
      return;
1✔
285
    }
286
    try (ResultSet rs = statement.getResultSet()) {
1✔
287
      ResultSetMetaData md = rs.getMetaData();
1✔
288
      int cols = md.getColumnCount();
1✔
289
      for (int i = 0; i < cols; i++) {
1✔
290
        String name = md.getColumnLabel(i + 1);
1✔
291
        print(name + "\t");
1✔
292
      }
293
      println("");
1✔
294
      while (rs.next()) {
1✔
295
        for (int i = 0; i < cols; i++) {
1✔
296
          String value = rs.getString(i + 1);
1✔
297
          print(value + "\t");
1✔
298
        }
299
        println("");
1✔
300
      }
301
    } catch (SQLException e) {
×
302
      printlnError("Error printing results: " + e.getMessage());
×
303
    }
1✔
304
  }
1✔
305

306
  private void print(Object o) {
307
    if (logWriter != null) {
1✔
308
      logWriter.print(o);
1✔
309
      logWriter.flush();
1✔
310
    }
311
  }
1✔
312

313
  private void println(Object o) {
314
    if (logWriter != null) {
1✔
315
      logWriter.println(o);
1✔
316
      logWriter.flush();
1✔
317
    }
318
  }
1✔
319

320
  private void printlnError(Object o) {
321
    if (errorLogWriter != null) {
×
322
      errorLogWriter.println(o);
×
323
      errorLogWriter.flush();
×
324
    }
325
  }
×
326

327
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc