/* * This file or a portion of this file is licensed under the terms of * the Globus Toolkit Public License, found in file ../GTPL, or at * http://www.globus.org/toolkit/download/license.html. This notice must * appear in redistributions of this file, with or without modification. * * Redistributions of this Software, with or without modification, must * reproduce the GTPL in: (1) the Software, or (2) the Documentation or * some other similar material which is provided with the Software (if * any). * * Copyright 1999-2004 University of Chicago and The University of * Southern California. All rights reserved. */ package org.griphyn.vdl.dbdriver; import org.griphyn.vdl.dbdriver.DatabaseDriver; import java.sql.*; import java.util.*; import org.griphyn.vdl.util.*; /** * This class implements the driver API for the MySQL 4.* database. * * @author Jens-S. Vöckler * @author Yong Zhao * @version $Revision$ * * @see DatabaseDriver * @see org.griphyn.vdl.dbschema */ public class MySQL extends DatabaseDriver { /** * Maintains the property, if locking of sequence table is required. */ boolean m_lockSequenceTable; /** * Default constructor. As the constructor will do nothing, please use * the connect method to obtain a database connection. * * @see #connect( String, Properties, Set ) */ public MySQL() { super(); m_lockSequenceTable = true; } /** * Establish a connection to your database. The parameters will often * be ignored or abused for different purposes on different backends. * It is assumed that the connection is not in auto-commit mode, and * explicit commits must be issued. * * @param url the contact string to database, or schema location * @param info additional parameters, usually username and password * @param tables is a set of all table names in the schema. The * existence of all tables will be checked to verify * that the schema is active in the database. * @return true if the connection succeeded, false otherwise. Usually, * false is returned, if the any of the tables or sequences is missing. * @exception SQLException if the driver is incapable of establishing * a connection. */ public boolean connect( String url, Properties info, Set tables ) throws SQLException, ClassNotFoundException { // load MySQL driver class into memory boolean save = this.connect( "com.mysql.jdbc.Driver", url, info, tables ); // check for non-locking sequences /* DO NOT DO THIS, * because the PTC will always have several instances run in parallel. * If necessary, use an extra property that is specifically linked to * the combination of VDC and MySQL (argh, I want to avoid just that). * * String lock = info.getProperty( "lockSequenceTable", "true" ); * m_lockSequenceTable = Boolean.valueOf(lock).booleanValue(); */ // add preparsed statement for sequence management this.addPreparedStatement( "vds.sequence.0", "LOCK TABLE sequences WRITE" ); this.addPreparedStatement( "vds.sequence.1", "UPDATE sequences SET currval=currval+1 WHERE name=?" ); this.addPreparedStatement( "vds.sequence.2", "SELECT currval FROM sequences where name=?" ); this.addPreparedStatement( "vds.sequence.3", "UNLOCK TABLE" ); // done return save; } /** * Determines, if the backend is expensive, and results should be cached. * Ideally, this will move transparently into the backend itself. * @return true if caching is advisable, false for no caching. */ public boolean cachingMakesSense() { return true; } /** * Quotes a string that may contain special SQL characters. * @param s is the raw string. * @return the quoted string, which may be just the input string. */ public String quote( String s ) { if ( s.indexOf('\'') != -1 ) { StringBuffer result = new StringBuffer(); for ( int i=0; i < s.length(); ++i ) { char ch = s.charAt(i); result.append(ch); if ( ch == '\'' ) result.append(ch); } return result.toString(); } else { return s; } } /** * Obtains the next value from a sequence. * * @param name is the name of the sequence. * @return the next sequence number. * @exception SQLException if something goes wrong while fetching the * new value. */ public long sequence1( String name ) throws SQLException { PreparedStatement ps = null; Logging.instance().log( "sql", 2, "SELECT nextval(" + name + ")" ); Logging.instance().log( "xaction", 1, "START sequence " + name ); // phase 1: lock sequence table if ( m_lockSequenceTable ) { ps = this.getPreparedStatement( "vds.sequence.0" ); ps.executeUpdate(); } // phase 2: increment sequence ps = this.getPreparedStatement( "vds.sequence.1" ); ps.setString( 1, name ); ps.executeUpdate(); // phase 3: obtain new value ps = this.getPreparedStatement( "vds.sequence.2" ); ps.setString( 1, name ); ResultSet rs = ps.executeQuery(); rs.next(); long result = rs.getLong(1); rs.close(); // phase 4: unlock table if ( m_lockSequenceTable ) { ps = this.getPreparedStatement( "vds.sequence.3" ); ps.executeUpdate(); } // done // ??? commit(); Logging.instance().log("xaction", 1, "FINAL sequence " + name + " = " + result ); return result; } /** * Obtains the sequence value for the current statement. Sigh. * * @param s is a statment or prepared statement * @param name is the name of the sequence. * @param pos is the column number of the auto-increment column. * @return the next sequence number. * @exception SQLException if something goes wrong while fetching the * new value. */ public long sequence2( Statement s, String name, int pos ) throws SQLException { return -1; } /** * Predicate to tell the schema, if using a string instead of number * will result in the speedier index scans instead of sequential scans. * PostGreSQL has this problem, but using strings in the place of * integers may not be universally portable. * * @return true, if using strings instead of integers and bigints * will yield better performance. * */ public boolean preferString() { return false; } }