Applied Anders patch to move the startup code out of Connection into StartupPacket
authorDave Cramer <davec@fastcrypt.com>
Thu, 21 Mar 2002 02:39:06 +0000 (02:39 +0000)
committerDave Cramer <davec@fastcrypt.com>
Thu, 21 Mar 2002 02:39:06 +0000 (02:39 +0000)
* Introduces a new class, StartupPacket.
* Moves a lot of constants from Connection to StartupPacket.
* Makes two instance variables in Connection into locals.

src/interfaces/jdbc/org/postgresql/Connection.java

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