You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@turbine.apache.org by jv...@apache.org on 2001/10/29 16:10:40 UTC

cvs commit: jakarta-turbine-torque/src/java/org/apache/torque/task TorqueSQLExec.java

jvanzyl     01/10/29 07:10:40

  Added:       src/java/org/apache/torque/task TorqueSQLExec.java
  Log:
  - modified ant task for executing arbitrary SQL files, but the addition is
    where the task uses a map to execute the SQL against a particular database.
    made to account for multi-database setups.
  
  Revision  Changes    Path
  1.1                  jakarta-turbine-torque/src/java/org/apache/torque/task/TorqueSQLExec.java
  
  Index: TorqueSQLExec.java
  ===================================================================
  package org.apache.tdk.task;
  
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Ant", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  import java.io.BufferedOutputStream;
  import java.io.BufferedReader;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.FileReader;
  import java.io.PrintStream;
  import java.io.StringReader;
  import java.io.Reader;
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.Enumeration;
  import java.util.Hashtable;
  import java.util.StringTokenizer;
  import java.util.Vector;
  import java.util.Properties;
  import java.sql.Connection;
  import java.sql.DatabaseMetaData;
  import java.sql.Driver;
  import java.sql.ResultSet;
  import java.sql.ResultSetMetaData;
  import java.sql.SQLException;
  import java.sql.SQLWarning;
  import java.sql.Statement;
  import org.apache.commons.util.StringUtils;
  import org.apache.tools.ant.AntClassLoader;
  import org.apache.tools.ant.BuildException;
  import org.apache.tools.ant.Project;
  import org.apache.tools.ant.ProjectHelper;
  import org.apache.tools.ant.Task;
  import org.apache.tools.ant.types.EnumeratedAttribute;
  import org.apache.tools.ant.types.Path;
  import org.apache.tools.ant.types.Reference;
  
  /**
   * This task uses an SQL -> Database map in the form of a properties
   * file to insert each SQL file listed into its designated database.
   * 
   * @author <a href="mailto:jeff@custommonkey.org">Jeff Martin</a>
   * @author <a href="mailto:gholam@xtra.co.nz">Michael McCallum</A>
   * @author <a href="mailto:tim.stephenson@sybase.com">Tim Stephenson</A>
   * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</A>
   */
  public class TorqueSQLExec 
      extends Task 
  {
      static public class DelimiterType 
          extends EnumeratedAttribute 
      {
          static public final String NORMAL = "normal";
          static public final String ROW = "row";
          public String[] getValues() 
          {
              return new String[] {NORMAL, ROW};
          }
      }
      
      private int goodSql = 0, totalSql = 0;
      private Path classpath;
      private AntClassLoader loader;
  
      /**
       * Database connection
       */
      private Connection conn = null;
      
      /**
       * Autocommit flag. Default value is false
       */
      private boolean autocommit=false;
      
      /**
       * SQL statement
       */
      private Statement statement = null;
  
      /**
       * DB driver.
       */
      private String driver = null;
  
      /**
       * DB url.
       */
      private String url = null;
  
      /**
       * User name.
       */
      private String userId = null;
  
      /**
       * Password
       */
      private String password = null;
  
      /**
       * SQL input command
       */
      private String sqlCommand = "";
  
      /**
       * SQL transactions to perform
       */
      private Vector transactions = new Vector();
  
      /**
       * SQL Statement delimiter
       */
      private String delimiter = ";";
      
      /**
       * The delimiter type indicating whether the delimiter will
       * only be recognized on a line by itself
       */
      private String delimiterType = DelimiterType.NORMAL;
      
      /**
       * Print SQL results.
       */
      private boolean print = false;
  
      /**
       * Print header columns.
       */
      private boolean showheaders = true;
  
      /**
       * Results Output file.
       */
      private File output = null;
  
      /**
       * RDBMS Product needed for this SQL.
       **/
      private String rdbms = null;
  
      /**
       * RDBMS Version needed for this SQL.
       **/
      private String version = null;
  
      /**
       * Action to perform if an error is found
       **/
      private String onError = "abort";
      
      /**
       * Encoding to use when reading SQL statements from a file
       */
      private String encoding = null;
      
      /**
       * Src directory for the files listed in the sqldbmap.
       */
      private String srcDir;
  
      /**
       * Properties file that maps an individual SQL
       * file to a database.
       */
      private File sqldbmap;
      
      /**
       * Set the sqldbmap properties file.
       */
      public void setSqlDbMap(File sqldbmap)
      {
          this.sqldbmap = sqldbmap;
      }
      
      /**
       * Get the sqldbmap properties file.
       */
      public File getSqlDbMap()
      {
          return sqldbmap;
      }        
      
      /**
       * Set the src directory for the sql files
       * listed in the sqldbmap file.
       */
      public void setSrcDir(String srcDir)
      {
          this.srcDir = srcDir;
      }
      
      /**
       * Get the src directory for the sql files
       * listed in the sqldbmap file.
       */
      public String getSrcDir()
      {
          return srcDir;
      }        
  
      /**
       * Set the classpath for loading the driver.
       */
      public void setClasspath(Path classpath) 
      {
          if (this.classpath == null) 
          {
              this.classpath = classpath;
          } 
          else 
          {
              this.classpath.append(classpath);
          }
      }
  
      /**
       * Create the classpath for loading the driver.
       */
      public Path createClasspath() 
      {
          if (this.classpath == null) 
          {
              this.classpath = new Path(project);
          }
          return this.classpath.createPath();
      }
  
      /**
       * Set the classpath for loading the driver using the classpath reference.
       */
      public void setClasspathRef(Reference r) 
      {
          createClasspath().setRefid(r);
      }
      
      /**
       * Set the sql command to execute
       */
      public void addText(String sql) 
      {
          this.sqlCommand += sql;
      }
      
      /**
       * Set the JDBC driver to be used.
       */
      public void setDriver(String driver) 
      {
          this.driver = driver;
      }
      
      /**
       * Set the DB connection url.
       */
      public void setUrl(String url) 
      {
          this.url = url;
      }
      
      /**
       * Set the user name for the DB connection.
       */
      public void setUserid(String userId) 
      {
          this.userId = userId;
      }
  
      /**
       * Set the file encoding to use on the sql files read in
       *
       * @param encoding the encoding to use on the files
       */
      public void setEncoding(String encoding) 
      {
          this.encoding = encoding;
      }
      
      /**
       * Set the password for the DB connection.
       */
      public void setPassword(String password) 
      {
          this.password = password;
      }
      
      /**
       * Set the autocommit flag for the DB connection.
       */
      public void setAutocommit(boolean autocommit) 
      {
          this.autocommit = autocommit;
      }
  
      /**
       * Set the statement delimiter.
       *
       * <p>For example, set this to "go" and delimitertype to "ROW" for
       * Sybase ASE or MS SQL Server.</p>
       */
      public void setDelimiter(String delimiter) 
      {
          this.delimiter = delimiter;
      }
  
      /**
       * Set the Delimiter type for this sql task. The delimiter type takes
       * two values - normal and row. Normal means that any occurence of the delimiter
       * terminate the SQL command whereas with row, only a line containing just the
       * delimiter is recognized as the end of the command.
       */
      public void setDelimiterType(DelimiterType delimiterType) 
      {
          this.delimiterType = delimiterType.getValue();
      }
      
      /**
       * Set the print flag.
       */
      public void setPrint(boolean print) 
      {
          this.print = print;
      }
      
      /**
       * Set the showheaders flag.
       */
      public void setShowheaders(boolean showheaders) 
      {
          this.showheaders = showheaders;
      }
  
      /**
       * Set the output file.
       */
      public void setOutput(File output) 
      {
          this.output = output;
      }
  
      /**
       * Set the rdbms required
       */
      public void setRdbms(String vendor) 
      {
          this.rdbms = vendor.toLowerCase();
      }
  
      /**
       * Set the version required
       */
      public void setVersion(String version) 
      {
          this.version = version.toLowerCase();
      }
  
      /**
       * Set the action to perform onerror
       */
      public void setOnerror(OnError action) 
      {
          this.onError = action.getValue();
      }
  
      /**
       * Load the sql file and then execute it
       */
      public void execute() 
          throws BuildException 
      {
          sqlCommand = sqlCommand.trim();
  
          if (sqldbmap == null || getSqlDbMap().exists() == false) 
          {
              throw new BuildException("You haven't provided an sqldbmap, or " +
                  "the one you specified doesn't exist: " + sqldbmap);
          }            
          
          if (driver == null) 
          {
              throw new BuildException("Driver attribute must be set!", location);
          }
          if (userId == null) 
          {
              throw new BuildException("User Id attribute must be set!", location);
          }
          if (password == null) 
          {
              throw new BuildException("Password attribute must be set!", location);
          }
          if (url == null) 
          {
              throw new BuildException("Url attribute must be set!", location);
          }
          
          Properties p = new Properties();
          
          try
          {
              FileInputStream fis = new FileInputStream(getSqlDbMap());
              p.load(fis);
              fis.close();
          }
          catch (IOException ioe)
          {
              throw new BuildException("Cannot open and process the sqldbmap!");
          }                        
  
          Hashtable h = new Hashtable();
  
          for (Enumeration e = p.propertyNames(); e.hasMoreElements();)
          {
              String sqlfile = (String) e.nextElement();
              String database = p.getProperty(sqlfile);
              
              ArrayList x = (ArrayList) h.get(database);
              
              if (x == null)
              {
                  x = new ArrayList();
                  h.put(database, x);
              }
              
              // We want to make sure that the base schemas
              // are inserted first.
              if (sqlfile.indexOf("schema.sql") != -1)
              {
                  x.add(0, sqlfile);
              }
              else
              {
                  x.add(sqlfile);
              }                
          }
          
          Iterator k = h.keySet().iterator();
          
          while (k.hasNext())
          {
              String db = (String) k.next();
              ArrayList l = (ArrayList) h.get(db);
              Iterator j = l.iterator();
              Vector ts = new Vector();
              while (j.hasNext())
              {
                  String s = (String) j.next();
                  Transaction t = new Transaction();
                  t.setSrc(new File(srcDir,s));
                  ts.addElement(t);
              }                
              
              insertDatabaseSqlFiles(url, db, ts);
          }
      }        
      
      /**
       * Take the base url, the target database and insert a set of SQL
       * files into the target database.
       */
      private void insertDatabaseSqlFiles(String url, String database, Vector transactions)
      {
          url = StringUtils.replace(url, "@DB@", database);
          System.out.println("Our new url -> " + url);
      
          Driver driverInstance = null;
          try 
          {
              Class dc;
              if (classpath != null) 
              {
                  log( "Loading " + driver + " using AntClassLoader with classpath " + classpath, 
                       Project.MSG_VERBOSE );
  
                  loader = new AntClassLoader(project, classpath);
                  dc = loader.loadClass(driver);
              }
              else 
              {
                  log("Loading " + driver + " using system loader.", Project.MSG_VERBOSE);
                  dc = Class.forName(driver);
              }
              driverInstance = (Driver) dc.newInstance();
          }
          catch(ClassNotFoundException e)
          {
              throw new BuildException("Class Not Found: JDBC driver " + driver + 
                  " could not be loaded", location);
          }
          catch(IllegalAccessException e)
          {
              throw new BuildException("Illegal Access: JDBC driver " + driver + 
                  " could not be loaded", location);
          }
          catch(InstantiationException e) 
          {
              throw new BuildException("Instantiation Exception: JDBC driver " + 
                  driver + " could not be loaded", location);
          }
  
          try
          {
              log("connecting to " + url, Project.MSG_VERBOSE );
              Properties info = new Properties();
              info.put("user", userId);
              info.put("password", password);
              conn = driverInstance.connect(url, info);
  
              if (conn == null) 
              {
                  // Driver doesn't understand the URL
                  throw new SQLException("No suitable Driver for "+url);
              }
  
              if (!isValidRdbms(conn)) return;
  
              conn.setAutoCommit(autocommit);
              statement = conn.createStatement();
              PrintStream out = System.out;
              try 
              {
                  if (output != null) 
                  {
                      log("Opening PrintStream to output file " + output, Project.MSG_VERBOSE);
                      out = new PrintStream(new BufferedOutputStream(new FileOutputStream(output)));
                  }
                          
                  // Process all transactions
                  for (Enumeration e = transactions.elements(); e.hasMoreElements();) 
                  {
                      ((Transaction) e.nextElement()).runTransaction(out);
                      if (!autocommit) 
                      {
                          log("Commiting transaction", Project.MSG_VERBOSE);
                          conn.commit();
                      }
                  }
              }
              finally 
              {
                  if (out != null && out != System.out) 
                  {
                      out.close();
                  }
              }
          } 
          catch(IOException e)
          {
              if (!autocommit && conn != null && onError.equals("abort")) 
              {
                  try 
                  {
                      conn.rollback();
                  } 
                  catch (SQLException ex) 
                  {
                      // do nothing.
                  }
              }
              throw new BuildException(e, location);
          } 
          catch(SQLException e)
          {
              if (!autocommit && conn != null && onError.equals("abort")) 
              {
                  try 
                  {
                      conn.rollback();
                  } 
                  catch (SQLException ex) 
                  {
                      // do nothing.
                  }
              }
              throw new BuildException(e, location);
          }
          finally 
          {
              try 
              {
                  if (statement != null) 
                  {
                      statement.close();
                  }
                  if (conn != null) 
                  {
                      conn.close();
                  }
              }
              catch (SQLException e) {}
          }
            
          log(goodSql + " of " + totalSql + 
              " SQL statements executed successfully");
      }
  
      protected void runStatements(Reader reader, PrintStream out) 
          throws SQLException, IOException 
      {
          String sql = "";
          String line = "";
   
          BufferedReader in = new BufferedReader(reader);
   
          try
          {
              while ((line=in.readLine()) != null)
              {
                  line = line.trim();
                  line = ProjectHelper.replaceProperties(project, line,
                                                         project.getProperties());
                  if (line.startsWith("//")) continue;
                  if (line.startsWith("--")) continue;
                  if (line.length() > 2 &&
                      line.substring(0,3).equalsIgnoreCase("REM")) continue;
  
                  sql += " " + line;
                  sql = sql.trim();
  
                  // SQL defines "--" as a comment to EOL
                  // and in Oracle it may contain a hint
                  // so we cannot just remove it, instead we must end it
                  if (line.indexOf("--") >= 0) sql += "\n";
  
                  if (delimiterType.equals(DelimiterType.NORMAL) && sql.endsWith(delimiter) ||
                      delimiterType.equals(DelimiterType.ROW) && line.equals(delimiter)) {
                      log("SQL: " + sql, Project.MSG_VERBOSE);
                      execSQL(sql.substring(0, sql.length() - delimiter.length()), out);
                      sql = "";
                  }
              }
   
              // Catch any statements not followed by ;
              if(!sql.equals(""))
              {
                  execSQL(sql, out);
              }
          }
          catch(SQLException e)
          {
              throw e;
          }
      }
   
      /**
       * Verify if connected to the correct RDBMS
       **/
      protected boolean isValidRdbms(Connection conn) 
      {
          if (rdbms == null && version == null)
          {
              return true;
          }            
          
          try 
          {
              DatabaseMetaData dmd = conn.getMetaData();
              
              if (rdbms != null) 
              {
                  String theVendor = dmd.getDatabaseProductName().toLowerCase();
                  
                  log("RDBMS = " + theVendor, Project.MSG_VERBOSE);
                  if (theVendor == null || theVendor.indexOf(rdbms) < 0) 
                  {
                      log("Not the required RDBMS: "+rdbms, Project.MSG_VERBOSE);
                      return false;
                  }
              }
              
              if (version != null) 
              {
                  String theVersion = dmd.getDatabaseProductVersion().toLowerCase();
                  
                  log("Version = " + theVersion, Project.MSG_VERBOSE);
                  if (theVersion == null || 
                      !(theVersion.startsWith(version) || 
                        theVersion.indexOf(" " + version) >= 0)) 
                  {
                      log("Not the required version: \""+ version +"\"", Project.MSG_VERBOSE);
                      return false;
                  }
              }
          }
          catch (SQLException e) 
          {
              // Could not get the required information
              log("Failed to obtain required RDBMS information", Project.MSG_ERR);
              return false;
          }
          
          return true;
      }
      
      /**
       * Exec the sql statement.
       */
      protected void execSQL(String sql, PrintStream out) throws SQLException 
      {
          // Check and ignore empty statements
          if ("".equals(sql.trim())) return;
          
          try 
          {
              totalSql++;
              if (!statement.execute(sql)) 
              {
                  log(statement.getUpdateCount()+" rows affected", 
                      Project.MSG_VERBOSE);
              }
              else 
              {
                  if (print) 
                  {
                      printResults(out);
                  }
              }
              
              SQLWarning warning = conn.getWarnings();
              while(warning!=null)
              {
                  log(warning + " sql warning", Project.MSG_VERBOSE);
                  warning=warning.getNextWarning();
              }
              conn.clearWarnings();
              goodSql++;
          }
          catch (SQLException e) 
          {
              log("Failed to execute: " + sql, Project.MSG_ERR);
              if (!onError.equals("continue")) 
              { 
                  throw e;
              }                
              log(e.toString(), Project.MSG_ERR);
          }
      }
      
      /**
       * print any results in the statement.
       */
      protected void printResults(PrintStream out) throws java.sql.SQLException 
      {
          ResultSet rs = null;
          do 
          {
              rs = statement.getResultSet();
              if (rs != null) 
              {
                  log("Processing new result set.", Project.MSG_VERBOSE);
                  ResultSetMetaData md = rs.getMetaData();
                  int columnCount = md.getColumnCount();
                  StringBuffer line = new StringBuffer();
                  if (showheaders) 
                  {
                      for (int col = 1; col < columnCount; col++) 
                      {
                           line.append(md.getColumnName(col));
                           line.append(",");
                      }
                      line.append(md.getColumnName(columnCount));
                      out.println(line);
                      line.setLength(0);
                  }
                  while (rs.next()) 
                  {
                      boolean first = true;
                      for (int col = 1; col <= columnCount; col++) 
                      {
                          String columnValue = rs.getString(col);
                          if (columnValue != null) 
                          {
                              columnValue = columnValue.trim();
                          }
                           
                          if (first) 
                          {
                              first = false;
                          }
                          else 
                          {
                              line.append(",");
                          }
                          line.append(columnValue);
                      }
                      out.println(line);
                      line.setLength(0);
                  }
              }
          }
          while (statement.getMoreResults());
          out.println();
      }
  
      /**
       * Enumerated attribute with the values "continue", "stop" and "abort"
       * for the onerror attribute.  
       */
      public static class OnError extends EnumeratedAttribute 
      {
          public String[] getValues() 
          {
              return new String[] {"continue", "stop", "abort"};
          }
      }
  
      /**
       * Contains the definition of a new transaction element.
       * Transactions allow several files or blocks of statements
       * to be executed using the same JDBC connection and commit
       * operation in between.
       */
      public class Transaction 
      {
          private File tSrcFile = null;
          private String tSqlCommand = "";
  
          public void setSrc(File src) 
          {
              this.tSrcFile = src;
          }
  
          public void addText(String sql) 
          {
              this.tSqlCommand += sql;
          }
  
          private void runTransaction(PrintStream out) 
              throws IOException, SQLException 
          {
              if (tSqlCommand.length() != 0) 
              {
                  log("Executing commands", Project.MSG_INFO);
                  runStatements(new StringReader(tSqlCommand), out);
              }
        
              if (tSrcFile != null) 
              {
                  log("Executing file: " + tSrcFile.getAbsolutePath(), 
                      Project.MSG_INFO);
                  Reader reader = (encoding == null) ? new FileReader(tSrcFile)
                                                     : new InputStreamReader(new FileInputStream(tSrcFile), encoding);
                  runStatements(reader, out);
                  reader.close();
              }
          }
      }
  }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>