DatabaseUtils.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 23k
Category:

Windows Develop

Development Platform:

Java

  1. /*
  2.  *    This program is free software; you can redistribute it and/or modify
  3.  *    it under the terms of the GNU General Public License as published by
  4.  *    the Free Software Foundation; either version 2 of the License, or
  5.  *    (at your option) any later version.
  6.  *
  7.  *    This program is distributed in the hope that it will be useful,
  8.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.  *    GNU General Public License for more details.
  11.  *
  12.  *    You should have received a copy of the GNU General Public License
  13.  *    along with this program; if not, write to the Free Software
  14.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15.  */
  16. /*
  17.  *    DatabaseUtils.java
  18.  *    Copyright (C) 1999 Len Trigg
  19.  *
  20.  */
  21. package weka.experiment;
  22. import weka.core.Utils;
  23. import java.util.Properties;
  24. import java.util.Vector;
  25. import java.util.StringTokenizer;
  26. import java.io.Serializable;
  27. import java.io.FileInputStream;
  28. import java.sql.SQLException;
  29. import java.sql.ResultSet;
  30. import java.sql.Statement;
  31. import java.sql.Types;
  32. import java.sql.DriverManager;
  33. import java.sql.Connection;
  34. import java.sql.DatabaseMetaData;
  35. import java.sql.ResultSetMetaData;
  36. /**
  37.  * DatabaseUtils provides utility functions for accessing the experiment
  38.  * database. The jdbc
  39.  * driver and database to be used default to "jdbc.idbDriver" and
  40.  * "jdbc:idb=experiments.prp". These may be changed by creating
  41.  * a java properties file called DatabaseUtils.props in user.home or
  42.  * the current directory. eg:<p>
  43.  *
  44.  * <code><pre>
  45.  * jdbcDriver=jdbc.idbDriver
  46.  * jdbcURL=jdbc:idb=experiments.prp
  47.  * </pre></code><p>
  48.  *
  49.  * @author Len Trigg (trigg@cs.waikato.ac.nz)
  50.  * @version $Revision: 1.15 $
  51.  */
  52. public class DatabaseUtils implements Serializable {
  53.   /** The name of the table containing the index to experiments */
  54.   public static final String EXP_INDEX_TABLE = "Experiment_index";
  55.   /** The name of the column containing the experiment type (ResultProducer) */
  56.   public static final String EXP_TYPE_COL    = "Experiment_type";
  57.   /** The name of the column containing the experiment setup (parameters) */
  58.   public static final String EXP_SETUP_COL   = "Experiment_setup";
  59.   
  60.   /** The name of the column containing the results table name */
  61.   public static final String EXP_RESULT_COL  = "Result_table";
  62.   /** The prefix for result table names */
  63.   public static final String EXP_RESULT_PREFIX = "Results";
  64.   /** The name of the properties file */
  65.   protected static String PROPERTY_FILE
  66.     = "weka/experiment/DatabaseUtils.props";
  67.   /** Holds the jdbc drivers to be used (only to stop them being gc'ed) */
  68.   protected static Vector DRIVERS = new Vector();
  69.   /** Properties associated with the database connection */
  70.   protected static Properties PROPERTIES;
  71.   /* Load the database drivers -- the properties files only get consulted
  72.    * when the class is initially loaded, not for every object instantiated
  73.    */
  74.   static {
  75.     try {
  76.       PROPERTIES = Utils.readProperties(PROPERTY_FILE);
  77.    
  78.       // Register the drivers in jdbc DriverManager
  79.       String drivers = PROPERTIES.getProperty("jdbcDriver",
  80.     "jdbc.idbDriver");
  81.       if (drivers == null) {
  82. throw new Exception("No jdbc drivers specified");
  83.       }
  84.       // The call to newInstance() is necessary on some platforms
  85.       // (with some java VM implementations)
  86.       StringTokenizer st = new StringTokenizer(drivers, ", ");
  87.       while (st.hasMoreTokens()) {
  88. String driver = st.nextToken();
  89. try {
  90.   DRIVERS.addElement(Class.forName(driver).newInstance());
  91.   System.err.println("Loaded driver: " + driver);
  92. } catch (Exception ex) {
  93.   // Drop through
  94. }
  95.       }
  96.     } catch (Exception ex) {
  97.       System.err.println("Problem reading properties. Fix before continuing.");
  98.       System.err.println(ex);
  99.     }
  100.   }
  101.   
  102.   /** Database URL */
  103.   protected String m_DatabaseURL;
  104.   
  105.   /** The database connection */
  106.   protected Connection m_Connection;
  107.   /** The statement used for database queries */
  108.   protected Statement m_Statement;
  109.   /** True if debugging output should be printed */
  110.   protected boolean m_Debug = true;
  111.   
  112.   /**
  113.    * Sets up the database drivers
  114.    *
  115.    * @exception Exception if an error occurs
  116.    */
  117.   public DatabaseUtils() throws Exception {
  118.     m_DatabaseURL = PROPERTIES.getProperty("jdbcURL",
  119.    "jdbc:idb=experiments.prp");
  120.   }
  121.   /**
  122.    * Converts an array of objects to a string by inserting a space
  123.    * between each element. Null elements are printed as ?
  124.    *
  125.    * @param array the array of objects
  126.    * @return a value of type 'String'
  127.    */
  128.   public static String arrayToString(Object [] array) {
  129.     String result = "";
  130.     if (array == null) {
  131.       result = "<null>";
  132.     } else {
  133.       for (int i = 0; i < array.length; i++) {
  134. if (array[i] == null) {
  135.   result += " ?";
  136. } else {
  137.   result += " " + array[i];
  138. }
  139.       }
  140.     }
  141.     return result;
  142.   }
  143.   /**
  144.    * Returns the name associated with a SQL type.
  145.    *
  146.    * @param type the SQL type
  147.    * @return the name of the type
  148.    */
  149.   static public String typeName(int type) {
  150.   
  151.     switch (type) {
  152.     case Types.BIGINT :
  153.       return "BIGINT ";
  154.     case Types.BINARY:
  155.       return "BINARY";
  156.     case Types.BIT:
  157.       return "BIT";
  158.     case Types.CHAR:
  159.       return "CHAR";
  160.     case Types.DATE:
  161.       return "DATE";
  162.     case Types.DECIMAL:
  163.       return "DECIMAL";
  164.     case Types.DOUBLE:
  165.       return "DOUBLE";
  166.     case Types.FLOAT:
  167.       return "FLOAT";
  168.     case Types.INTEGER:
  169.       return "INTEGER";
  170.     case Types.LONGVARBINARY:
  171.       return "LONGVARBINARY";
  172.     case Types.LONGVARCHAR:
  173.       return "LONGVARCHAR";
  174.     case Types.NULL:
  175.       return "NULL";
  176.     case Types.NUMERIC:
  177.       return "NUMERIC";
  178.     case Types.OTHER:
  179.       return "OTHER";
  180.     case Types.REAL:
  181.       return "REAL";
  182.     case Types.SMALLINT:
  183.       return "SMALLINT";
  184.     case Types.TIME:
  185.       return "TIME";
  186.     case Types.TIMESTAMP:
  187.       return "TIMESTAMP";
  188.     case Types.TINYINT:
  189.       return "TINYINT";
  190.     case Types.VARBINARY:
  191.       return "VARBINARY";
  192.     case Types.VARCHAR:
  193.       return "VARCHAR";
  194.     default:
  195.       return "Unknown";
  196.     }
  197.   }
  198.   /**
  199.    * Returns the tip text for this property
  200.    * @return tip text for this property suitable for
  201.    * displaying in the explorer/experimenter gui
  202.    */
  203.   public String databaseURLTipText() {
  204.     return "Set the URL to the database.";
  205.   }
  206.   /**
  207.    * Get the value of DatabaseURL.
  208.    *
  209.    * @return Value of DatabaseURL.
  210.    */
  211.   public String getDatabaseURL() {
  212.     
  213.     return m_DatabaseURL;
  214.   }
  215.   
  216.   /**
  217.    * Set the value of DatabaseURL.
  218.    *
  219.    * @param newDatabaseURL Value to assign to DatabaseURL.
  220.    */
  221.   public void setDatabaseURL(String newDatabaseURL) {
  222.     
  223.     m_DatabaseURL = newDatabaseURL;
  224.   }
  225.   /**
  226.    * Opens a connection to the database
  227.    *
  228.    * @exception Exception if an error occurs
  229.    */
  230.   public void connectToDatabase() throws Exception {
  231.     
  232.     if (m_Debug) {
  233.       System.err.println("Connecting to " + m_DatabaseURL);
  234.     }
  235.     if (m_Connection == null) {
  236.       m_Connection = DriverManager.getConnection(m_DatabaseURL);
  237.       m_Statement = m_Connection.createStatement();
  238.     }
  239.   }
  240.   /**
  241.    * Closes the connection to the database.
  242.    *
  243.    * @exception Exception if an error occurs
  244.    */
  245.   public void disconnectFromDatabase() throws Exception {
  246.     if (m_Debug) {
  247.       System.err.println("Disconnecting from " + m_DatabaseURL);
  248.     }
  249.     if (m_Connection != null) {
  250.       m_Connection.close();
  251.       m_Connection = null;
  252.       m_Statement = null;
  253.     }
  254.   }
  255.   
  256.   /**
  257.    * Returns true if a database connection is active.
  258.    *
  259.    * @return a value of type 'boolean'
  260.    */
  261.   public boolean isConnected() {
  262.     return (m_Connection != null);
  263.   }
  264.   /**
  265.    * Executes a SQL query.
  266.    *
  267.    * @param query the SQL query
  268.    * @return true if the query generated results
  269.    * @exception SQLException if an error occurs
  270.    */
  271.   public boolean execute(String query) throws SQLException {
  272.     return m_Statement.execute(query);
  273.   }
  274.   /**
  275.    * Gets the results generated by a previous query.
  276.    *
  277.    * @return the result set.
  278.    * @exception SQLException if an error occurs
  279.    */
  280.   public ResultSet getResultSet() throws SQLException {
  281.     return m_Statement.getResultSet();
  282.   }
  283.   
  284.   /**
  285.    * Checks that a given table exists.
  286.    *
  287.    * @param tableName the name of the table to look for.
  288.    * @return true if the table exists.
  289.    * @exception Exception if an error occurs.
  290.    */
  291.   public boolean tableExists(String tableName) throws Exception {
  292.     if (m_Debug) {
  293.       System.err.println("Checking if table " + tableName + " exists...");
  294.     }
  295.     DatabaseMetaData dbmd = m_Connection.getMetaData();
  296.     ResultSet rs = dbmd.getTables (null, null, tableName, null);
  297.     boolean tableExists = rs.next();
  298.     if (rs.next()) {
  299.       throw new Exception("This table seems to exist more than once!");
  300.     }
  301.     rs.close();
  302.     if (m_Debug) {
  303.       if (tableExists) {
  304. System.err.println("... " + tableName + " exists");
  305.       } else {
  306. System.err.println("... " + tableName + " does not exist");
  307.       }
  308.     }
  309.     return tableExists;
  310.   }
  311.   
  312.   /**
  313.    * Executes a database query to see whether a result for the supplied key
  314.    * is already in the database.           
  315.    *
  316.    * @param tableName the name of the table to search for the key in
  317.    * @param rp the ResultProducer who will generate the result if required
  318.    * @param key the key for the result
  319.    * @return true if the result with that key is in the database already
  320.    * @exception Exception if an error occurs
  321.    */
  322.   protected boolean isKeyInTable(String tableName,
  323.  ResultProducer rp,
  324.  Object[] key)
  325.     throws Exception {
  326.     String query = "SELECT Key_Run"
  327.       + " FROM " + tableName;
  328.     String [] keyNames = rp.getKeyNames();
  329.     if (keyNames.length != key.length) {
  330.       throw new Exception("Key names and key values of different lengths");
  331.     }
  332.     boolean first = true;
  333.     for (int i = 0; i < key.length; i++) {
  334.       if (key[i] != null) {
  335. if (first) {
  336.   query += " WHERE ";
  337.   first = false;
  338. } else {
  339.   query += " AND ";
  340. }
  341. query += "Key_" + keyNames[i] + '=';
  342. if (key[i] instanceof String) {
  343.   query += ''' + key[i].toString() + ''';
  344. } else {
  345.   query += key[i].toString();
  346. }
  347.       }
  348.     }
  349.     boolean retval = false;
  350.     if (m_Statement.execute(query)) {
  351.       ResultSet rs = m_Statement.getResultSet();
  352.       int numAttributes = rs.getMetaData().getColumnCount();
  353.       if (rs.next()) {
  354. retval = true;
  355. if (rs.next()) {
  356.   throw new Exception("More than one result entry "
  357.       + "for result key: " + query);
  358. }
  359.       }
  360.       rs.close();
  361.     }
  362.     return retval;
  363.   }
  364.   /**
  365.    * Executes a database query to extract a result for the supplied key
  366.    * from the database.           
  367.    *
  368.    * @param tableName the name of the table where the result is stored
  369.    * @param rp the ResultProducer who will generate the result if required
  370.    * @param key the key for the result
  371.    * @return true if the result with that key is in the database already
  372.    * @exception Exception if an error occurs
  373.    */
  374.   public Object [] getResultFromTable(String tableName,
  375.  ResultProducer rp,
  376.  Object [] key)
  377.     throws Exception {
  378.     String query = "SELECT ";
  379.     String [] resultNames = rp.getResultNames();
  380.     for (int i = 0; i < resultNames.length; i++) {
  381.       if (i != 0) {
  382. query += ", ";
  383.       }
  384.       query += resultNames[i];
  385.     }
  386.     query += " FROM " + tableName;
  387.     String [] keyNames = rp.getKeyNames();
  388.     if (keyNames.length != key.length) {
  389.       throw new Exception("Key names and key values of different lengths");
  390.     }
  391.     boolean first = true;
  392.     for (int i = 0; i < key.length; i++) {
  393.       if (key[i] != null) {
  394. if (first) {
  395.   query += " WHERE ";
  396.   first = false;
  397. } else {
  398.   query += " AND ";
  399. }
  400. query += "Key_" + keyNames[i] + '=';
  401. if (key[i] instanceof String) {
  402.   query += '"' + key[i].toString() + '"';
  403. } else {
  404.   query += key[i].toString();
  405. }
  406.       }
  407.     }
  408.     if (!m_Statement.execute(query)) {
  409.       throw new Exception("Couldn't execute query: " + query);
  410.     }
  411.     ResultSet rs = m_Statement.getResultSet();
  412.     ResultSetMetaData md = rs.getMetaData();
  413.     int numAttributes = md.getColumnCount();
  414.     if (!rs.next()) {
  415.       throw new Exception("No result for query: " + query);
  416.     }
  417.     // Extract the columns for the result
  418.     Object [] result = new Object [numAttributes];
  419.     for(int i = 1; i <= numAttributes; i++) {
  420.       switch (md.getColumnType(i)) {
  421.       case Types.CHAR:
  422.       case Types.VARCHAR:
  423.       case Types.LONGVARCHAR:
  424.       case Types.BINARY:
  425.       case Types.VARBINARY:
  426.       case Types.LONGVARBINARY:
  427. result[i - 1] = rs.getString(i);
  428. if (rs.wasNull()) {
  429.   result[i - 1] = null;
  430. }
  431. break;
  432.       case Types.FLOAT:
  433.       case Types.DOUBLE:
  434. result[i - 1] = new Double(rs.getDouble(i));
  435. if (rs.wasNull()) {
  436.   result[i - 1] = null;
  437. }
  438. break;
  439.       default:
  440. throw new Exception("Unhandled SQL result type (field " + (i + 1)
  441.     + "): "
  442.     + DatabaseUtils.typeName(md.getColumnType(i)));
  443.       }
  444.     }
  445.     if (rs.next()) {
  446.       throw new Exception("More than one result entry "
  447.   + "for result key: " + query);
  448.     }
  449.     rs.close();
  450.     return result;
  451.   }
  452.   /**
  453.    * Executes a database query to insert a result for the supplied key
  454.    * into the database.           
  455.    *
  456.    * @param tableName the name of the table where the result is stored
  457.    * @param rp the ResultProducer who will generate the result if required
  458.    * @param key the key for the result
  459.    * @param result the result to store
  460.    * @return true if the result with that key is in the database already
  461.    * @exception Exception if an error occurs
  462.    */
  463.   public void putResultInTable(String tableName,
  464.        ResultProducer rp,
  465.        Object [] key,
  466.        Object [] result)
  467.     throws Exception {
  468.     
  469.     String query = "INSERT INTO " + tableName
  470.       + " VALUES ( ";
  471.     // Add the results to the table
  472.     for (int i = 0; i < key.length; i++) {
  473.       if (i != 0) {
  474. query += ',';
  475.       }
  476.       if (key[i] != null) {
  477. if (key[i] instanceof String) {
  478.   query += ''' + key[i].toString() + ''';
  479. } else if (key[i] instanceof Double) {
  480.   query += safeDoubleToString((Double)key[i]);
  481. } else {
  482.   query += key[i].toString();
  483. }
  484.       } else {
  485. query += "NULL";
  486.       }
  487.     }
  488.     for (int i = 0; i < result.length; i++) {
  489.       query +=  ',';
  490.       if (result[i] != null) {
  491. if (result[i] instanceof String) {
  492.   query += '"' + result[i].toString() + '"';
  493. } else  if (result[i] instanceof Double) {
  494.   query += safeDoubleToString((Double)result[i]);
  495. } else {
  496.   query += result[i].toString();
  497. }
  498.       } else {
  499. query += "NULL";
  500.       }
  501.     }
  502.     query += ')';
  503.     if (m_Debug) {
  504.       System.err.println("Submitting result: " + query);
  505.     }
  506.     if (m_Statement.execute(query)) {
  507.       if (m_Debug) {
  508. System.err.println("...acceptResult returned resultset");
  509.       }
  510.     }
  511.   }
  512.   
  513.   /**
  514.    * Inserts a + if the double is in scientific notation.
  515.    * MySQL doesn't understand the number otherwise.
  516.    */
  517.   private String safeDoubleToString(Double number) {
  518.     String orig = number.toString();
  519.     int pos = orig.indexOf('E');
  520.     if ((pos == -1) || (orig.charAt(pos + 1) == '-')) {
  521.       return orig;
  522.     } else {
  523.       StringBuffer buff = new StringBuffer(orig);
  524.       buff.insert(pos + 1, '+');
  525.       return new String(buff);
  526.     }
  527.   }
  528.   /**
  529.    * Returns true if the experiment index exists.
  530.    *
  531.    * @return true if the index exists
  532.    * @exception Exception if an error occurs
  533.    */
  534.   public boolean experimentIndexExists() throws Exception {
  535.     return tableExists(EXP_INDEX_TABLE);
  536.   }
  537.   
  538.   /**
  539.    * Attempts to create the experiment index table
  540.    *
  541.    * @exception Exception if an error occurs.
  542.    */
  543.   public void createExperimentIndex() throws Exception {
  544.     if (m_Debug) {
  545.       System.err.println("Creating experiment index table...");
  546.     }
  547.     String query;
  548.     // Workaround for MySQL (doesn't support LONGVARBINARY)
  549.     // Also for InstantDB which attempts to interpret numbers when storing
  550.     // in LONGVARBINARY
  551.     if (m_Connection.getMetaData().getDriverName().
  552. equals("Mark Matthews' MySQL Driver")
  553. || (m_Connection.getMetaData().getDriverName().
  554. indexOf("InstantDB JDBC Driver") != -1)) {
  555.       query = "CREATE TABLE " + EXP_INDEX_TABLE 
  556. + " ( " + EXP_TYPE_COL + " TEXT,"
  557. + "  " + EXP_SETUP_COL + " TEXT,"
  558. + "  " + EXP_RESULT_COL + " INT )";
  559.     } else {
  560.       query = "CREATE TABLE " + EXP_INDEX_TABLE 
  561. + " ( " + EXP_TYPE_COL + " LONGVARBINARY,"
  562. + "  " + EXP_SETUP_COL + " LONGVARBINARY,"
  563. + "  " + EXP_RESULT_COL + " INT )";
  564.     }
  565.     // Other possible fields:
  566.     //   creator user name (from System properties)
  567.     //   creation date
  568.     if (m_Statement.execute(query)) {
  569.       if (m_Debug) {
  570. System.err.println("...create returned resultset");
  571.       }
  572.     }
  573.   }
  574.   /**
  575.    * Attempts to insert a results entry for the table into the
  576.    * experiment index.
  577.    *
  578.    * @param rp the ResultProducer generating the results
  579.    * @return the name of the created results table
  580.    * @exception Exception if an error occurs.
  581.    */
  582.   public String createExperimentIndexEntry(ResultProducer rp)
  583.     throws Exception {
  584.     if (m_Debug) {
  585.       System.err.println("Creating experiment index entry...");
  586.     }
  587.     // Execute compound transaction
  588.     int numRows = 0;
  589.     
  590.     // Workaround for MySQL (doesn't support transactions)
  591.     if (m_Connection.getMetaData().getDriverName().
  592. equals("Mark Matthews' MySQL Driver")) {
  593.       m_Statement.execute("LOCK TABLES " + EXP_INDEX_TABLE + " WRITE");
  594.       System.err.println("LOCKING TABLE");
  595.     } else {
  596.       m_Connection.setAutoCommit(false);
  597.     }
  598.     // Get the number of rows
  599.     String query = "SELECT COUNT(*) FROM " + EXP_INDEX_TABLE;
  600.     if (m_Statement.execute(query)) {
  601.       if (m_Debug) {
  602. System.err.println("...getting number of rows");
  603.       }
  604.       ResultSet rs = m_Statement.getResultSet();
  605.       if (rs.next()) {
  606. numRows = rs.getInt(1);
  607.       }
  608.       rs.close();
  609.     }
  610.     // Add an entry in the index table
  611.     String expType = rp.getClass().getName();
  612.     String expParams = rp.getCompatibilityState();
  613.     query = "INSERT INTO " + EXP_INDEX_TABLE
  614.       + " VALUES ( ""
  615.       + expType + "", "" + expParams
  616.       + "", " + numRows + " )";
  617.     if (m_Statement.execute(query)) {
  618.       if (m_Debug) {
  619. System.err.println("...create returned resultset");
  620.       }
  621.     }
  622.     
  623.     // Finished compound transaction
  624.     // Workaround for MySQL (doesn't support transactions)
  625.     if (m_Connection.getMetaData().getDriverName().
  626. equals("Mark Matthews' MySQL Driver")) {
  627.       m_Statement.execute("UNLOCK TABLES");
  628.       System.err.println("UNLOCKING TABLE");
  629.     } else {
  630.       m_Connection.commit();
  631.       m_Connection.setAutoCommit(true);
  632.     }
  633.     String tableName = getResultsTableName(rp);
  634.     if (tableName == null) {
  635.       throw new Exception("Problem adding experiment index entry");
  636.     }
  637.     // Drop any existing table by that name (shouldn't occur unless
  638.     // the experiment index is destroyed, in which case the experimental
  639.     // conditions of the existing table are unknown)
  640.     try {
  641.       query = "DROP TABLE " + tableName;
  642.       if (m_Debug) {
  643. System.err.println(query);
  644.       }
  645.       m_Statement.execute(query);
  646.     } catch (SQLException ex) {
  647.       System.err.println(ex.getMessage());
  648.     }
  649.     return tableName;
  650.   }
  651.   /**
  652.    * Gets the name of the experiment table that stores results from a
  653.    * particular ResultProducer.
  654.    *
  655.    * @param rp the ResultProducer
  656.    * @return the name of the table where the results for this ResultProducer
  657.    * are stored, or null if there is no table for this ResultProducer.
  658.    * @exception Exception if an error occurs
  659.    */
  660.   public String getResultsTableName(ResultProducer rp) throws Exception {
  661.     // Get the experiment table name, or create a new table if necessary.
  662.     if (m_Debug) {
  663.       System.err.println("Getting results table name...");
  664.     }
  665.     String expType = rp.getClass().getName();
  666.     String expParams = rp.getCompatibilityState();
  667.     String query = "SELECT " + EXP_RESULT_COL 
  668.       + " FROM " + EXP_INDEX_TABLE
  669.       + " WHERE " + EXP_TYPE_COL + "="" + expType 
  670.       + "" AND " + EXP_SETUP_COL + "="" + expParams + '"';
  671.     String tableName = null;
  672.     if (m_Statement.execute(query)) {
  673.       ResultSet rs = m_Statement.getResultSet();
  674.       int numAttributes = rs.getMetaData().getColumnCount();
  675.       if (rs.next()) {
  676. tableName = rs.getString(1);
  677. if (rs.next()) {
  678.   throw new Exception("More than one index entry "
  679.       + "for experiment config: " + query);
  680. }
  681.       }
  682.       rs.close();
  683.     }
  684.     if (m_Debug) {
  685.       System.err.println("...results table = " + ((tableName == null) 
  686.   ? "<null>" 
  687.   : EXP_RESULT_PREFIX
  688.   + tableName));
  689.     }
  690.     return (tableName == null) ? tableName : EXP_RESULT_PREFIX + tableName;
  691.   }
  692.   /**
  693.    * Creates a results table for the supplied result producer.
  694.    *
  695.    * @param rp the ResultProducer generating the results
  696.    * @param tableName the name of the resultsTable
  697.    * @return the name of the created results table
  698.    * @exception Exception if an error occurs.
  699.    */
  700.   public String createResultsTable(ResultProducer rp, String tableName)
  701.     throws Exception {
  702.     if (m_Debug) {
  703.       System.err.println("Creating results table " + tableName + "...");
  704.     }
  705.     String query = "CREATE TABLE " + tableName + " ( ";
  706.     // Loop over the key fields
  707.     String [] names = rp.getKeyNames();
  708.     Object [] types = rp.getKeyTypes();
  709.     if (names.length != types.length) {
  710.       throw new Exception("key names types differ in length");
  711.     }
  712.     for (int i = 0; i < names.length; i++) {
  713.       query += "Key_" + names[i] + " ";
  714.       if (types[i] instanceof Double) {
  715. query += "DOUBLE";
  716.       } else if (types[i] instanceof String) {
  717. // Workaround for MySQL (doesn't support LONGVARCHAR)
  718. // Also for InstantDB which attempts to interpret numbers when storing
  719. // in LONGVARBINARY
  720. if (m_Connection.getMetaData().getDriverName().
  721.     equals("Mark Matthews' MySQL Driver")
  722.     || (m_Connection.getMetaData().getDriverName().
  723. indexOf("InstantDB JDBC Driver")) != -1) {
  724.   query += "TEXT ";
  725. } else {
  726.   query += "LONGVARBINARY ";
  727. }
  728.       } else {
  729. throw new Exception("Unknown/unsupported field type in key");
  730.       }
  731.       query += ", ";
  732.     }
  733.     // Loop over the result fields
  734.     names = rp.getResultNames();
  735.     types = rp.getResultTypes();
  736.     if (names.length != types.length) {
  737.       throw new Exception("result names and types differ in length");
  738.     }
  739.     for (int i = 0; i < names.length; i++) {
  740.       query += names[i] + " ";
  741.       if (types[i] instanceof Double) {
  742. query += "DOUBLE";
  743.       } else if (types[i] instanceof String) {
  744. // Workaround for MySQL (doesn't support LONGVARCHAR)
  745. // Also for InstantDB which attempts to interpret numbers when storing
  746. // in LONGVARBINARY
  747. if (m_Connection.getMetaData().getDriverName().
  748.     equals("Mark Matthews' MySQL Driver")
  749.     || (m_Connection.getMetaData().getDriverName().
  750. equals("InstantDB JDBC Driver"))) {
  751.   query += "TEXT ";
  752. } else {
  753.   query += "LONGVARBINARY ";
  754. }
  755.       } else {
  756. throw new Exception("Unknown/unsupported field type in key");
  757.       }
  758.       if (i < names.length - 1) {
  759. query += ", ";
  760.       }
  761.     }
  762.     query += " )";
  763.     System.err.println(query);
  764.     if (m_Statement.execute(query)) {
  765.       if (m_Debug) {
  766. System.err.println("...create returned resultset");
  767.       }
  768.     }
  769.     return tableName;
  770.   }
  771. } // DatabaseUtils