/* * 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 PostGreSQL 7.3.* and * 7.4.* series database. Please note that at this point, we cannot * recommend to use Postgres 8.0.*. * * @author Jens-S. Vöckler * @author Yong Zhao * @version $Revision$ * * @see DatabaseDriver * @see org.griphyn.vdl.dbschema */ public class Postgres extends DatabaseDriver { /** * Caches the driver version major, because this has side-effects on * the behavior. */ private int m_driver_major = -1; /** * Default constructor. As the constructor will do nothing, please use * the connect method to obtain a database connection. * * @see #connect( String, Properties, Set ) */ public Postgres() { super(); } /** * 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 if the driver is incapable of establishing a connection. */ public boolean connect( String url, Properties info, Set tables ) throws SQLException, ClassNotFoundException { // load PostGreSQL driver class into memory boolean save = this.connect( "org.postgresql.Driver", url, info, tables ); // add preparsed statement for sequence this.addPreparedStatement( "vds.sequence", "SELECT nextval(?)" ); // 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; } /** * Determines, if the JDBC driver is the right one for the database we * talk to. Throws an exception if not. */ public void driverMatch() throws SQLException { DatabaseMetaData meta = m_connection.getMetaData(); int driver_major = meta.getDriverMajorVersion(); int driver_minor = meta.getDriverMinorVersion(); String database = meta.getDatabaseProductVersion(); boolean flag = false; int database_major = -1; int database_minor = -1; try { database_major = meta.getDatabaseMajorVersion(); database_minor = meta.getDatabaseMinorVersion(); } catch ( SQLException e ) { // check for "This method is not yet implemented" if ( e.getErrorCode() == 0 ) flag = true; else throw e; } if ( flag ) { // use old-style check String jdbc = driver_major + "." + driver_minor; if ( ! database.startsWith(jdbc) ) throw new RuntimeException( "JDBC driver " + jdbc + " does not match database version " + database ); } else { // use new-style check - requires 7.4 JDBC driver if ( driver_major < database_major || ( driver_major == database_major && driver_minor < database_minor ) ) throw new RuntimeException( "JDBC driver " + driver_major + "." + driver_minor + " does not match database version " + database_major + "." + database_minor ); } } /** * 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. Postgres uses explicit * sequence generators, so this function will return the new id. * * @param name is the name of the sequence. * @return the next sequence number. * @exception if something goes wrong while fetching the new value. */ public long sequence1( String name ) throws SQLException { PreparedStatement ps = this.getPreparedStatement("vds.sequence"); Logging.instance().log( "sql", 2, "SELECT nextval(" + name + ")" ); Logging.instance().log( "xaction", 1, "START sequence " + name ); // obtain new sequence number ps.setString( 1, name ); ResultSet rs = ps.executeQuery(); rs.next(); long result = rs.getLong("nextval"); rs.close(); Logging.instance().log("xaction", 1, "FINAL sequence " + name + " = " + result ); // done return result; } /** * Obtains the sequence value for the current statement. Postgres does * not permit NULL-driven auto-increment columns. Postgres uses * explicit sequence generators, so this function always returns -1. * * @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 if something goes wrong while fetching the new value. */ public long sequence2( Statement s, String name, int pos ) throws SQLException { // should not be called here 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 suffers from this problem. * * @return true, if using strings instead of integers and bigints * will yield better performance. * */ public boolean preferString() { if ( m_driver_major == -1 ) { try { DatabaseMetaData meta = m_connection.getMetaData(); m_driver_major = meta.getDriverMajorVersion(); } catch ( SQLException e ) { // cache failure m_driver_major = 0; } } return ( m_driver_major < 8 ); } }