001/*
002 * Slightly modified version of the com.ibatis.common.jdbc.ScriptRunner class
003 * from the iBATIS Apache project. Only removed dependency on Resource class
004 * and a constructor
005 */
006/**
007 * Copyright 2004-2020 the original author or authors.
008 *
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *    http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 */
021package gu.sql2java;
022
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.InputStreamReader;
026import java.io.LineNumberReader;
027import java.io.PrintWriter;
028import java.io.Reader;
029import java.io.StringReader;
030import java.sql.Connection;
031import java.sql.ResultSet;
032import java.sql.ResultSetMetaData;
033import java.sql.SQLException;
034import java.sql.Statement;
035
036import com.google.common.base.Throwables;
037
038import gu.sql2java.exception.DaoException;
039
040import static com.google.common.base.Preconditions.checkNotNull;
041/**
042 * Tool to run database scripts.
043 */
044public class ScriptRunner {
045
046        /** The Constant DEFAULT_DELIMITER. */
047        private static final String DEFAULT_DELIMITER = ";";
048
049        /** The stop on error. */
050        private boolean stopOnError;
051
052        /** The auto commit. */
053        private boolean autoCommit;
054
055        /** The log writer. */
056        private PrintWriter logWriter = new PrintWriter(System.out);
057
058        /** The error log writer. */
059        private PrintWriter errorLogWriter = new PrintWriter(System.err);
060
061        /** The delimiter. */
062        private String delimiter = DEFAULT_DELIMITER;
063
064        /** The full line delimiter. */
065        private boolean fullLineDelimiter = false;
066
067        /**
068         * Default constructor.
069         * @param autoCommit
070         *          the auto commit
071         * @param stopOnError
072         *          stop on error
073         */
074        public ScriptRunner(boolean autoCommit, boolean stopOnError) {
075                this.autoCommit = autoCommit;
076                this.stopOnError = stopOnError;
077        }
078
079        /**
080         * Sets the delimiter.
081         *
082         * @param delimiter
083         *          the delimiter
084         * @param fullLineDelimiter
085         *          the full line delimiter
086         * @return 
087         */
088        public ScriptRunner setDelimiter(String delimiter, boolean fullLineDelimiter) {
089                this.delimiter = delimiter;
090                this.fullLineDelimiter = fullLineDelimiter;
091                return this;
092        }
093
094        /**
095         * Setter for logWriter property.
096         *
097         * @param logWriter
098         *          - the new value of the logWriter property
099         * @return 
100         */
101        public ScriptRunner setLogWriter(PrintWriter logWriter) {
102                this.logWriter = logWriter;
103                return this;
104        }
105
106        /**
107         * Setter for errorLogWriter property.
108         *
109         * @param errorLogWriter
110         *          - the new value of the errorLogWriter property
111         * @return 
112         */
113        public ScriptRunner setErrorLogWriter(PrintWriter errorLogWriter) {
114                this.errorLogWriter = errorLogWriter;
115                return this;
116        }
117
118        /**
119         * Runs an SQL script (read in using the Reader parameter).
120         *
121         * @param reader
122         *          - the source of the script
123         * @throws IOException
124         *           Signals that an I/O exception has occurred.
125         * @throws DaoException
126         *           the wrapped SQL exception
127         */
128        public void runScript(Reader reader) throws IOException, DaoException {
129                try {
130                        Connection connection = Manager.getInstance().getConnection();
131                        boolean originalAutoCommit = connection.getAutoCommit();
132                        boolean commit = false;
133                        try {
134                                if (originalAutoCommit != this.autoCommit) {
135                                        connection.setAutoCommit(this.autoCommit);
136                                }
137                                runScript(connection, checkNotNull(reader,"reader is null"));
138                                commit = true;
139                        } finally {
140                                if(!autoCommit){
141                                        if(commit){
142                                                connection.commit();    
143                                        }else{
144                                                connection.rollback();
145                                        }
146                                }
147                                connection.setAutoCommit(originalAutoCommit);
148                                Manager.getInstance().releaseConnection(connection);    
149                        }
150
151                } catch (SQLException e) {
152                        throw new DaoException(e);
153                }catch (Exception e) {
154                        Throwables.throwIfInstanceOf(e, IOException.class);
155                        Throwables.throwIfUnchecked(e);
156                        throw new RuntimeException(e);
157                } 
158        }
159        /**
160         * Runs an SQL script (read in using the String parameter).
161         * @param input
162         *          - the source of the script
163         * @throws IOException
164         * @throws DaoException
165         */
166        public void runScript(String input) throws IOException, DaoException {          
167                runScript(new StringReader(checkNotNull(input,"input is null")));
168        }
169        /**
170         * Runs an SQL script (read in using the InputStream parameter).
171         * @param input
172         *          - the source of the script
173         * @throws IOException
174         * @throws DaoException
175         */
176        public void runScript(InputStream input) throws IOException, DaoException {             
177                runScript(new InputStreamReader(checkNotNull(input,"input is null")));
178        }
179        /**
180         * Runs an SQL script (read in using the Reader parameter) using the connection passed in.
181         *
182         * @param conn
183         *          - the connection to use for the script
184         * @param reader
185         *          - the source of the script
186         * @throws IOException
187         *           if there is an error reading from the Reader
188         * @throws SQLException
189         *           if any SQL errors occur
190         */
191        private void runScript(Connection conn, Reader reader) throws IOException, SQLException {
192                StringBuilder command = null;
193                // auto close the reader
194                try (LineNumberReader lineReader = new LineNumberReader(reader)){
195                        String line = null;
196                        while ((line = lineReader.readLine()) != null) {
197                                if (command == null) {
198                                        command = new StringBuilder();
199                                }
200                                String trimmedLine = line.trim();
201                                if (trimmedLine.startsWith("--")) {
202                                        println(trimmedLine);
203                                } else if (trimmedLine.length() < 1 || trimmedLine.startsWith("//")) {
204                                        // Do nothing
205                                } else if (trimmedLine.length() < 1 || trimmedLine.startsWith("--")) {
206                                        // Do nothing
207                                } else if (!fullLineDelimiter && trimmedLine.endsWith(getDelimiter())
208                                                || fullLineDelimiter && trimmedLine.equals(getDelimiter())) {
209                                        command.append(line.substring(0, line.lastIndexOf(getDelimiter())));
210                                        command.append(" ");
211                                        Statement statement = conn.createStatement();
212
213                                        println(command);
214
215                                        boolean hasResults = false;
216                                        if (stopOnError) {
217                                                hasResults = statement.execute(command.toString());
218                                        } else {
219                                                try {
220                                                        statement.execute(command.toString());
221                                                } catch (SQLException e) {
222                                                        e.fillInStackTrace();
223                                                        printlnError("Error executing: " + command);
224                                                        printlnError(e);
225                                                }
226                                        }
227
228//                                      if (autoCommit && !conn.getAutoCommit()) {
229//                                              conn.commit();
230//                                      }
231
232                                        ResultSet rs = statement.getResultSet();
233                                        if (hasResults && rs != null) {
234                                                ResultSetMetaData md = rs.getMetaData();
235                                                int cols = md.getColumnCount();
236                                                for (int i = 0; i < cols; i++) {
237                                                        String name = md.getColumnLabel(i + 1);
238                                                        print(name + "\t");
239                                                }
240                                                println("");
241                                                while (rs.next()) {
242                                                        for (int i = 0; i < cols; i++) {
243                                                                String value = rs.getString(i + 1);
244                                                                print(value + "\t");
245                                                        }
246                                                        println("");
247                                                }
248                                        }
249
250                                        command = null;
251                                        try {
252                                                statement.close();
253                                        } catch (Exception e) {
254                                                // Ignore to workaround a bug in Jakarta DBCP
255                                        }
256                                        Thread.yield();
257                                } else {
258                                        command.append(line);
259                                        command.append(" ");
260                                }
261                        }
262//                      if (!autoCommit) {
263//                              conn.commit();
264//                      }
265                } catch (SQLException e) {
266                        e.fillInStackTrace();
267                        printlnError("Error executing: " + command);
268                        printlnError(e);
269                        throw e;
270                } catch (IOException e) {
271                        e.fillInStackTrace();
272                        printlnError("Error executing: " + command);
273                        printlnError(e);
274                        throw e;
275                } finally {
276//                      conn.rollback();
277                        flush();
278                }
279        }
280
281        /**
282         * Gets the delimiter.
283         *
284         * @return the delimiter
285         */
286        private String getDelimiter() {
287                return delimiter;
288        }
289
290        /**
291         * Prints the.
292         *
293         * @param o
294         *          the o
295         */
296        private void print(Object o) {
297                if (logWriter != null) {
298                        System.out.print(o);
299                }
300        }
301
302        /**
303         * Println.
304         *
305         * @param o
306         *          the o
307         */
308        private void println(Object o) {
309                if (logWriter != null) {
310                        logWriter.println(o);
311                }
312        }
313
314        /**
315         * Println error.
316         *
317         * @param o
318         *          the o
319         */
320        private void printlnError(Object o) {
321                if (errorLogWriter != null) {
322                        errorLogWriter.println(o);
323                }
324        }
325
326        /**
327         * Flush.
328         */
329        private void flush() {
330                if (logWriter != null) {
331                        logWriter.flush();
332                }
333                if (errorLogWriter != null) {
334                        errorLogWriter.flush();
335                }
336        }
337
338}