Commit e25c93c7 authored by Barry Lind's avatar Barry Lind

fixed problem connecting to server with client_min_messages set to debug. The...

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