001: /*
002: * This file or a portion of this file is licensed under the terms of
003: * the Globus Toolkit Public License, found in file ../GTPL, or at
004: * http://www.globus.org/toolkit/download/license.html. This notice must
005: * appear in redistributions of this file, with or without modification.
006: *
007: * Redistributions of this Software, with or without modification, must
008: * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
009: * some other similar material which is provided with the Software (if
010: * any).
011: *
012: * Copyright 1999-2004 University of Chicago and The University of
013: * Southern California. All rights reserved.
014: */
015: package org.griphyn.vdl.dbdriver;
016:
017: import org.griphyn.vdl.dbdriver.DatabaseDriver;
018: import java.sql.*;
019: import java.util.*;
020: import org.griphyn.vdl.util.*;
021:
022: /**
023: * This class implements the driver API for the PostGreSQL 7.3.* and
024: * 7.4.* series database. Please note that at this point, we cannot
025: * recommend to use Postgres 8.0.*.
026: *
027: * @author Jens-S. Vöckler
028: * @author Yong Zhao
029: * @version $Revision: 50 $
030: *
031: * @see DatabaseDriver
032: * @see org.griphyn.vdl.dbschema
033: */
034: public class Postgres extends DatabaseDriver {
035: /**
036: * Caches the driver version major, because this has side-effects on
037: * the behavior.
038: */
039: private int m_driver_major = -1;
040:
041: /**
042: * Default constructor. As the constructor will do nothing, please use
043: * the connect method to obtain a database connection.
044: *
045: * @see #connect( String, Properties, Set )
046: */
047: public Postgres() {
048: super ();
049: }
050:
051: /**
052: * Establish a connection to your database. The parameters will often
053: * be ignored or abused for different purposes on different backends.
054: * It is assumed that the connection is not in auto-commit mode, and
055: * explicit commits must be issued.
056: *
057: * @param url the contact string to database, or schema location
058: * @param info additional parameters, usually username and password
059: * @param tables is a set of all table names in the schema. The
060: * existence of all tables will be checked to verify
061: * that the schema is active in the database.
062: * @return true if the connection succeeded, false otherwise. Usually,
063: * false is returned, if the any of the tables or sequences is missing.
064: * @exception if the driver is incapable of establishing a connection.
065: */
066: public boolean connect(String url, Properties info, Set tables)
067: throws SQLException, ClassNotFoundException {
068: // load PostGreSQL driver class into memory
069: boolean save = this .connect("org.postgresql.Driver", url, info,
070: tables);
071:
072: // add preparsed statement for sequence
073: this .addPreparedStatement("vds.sequence", "SELECT nextval(?)");
074:
075: // done
076: return save;
077: }
078:
079: /**
080: * Determines, if the backend is expensive, and results should be cached.
081: * Ideally, this will move transparently into the backend itself.
082: * @return true if caching is advisable, false for no caching.
083: */
084: public boolean cachingMakesSense() {
085: return true;
086: }
087:
088: /**
089: * Determines, if the JDBC driver is the right one for the database we
090: * talk to. Throws an exception if not.
091: */
092: public void driverMatch() throws SQLException {
093: DatabaseMetaData meta = m_connection.getMetaData();
094: int driver_major = meta.getDriverMajorVersion();
095: int driver_minor = meta.getDriverMinorVersion();
096: String database = meta.getDatabaseProductVersion();
097:
098: boolean flag = false;
099: int database_major = -1;
100: int database_minor = -1;
101: try {
102: database_major = meta.getDatabaseMajorVersion();
103: database_minor = meta.getDatabaseMinorVersion();
104: } catch (SQLException e) {
105: // check for "This method is not yet implemented"
106: if (e.getErrorCode() == 0)
107: flag = true;
108: else
109: throw e;
110: }
111:
112: if (flag) {
113: // use old-style check
114: String jdbc = driver_major + "." + driver_minor;
115: if (!database.startsWith(jdbc))
116: throw new RuntimeException("JDBC driver " + jdbc
117: + " does not match database version "
118: + database);
119: } else {
120: // use new-style check - requires 7.4 JDBC driver
121: if (driver_major < database_major
122: || (driver_major == database_major && driver_minor < database_minor))
123: throw new RuntimeException("JDBC driver "
124: + driver_major + "." + driver_minor
125: + " does not match database version "
126: + database_major + "." + database_minor);
127: }
128: }
129:
130: /**
131: * Quotes a string that may contain special SQL characters.
132: * @param s is the raw string.
133: * @return the quoted string, which may be just the input string.
134: */
135: public String quote(String s) {
136: if (s.indexOf('\'') != -1) {
137: StringBuffer result = new StringBuffer();
138: for (int i = 0; i < s.length(); ++i) {
139: char ch = s.charAt(i);
140: result.append(ch);
141: if (ch == '\'')
142: result.append(ch);
143: }
144: return result.toString();
145: } else {
146: return s;
147: }
148: }
149:
150: /**
151: * Obtains the next value from a sequence. Postgres uses explicit
152: * sequence generators, so this function will return the new id.
153: *
154: * @param name is the name of the sequence.
155: * @return the next sequence number.
156: * @exception if something goes wrong while fetching the new value.
157: */
158: public long sequence1(String name) throws SQLException {
159: PreparedStatement ps = this
160: .getPreparedStatement("vds.sequence");
161:
162: Logging.instance()
163: .log("sql", 2, "SELECT nextval(" + name + ")");
164: Logging.instance().log("xaction", 1, "START sequence " + name);
165:
166: // obtain new sequence number
167: ps.setString(1, name);
168: ResultSet rs = ps.executeQuery();
169: rs.next();
170: long result = rs.getLong("nextval");
171: rs.close();
172: Logging.instance().log("xaction", 1,
173: "FINAL sequence " + name + " = " + result);
174:
175: // done
176: return result;
177: }
178:
179: /**
180: * Obtains the sequence value for the current statement. Postgres does
181: * not permit NULL-driven auto-increment columns. Postgres uses
182: * explicit sequence generators, so this function always returns -1.
183: *
184: * @param s is a statment or prepared statement
185: * @param name is the name of the sequence.
186: * @param pos is the column number of the auto-increment column.
187: * @return the next sequence number.
188: * @exception if something goes wrong while fetching the new value.
189: */
190: public long sequence2(Statement s, String name, int pos)
191: throws SQLException {
192: // should not be called here
193: return -1;
194: }
195:
196: /**
197: * Predicate to tell the schema, if using a string instead of number
198: * will result in the speedier index scans instead of sequential scans.
199: * PostGreSQL suffers from this problem.
200: *
201: * @return true, if using strings instead of integers and bigints
202: * will yield better performance.
203: *
204: */
205: public boolean preferString() {
206: if (m_driver_major == -1) {
207: try {
208: DatabaseMetaData meta = m_connection.getMetaData();
209: m_driver_major = meta.getDriverMajorVersion();
210: } catch (SQLException e) {
211: // cache failure
212: m_driver_major = 0;
213: }
214: }
215: return (m_driver_major < 8);
216: }
217: }
|