Commit d96c29ab authored by Dave Cramer's avatar Dave Cramer

Applied Anders patch to move the startup code out of Connection into StartupPacket

* Introduces a new class, StartupPacket.
* Moves a lot of constants from Connection to StartupPacket.
* Makes two instance variables in Connection into locals.
parent 8e9b215f
package org.postgresql; package org.postgresql;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.sql.*; import java.sql.*;
import java.util.*; import java.util.*;
import org.postgresql.Field; import org.postgresql.Field;
import org.postgresql.fastpath.*; import org.postgresql.fastpath.*;
import org.postgresql.largeobject.*; import org.postgresql.largeobject.*;
import org.postgresql.util.*; import org.postgresql.util.*;
import org.postgresql.core.*; import org.postgresql.core.*;
/* /*
* $Id: Connection.java,v 1.43 2002/03/09 17:08:39 davec Exp $ * $Id: Connection.java,v 1.44 2002/03/21 02:39:06 davec Exp $
* *
* This abstract class is used by org.postgresql.Driver to open either the JDBC1 or * This abstract class is used by org.postgresql.Driver to open either the JDBC1 or
* JDBC2 versions of the Connection class. * JDBC2 versions of the Connection class.
* *
*/ */
public abstract class Connection public abstract class Connection
{ {
// This is the network stream associated with this connection // This is the network stream associated with this connection
public PG_Stream pg_stream; public PG_Stream pg_stream;
private String PG_HOST; private String PG_HOST;
private int PG_PORT; private int PG_PORT;
private String PG_USER; private String PG_USER;
private String PG_PASSWORD; private String PG_DATABASE;
private String PG_DATABASE; private boolean PG_STATUS;
private boolean PG_STATUS; private String compatible;
private String compatible;
/*
/* * The encoding to use for this connection.
* The encoding to use for this connection. */
*/ private Encoding encoding = Encoding.defaultEncoding();
private Encoding encoding = Encoding.defaultEncoding();
private String dbVersionNumber;
private String dbVersionNumber;
public boolean CONNECTION_OK = true;
public boolean CONNECTION_OK = true; public boolean CONNECTION_BAD = false;
public boolean CONNECTION_BAD = false;
public boolean autoCommit = true;
public boolean autoCommit = true; public boolean readOnly = false;
public boolean readOnly = false;
public Driver this_driver;
public Driver this_driver; private String this_url;
private String this_url; private String cursor = null; // The positioned update cursor name
private String cursor = null; // The positioned update cursor name
// These are new for v6.3, they determine the current protocol versions
// These are new for v6.3, they determine the current protocol versions // supported by this version of the driver. They are defined in
// supported by this version of the driver. They are defined in // src/include/libpq/pqcomm.h
// src/include/libpq/pqcomm.h protected static final int PG_PROTOCOL_LATEST_MAJOR = 2;
protected static final int PG_PROTOCOL_LATEST_MAJOR = 2; protected static final int PG_PROTOCOL_LATEST_MINOR = 0;
protected static final int PG_PROTOCOL_LATEST_MINOR = 0;
private static final int SM_DATABASE = 64; private static final int AUTH_REQ_OK = 0;
private static final int SM_USER = 32; private static final int AUTH_REQ_KRB4 = 1;
private static final int SM_OPTIONS = 64; private static final int AUTH_REQ_KRB5 = 2;
private static final int SM_UNUSED = 64; private static final int AUTH_REQ_PASSWORD = 3;
private static final int SM_TTY = 64; private static final int AUTH_REQ_CRYPT = 4;
private static final int AUTH_REQ_MD5 = 5;
private static final int AUTH_REQ_OK = 0;
private static final int AUTH_REQ_KRB4 = 1; public final static int PGASYNC_IDLE = 0; /* nothing's happening, dude */
private static final int AUTH_REQ_KRB5 = 2; public final static int PGASYNC_BUSY = 1; /* query in progress */
private static final int AUTH_REQ_PASSWORD = 3; public final static int PGASYNC_READY = 2; /* result ready for PQgetResult */
private static final int AUTH_REQ_CRYPT = 4;
private static final int AUTH_REQ_MD5 = 5;
// These are used to cache oids, PGTypes and SQLTypes
// New for 6.3, salt value for crypt authorisation private static Hashtable sqlTypeCache = new Hashtable(); // oid -> SQLType
private String salt; private static Hashtable pgTypeCache = new Hashtable(); // oid -> PGType
private static Hashtable typeOidCache = new Hashtable(); //PGType -> oid
// These are used to cache oids, PGTypes and SQLTypes
private static Hashtable sqlTypeCache = new Hashtable(); // oid -> SQLType // Now handle notices as warnings, so things like "show" now work
private static Hashtable pgTypeCache = new Hashtable(); // oid -> PGType public SQLWarning firstWarning = null;
private static Hashtable typeOidCache = new Hashtable(); //PGType -> oid
/*
// Now handle notices as warnings, so things like "show" now work * Cache of the current isolation level
public SQLWarning firstWarning = null; */
private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED;
/*
* Cache of the current isolation level // The PID an cancellation key we get from the backend process
*/ public int pid;
private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED; public int ckey;
// The PID an cancellation key we get from the backend process public int asyncStatus = PGASYNC_READY;
public int pid; /*
public int ckey; * This is called by Class.forName() from within org.postgresql.Driver
*/
/* public Connection()
* This is called by Class.forName() from within org.postgresql.Driver {}
*/
public Connection() public void cancelQuery() throws SQLException
{} {
PG_Stream cancelStream = null;
public void cancelQuery() throws SQLException try {
{ cancelStream = new PG_Stream(PG_HOST, PG_PORT);
PG_Stream cancelStream = null; } catch (ConnectException cex) {
try { // Added by Peter Mount <peter@retep.org.uk>
cancelStream = new PG_Stream(PG_HOST, PG_PORT); // ConnectException is thrown when the connection cannot be made.
} catch (ConnectException cex) { // we trap this an return a more meaningful message for the end user
// Added by Peter Mount <peter@retep.org.uk> throw new PSQLException ("postgresql.con.refused");
// ConnectException is thrown when the connection cannot be made. } catch (IOException e) {
// we trap this an return a more meaningful message for the end user throw new PSQLException ("postgresql.con.failed",e);
throw new PSQLException ("postgresql.con.refused"); }
} catch (IOException e) {
throw new PSQLException ("postgresql.con.failed",e); // Now we need to construct and send a cancel packet
} try {
cancelStream.SendInteger(16, 4);
// Now we need to construct and send a cancel packet cancelStream.SendInteger(80877102, 4);
try { cancelStream.SendInteger(pid, 4);
cancelStream.SendInteger(16, 4); cancelStream.SendInteger(ckey, 4);
cancelStream.SendInteger(80877102, 4); cancelStream.flush();
cancelStream.SendInteger(pid, 4); }
cancelStream.SendInteger(ckey, 4); catch(IOException e) {
cancelStream.flush(); throw new PSQLException("postgresql.con.failed",e);
} }
catch(IOException e) { finally {
throw new PSQLException("postgresql.con.failed",e); try {
} if(cancelStream != null)
finally { cancelStream.close();
try { }
if(cancelStream != null) catch(IOException e) {} // Ignore
cancelStream.close(); }
} }
catch(IOException e) {} // Ignore
} /*
} * This method actually opens the connection. It is called by Driver.
*
/* * @param host the hostname of the database back end
* This method actually opens the connection. It is called by Driver. * @param port the port number of the postmaster process
* * @param info a Properties[] thing of the user and password
* @param host the hostname of the database back end * @param database the database to connect to
* @param port the port number of the postmaster process * @param u the URL of the connection
* @param info a Properties[] thing of the user and password * @param d the Driver instantation of the connection
* @param database the database to connect to * @return a valid connection profile
* @param u the URL of the connection * @exception SQLException if a database access error occurs
* @param d the Driver instantation of the connection */
* @return a valid connection profile protected void openConnection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException
* @exception SQLException if a database access error occurs {
*/ firstWarning = null;
protected void openConnection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException
{ // Throw an exception if the user or password properties are missing
firstWarning = null; // This occasionally occurs when the client uses the properties version
// of getConnection(), and is a common question on the email lists
// Throw an exception if the user or password properties are missing if (info.getProperty("user") == null)
// This occasionally occurs when the client uses the properties version throw new PSQLException("postgresql.con.user");
// of getConnection(), and is a common question on the email lists
if (info.getProperty("user") == null) this_driver = d;
throw new PSQLException("postgresql.con.user"); this_url = url;
this_driver = d; PG_DATABASE = database;
this_url = url; PG_USER = info.getProperty("user");
PG_DATABASE = database;
PG_USER = info.getProperty("user"); String password = info.getProperty("password", "");
PG_PASSWORD = info.getProperty("password", ""); PG_PORT = port;
PG_PORT = port;
PG_HOST = host; PG_HOST = host;
PG_STATUS = CONNECTION_BAD; PG_STATUS = CONNECTION_BAD;
if (info.getProperty("compatible") == null)
{ if (info.getProperty("compatible") == null)
compatible = d.getMajorVersion() + "." + d.getMinorVersion(); {
} compatible = d.getMajorVersion() + "." + d.getMinorVersion();
else }
{ else
compatible = info.getProperty("compatible"); {
} compatible = info.getProperty("compatible");
}
// Now make the initial connection
try // Now make the initial connection
{ try
pg_stream = new PG_Stream(host, port); {
} pg_stream = new PG_Stream(host, port);
catch (ConnectException cex) }
{ catch (ConnectException cex)
// Added by Peter Mount <peter@retep.org.uk> {
// ConnectException is thrown when the connection cannot be made. // Added by Peter Mount <peter@retep.org.uk>
// we trap this an return a more meaningful message for the end user // ConnectException is thrown when the connection cannot be made.
throw new PSQLException ("postgresql.con.refused"); // we trap this an return a more meaningful message for the end user
} throw new PSQLException ("postgresql.con.refused");
catch (IOException e) }
{ catch (IOException e)
throw new PSQLException ("postgresql.con.failed", e); {
} throw new PSQLException ("postgresql.con.failed", e);
}
// Now we need to construct and send a startup packet
try // Now we need to construct and send a startup packet
{ try
// Ver 6.3 code {
pg_stream.SendInteger(4 + 4 + SM_DATABASE + SM_USER + SM_OPTIONS + SM_UNUSED + SM_TTY, 4); new StartupPacket(PG_PROTOCOL_LATEST_MAJOR,
pg_stream.SendInteger(PG_PROTOCOL_LATEST_MAJOR, 2); PG_PROTOCOL_LATEST_MINOR,
pg_stream.SendInteger(PG_PROTOCOL_LATEST_MINOR, 2); PG_USER,
pg_stream.Send(database.getBytes(), SM_DATABASE); database).writeTo(pg_stream);
// This last send includes the unused fields // now flush the startup packets to the backend
pg_stream.Send(PG_USER.getBytes(), SM_USER + SM_OPTIONS + SM_UNUSED + SM_TTY); pg_stream.flush();
// now flush the startup packets to the backend // Now get the response from the backend, either an error message
pg_stream.flush(); // or an authentication request
int areq = -1; // must have a value here
// Now get the response from the backend, either an error message do
// or an authentication request {
int areq = -1; // must have a value here int beresp = pg_stream.ReceiveChar();
do String salt = null;
{ switch (beresp)
int beresp = pg_stream.ReceiveChar(); {
switch (beresp) case 'E':
{ // An error occured, so pass the error message to the
case 'E': // user.
// An error occured, so pass the error message to the //
// user. // The most common one to be thrown here is:
// // "User authentication failed"
// The most common one to be thrown here is: //
// "User authentication failed" throw new PSQLException("postgresql.con.misc", pg_stream.ReceiveString(encoding));
//
throw new PSQLException("postgresql.con.misc", pg_stream.ReceiveString(encoding)); case 'R':
// Get the type of request
case 'R': areq = pg_stream.ReceiveIntegerR(4);
// Get the type of request
areq = pg_stream.ReceiveIntegerR(4); // Get the crypt password salt if there is one
if (areq == AUTH_REQ_CRYPT)
// Get the crypt password salt if there is one {
if (areq == AUTH_REQ_CRYPT) byte[] rst = new byte[2];
{ rst[0] = (byte)pg_stream.ReceiveChar();
byte[] rst = new byte[2]; rst[1] = (byte)pg_stream.ReceiveChar();
rst[0] = (byte)pg_stream.ReceiveChar(); salt = new String(rst, 0, 2);
rst[1] = (byte)pg_stream.ReceiveChar(); DriverManager.println("Crypt salt=" + salt);
salt = new String(rst, 0, 2); }
DriverManager.println("Crypt salt=" + salt);
} // Or get the md5 password salt if there is one
if (areq == AUTH_REQ_MD5)
// Or get the md5 password salt if there is one {
if (areq == AUTH_REQ_MD5) byte[] rst = new byte[4];
{ rst[0] = (byte)pg_stream.ReceiveChar();
byte[] rst = new byte[4]; rst[1] = (byte)pg_stream.ReceiveChar();
rst[0] = (byte)pg_stream.ReceiveChar(); rst[2] = (byte)pg_stream.ReceiveChar();
rst[1] = (byte)pg_stream.ReceiveChar(); rst[3] = (byte)pg_stream.ReceiveChar();
rst[2] = (byte)pg_stream.ReceiveChar(); salt = new String(rst, 0, 4);
rst[3] = (byte)pg_stream.ReceiveChar(); DriverManager.println("MD5 salt=" + salt);
salt = new String(rst, 0, 4); }
DriverManager.println("MD5 salt=" + salt);
} // now send the auth packet
switch (areq)
// now send the auth packet {
switch (areq) case AUTH_REQ_OK:
{ break;
case AUTH_REQ_OK:
break; case AUTH_REQ_KRB4:
DriverManager.println("postgresql: KRB4");
case AUTH_REQ_KRB4: throw new PSQLException("postgresql.con.kerb4");
DriverManager.println("postgresql: KRB4");
throw new PSQLException("postgresql.con.kerb4"); case AUTH_REQ_KRB5:
DriverManager.println("postgresql: KRB5");
case AUTH_REQ_KRB5: throw new PSQLException("postgresql.con.kerb5");
DriverManager.println("postgresql: KRB5");
throw new PSQLException("postgresql.con.kerb5"); case AUTH_REQ_PASSWORD:
DriverManager.println("postgresql: PASSWORD");
case AUTH_REQ_PASSWORD: pg_stream.SendInteger(5 + password.length(), 4);
DriverManager.println("postgresql: PASSWORD"); pg_stream.Send(password.getBytes());
pg_stream.SendInteger(5 + PG_PASSWORD.length(), 4); pg_stream.SendInteger(0, 1);
pg_stream.Send(PG_PASSWORD.getBytes()); pg_stream.flush();
pg_stream.SendInteger(0, 1); break;
pg_stream.flush();
break; case AUTH_REQ_CRYPT:
DriverManager.println("postgresql: CRYPT");
case AUTH_REQ_CRYPT: String crypted = UnixCrypt.crypt(salt, password);
DriverManager.println("postgresql: CRYPT"); pg_stream.SendInteger(5 + crypted.length(), 4);
String crypted = UnixCrypt.crypt(salt, PG_PASSWORD); pg_stream.Send(crypted.getBytes());
pg_stream.SendInteger(5 + crypted.length(), 4); pg_stream.SendInteger(0, 1);
pg_stream.Send(crypted.getBytes()); pg_stream.flush();
pg_stream.SendInteger(0, 1); break;
pg_stream.flush();
break; case AUTH_REQ_MD5:
DriverManager.println("postgresql: MD5");
case AUTH_REQ_MD5: byte[] digest = MD5Digest.encode(PG_USER, password, salt);
DriverManager.println("postgresql: MD5"); pg_stream.SendInteger(5 + digest.length, 4);
byte[] digest = MD5Digest.encode(PG_USER, PG_PASSWORD, salt); pg_stream.Send(digest);
pg_stream.SendInteger(5 + digest.length, 4); pg_stream.SendInteger(0, 1);
pg_stream.Send(digest); pg_stream.flush();
pg_stream.SendInteger(0, 1); break;
pg_stream.flush();
break; default:
throw new PSQLException("postgresql.con.auth", new Integer(areq));
default: }
throw new PSQLException("postgresql.con.auth", new Integer(areq)); break;
}
break; default:
throw new PSQLException("postgresql.con.authfail");
default: }
throw new PSQLException("postgresql.con.authfail"); }
} while (areq != AUTH_REQ_OK);
}
while (areq != AUTH_REQ_OK); }
catch (IOException e)
} {
catch (IOException e) throw new PSQLException("postgresql.con.failed", e);
{ }
throw new PSQLException("postgresql.con.failed", e);
}
// As of protocol version 2.0, we should now receive the cancellation key and the pid
int beresp = pg_stream.ReceiveChar();
// As of protocol version 2.0, we should now receive the cancellation key and the pid switch (beresp)
int beresp = pg_stream.ReceiveChar(); {
switch (beresp) case 'K':
{ pid = pg_stream.ReceiveIntegerR(4);
case 'K': ckey = pg_stream.ReceiveIntegerR(4);
pid = pg_stream.ReceiveIntegerR(4); break;
ckey = pg_stream.ReceiveIntegerR(4); case 'E':
break; throw new PSQLException("postgresql.con.backend", pg_stream.ReceiveString(encoding));
case 'E': case 'N':
throw new PSQLException("postgresql.con.backend", pg_stream.ReceiveString(encoding)); addWarning(pg_stream.ReceiveString(encoding));
case 'N': break;
addWarning(pg_stream.ReceiveString(encoding)); default:
break; throw new PSQLException("postgresql.con.setup");
default: }
throw new PSQLException("postgresql.con.setup");
} // Expect ReadyForQuery packet
beresp = pg_stream.ReceiveChar();
// Expect ReadyForQuery packet switch (beresp)
beresp = pg_stream.ReceiveChar(); {
switch (beresp) case 'Z':
{ break;
case 'Z': case 'E':
break; throw new PSQLException("postgresql.con.backend", pg_stream.ReceiveString(encoding));
case 'E': default:
throw new PSQLException("postgresql.con.backend", pg_stream.ReceiveString(encoding)); throw new PSQLException("postgresql.con.setup");
default: }
throw new PSQLException("postgresql.con.setup");
} // "pg_encoding_to_char(1)" will return 'EUC_JP' for a backend compiled with multibyte,
// otherwise it's hardcoded to 'SQL_ASCII'.
// "pg_encoding_to_char(1)" will return 'EUC_JP' for a backend compiled with multibyte, // If the backend doesn't know about multibyte we can't assume anything about the encoding
// otherwise it's hardcoded to 'SQL_ASCII'. // used, so we denote this with 'UNKNOWN'.
// If the backend doesn't know about multibyte we can't assume anything about the encoding //Note: begining with 7.2 we should be using pg_client_encoding() which
// used, so we denote this with 'UNKNOWN'. //is new in 7.2. However it isn't easy to conditionally call this new
//Note: begining with 7.2 we should be using pg_client_encoding() which //function, since we don't yet have the information as to what server
//is new in 7.2. However it isn't easy to conditionally call this new //version we are talking to. Thus we will continue to call
//function, since we don't yet have the information as to what server //getdatabaseencoding() until we drop support for 7.1 and older versions
//version we are talking to. Thus we will continue to call //or until someone comes up with a conditional way to run one or
//getdatabaseencoding() until we drop support for 7.1 and older versions //the other function depending on server version that doesn't require
//or until someone comes up with a conditional way to run one or //two round trips to the server per connection
//the other function depending on server version that doesn't require
//two round trips to the server per connection final String encodingQuery =
"case when pg_encoding_to_char(1) = 'SQL_ASCII' then 'UNKNOWN' else getdatabaseencoding() end";
final String encodingQuery =
"case when pg_encoding_to_char(1) = 'SQL_ASCII' then 'UNKNOWN' else getdatabaseencoding() end"; // Set datestyle and fetch db encoding in a single call, to avoid making
// more than one round trip to the backend during connection startup.
// Set datestyle and fetch db encoding in a single call, to avoid making
// more than one round trip to the backend during connection startup. java.sql.ResultSet resultSet =
ExecSQL("set datestyle to 'ISO'; select version(), " + encodingQuery + ";");
java.sql.ResultSet resultSet =
ExecSQL("set datestyle to 'ISO'; select version(), " + encodingQuery + ";"); if (! resultSet.next())
{
if (! resultSet.next()) throw new PSQLException("postgresql.con.failed", "failed getting backend encoding");
{ }
throw new PSQLException("postgresql.con.failed", "failed getting backend encoding"); String version = resultSet.getString(1);
} dbVersionNumber = extractVersionNumber(version);
String version = resultSet.getString(1);
dbVersionNumber = extractVersionNumber(version); String dbEncoding = resultSet.getString(2);
encoding = Encoding.getEncoding(dbEncoding, info.getProperty("charSet"));
String dbEncoding = resultSet.getString(2);
encoding = Encoding.getEncoding(dbEncoding, info.getProperty("charSet")); // Initialise object handling
initObjectTypes();
// Initialise object handling
initObjectTypes(); // Mark the connection as ok, and cleanup
PG_STATUS = CONNECTION_OK;
// Mark the connection as ok, and cleanup }
PG_STATUS = CONNECTION_OK;
} // These methods used to be in the main Connection implementation. As they
// are common to all implementations (JDBC1 or 2), they are placed here.
// These methods used to be in the main Connection implementation. As they // This should make it easy to maintain the two specifications.
// are common to all implementations (JDBC1 or 2), they are placed here.
// This should make it easy to maintain the two specifications. /*
* This adds a warning to the warning chain.
/* * @param msg message to add
* This adds a warning to the warning chain. */
* @param msg message to add public void addWarning(String msg)
*/ {
public void addWarning(String msg) DriverManager.println(msg);
{
DriverManager.println(msg); // Add the warning to the chain
if (firstWarning != null)
// Add the warning to the chain firstWarning.setNextWarning(new SQLWarning(msg));
if (firstWarning != null) else
firstWarning.setNextWarning(new SQLWarning(msg)); firstWarning = new SQLWarning(msg);
else
firstWarning = new SQLWarning(msg); // Now check for some specific messages
// Now check for some specific messages // This is obsolete in 6.5, but I've left it in here so if we need to use this
// technique again, we'll know where to place it.
// This is obsolete in 6.5, but I've left it in here so if we need to use this //
// technique again, we'll know where to place it. // This is generated by the SQL "show datestyle"
// //if (msg.startsWith("NOTICE:") && msg.indexOf("DateStyle")>0) {
// This is generated by the SQL "show datestyle" //// 13 is the length off "DateStyle is "
//if (msg.startsWith("NOTICE:") && msg.indexOf("DateStyle")>0) { //msg = msg.substring(msg.indexOf("DateStyle is ")+13);
//// 13 is the length off "DateStyle is " //
//msg = msg.substring(msg.indexOf("DateStyle is ")+13); //for(int i=0;i<dateStyles.length;i+=2)
// //if (msg.startsWith(dateStyles[i]))
//for(int i=0;i<dateStyles.length;i+=2) //currentDateStyle=i+1; // this is the index of the format
//if (msg.startsWith(dateStyles[i])) //}
//currentDateStyle=i+1; // this is the index of the format }
//}
} /*
* Send a query to the backend. Returns one of the ResultSet
/* * objects.
* Send a query to the backend. Returns one of the ResultSet *
* objects. * <B>Note:</B> there does not seem to be any method currently
* * in existance to return the update count.
* <B>Note:</B> there does not seem to be any method currently *
* in existance to return the update count. * @param sql the SQL statement to be executed
* * @return a ResultSet holding the results
* @param sql the SQL statement to be executed * @exception SQLException if a database error occurs
* @return a ResultSet holding the results */
* @exception SQLException if a database error occurs public java.sql.ResultSet ExecSQL(String sql) throws SQLException
*/ {
public java.sql.ResultSet ExecSQL(String sql) throws SQLException return ExecSQL(sql, null);
{ }
return ExecSQL(sql, null);
} /*
* Send a query to the backend. Returns one of the ResultSet
/* * objects.
* Send a query to the backend. Returns one of the ResultSet *
* objects. * <B>Note:</B> there does not seem to be any method currently
* * in existance to return the update count.
* <B>Note:</B> there does not seem to be any method currently *
* in existance to return the update count. * @param sql the SQL statement to be executed
* * @param stat The Statement associated with this query (may be null)
* @param sql the SQL statement to be executed * @return a ResultSet holding the results
* @param stat The Statement associated with this query (may be null) * @exception SQLException if a database error occurs
* @return a ResultSet holding the results */
* @exception SQLException if a database error occurs public java.sql.ResultSet ExecSQL(String sql, java.sql.Statement stat) throws SQLException
*/ {
public java.sql.ResultSet ExecSQL(String sql, java.sql.Statement stat) throws SQLException return new QueryExecutor2(sql, stat, pg_stream, this).execute();
{ }
return new QueryExecutor(sql, stat, pg_stream, this).execute();
} /*
* In SQL, a result table can be retrieved through a cursor that
/* * is named. The current row of a result can be updated or deleted
* In SQL, a result table can be retrieved through a cursor that * using a positioned update/delete statement that references the
* is named. The current row of a result can be updated or deleted * cursor name.
* using a positioned update/delete statement that references the *
* cursor name. * We support one cursor per connection.
* *
* We support one cursor per connection. * setCursorName sets the cursor name.
* *
* setCursorName sets the cursor name. * @param cursor the cursor name
* * @exception SQLException if a database access error occurs
* @param cursor the cursor name */
* @exception SQLException if a database access error occurs public void setCursorName(String cursor) throws SQLException
*/ {
public void setCursorName(String cursor) throws SQLException this.cursor = cursor;
{ }
this.cursor = cursor;
} /*
* getCursorName gets the cursor name.
/* *
* getCursorName gets the cursor name. * @return the current cursor name
* * @exception SQLException if a database access error occurs
* @return the current cursor name */
* @exception SQLException if a database access error occurs public String getCursorName() throws SQLException
*/ {
public String getCursorName() throws SQLException return cursor;
{ }
return cursor;
} /*
* We are required to bring back certain information by
/* * the DatabaseMetaData class. These functions do that.
* We are required to bring back certain information by *
* the DatabaseMetaData class. These functions do that. * Method getURL() brings back the URL (good job we saved it)
* *
* Method getURL() brings back the URL (good job we saved it) * @return the url
* * @exception SQLException just in case...
* @return the url */
* @exception SQLException just in case... public String getURL() throws SQLException
*/ {
public String getURL() throws SQLException return this_url;
{ }
return this_url;
} /*
* Method getUserName() brings back the User Name (again, we
/* * saved it)
* Method getUserName() brings back the User Name (again, we *
* saved it) * @return the user name
* * @exception SQLException just in case...
* @return the user name */
* @exception SQLException just in case... int lastMessage = 0;
*/ public String getUserName() throws SQLException
int lastMessage = 0; {
public String getUserName() throws SQLException return PG_USER;
{ }
return PG_USER;
} /*
* Get the character encoding to use for this connection.
/* */
* Get the character encoding to use for this connection. public Encoding getEncoding() throws SQLException
*/ {
public Encoding getEncoding() throws SQLException return encoding;
{ }
return encoding;
} /*
* This returns the Fastpath API for the current connection.
/* *
* This returns the Fastpath API for the current connection. * <p><b>NOTE:</b> This is not part of JDBC, but allows access to
* * functions on the org.postgresql backend itself.
* <p><b>NOTE:</b> This is not part of JDBC, but allows access to *
* functions on the org.postgresql backend itself. * <p>It is primarily used by the LargeObject API
* *
* <p>It is primarily used by the LargeObject API * <p>The best way to use this is as follows:
* *
* <p>The best way to use this is as follows: * <p><pre>
* * import org.postgresql.fastpath.*;
* <p><pre> * ...
* import org.postgresql.fastpath.*; * Fastpath fp = ((org.postgresql.Connection)myconn).getFastpathAPI();
* ... * </pre>
* Fastpath fp = ((org.postgresql.Connection)myconn).getFastpathAPI(); *
* </pre> * <p>where myconn is an open Connection to org.postgresql.
* *
* <p>where myconn is an open Connection to org.postgresql. * @return Fastpath object allowing access to functions on the org.postgresql
* * backend.
* @return Fastpath object allowing access to functions on the org.postgresql * @exception SQLException by Fastpath when initialising for first time
* backend. */
* @exception SQLException by Fastpath when initialising for first time public Fastpath getFastpathAPI() throws SQLException
*/ {
public Fastpath getFastpathAPI() throws SQLException if (fastpath == null)
{ fastpath = new Fastpath(this, pg_stream);
if (fastpath == null) return fastpath;
fastpath = new Fastpath(this, pg_stream); }
return fastpath;
} // This holds a reference to the Fastpath API if already open
private Fastpath fastpath = null;
// This holds a reference to the Fastpath API if already open
private Fastpath fastpath = null; /*
* This returns the LargeObject API for the current connection.
/* *
* This returns the LargeObject API for the current connection. * <p><b>NOTE:</b> This is not part of JDBC, but allows access to
* * functions on the org.postgresql backend itself.
* <p><b>NOTE:</b> This is not part of JDBC, but allows access to *
* functions on the org.postgresql backend itself. * <p>The best way to use this is as follows:
* *
* <p>The best way to use this is as follows: * <p><pre>
* * import org.postgresql.largeobject.*;
* <p><pre> * ...
* import org.postgresql.largeobject.*; * LargeObjectManager lo = ((org.postgresql.Connection)myconn).getLargeObjectAPI();
* ... * </pre>
* LargeObjectManager lo = ((org.postgresql.Connection)myconn).getLargeObjectAPI(); *
* </pre> * <p>where myconn is an open Connection to org.postgresql.
* *
* <p>where myconn is an open Connection to org.postgresql. * @return LargeObject object that implements the API
* * @exception SQLException by LargeObject when initialising for first time
* @return LargeObject object that implements the API */
* @exception SQLException by LargeObject when initialising for first time public LargeObjectManager getLargeObjectAPI() throws SQLException
*/ {
public LargeObjectManager getLargeObjectAPI() throws SQLException if (largeobject == null)
{ largeobject = new LargeObjectManager(this);
if (largeobject == null) return largeobject;
largeobject = new LargeObjectManager(this); }
return largeobject;
} // This holds a reference to the LargeObject API if already open
private LargeObjectManager largeobject = null;
// This holds a reference to the LargeObject API if already open
private LargeObjectManager largeobject = null; /*
* This method is used internally to return an object based around
/* * org.postgresql's more unique data types.
* This method is used internally to return an object based around *
* org.postgresql's more unique data types. * <p>It uses an internal Hashtable to get the handling class. If the
* * type is not supported, then an instance of org.postgresql.util.PGobject
* <p>It uses an internal Hashtable to get the handling class. If the * is returned.
* type is not supported, then an instance of org.postgresql.util.PGobject *
* is returned. * You can use the getValue() or setValue() methods to handle the returned
* * object. Custom objects can have their own methods.
* You can use the getValue() or setValue() methods to handle the returned *
* object. Custom objects can have their own methods. * In 6.4, this is extended to use the org.postgresql.util.Serialize class to
* * allow the Serialization of Java Objects into the database without using
* In 6.4, this is extended to use the org.postgresql.util.Serialize class to * Blobs. Refer to that class for details on how this new feature works.
* allow the Serialization of Java Objects into the database without using *
* Blobs. Refer to that class for details on how this new feature works. * @return PGobject for this type, and set to value
* * @exception SQLException if value is not correct for this type
* @return PGobject for this type, and set to value * @see org.postgresql.util.Serialize
* @exception SQLException if value is not correct for this type */
* @see org.postgresql.util.Serialize public Object getObject(String type, String value) throws SQLException
*/ {
public Object getObject(String type, String value) throws SQLException try
{ {
try Object o = objectTypes.get(type);
{
Object o = objectTypes.get(type); // If o is null, then the type is unknown, so check to see if type
// is an actual table name. If it does, see if a Class is known that
// If o is null, then the type is unknown, so check to see if type // can handle it
// is an actual table name. If it does, see if a Class is known that if (o == null)
// can handle it {
if (o == null) Serialize ser = new Serialize(this, type);
{ objectTypes.put(type, ser);
Serialize ser = new Serialize(this, type); return ser.fetch(Integer.parseInt(value));
objectTypes.put(type, ser); }
return ser.fetch(Integer.parseInt(value));
} // If o is not null, and it is a String, then its a class name that
// extends PGobject.
// If o is not null, and it is a String, then its a class name that //
// extends PGobject. // This is used to implement the org.postgresql unique types (like lseg,
// // point, etc).
// This is used to implement the org.postgresql unique types (like lseg, if (o instanceof String)
// point, etc). {
if (o instanceof String) // 6.3 style extending PG_Object
{ PGobject obj = null;
// 6.3 style extending PG_Object obj = (PGobject)(Class.forName((String)o).newInstance());
PGobject obj = null; obj.setType(type);
obj = (PGobject)(Class.forName((String)o).newInstance()); obj.setValue(value);
obj.setType(type); return (Object)obj;
obj.setValue(value); }
return (Object)obj; else
} {
else // If it's an object, it should be an instance of our Serialize class
{ // If so, then call it's fetch method.
// If it's an object, it should be an instance of our Serialize class if (o instanceof Serialize)
// If so, then call it's fetch method. return ((Serialize)o).fetch(Integer.parseInt(value));
if (o instanceof Serialize) }
return ((Serialize)o).fetch(Integer.parseInt(value)); }
} catch (SQLException sx)
} {
catch (SQLException sx) // rethrow the exception. Done because we capture any others next
{ sx.fillInStackTrace();
// rethrow the exception. Done because we capture any others next throw sx;
sx.fillInStackTrace(); }
throw sx; catch (Exception ex)
} {
catch (Exception ex) throw new PSQLException("postgresql.con.creobj", type, ex);
{ }
throw new PSQLException("postgresql.con.creobj", type, ex);
} // should never be reached
return null;
// should never be reached }
return null;
} /*
* This stores an object into the database. This method was
/* * deprecated in 7.2 bacause an OID can be larger than the java signed
* This stores an object into the database. This method was * int returned by this method.
* deprecated in 7.2 bacause an OID can be larger than the java signed * @deprecated Replaced by storeObject() in 7.2
* int returned by this method. */
* @deprecated Replaced by storeObject() in 7.2 public int putObject(Object o) throws SQLException
*/ {
public int putObject(Object o) throws SQLException return (int) storeObject(o);
{ }
return (int) storeObject(o);
} /*
* This stores an object into the database.
/* * @param o Object to store
* This stores an object into the database. * @return OID of the new rectord
* @param o Object to store * @exception SQLException if value is not correct for this type
* @return OID of the new rectord * @see org.postgresql.util.Serialize
* @exception SQLException if value is not correct for this type * @since 7.2
* @see org.postgresql.util.Serialize */
* @since 7.2 public long storeObject(Object o) throws SQLException
*/ {
public long storeObject(Object o) throws SQLException try
{ {
try String type = o.getClass().getName();
{ Object x = objectTypes.get(type);
String type = o.getClass().getName();
Object x = objectTypes.get(type); // If x is null, then the type is unknown, so check to see if type
// is an actual table name. If it does, see if a Class is known that
// If x is null, then the type is unknown, so check to see if type // can handle it
// is an actual table name. If it does, see if a Class is known that if (x == null)
// can handle it {
if (x == null) Serialize ser = new Serialize(this, type);
{ objectTypes.put(type, ser);
Serialize ser = new Serialize(this, type); return ser.storeObject(o);
objectTypes.put(type, ser); }
return ser.storeObject(o);
} // If it's an object, it should be an instance of our Serialize class
// If so, then call it's fetch method.
// If it's an object, it should be an instance of our Serialize class if (x instanceof Serialize)
// If so, then call it's fetch method. return ((Serialize)x).storeObject(o);
if (x instanceof Serialize)
return ((Serialize)x).storeObject(o); // Thow an exception because the type is unknown
throw new PSQLException("postgresql.con.strobj");
// Thow an exception because the type is unknown
throw new PSQLException("postgresql.con.strobj"); }
catch (SQLException sx)
} {
catch (SQLException sx) // rethrow the exception. Done because we capture any others next
{ sx.fillInStackTrace();
// rethrow the exception. Done because we capture any others next throw sx;
sx.fillInStackTrace(); }
throw sx; catch (Exception ex)
} {
catch (Exception ex) throw new PSQLException("postgresql.con.strobjex", ex);
{ }
throw new PSQLException("postgresql.con.strobjex", ex); }
}
} /*
* This allows client code to add a handler for one of org.postgresql's
/* * more unique data types.
* This allows client code to add a handler for one of org.postgresql's *
* more unique data types. * <p><b>NOTE:</b> This is not part of JDBC, but an extension.
* *
* <p><b>NOTE:</b> This is not part of JDBC, but an extension. * <p>The best way to use this is as follows:
* *
* <p>The best way to use this is as follows: * <p><pre>
* * ...
* <p><pre> * ((org.postgresql.Connection)myconn).addDataType("mytype","my.class.name");
* ... * ...
* ((org.postgresql.Connection)myconn).addDataType("mytype","my.class.name"); * </pre>
* ... *
* </pre> * <p>where myconn is an open Connection to org.postgresql.
* *
* <p>where myconn is an open Connection to org.postgresql. * <p>The handling class must extend org.postgresql.util.PGobject
* *
* <p>The handling class must extend org.postgresql.util.PGobject * @see org.postgresql.util.PGobject
* */
* @see org.postgresql.util.PGobject public void addDataType(String type, String name)
*/ {
public void addDataType(String type, String name) objectTypes.put(type, name);
{ }
objectTypes.put(type, name);
} // This holds the available types
private Hashtable objectTypes = new Hashtable();
// This holds the available types
private Hashtable objectTypes = new Hashtable(); // This array contains the types that are supported as standard.
//
// This array contains the types that are supported as standard. // The first entry is the types name on the database, the second
// // the full class name of the handling class.
// The first entry is the types name on the database, the second //
// the full class name of the handling class. private static final String defaultObjectTypes[][] = {
// {"box", "org.postgresql.geometric.PGbox"},
private static final String defaultObjectTypes[][] = { {"circle", "org.postgresql.geometric.PGcircle"},
{"box", "org.postgresql.geometric.PGbox"}, {"line", "org.postgresql.geometric.PGline"},
{"circle", "org.postgresql.geometric.PGcircle"}, {"lseg", "org.postgresql.geometric.PGlseg"},
{"line", "org.postgresql.geometric.PGline"}, {"path", "org.postgresql.geometric.PGpath"},
{"lseg", "org.postgresql.geometric.PGlseg"}, {"point", "org.postgresql.geometric.PGpoint"},
{"path", "org.postgresql.geometric.PGpath"}, {"polygon", "org.postgresql.geometric.PGpolygon"},
{"point", "org.postgresql.geometric.PGpoint"}, {"money", "org.postgresql.util.PGmoney"}
{"polygon", "org.postgresql.geometric.PGpolygon"}, };
{"money", "org.postgresql.util.PGmoney"}
}; // This initialises the objectTypes hashtable
private void initObjectTypes()
// This initialises the objectTypes hashtable {
private void initObjectTypes() for (int i = 0;i < defaultObjectTypes.length;i++)
{ objectTypes.put(defaultObjectTypes[i][0], defaultObjectTypes[i][1]);
for (int i = 0;i < defaultObjectTypes.length;i++) }
objectTypes.put(defaultObjectTypes[i][0], defaultObjectTypes[i][1]);
} // These are required by other common classes
public abstract java.sql.Statement createStatement() throws SQLException;
// These are required by other common classes
public abstract java.sql.Statement createStatement() throws SQLException; /*
* This returns a resultset. It must be overridden, so that the correct
/* * version (from jdbc1 or jdbc2) are returned.
* This returns a resultset. It must be overridden, so that the correct */
* version (from jdbc1 or jdbc2) are returned. public abstract java.sql.ResultSet getResultSet(org.postgresql.Connection conn, java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException;
*/
public abstract java.sql.ResultSet getResultSet(org.postgresql.Connection conn, java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException; /*
* In some cases, it is desirable to immediately release a Connection's
/* * database and JDBC resources instead of waiting for them to be
* In some cases, it is desirable to immediately release a Connection's * automatically released (cant think why off the top of my head)
* database and JDBC resources instead of waiting for them to be *
* automatically released (cant think why off the top of my head) * <B>Note:</B> A Connection is automatically closed when it is
* * garbage collected. Certain fatal errors also result in a closed
* <B>Note:</B> A Connection is automatically closed when it is * connection.
* garbage collected. Certain fatal errors also result in a closed *
* connection. * @exception SQLException if a database access error occurs
* */
* @exception SQLException if a database access error occurs public void close() throws SQLException
*/ {
public void close() throws SQLException if (pg_stream != null)
{ {
if (pg_stream != null) try
{ {
try pg_stream.SendChar('X');
{ pg_stream.flush();
pg_stream.SendChar('X'); pg_stream.close();
pg_stream.flush(); }
pg_stream.close(); catch (IOException e)
} {}
catch (IOException e) pg_stream = null;
{} }
pg_stream = null; }
}
} /*
* A driver may convert the JDBC sql grammar into its system's
/* * native SQL grammar prior to sending it; nativeSQL returns the
* A driver may convert the JDBC sql grammar into its system's * native form of the statement that the driver would have sent.
* native SQL grammar prior to sending it; nativeSQL returns the *
* native form of the statement that the driver would have sent. * @param sql a SQL statement that may contain one or more '?'
* * parameter placeholders
* @param sql a SQL statement that may contain one or more '?' * @return the native form of this statement
* parameter placeholders * @exception SQLException if a database access error occurs
* @return the native form of this statement */
* @exception SQLException if a database access error occurs public String nativeSQL(String sql) throws SQLException
*/ {
public String nativeSQL(String sql) throws SQLException return sql;
{ }
return sql;
} /*
* The first warning reported by calls on this Connection is
/* * returned.
* The first warning reported by calls on this Connection is *
* returned. * <B>Note:</B> Sebsequent warnings will be changed to this
* * SQLWarning
* <B>Note:</B> Sebsequent warnings will be changed to this *
* SQLWarning * @return the first SQLWarning or null
* * @exception SQLException if a database access error occurs
* @return the first SQLWarning or null */
* @exception SQLException if a database access error occurs public SQLWarning getWarnings() throws SQLException
*/ {
public SQLWarning getWarnings() throws SQLException return firstWarning;
{ }
return firstWarning;
} /*
* After this call, getWarnings returns null until a new warning
/* * is reported for this connection.
* After this call, getWarnings returns null until a new warning *
* is reported for this connection. * @exception SQLException if a database access error occurs
* */
* @exception SQLException if a database access error occurs public void clearWarnings() throws SQLException
*/ {
public void clearWarnings() throws SQLException firstWarning = null;
{ }
firstWarning = null;
}
/*
* You can put a connection in read-only mode as a hunt to enable
/* * database optimizations
* You can put a connection in read-only mode as a hunt to enable *
* database optimizations * <B>Note:</B> setReadOnly cannot be called while in the middle
* * of a transaction
* <B>Note:</B> setReadOnly cannot be called while in the middle *
* of a transaction * @param readOnly - true enables read-only mode; false disables it
* * @exception SQLException if a database access error occurs
* @param readOnly - true enables read-only mode; false disables it */
* @exception SQLException if a database access error occurs public void setReadOnly(boolean readOnly) throws SQLException
*/ {
public void setReadOnly(boolean readOnly) throws SQLException this.readOnly = readOnly;
{ }
this.readOnly = readOnly;
} /*
* Tests to see if the connection is in Read Only Mode. Note that
/* * we cannot really put the database in read only mode, but we pretend
* Tests to see if the connection is in Read Only Mode. Note that * we can by returning the value of the readOnly flag
* we cannot really put the database in read only mode, but we pretend *
* we can by returning the value of the readOnly flag * @return true if the connection is read only
* * @exception SQLException if a database access error occurs
* @return true if the connection is read only */
* @exception SQLException if a database access error occurs public boolean isReadOnly() throws SQLException
*/ {
public boolean isReadOnly() throws SQLException return readOnly;
{ }
return readOnly;
} /*
* If a connection is in auto-commit mode, than all its SQL
/* * statements will be executed and committed as individual
* If a connection is in auto-commit mode, than all its SQL * transactions. Otherwise, its SQL statements are grouped
* statements will be executed and committed as individual * into transactions that are terminated by either commit()
* transactions. Otherwise, its SQL statements are grouped * or rollback(). By default, new connections are in auto-
* into transactions that are terminated by either commit() * commit mode. The commit occurs when the statement completes
* or rollback(). By default, new connections are in auto- * or the next execute occurs, whichever comes first. In the
* commit mode. The commit occurs when the statement completes * case of statements returning a ResultSet, the statement
* or the next execute occurs, whichever comes first. In the * completes when the last row of the ResultSet has been retrieved
* case of statements returning a ResultSet, the statement * or the ResultSet has been closed. In advanced cases, a single
* completes when the last row of the ResultSet has been retrieved * statement may return multiple results as well as output parameter
* or the ResultSet has been closed. In advanced cases, a single * values. Here the commit occurs when all results and output param
* statement may return multiple results as well as output parameter * values have been retrieved.
* values. Here the commit occurs when all results and output param *
* values have been retrieved. * @param autoCommit - true enables auto-commit; false disables it
* * @exception SQLException if a database access error occurs
* @param autoCommit - true enables auto-commit; false disables it */
* @exception SQLException if a database access error occurs public void setAutoCommit(boolean autoCommit) throws SQLException
*/ {
public void setAutoCommit(boolean autoCommit) throws SQLException if (this.autoCommit == autoCommit)
{ return;
if (this.autoCommit == autoCommit) if (autoCommit)
return; ExecSQL("end");
if (autoCommit) else
ExecSQL("end"); {
else if (haveMinimumServerVersion("7.1"))
{ {
if (haveMinimumServerVersion("7.1")) ExecSQL("begin;" + getIsolationLevelSQL());
{ }
ExecSQL("begin;" + getIsolationLevelSQL()); else
} {
else ExecSQL("begin");
{ ExecSQL(getIsolationLevelSQL());
ExecSQL("begin"); }
ExecSQL(getIsolationLevelSQL()); }
} this.autoCommit = autoCommit;
} }
this.autoCommit = autoCommit;
} /*
* gets the current auto-commit state
/* *
* gets the current auto-commit state * @return Current state of the auto-commit mode
* * @exception SQLException (why?)
* @return Current state of the auto-commit mode * @see setAutoCommit
* @exception SQLException (why?) */
* @see setAutoCommit public boolean getAutoCommit() throws SQLException
*/ {
public boolean getAutoCommit() throws SQLException return this.autoCommit;
{ }
return this.autoCommit;
} /*
* The method commit() makes all changes made since the previous
/* * commit/rollback permanent and releases any database locks currently
* The method commit() makes all changes made since the previous * held by the Connection. This method should only be used when
* commit/rollback permanent and releases any database locks currently * auto-commit has been disabled. (If autoCommit == true, then we
* held by the Connection. This method should only be used when * just return anyhow)
* auto-commit has been disabled. (If autoCommit == true, then we *
* just return anyhow) * @exception SQLException if a database access error occurs
* * @see setAutoCommit
* @exception SQLException if a database access error occurs */
* @see setAutoCommit public void commit() throws SQLException
*/ {
public void commit() throws SQLException if (autoCommit)
{ return;
if (autoCommit) if (haveMinimumServerVersion("7.1"))
return; {
if (haveMinimumServerVersion("7.1")) ExecSQL("commit;begin;" + getIsolationLevelSQL());
{ }
ExecSQL("commit;begin;" + getIsolationLevelSQL()); else
} {
else ExecSQL("commit");
{ ExecSQL("begin");
ExecSQL("commit"); ExecSQL(getIsolationLevelSQL());
ExecSQL("begin"); }
ExecSQL(getIsolationLevelSQL()); }
}
} /*
* The method rollback() drops all changes made since the previous
/* * commit/rollback and releases any database locks currently held by
* The method rollback() drops all changes made since the previous * the Connection.
* commit/rollback and releases any database locks currently held by *
* the Connection. * @exception SQLException if a database access error occurs
* * @see commit
* @exception SQLException if a database access error occurs */
* @see commit public void rollback() throws SQLException
*/ {
public void rollback() throws SQLException if (autoCommit)
{ return;
if (autoCommit) if (haveMinimumServerVersion("7.1"))
return; {
if (haveMinimumServerVersion("7.1")) ExecSQL("rollback; begin;" + getIsolationLevelSQL());
{ }
ExecSQL("rollback; begin;" + getIsolationLevelSQL()); else
} {
else ExecSQL("rollback");
{ ExecSQL("begin");
ExecSQL("rollback"); ExecSQL(getIsolationLevelSQL());
ExecSQL("begin"); }
ExecSQL(getIsolationLevelSQL()); }
}
} /*
* Get this Connection's current transaction isolation mode.
/* *
* Get this Connection's current transaction isolation mode. * @return the current TRANSACTION_* mode value
* * @exception SQLException if a database access error occurs
* @return the current TRANSACTION_* mode value */
* @exception SQLException if a database access error occurs public int getTransactionIsolation() throws SQLException
*/ {
public int getTransactionIsolation() throws SQLException clearWarnings();
{ ExecSQL("show xactisolevel");
clearWarnings();
ExecSQL("show xactisolevel"); SQLWarning warning = getWarnings();
if (warning != null)
SQLWarning warning = getWarnings(); {
if (warning != null) String message = warning.getMessage();
{ clearWarnings();
String message = warning.getMessage(); if (message.indexOf("READ COMMITTED") != -1)
clearWarnings(); return java.sql.Connection.TRANSACTION_READ_COMMITTED;
if (message.indexOf("READ COMMITTED") != -1) else if (message.indexOf("READ UNCOMMITTED") != -1)
return java.sql.Connection.TRANSACTION_READ_COMMITTED; return java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
else if (message.indexOf("READ UNCOMMITTED") != -1) else if (message.indexOf("REPEATABLE READ") != -1)
return java.sql.Connection.TRANSACTION_READ_UNCOMMITTED; return java.sql.Connection.TRANSACTION_REPEATABLE_READ;
else if (message.indexOf("REPEATABLE READ") != -1) else if (message.indexOf("SERIALIZABLE") != -1)
return java.sql.Connection.TRANSACTION_REPEATABLE_READ; return java.sql.Connection.TRANSACTION_SERIALIZABLE;
else if (message.indexOf("SERIALIZABLE") != -1) }
return java.sql.Connection.TRANSACTION_SERIALIZABLE; return java.sql.Connection.TRANSACTION_READ_COMMITTED;
} }
return java.sql.Connection.TRANSACTION_READ_COMMITTED;
} /*
* You can call this method to try to change the transaction
/* * isolation level using one of the TRANSACTION_* values.
* You can call this method to try to change the transaction *
* isolation level using one of the TRANSACTION_* values. * <B>Note:</B> setTransactionIsolation cannot be called while
* * in the middle of a transaction
* <B>Note:</B> setTransactionIsolation cannot be called while *
* in the middle of a transaction * @param level one of the TRANSACTION_* isolation values with
* * the exception of TRANSACTION_NONE; some databases may
* @param level one of the TRANSACTION_* isolation values with * not support other values
* the exception of TRANSACTION_NONE; some databases may * @exception SQLException if a database access error occurs
* not support other values * @see java.sql.DatabaseMetaData#supportsTransactionIsolationLevel
* @exception SQLException if a database access error occurs */
* @see java.sql.DatabaseMetaData#supportsTransactionIsolationLevel public void setTransactionIsolation(int level) throws SQLException
*/ {
public void setTransactionIsolation(int level) throws SQLException //In 7.1 and later versions of the server it is possible using
{ //the "set session" command to set this once for all future txns
//In 7.1 and later versions of the server it is possible using //however in 7.0 and prior versions it is necessary to set it in
//the "set session" command to set this once for all future txns //each transaction, thus adding complexity below.
//however in 7.0 and prior versions it is necessary to set it in //When we decide to drop support for servers older than 7.1
//each transaction, thus adding complexity below. //this can be simplified
//When we decide to drop support for servers older than 7.1 isolationLevel = level;
//this can be simplified String isolationLevelSQL;
isolationLevel = level;
String isolationLevelSQL; if (!haveMinimumServerVersion("7.1"))
{
if (!haveMinimumServerVersion("7.1")) isolationLevelSQL = getIsolationLevelSQL();
{ }
isolationLevelSQL = getIsolationLevelSQL(); else
} {
else isolationLevelSQL = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ";
{ switch (isolationLevel)
isolationLevelSQL = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL "; {
switch (isolationLevel) case java.sql.Connection.TRANSACTION_READ_COMMITTED:
{ isolationLevelSQL += "READ COMMITTED";
case java.sql.Connection.TRANSACTION_READ_COMMITTED: break;
isolationLevelSQL += "READ COMMITTED"; case java.sql.Connection.TRANSACTION_SERIALIZABLE:
break; isolationLevelSQL += "SERIALIZABLE";
case java.sql.Connection.TRANSACTION_SERIALIZABLE: break;
isolationLevelSQL += "SERIALIZABLE"; default:
break; throw new PSQLException("postgresql.con.isolevel",
default: new Integer(isolationLevel));
throw new PSQLException("postgresql.con.isolevel", }
new Integer(isolationLevel)); }
} ExecSQL(isolationLevelSQL);
} }
ExecSQL(isolationLevelSQL);
} /*
* Helper method used by setTransactionIsolation(), commit(), rollback()
/* * and setAutoCommit(). This returns the SQL string needed to
* Helper method used by setTransactionIsolation(), commit(), rollback() * set the isolation level for a transaction. In 7.1 and later it
* and setAutoCommit(). This returns the SQL string needed to * is possible to set a default isolation level that applies to all
* set the isolation level for a transaction. In 7.1 and later it * future transactions, this method is only necesary for 7.0 and older
* is possible to set a default isolation level that applies to all * servers, and should be removed when support for these older
* future transactions, this method is only necesary for 7.0 and older * servers are dropped
* servers, and should be removed when support for these older */
* servers are dropped protected String getIsolationLevelSQL() throws SQLException
*/ {
protected String getIsolationLevelSQL() throws SQLException //7.1 and higher servers have a default specified so
{ //no additional SQL is required to set the isolation level
//7.1 and higher servers have a default specified so if (haveMinimumServerVersion("7.1"))
//no additional SQL is required to set the isolation level {
if (haveMinimumServerVersion("7.1")) return "";
{ }
return ""; StringBuffer sb = new StringBuffer("SET TRANSACTION ISOLATION LEVEL");
}
StringBuffer sb = new StringBuffer("SET TRANSACTION ISOLATION LEVEL"); switch (isolationLevel)
{
switch (isolationLevel) case java.sql.Connection.TRANSACTION_READ_COMMITTED:
{ sb.append(" READ COMMITTED");
case java.sql.Connection.TRANSACTION_READ_COMMITTED: break;
sb.append(" READ COMMITTED");
break; case java.sql.Connection.TRANSACTION_SERIALIZABLE:
sb.append(" SERIALIZABLE");
case java.sql.Connection.TRANSACTION_SERIALIZABLE: break;
sb.append(" SERIALIZABLE");
break; default:
throw new PSQLException("postgresql.con.isolevel", new Integer(isolationLevel));
default: }
throw new PSQLException("postgresql.con.isolevel", new Integer(isolationLevel)); return sb.toString();
} }
return sb.toString();
} /*
* A sub-space of this Connection's database may be selected by
/* * setting a catalog name. If the driver does not support catalogs,
* A sub-space of this Connection's database may be selected by * it will silently ignore this request
* setting a catalog name. If the driver does not support catalogs, *
* it will silently ignore this request * @exception SQLException if a database access error occurs
* */
* @exception SQLException if a database access error occurs public void setCatalog(String catalog) throws SQLException
*/ {
public void setCatalog(String catalog) throws SQLException //no-op
{ }
//no-op
} /*
* Return the connections current catalog name, or null if no
/* * catalog name is set, or we dont support catalogs.
* Return the connections current catalog name, or null if no *
* catalog name is set, or we dont support catalogs. * @return the current catalog name or null
* * @exception SQLException if a database access error occurs
* @return the current catalog name or null */
* @exception SQLException if a database access error occurs public String getCatalog() throws SQLException
*/ {
public String getCatalog() throws SQLException return PG_DATABASE;
{ }
return PG_DATABASE;
} /*
* Overides finalize(). If called, it closes the connection.
/* *
* Overides finalize(). If called, it closes the connection. * This was done at the request of Rachel Greenham
* * <rachel@enlarion.demon.co.uk> who hit a problem where multiple
* This was done at the request of Rachel Greenham * clients didn't close the connection, and once a fortnight enough
* <rachel@enlarion.demon.co.uk> who hit a problem where multiple * clients were open to kill the org.postgres server.
* clients didn't close the connection, and once a fortnight enough */
* clients were open to kill the org.postgres server. public void finalize() throws Throwable
*/ {
public void finalize() throws Throwable close();
{ }
close();
} private static String extractVersionNumber(String fullVersionString)
{
private static String extractVersionNumber(String fullVersionString) StringTokenizer versionParts = new StringTokenizer(fullVersionString);
{ versionParts.nextToken(); /* "PostgreSQL" */
StringTokenizer versionParts = new StringTokenizer(fullVersionString); return versionParts.nextToken(); /* "X.Y.Z" */
versionParts.nextToken(); /* "PostgreSQL" */ }
return versionParts.nextToken(); /* "X.Y.Z" */
} /*
* Get server version number
/* */
* Get server version number public String getDBVersionNumber()
*/ {
public String getDBVersionNumber() return dbVersionNumber;
{ }
return dbVersionNumber;
} public boolean haveMinimumServerVersion(String ver) throws SQLException
{
public boolean haveMinimumServerVersion(String ver) throws SQLException return (getDBVersionNumber().compareTo(ver) >= 0);
{ }
return (getDBVersionNumber().compareTo(ver) >= 0);
} /*
* This method returns true if the compatible level set in the connection
/* * (which can be passed into the connection or specified in the URL)
* This method returns true if the compatible level set in the connection * is at least the value passed to this method. This is used to toggle
* (which can be passed into the connection or specified in the URL) * between different functionality as it changes across different releases
* is at least the value passed to this method. This is used to toggle * of the jdbc driver code. The values here are versions of the jdbc client
* between different functionality as it changes across different releases * and not server versions. For example in 7.1 get/setBytes worked on
* of the jdbc driver code. The values here are versions of the jdbc client * LargeObject values, in 7.2 these methods were changed to work on bytea
* and not server versions. For example in 7.1 get/setBytes worked on * values. This change in functionality could be disabled by setting the
* LargeObject values, in 7.2 these methods were changed to work on bytea * "compatible" level to be 7.1, in which case the driver will revert to
* values. This change in functionality could be disabled by setting the * the 7.1 functionality.
* "compatible" level to be 7.1, in which case the driver will revert to */
* the 7.1 functionality. public boolean haveMinimumCompatibleVersion(String ver) throws SQLException
*/ {
public boolean haveMinimumCompatibleVersion(String ver) throws SQLException return (compatible.compareTo(ver) >= 0);
{ }
return (compatible.compareTo(ver) >= 0);
}
/*
* This returns the java.sql.Types type for a PG type oid
/* *
* This returns the java.sql.Types type for a PG type oid * @param oid PostgreSQL type oid
* * @return the java.sql.Types type
* @param oid PostgreSQL type oid * @exception SQLException if a database access error occurs
* @return the java.sql.Types type */
* @exception SQLException if a database access error occurs public int getSQLType(int oid) throws SQLException
*/ {
public int getSQLType(int oid) throws SQLException Integer sqlType = (Integer)sqlTypeCache.get(new Integer(oid));
{
Integer sqlType = (Integer)sqlTypeCache.get(new Integer(oid)); // it's not in the cache, so perform a query, and add the result to the cache
if (sqlType == null)
// it's not in the cache, so perform a query, and add the result to the cache {
if (sqlType == null) ResultSet result = (org.postgresql.ResultSet)ExecSQL("select typname from pg_type where oid = " + oid);
{ if (result.getColumnCount() != 1 || result.getTupleCount() != 1)
ResultSet result = (org.postgresql.ResultSet)ExecSQL("select typname from pg_type where oid = " + oid); throw new PSQLException("postgresql.unexpected");
if (result.getColumnCount() != 1 || result.getTupleCount() != 1) result.next();
throw new PSQLException("postgresql.unexpected"); String pgType = result.getString(1);
result.next(); Integer iOid = new Integer(oid);
String pgType = result.getString(1); sqlType = new Integer(getSQLType(result.getString(1)));
Integer iOid = new Integer(oid); sqlTypeCache.put(iOid, sqlType);
sqlType = new Integer(getSQLType(result.getString(1))); pgTypeCache.put(iOid, pgType);
sqlTypeCache.put(iOid, sqlType); result.close();
pgTypeCache.put(iOid, pgType); }
result.close();
} return sqlType.intValue();
}
return sqlType.intValue();
} /*
* This returns the java.sql.Types type for a PG type
/* *
* This returns the java.sql.Types type for a PG type * @param pgTypeName PostgreSQL type name
* * @return the java.sql.Types type
* @param pgTypeName PostgreSQL type name */
* @return the java.sql.Types type public abstract int getSQLType(String pgTypeName);
*/
public abstract int getSQLType(String pgTypeName); /*
* This returns the oid for a given PG data type
/* * @param typeName PostgreSQL type name
* This returns the oid for a given PG data type * @return PostgreSQL oid value for a field of this type
* @param typeName PostgreSQL type name */
* @return PostgreSQL oid value for a field of this type public int getOID(String typeName) throws SQLException
*/ {
public int getOID(String typeName) throws SQLException int oid = -1;
{ if (typeName != null)
int oid = -1; {
if (typeName != null) Integer oidValue = (Integer) typeOidCache.get(typeName);
{ if (oidValue != null)
Integer oidValue = (Integer) typeOidCache.get(typeName); {
if (oidValue != null) oid = oidValue.intValue();
{ }
oid = oidValue.intValue(); else
} {
else // it's not in the cache, so perform a query, and add the result to the cache
{ ResultSet result = (org.postgresql.ResultSet)ExecSQL("select oid from pg_type where typname='"
// it's not in the cache, so perform a query, and add the result to the cache + typeName + "'");
ResultSet result = (org.postgresql.ResultSet)ExecSQL("select oid from pg_type where typname='" if (result.getColumnCount() != 1 || result.getTupleCount() != 1)
+ typeName + "'"); throw new PSQLException("postgresql.unexpected");
if (result.getColumnCount() != 1 || result.getTupleCount() != 1) result.next();
throw new PSQLException("postgresql.unexpected"); oid = Integer.parseInt(result.getString(1));
result.next(); typeOidCache.put(typeName, new Integer(oid));
oid = Integer.parseInt(result.getString(1)); result.close();
typeOidCache.put(typeName, new Integer(oid)); }
result.close(); }
} return oid;
} }
return oid;
} /*
* We also need to get the PG type name as returned by the back end.
/* *
* We also need to get the PG type name as returned by the back end. * @return the String representation of the type of this field
* * @exception SQLException if a database access error occurs
* @return the String representation of the type of this field */
* @exception SQLException if a database access error occurs public String getPGType(int oid) throws SQLException
*/ {
public String getPGType(int oid) throws SQLException String pgType = (String) pgTypeCache.get(new Integer(oid));
{ if (pgType == null)
String pgType = (String) pgTypeCache.get(new Integer(oid)); {
if (pgType == null) getSQLType(oid);
{ pgType = (String) pgTypeCache.get(new Integer(oid));
getSQLType(oid); }
pgType = (String) pgTypeCache.get(new Integer(oid)); return pgType;
} }
return pgType; }
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment