Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 646   Methods: 22
NCLOC: 338   Classes: 4
 
 Source file Conditionals Statements Methods TOTAL
SQLExec.java 0% 0% 0% 0%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights 
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 
 55   
 package org.apache.tools.ant.taskdefs;
 56   
 
 57   
 import org.apache.tools.ant.BuildException;
 58   
 import org.apache.tools.ant.DirectoryScanner;
 59   
 import org.apache.tools.ant.Project;
 60   
 import org.apache.tools.ant.types.EnumeratedAttribute;
 61   
 import org.apache.tools.ant.types.FileSet;
 62   
 
 63   
 import java.io.File;
 64   
 import java.io.PrintStream;
 65   
 import java.io.BufferedOutputStream;
 66   
 import java.io.FileOutputStream;
 67   
 import java.io.IOException;
 68   
 import java.io.Reader;
 69   
 import java.io.BufferedReader;
 70   
 import java.io.StringReader;
 71   
 import java.io.FileReader;
 72   
 import java.io.InputStreamReader;
 73   
 import java.io.FileInputStream;
 74   
 import java.util.Enumeration;
 75   
 import java.util.StringTokenizer;
 76   
 import java.util.Vector;
 77   
 
 78   
 import java.sql.Connection;
 79   
 import java.sql.Statement;
 80   
 import java.sql.SQLException;
 81   
 import java.sql.SQLWarning;
 82   
 import java.sql.ResultSet;
 83   
 import java.sql.ResultSetMetaData;
 84   
 
 85   
 /**
 86   
  * Executes a series of SQL statements on a database using JDBC.
 87   
  *
 88   
  * <p>Statements can
 89   
  * either be read in from a text file using the <i>src</i> attribute or from 
 90   
  * between the enclosing SQL tags.</p>
 91   
  * 
 92   
  * <p>Multiple statements can be provided, separated by semicolons (or the 
 93   
  * defined <i>delimiter</i>). Individual lines within the statements can be 
 94   
  * commented using either --, // or REM at the start of the line.</p>
 95   
  * 
 96   
  * <p>The <i>autocommit</i> attribute specifies whether auto-commit should be 
 97   
  * turned on or off whilst executing the statements. If auto-commit is turned 
 98   
  * on each statement will be executed and committed. If it is turned off the 
 99   
  * statements will all be executed as one transaction.</p>
 100   
  * 
 101   
  * <p>The <i>onerror</i> attribute specifies how to proceed when an error occurs 
 102   
  * during the execution of one of the statements. 
 103   
  * The possible values are: <b>continue</b> execution, only show the error;
 104   
  * <b>stop</b> execution and commit transaction;
 105   
  * and <b>abort</b> execution and transaction and fail task.</p>
 106   
 
 107   
  * 
 108   
  * @author <a href="mailto:jeff@custommonkey.org">Jeff Martin</a>
 109   
  * @author <A href="mailto:gholam@xtra.co.nz">Michael McCallum</A>
 110   
  * @author <A href="mailto:tim.stephenson@sybase.com">Tim Stephenson</A>
 111   
  *
 112   
  * @since Ant 1.2
 113   
  *
 114   
  * @ant.task name="sql" category="database"
 115   
  */
 116   
 public class SQLExec extends JDBCTask {
 117   
 
 118   
     /**
 119   
      * delimiters we support, "normal" and "row"
 120   
      */
 121   
     public static class DelimiterType extends EnumeratedAttribute {
 122   
         public static final String NORMAL = "normal";
 123   
         public static final String ROW = "row";
 124  0
         public String[] getValues() {
 125  0
             return new String[] {NORMAL, ROW};
 126   
         }
 127   
     }
 128   
 
 129   
     
 130   
     
 131   
     private int goodSql = 0;
 132   
 
 133   
     private int totalSql = 0;
 134   
 
 135   
     /**
 136   
      * Database connection
 137   
      */
 138   
     private Connection conn = null;
 139   
 
 140   
     /**
 141   
      * files to load
 142   
      */
 143   
     private Vector filesets = new Vector();
 144   
 
 145   
     /**
 146   
      * SQL statement
 147   
      */
 148   
     private Statement statement = null;
 149   
 
 150   
     /**
 151   
      * SQL input file
 152   
      */
 153   
     private File srcFile = null;
 154   
 
 155   
     /**
 156   
      * SQL input command
 157   
      */
 158   
     private String sqlCommand = "";
 159   
 
 160   
     /**
 161   
      * SQL transactions to perform
 162   
      */
 163   
     private Vector transactions = new Vector();
 164   
 
 165   
     /**
 166   
      * SQL Statement delimiter
 167   
      */
 168   
     private String delimiter = ";";
 169   
     
 170   
     /**
 171   
      * The delimiter type indicating whether the delimiter will
 172   
      * only be recognized on a line by itself
 173   
      */
 174   
     private String delimiterType = DelimiterType.NORMAL;
 175   
     
 176   
     /**
 177   
      * Print SQL results.
 178   
      */
 179   
     private boolean print = false;
 180   
 
 181   
     /**
 182   
      * Print header columns.
 183   
      */
 184   
     private boolean showheaders = true;
 185   
 
 186   
     /**
 187   
      * Results Output file.
 188   
      */
 189   
     private File output = null;
 190   
 
 191   
     
 192   
     /**
 193   
      * Action to perform if an error is found
 194   
      **/
 195   
     private String onError = "abort";
 196   
     
 197   
     /**
 198   
      * Encoding to use when reading SQL statements from a file
 199   
      */
 200   
     private String encoding = null;
 201   
 
 202   
     /**
 203   
      * Append to an existing file or overwrite it?
 204   
      */
 205   
     private boolean append = false;
 206   
  
 207   
     /**
 208   
      * Keep the format of a sql block?
 209   
      */
 210   
     private boolean keepformat = false;
 211   
 
 212   
     /**
 213   
      * Set the name of the SQL file to be run.
 214   
      * Required unless statements are enclosed in the build file
 215   
      */
 216  0
     public void setSrc(File srcFile) {
 217  0
         this.srcFile = srcFile;
 218   
     }
 219   
     
 220   
     /**
 221   
      * Set an inline SQL command to execute. 
 222   
      * NB: Properties are not expanded in this text.
 223   
      */
 224  0
     public void addText(String sql) {
 225  0
         this.sqlCommand += sql;
 226   
     }
 227   
     
 228   
     /**
 229   
      * Adds a set of files (nested fileset attribute).
 230   
      */
 231  0
     public void addFileset(FileSet set) {
 232  0
         filesets.addElement(set);
 233   
     }
 234   
 
 235   
 
 236   
     /**
 237   
      * Add a SQL transaction to execute
 238   
      */
 239  0
     public Transaction createTransaction() {
 240  0
         Transaction t = new Transaction();
 241  0
         transactions.addElement(t);
 242  0
         return t;
 243   
     }
 244   
     
 245   
     /**
 246   
      * Set the file encoding to use on the SQL files read in
 247   
      *
 248   
      * @param encoding the encoding to use on the files
 249   
      */
 250  0
     public void setEncoding(String encoding) {
 251  0
         this.encoding = encoding;
 252   
     }
 253   
     
 254   
     /**
 255   
      * Set the delimiter that separates SQL statements; 
 256   
      * optional, default &quot;;&quot;
 257   
      *
 258   
      * <p>For example, set this to "go" and delimitertype to "ROW" for
 259   
      * Sybase ASE or MS SQL Server.</p>
 260   
      */
 261  0
     public void setDelimiter(String delimiter) {
 262  0
         this.delimiter = delimiter;
 263   
     }
 264   
 
 265   
     /**
 266   
      * Set the delimiter type: "normal" or "row" (default "normal").
 267   
      *
 268   
      * <p>The delimiter type takes two values - normal and row. Normal
 269   
      * means that any occurence of the delimiter terminate the SQL
 270   
      * command whereas with row, only a line containing just the
 271   
      * delimiter is recognized as the end of the command.</p>
 272   
      */
 273  0
     public void setDelimiterType(DelimiterType delimiterType) {
 274  0
         this.delimiterType = delimiterType.getValue();
 275   
     }
 276   
     
 277   
     /**
 278   
      * Print result sets from the statements;
 279   
      * optional, default false
 280   
      */
 281  0
     public void setPrint(boolean print) {
 282  0
         this.print = print;
 283   
     }
 284   
     
 285   
     /**
 286   
      * Print headers for result sets from the 
 287   
      * statements; optional, default true.
 288   
      */
 289  0
     public void setShowheaders(boolean showheaders) {
 290  0
         this.showheaders = showheaders;
 291   
     }
 292   
 
 293   
     /**
 294   
      * Set the output file; 
 295   
      * optional, defaults to the Ant log.
 296   
      */
 297  0
     public void setOutput(File output) {
 298  0
         this.output = output;
 299   
     }
 300   
 
 301   
     /**
 302   
      * whether output should be appended to or overwrite
 303   
      * an existing file.  Defaults to false.
 304   
      *
 305   
      * @since Ant 1.5
 306   
      */
 307  0
     public void setAppend(boolean append) {
 308  0
         this.append = append;
 309   
     }
 310   
 
 311   
     
 312   
     /**
 313   
      * Action to perform when statement fails: continue, stop, or abort
 314   
      * optional; default &quot;abort&quot;
 315   
      */
 316  0
     public void setOnerror(OnError action) {
 317  0
         this.onError = action.getValue();
 318   
     }
 319   
 
 320   
     /**
 321   
      * whether or not format should be preserved.
 322   
      * Defaults to false.
 323   
      * 
 324   
      * @param keepformat The keepformat to set
 325   
      */
 326  0
     public void setKeepformat(boolean keepformat) {
 327  0
         this.keepformat = keepformat;
 328   
     }
 329   
     
 330   
     /**
 331   
      * Load the sql file and then execute it
 332   
      */
 333  0
     public void execute() throws BuildException {
 334  0
         Vector savedTransaction = (Vector) transactions.clone();
 335  0
         String savedSqlCommand = sqlCommand;
 336   
 
 337  0
         sqlCommand = sqlCommand.trim();
 338   
 
 339  0
         try {
 340  0
             if (srcFile == null && sqlCommand.length() == 0 
 341   
                 && filesets.isEmpty()) { 
 342  0
                 if (transactions.size() == 0) {
 343  0
                     throw new BuildException("Source file or fileset, "
 344   
                                              + "transactions or sql statement "
 345   
                                              + "must be set!", location);
 346   
                 }
 347   
             }
 348   
         
 349  0
             if (srcFile != null && !srcFile.exists()) {
 350  0
                 throw new BuildException("Source file does not exist!", location);
 351   
             }
 352   
 
 353   
             // deal with the filesets
 354  0
             for (int i = 0; i < filesets.size(); i++) {
 355  0
                 FileSet fs = (FileSet) filesets.elementAt(i);
 356  0
                 DirectoryScanner ds = fs.getDirectoryScanner(project);
 357  0
                 File srcDir = fs.getDir(project);
 358   
                 
 359  0
                 String[] srcFiles = ds.getIncludedFiles();
 360   
                 
 361   
                 // Make a transaction for each file
 362  0
                 for (int j = 0 ; j < srcFiles.length ; j++) {
 363  0
                     Transaction t = createTransaction();
 364  0
                     t.setSrc(new File(srcDir, srcFiles[j]));
 365   
                 }
 366   
             }
 367   
             
 368   
             // Make a transaction group for the outer command
 369  0
             Transaction t = createTransaction();
 370  0
             t.setSrc(srcFile);
 371  0
             t.addText(sqlCommand);
 372  0
             conn = getConnection();
 373  0
             if (!isValidRdbms(conn)) {
 374  0
                 return;
 375   
             }
 376  0
             try {
 377  0
                 statement = conn.createStatement();
 378   
 
 379   
             
 380  0
                 PrintStream out = System.out;
 381  0
                 try {
 382  0
                     if (output != null) {
 383  0
                         log("Opening PrintStream to output file " + output, 
 384   
                             Project.MSG_VERBOSE);
 385  0
                         out = new PrintStream(
 386   
                                   new BufferedOutputStream(
 387   
                                       new FileOutputStream(output
 388   
                                                            .getAbsolutePath(),
 389   
                                                            append)));
 390   
                     }
 391   
                     
 392   
                     // Process all transactions
 393  0
                     for (Enumeration e = transactions.elements(); 
 394  0
                          e.hasMoreElements();) {
 395   
                        
 396  0
                         ((Transaction) e.nextElement()).runTransaction(out);
 397  0
                         if (!isAutocommit()) {
 398  0
                             log("Commiting transaction", Project.MSG_VERBOSE);
 399  0
                             conn.commit();
 400   
                         }
 401   
                     }
 402   
                 } finally {
 403  0
                     if (out != null && out != System.out) {
 404  0
                         out.close();
 405   
                     }
 406   
                 } 
 407   
             } catch (IOException e){
 408  0
                 if (!isAutocommit() && conn != null && onError.equals("abort")) {
 409  0
                     try {
 410  0
                         conn.rollback();
 411   
                     } catch (SQLException ex) {}
 412   
                 }
 413  0
                 throw new BuildException(e, location);
 414   
             } catch (SQLException e){
 415  0
                 if (!isAutocommit() && conn != null && onError.equals("abort")) {
 416  0
                     try {
 417  0
                         conn.rollback();
 418   
                     } catch (SQLException ex) {}
 419   
                 }
 420  0
                 throw new BuildException(e, location);
 421   
             } finally {
 422  0
                 try {
 423  0
                     if (statement != null) {
 424  0
                         statement.close();
 425   
                     }
 426  0
                     if (conn != null) {
 427  0
                         conn.close();
 428   
                     }
 429   
                 } catch (SQLException e) {}
 430   
             }
 431   
             
 432  0
             log(goodSql + " of " + totalSql + 
 433   
                 " SQL statements executed successfully");
 434   
         } finally {
 435  0
             transactions = savedTransaction;
 436  0
             sqlCommand = savedSqlCommand;
 437   
         }
 438   
     }
 439   
 
 440   
     /**
 441   
      * read in lines and execute them
 442   
      */
 443  0
     protected void runStatements(Reader reader, PrintStream out) 
 444   
         throws SQLException, IOException {
 445  0
         StringBuffer sql = new StringBuffer();
 446  0
         String line = "";
 447   
  
 448  0
         BufferedReader in = new BufferedReader(reader);
 449   
  
 450  0
         while ((line = in.readLine()) != null){
 451  0
             if (!keepformat) {
 452  0
                 line = line.trim();
 453   
             }
 454  0
             line = project.replaceProperties(line);
 455  0
             if (!keepformat) {
 456  0
                 if (line.startsWith("//")) {
 457  0
                     continue;
 458   
                 }
 459  0
                 if (line.startsWith("--")) {
 460  0
                     continue;
 461   
                 }
 462  0
                 StringTokenizer st = new StringTokenizer(line);
 463  0
                 if (st.hasMoreTokens()) {
 464  0
                     String token = st.nextToken();
 465  0
                     if ("REM".equalsIgnoreCase(token)) {
 466  0
                         continue;
 467   
                     }
 468   
                 }
 469   
             }
 470   
             
 471  0
             if (!keepformat) {
 472  0
                 sql.append(" " + line);
 473   
             } else {
 474  0
                 sql.append("\n" + line);
 475   
             }
 476   
             
 477   
             // SQL defines "--" as a comment to EOL
 478   
             // and in Oracle it may contain a hint
 479   
             // so we cannot just remove it, instead we must end it
 480  0
             if (!keepformat) {
 481  0
                 if (line.indexOf("--") >= 0) {
 482  0
                     sql.append("\n");
 483   
                 }
 484   
             }
 485  0
             if ((delimiterType.equals(DelimiterType.NORMAL) 
 486   
                  && sql.toString().endsWith(delimiter)) 
 487   
                 ||
 488   
                 (delimiterType.equals(DelimiterType.ROW) 
 489   
                  && line.equals(delimiter))) {
 490  0
                 log("SQL: " + sql, Project.MSG_VERBOSE);
 491  0
                 execSQL(sql.substring(0, sql.length() - delimiter.length()), 
 492   
                         out);
 493  0
                 sql.replace(0, sql.length(), "");
 494   
             }
 495   
         }
 496   
         // Catch any statements not followed by ;
 497  0
         if (!sql.equals("")){
 498  0
             execSQL(sql.toString(), out);
 499   
         }
 500   
     }
 501   
  
 502   
         
 503   
     /**
 504   
      * Exec the sql statement.
 505   
      */
 506  0
     protected void execSQL(String sql, PrintStream out) throws SQLException {
 507   
         // Check and ignore empty statements
 508  0
         if ("".equals(sql.trim())) {
 509  0
             return;
 510   
         }
 511   
         
 512  0
         try {  
 513  0
             totalSql++;
 514  0
             if (!statement.execute(sql)) {
 515  0
                 log(statement.getUpdateCount() + " rows affected", 
 516   
                     Project.MSG_VERBOSE);
 517   
             } else {
 518  0
                 if (print) {
 519  0
                     printResults(out);
 520   
                 }
 521   
             }
 522   
             
 523  0
             SQLWarning warning = conn.getWarnings();
 524  0
             while (warning != null){
 525  0
                 log(warning + " sql warning", Project.MSG_VERBOSE);
 526  0
                 warning = warning.getNextWarning();
 527   
             }
 528  0
             conn.clearWarnings();
 529  0
             goodSql++;
 530   
         } catch (SQLException e) {
 531  0
             log("Failed to execute: " + sql, Project.MSG_ERR);
 532  0
             if (!onError.equals("continue")) {
 533  0
                 throw e;
 534   
             }
 535  0
             log(e.toString(), Project.MSG_ERR);
 536   
         }
 537   
     }
 538   
     
 539   
     /**
 540   
      * print any results in the statement.
 541   
      */
 542  0
     protected void printResults(PrintStream out) throws java.sql.SQLException {
 543  0
         ResultSet rs = null;
 544  0
         do {
 545  0
             rs = statement.getResultSet();
 546  0
             if (rs != null) {
 547  0
                 log("Processing new result set.", Project.MSG_VERBOSE);
 548  0
                 ResultSetMetaData md = rs.getMetaData();
 549  0
                 int columnCount = md.getColumnCount();
 550  0
                 StringBuffer line = new StringBuffer();
 551  0
                 if (showheaders) {
 552  0
                     for (int col = 1; col < columnCount; col++) {
 553  0
                          line.append(md.getColumnName(col));
 554  0
                          line.append(",");
 555   
                     }
 556  0
                     line.append(md.getColumnName(columnCount));
 557  0
                     out.println(line);
 558  0
                     line = new StringBuffer();
 559   
                 }
 560  0
                 while (rs.next()) {
 561  0
                     boolean first = true;
 562  0
                     for (int col = 1; col <= columnCount; col++) {
 563  0
                         String columnValue = rs.getString(col);
 564  0
                         if (columnValue != null) {
 565  0
                             columnValue = columnValue.trim();
 566   
                         }
 567   
                          
 568  0
                         if (first) {
 569  0
                             first = false;
 570   
                         } else {
 571  0
                             line.append(",");
 572   
                         }
 573  0
                         line.append(columnValue);
 574   
                     }
 575  0
                     out.println(line);
 576  0
                     line = new StringBuffer();
 577   
                 }
 578   
             }
 579   
         }
 580  0
         while (statement.getMoreResults());
 581  0
         out.println();
 582   
     }
 583   
 
 584   
     /**
 585   
      * The action a task should perform on an error,
 586   
      * one of "continue", "stop" and "abort"
 587   
      */
 588   
     public static class OnError extends EnumeratedAttribute {
 589  0
         public String[] getValues() {
 590  0
             return new String[] {"continue", "stop", "abort"};
 591   
         }
 592   
     }
 593   
 
 594   
     /**
 595   
      * Contains the definition of a new transaction element.
 596   
      * Transactions allow several files or blocks of statements
 597   
      * to be executed using the same JDBC connection and commit
 598   
      * operation in between.
 599   
      */
 600   
     public class Transaction {
 601   
         private File tSrcFile = null;
 602   
         private String tSqlCommand = "";
 603   
 
 604   
         /**
 605   
          *
 606   
          */
 607  0
         public void setSrc(File src) {
 608  0
             this.tSrcFile = src;
 609   
         }
 610   
 
 611   
         /**
 612   
          *
 613   
          */
 614  0
         public void addText(String sql) {
 615  0
             this.tSqlCommand += sql;
 616   
         }
 617   
 
 618   
         /**
 619   
          *
 620   
          */
 621  0
         private void runTransaction(PrintStream out) 
 622   
             throws IOException, SQLException {
 623  0
             if (tSqlCommand.length() != 0) {
 624  0
                 log("Executing commands", Project.MSG_INFO);
 625  0
                 runStatements(new StringReader(tSqlCommand), out);
 626   
             }
 627   
       
 628  0
             if (tSrcFile != null) {
 629  0
                 log("Executing file: " + tSrcFile.getAbsolutePath(), 
 630   
                     Project.MSG_INFO);
 631  0
                 Reader reader = 
 632   
                     (encoding == null) ? new FileReader(tSrcFile)
 633   
                                        : new InputStreamReader(
 634   
                                              new FileInputStream(tSrcFile), 
 635   
                                              encoding);
 636  0
                 try {
 637  0
                     runStatements(reader, out);
 638   
                 } finally {
 639  0
                     reader.close();
 640   
                 }
 641   
             }
 642   
         }
 643   
     }
 644   
 
 645   
 }
 646