Coverage Report - org.kuali.db.jdbc.SQLExecutor
 
Classes in this File Line Coverage Branch Coverage Complexity
SQLExecutor
0%
0/264
0%
0/98
2.017
 
 1  
 package org.kuali.db.jdbc;
 2  
 
 3  
 import java.io.BufferedReader;
 4  
 import java.io.IOException;
 5  
 import java.io.PrintStream;
 6  
 import java.io.Reader;
 7  
 import java.sql.Connection;
 8  
 import java.sql.ResultSet;
 9  
 import java.sql.ResultSetMetaData;
 10  
 import java.sql.SQLException;
 11  
 import java.sql.SQLWarning;
 12  
 import java.sql.Statement;
 13  
 import java.util.ArrayList;
 14  
 import java.util.Enumeration;
 15  
 import java.util.List;
 16  
 import java.util.StringTokenizer;
 17  
 import java.util.Vector;
 18  
 
 19  
 import org.apache.commons.lang.StringEscapeUtils;
 20  
 import org.apache.commons.logging.Log;
 21  
 import org.apache.commons.logging.LogFactory;
 22  
 
 23  
 import static org.apache.commons.io.IOUtils.*;
 24  
 import static org.kuali.db.jdbc.JDBCUtils.*;
 25  
 
 26  
 /**
 27  
  * Executes SQL statements
 28  
  */
 29  0
 public class SQLExecutor {
 30  0
     private static final Log log = LogFactory.getLog(SQLExecutor.class);
 31  
 
 32  
     /**
 33  
      * Call {@link #setOnError(String)} with this value to abort SQL command execution if an error is found.
 34  
      */
 35  
     public static final String ON_ERROR_ABORT = "abort";
 36  
 
 37  
     /**
 38  
      * Call {@link #setOnError(String)} with this value to continue SQL command execution until all commands have been
 39  
      * attempted, then abort the build if an SQL error occurred in any of the commands.
 40  
      */
 41  
     public static final String ON_ERROR_ABORT_AFTER = "abortAfter";
 42  
 
 43  
     /**
 44  
      * Call {@link #setOnError(String)} with this value to continue SQL command execution if an error is found.
 45  
      */
 46  
     public static final String ON_ERROR_CONTINUE = "continue";
 47  
 
 48  
     boolean executeSql;
 49  
     Vector<Transaction> transactions;
 50  0
     boolean keepFormat = true;
 51  0
     String delimiterType = "row";
 52  0
     String delimiter = "/";
 53  0
     boolean printResultSet = false;;
 54  0
     String onError = ON_ERROR_ABORT;
 55  0
     boolean showheaders = true;
 56  0
     String outputDelimiter = ",";
 57  
     int totalStatements;
 58  
     int successfulStatements;
 59  
     ConnectionHandler connectionHandler;
 60  
     Statement statement;
 61  0
     boolean autocommit = false;
 62  0
     boolean escapeProcessing = true;
 63  
     boolean skipOnConnectionError;
 64  
     boolean connectionError;
 65  0
     boolean append = false;
 66  0
     List<SQLListener> listeners = new ArrayList<SQLListener>();
 67  
 
 68  
     public void addListener(SQLListener listener) {
 69  0
         listeners.add(listener);
 70  0
     }
 71  
 
 72  
     protected void fireMessageLogged(String message) {
 73  0
         fireMessageLogged(message, MessagePriority.INFO);
 74  0
     }
 75  
 
 76  
     protected void fireMessageLogged(String message, MessagePriority priority) {
 77  0
         SQLEvent event = new SQLEvent(message, priority);
 78  0
         for (SQLListener listener : listeners) {
 79  0
             listener.messageLogged(event);
 80  
         }
 81  0
     }
 82  
 
 83  
     protected void fireBeginTransaction(Transaction transaction) {
 84  0
         SQLEvent event = new SQLEvent();
 85  0
         event.setTransaction(transaction);
 86  0
         for (SQLListener listener : listeners) {
 87  0
             listener.beginTransaction(event);
 88  
         }
 89  0
     }
 90  
 
 91  
     protected void fireFinishTransaction(Transaction transaction) {
 92  0
         SQLEvent event = new SQLEvent();
 93  0
         event.setTransaction(transaction);
 94  0
         for (SQLListener listener : listeners) {
 95  0
             listener.finishTransaction(event);
 96  
         }
 97  0
     }
 98  
 
 99  
     protected SQLEvent getExecuteSQLEvent(int totalStatements, String sql) {
 100  0
         SQLEvent event = new SQLEvent();
 101  0
         event.setSql(sql);
 102  0
         event.setTotalStatements(totalStatements);
 103  0
         return event;
 104  
     }
 105  
 
 106  
     protected void fireBeforeExecuteSQL(int totalStatements, String sql) {
 107  0
         for (SQLListener listener : listeners) {
 108  0
             listener.beforeExecuteSQL(getExecuteSQLEvent(totalStatements, sql));
 109  
         }
 110  0
     }
 111  
 
 112  
     protected void fireAfterExecuteSQL(int totalStatements, String sql) {
 113  0
         for (SQLListener listener : listeners) {
 114  0
             listener.afterExecuteSQL(getExecuteSQLEvent(totalStatements, sql));
 115  
         }
 116  0
     }
 117  
 
 118  
     protected void fireAfterProcessingSQLResults(int totalStatements, int successfulStatements, int updateCountTotal,
 119  
             String sql) {
 120  0
         SQLEvent event = getExecuteSQLEvent(totalStatements, sql);
 121  0
         event.setSuccessfulStatements(successfulStatements);
 122  0
         event.setUpdateCountTotal(updateCountTotal);
 123  0
         for (SQLListener listener : listeners) {
 124  0
             listener.afterExecuteSQL(event);
 125  
         }
 126  0
     }
 127  
 
 128  
     public void info(String message) {
 129  0
         log.info(message);
 130  0
         fireMessageLogged(message);
 131  0
     }
 132  
 
 133  
     public void debug(String message) {
 134  0
         log.debug(message);
 135  0
         fireMessageLogged(message, MessagePriority.DEBUG);
 136  0
     }
 137  
 
 138  
     public void error(Throwable throwable, String message) {
 139  0
         if (throwable == null) {
 140  0
             log.error(message);
 141  
         } else {
 142  0
             log.error(message, throwable);
 143  
         }
 144  0
         SQLEvent event = new SQLEvent(message, MessagePriority.ERROR);
 145  0
         event.setException(throwable);
 146  0
         for (SQLListener listener : listeners) {
 147  0
             listener.messageLogged(event);
 148  
         }
 149  0
     }
 150  
 
 151  
     public void error(String message) {
 152  0
         error(null, message);
 153  0
     }
 154  
 
 155  
     public void executeSql(String sql) throws SQLException {
 156  0
         Transaction transaction = new Transaction();
 157  0
         transaction.setSqlCommand(sql);
 158  0
         executeTransaction(transaction);
 159  0
     }
 160  
 
 161  
     public void executeTransaction(Transaction transaction) throws SQLException {
 162  0
         this.transactions = new Vector<Transaction>();
 163  0
         transactions.add(transaction);
 164  0
         execute();
 165  0
     }
 166  
 
 167  
     public void execute() throws SQLException {
 168  0
         if (!isExecuteSql()) {
 169  0
             log.info("Skipping execution");
 170  0
             return;
 171  
         }
 172  0
         Connection conn = null;
 173  
         try {
 174  0
             conn = connectionHandler.getConnection();
 175  0
             statement = conn.createStatement();
 176  0
             statement.setEscapeProcessing(escapeProcessing);
 177  
 
 178  
             // Process all transactions
 179  0
             for (Enumeration<Transaction> e = transactions.elements(); e.hasMoreElements();) {
 180  0
                 Transaction t = (Transaction) e.nextElement();
 181  
 
 182  0
                 fireBeginTransaction(t);
 183  0
                 runTransaction(conn, t, System.out);
 184  0
                 fireFinishTransaction(t);
 185  
 
 186  0
                 if (!autocommit) {
 187  0
                     debug("Committing transaction");
 188  0
                     conn.commit();
 189  
                 }
 190  0
             }
 191  0
         } catch (IOException e) {
 192  0
             throw new SQLException(e);
 193  0
         } catch (SQLException e) {
 194  0
             if (!autocommit && ON_ERROR_ABORT.equalsIgnoreCase(getOnError())) {
 195  0
                 nullSafeRollback(conn);
 196  
             }
 197  0
             throw e;
 198  
         } finally {
 199  0
             closeQuietly(statement, conn);
 200  0
         }
 201  
 
 202  0
         log.info(getSuccessfulStatements() + " of " + getTotalStatements() + " SQL statements executed successfully");
 203  
 
 204  0
         if (ON_ERROR_ABORT_AFTER.equalsIgnoreCase(getOnError()) && totalStatements != successfulStatements) {
 205  0
             throw new SQLException("Some SQL statements failed to execute");
 206  
         }
 207  0
     }
 208  
 
 209  
     protected void runTransaction(Connection conn, Transaction t, PrintStream out) throws IOException, SQLException {
 210  0
         Reader in = null;
 211  
         try {
 212  0
             in = t.getReader();
 213  0
             runStatements(conn, in, out);
 214  
         } finally {
 215  0
             closeQuietly(in);
 216  0
         }
 217  0
     }
 218  
 
 219  
     /**
 220  
      * Exec the sql statement.
 221  
      * 
 222  
      * @param sql
 223  
      *            query to execute
 224  
      * @param out
 225  
      *            the outputstream
 226  
      */
 227  
     protected void execSQL(Connection conn, String sql, PrintStream out) throws SQLException {
 228  
         // Check and ignore empty statements
 229  0
         if ("".equals(sql.trim())) {
 230  0
             return;
 231  
         }
 232  
 
 233  0
         ResultSet resultSet = null;
 234  
         try {
 235  0
             totalStatements++;
 236  0
             debug("SQL: " + sql);
 237  
 
 238  
             boolean ret;
 239  0
             int updateCountTotal = 0;
 240  
 
 241  0
             fireBeforeExecuteSQL(totalStatements, sql);
 242  0
             ret = statement.execute(sql);
 243  0
             fireAfterExecuteSQL(totalStatements, sql);
 244  
             do {
 245  0
                 if (!ret) {
 246  0
                     int updateCount = statement.getUpdateCount();
 247  0
                     if (updateCount != -1) {
 248  0
                         updateCountTotal += updateCount;
 249  
                     }
 250  0
                 } else {
 251  0
                     resultSet = statement.getResultSet();
 252  0
                     if (printResultSet) {
 253  0
                         printResultSet(resultSet, out);
 254  
                     }
 255  
                 }
 256  0
                 ret = statement.getMoreResults();
 257  0
             } while (ret);
 258  
 
 259  0
             debug(updateCountTotal + " rows affected");
 260  
 
 261  0
             if (printResultSet) {
 262  0
                 StringBuffer line = new StringBuffer();
 263  0
                 line.append(updateCountTotal).append(" rows affected");
 264  0
                 out.println(line);
 265  
             }
 266  
 
 267  0
             SQLWarning warning = conn.getWarnings();
 268  0
             while (warning != null) {
 269  0
                 debug(warning + " sql warning");
 270  0
                 warning = warning.getNextWarning();
 271  
             }
 272  0
             conn.clearWarnings();
 273  0
             successfulStatements++;
 274  0
             fireAfterProcessingSQLResults(totalStatements, successfulStatements, updateCountTotal, sql);
 275  0
         } catch (SQLException e) {
 276  0
             error("Failed to execute: " + sql + "\n\n" + e.getMessage());
 277  0
             if (ON_ERROR_ABORT.equalsIgnoreCase(getOnError())) {
 278  0
                 throw e;
 279  
             }
 280  
         } finally {
 281  0
             closeQuietly(resultSet);
 282  0
         }
 283  0
     }
 284  
 
 285  
     /**
 286  
      * read in lines and execute them
 287  
      * 
 288  
      * @param reader
 289  
      *            the reader
 290  
      * @param out
 291  
      *            the outputstream
 292  
      * @throws SQLException
 293  
      * @throws IOException
 294  
      */
 295  
     protected void runStatements(Connection conn, Reader reader, PrintStream out) throws SQLException, IOException {
 296  
         String line;
 297  
 
 298  0
         StringBuffer sql = new StringBuffer();
 299  
 
 300  0
         BufferedReader in = new BufferedReader(reader);
 301  
 
 302  0
         while ((line = in.readLine()) != null) {
 303  0
             if (!keepFormat) {
 304  0
                 line = line.trim();
 305  
             }
 306  
 
 307  0
             if (!keepFormat) {
 308  0
                 if (line.startsWith("//")) {
 309  0
                     continue;
 310  
                 }
 311  0
                 if (line.startsWith("--")) {
 312  0
                     continue;
 313  
                 }
 314  0
                 StringTokenizer st = new StringTokenizer(line);
 315  0
                 if (st.hasMoreTokens()) {
 316  0
                     String token = st.nextToken();
 317  0
                     if ("REM".equalsIgnoreCase(token)) {
 318  0
                         continue;
 319  
                     }
 320  
                 }
 321  
             }
 322  
 
 323  0
             if (!keepFormat) {
 324  0
                 sql.append(" ").append(line);
 325  
             } else {
 326  0
                 sql.append("\n").append(line);
 327  
             }
 328  
 
 329  
             // SQL defines "--" as a comment to EOL
 330  
             // and in Oracle it may contain a hint
 331  
             // so we cannot just remove it, instead we must end it
 332  0
             if (!keepFormat) {
 333  0
                 if (SqlSplitter.containsSqlEnd(line, delimiter) == SqlSplitter.NO_END) {
 334  0
                     sql.append("\n");
 335  
                 }
 336  
             }
 337  
 
 338  0
             if ((delimiterType.equals(DelimiterType.NORMAL) && SqlSplitter.containsSqlEnd(line, delimiter) > 0)
 339  
                     || (delimiterType.equals(DelimiterType.ROW) && line.trim().equals(delimiter))) {
 340  0
                 execSQL(conn, sql.substring(0, sql.length() - delimiter.length()), out);
 341  0
                 sql.setLength(0); // clean buffer
 342  
             }
 343  
         }
 344  
 
 345  
         // Catch any statements not followed by ;
 346  0
         if (!sql.toString().equals("")) {
 347  0
             execSQL(conn, sql.toString(), out);
 348  
         }
 349  0
     }
 350  
 
 351  
     /**
 352  
      * print any results in the result set.
 353  
      * 
 354  
      * @param rs
 355  
      *            the resultset to print information about
 356  
      * @param out
 357  
      *            the place to print results
 358  
      * @throws SQLException
 359  
      *             on SQL problems.
 360  
      */
 361  
     protected void printResultSet(ResultSet rs, PrintStream out) throws SQLException {
 362  0
         if (rs == null) {
 363  0
             return;
 364  
         }
 365  0
         debug("Processing new result set.");
 366  0
         ResultSetMetaData md = rs.getMetaData();
 367  0
         int columnCount = md.getColumnCount();
 368  0
         StringBuffer line = new StringBuffer();
 369  0
         if (showheaders) {
 370  0
             boolean first = true;
 371  0
             for (int col = 1; col <= columnCount; col++) {
 372  0
                 String columnValue = md.getColumnName(col);
 373  
 
 374  0
                 if (columnValue != null) {
 375  0
                     columnValue = columnValue.trim();
 376  
 
 377  0
                     if (",".equals(outputDelimiter)) {
 378  0
                         columnValue = StringEscapeUtils.escapeCsv(columnValue);
 379  
                     }
 380  
                 }
 381  
 
 382  0
                 if (first) {
 383  0
                     first = false;
 384  
                 } else {
 385  0
                     line.append(outputDelimiter);
 386  
                 }
 387  0
                 line.append(columnValue);
 388  
             }
 389  0
             out.println(line);
 390  0
             line = new StringBuffer();
 391  
         }
 392  0
         while (rs.next()) {
 393  0
             boolean first = true;
 394  0
             for (int col = 1; col <= columnCount; col++) {
 395  0
                 String columnValue = rs.getString(col);
 396  0
                 if (columnValue != null) {
 397  0
                     columnValue = columnValue.trim();
 398  
 
 399  0
                     if (",".equals(outputDelimiter)) {
 400  0
                         columnValue = StringEscapeUtils.escapeCsv(columnValue);
 401  
                     }
 402  
                 }
 403  
 
 404  0
                 if (first) {
 405  0
                     first = false;
 406  
                 } else {
 407  0
                     line.append(outputDelimiter);
 408  
                 }
 409  0
                 line.append(columnValue);
 410  
             }
 411  0
             out.println(line);
 412  0
             line = new StringBuffer();
 413  0
         }
 414  0
         out.println();
 415  0
     }
 416  
 
 417  
     public boolean isKeepFormat() {
 418  0
         return keepFormat;
 419  
     }
 420  
 
 421  
     public void setKeepFormat(boolean keepFormat) {
 422  0
         this.keepFormat = keepFormat;
 423  0
     }
 424  
 
 425  
     public String getDelimiterType() {
 426  0
         return delimiterType;
 427  
     }
 428  
 
 429  
     public void setDelimiterType(String delimiterType) {
 430  0
         this.delimiterType = delimiterType;
 431  0
     }
 432  
 
 433  
     public String getDelimiter() {
 434  0
         return delimiter;
 435  
     }
 436  
 
 437  
     public void setDelimiter(String delimiter) {
 438  0
         this.delimiter = delimiter;
 439  0
     }
 440  
 
 441  
     public boolean isPrintResultSet() {
 442  0
         return printResultSet;
 443  
     }
 444  
 
 445  
     public void setPrintResultSet(boolean printResultSet) {
 446  0
         this.printResultSet = printResultSet;
 447  0
     }
 448  
 
 449  
     public String getOnError() {
 450  0
         return onError;
 451  
     }
 452  
 
 453  
     public void setOnError(String onError) {
 454  0
         this.onError = onError;
 455  0
     }
 456  
 
 457  
     public boolean isShowheaders() {
 458  0
         return showheaders;
 459  
     }
 460  
 
 461  
     public void setShowheaders(boolean showheaders) {
 462  0
         this.showheaders = showheaders;
 463  0
     }
 464  
 
 465  
     public String getOutputDelimiter() {
 466  0
         return outputDelimiter;
 467  
     }
 468  
 
 469  
     public void setOutputDelimiter(String outputDelimiter) {
 470  0
         this.outputDelimiter = outputDelimiter;
 471  0
     }
 472  
 
 473  
     public int getTotalStatements() {
 474  0
         return totalStatements;
 475  
     }
 476  
 
 477  
     public void setTotalStatements(int totalStatements) {
 478  0
         this.totalStatements = totalStatements;
 479  0
     }
 480  
 
 481  
     public int getSuccessfulStatements() {
 482  0
         return successfulStatements;
 483  
     }
 484  
 
 485  
     public void setSuccessfulStatements(int successfulStatements) {
 486  0
         this.successfulStatements = successfulStatements;
 487  0
     }
 488  
 
 489  
     public Statement getStatement() {
 490  0
         return statement;
 491  
     }
 492  
 
 493  
     public void setStatement(Statement statement) {
 494  0
         this.statement = statement;
 495  0
     }
 496  
 
 497  
     public boolean isAutocommit() {
 498  0
         return autocommit;
 499  
     }
 500  
 
 501  
     public void setAutocommit(boolean autocommit) {
 502  0
         this.autocommit = autocommit;
 503  0
     }
 504  
 
 505  
     public boolean isEscapeProcessing() {
 506  0
         return escapeProcessing;
 507  
     }
 508  
 
 509  
     public void setEscapeProcessing(boolean escapeProcessing) {
 510  0
         this.escapeProcessing = escapeProcessing;
 511  0
     }
 512  
 
 513  
     public boolean isAppend() {
 514  0
         return append;
 515  
     }
 516  
 
 517  
     public void setAppend(boolean append) {
 518  0
         this.append = append;
 519  0
     }
 520  
 
 521  
     public boolean isSkipOnConnectionError() {
 522  0
         return skipOnConnectionError;
 523  
     }
 524  
 
 525  
     public void setSkipOnConnectionError(boolean skipOnConnectionError) {
 526  0
         this.skipOnConnectionError = skipOnConnectionError;
 527  0
     }
 528  
 
 529  
     public boolean isConnectionError() {
 530  0
         return connectionError;
 531  
     }
 532  
 
 533  
     public void setConnectionError(boolean connectionError) {
 534  0
         this.connectionError = connectionError;
 535  0
     }
 536  
 
 537  
     public List<SQLListener> getListeners() {
 538  0
         return listeners;
 539  
     }
 540  
 
 541  
     public void setListeners(List<SQLListener> listeners) {
 542  0
         this.listeners = listeners;
 543  0
     }
 544  
 
 545  
     public Vector<Transaction> getTransactions() {
 546  0
         return transactions;
 547  
     }
 548  
 
 549  
     public void setTransactions(Vector<Transaction> transactions) {
 550  0
         this.transactions = transactions;
 551  0
     }
 552  
 
 553  
     public boolean isExecuteSql() {
 554  0
         return executeSql;
 555  
     }
 556  
 
 557  
     public void setExecuteSql(boolean skipExecutions) {
 558  0
         this.executeSql = skipExecutions;
 559  0
     }
 560  
 
 561  
     public ConnectionHandler getConnectionHandler() {
 562  0
         return connectionHandler;
 563  
     }
 564  
 
 565  
     public void setConnectionHandler(ConnectionHandler connectionHandler) {
 566  0
         this.connectionHandler = connectionHandler;
 567  0
     }
 568  
 }