]> granicus.if.org Git - postgresql/commitdiff
fixed problem connecting to server with client_min_messages set to debug. The code...
authorBarry Lind <barry@xythos.com>
Tue, 14 May 2002 03:00:35 +0000 (03:00 +0000)
committerBarry Lind <barry@xythos.com>
Tue, 14 May 2002 03:00:35 +0000 (03:00 +0000)
src/interfaces/jdbc/org/postgresql/Connection.java

index 54f067542de446718e9e16f0c6baf07abf5e0e7d..21f6c60ff3685701a4688ce11909ba20e065c17b 100644 (file)
@@ -4,6 +4,7 @@ import java.io.*;
 import java.net.*;\r
 import java.sql.*;\r
 import java.util.*;\r
+import org.postgresql.Driver;\r
 import org.postgresql.Field;\r
 import org.postgresql.fastpath.*;\r
 import org.postgresql.largeobject.*;\r
@@ -11,7 +12,7 @@ import org.postgresql.util.*;
 import org.postgresql.core.*;\r
 \r
 /*\r
- * $Id: Connection.java,v 1.45 2002/03/26 05:52:48 barry Exp $\r
+ * $Id: Connection.java,v 1.46 2002/05/14 03:00:35 barry 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
@@ -19,1192 +20,1196 @@ import org.postgresql.core.*;
  */\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
-\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
-       /*\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 QueryExecutor(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
+        // 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
+\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
+        /*\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
+                                                // 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
+                                                        Driver.debug("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
+                                                        Driver.debug("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
+                                                                Driver.debug("postgresql: KRB4");\r
+                                                                throw new PSQLException("postgresql.con.kerb4");\r
+\r
+                                                        case AUTH_REQ_KRB5:\r
+                                                                Driver.debug("postgresql: KRB5");\r
+                                                                throw new PSQLException("postgresql.con.kerb5");\r
+\r
+                                                        case AUTH_REQ_PASSWORD:\r
+                                                                Driver.debug("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
+                                                                Driver.debug("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
+                                                                Driver.debug("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;\r
+                do {\r
+                    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
+                } while (beresp == 'N');\r
+\r
+                // Expect ReadyForQuery packet\r
+                do {\r
+                    beresp = pg_stream.ReceiveChar();\r
+                    switch (beresp)\r
+                    {\r
+                        case 'Z':\r
+                                break;\r
+                        case 'N':\r
+                                addWarning(pg_stream.ReceiveString(encoding));\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
+                } while (beresp == 'N');\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
+                // 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 QueryExecutor(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
+        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
+         * @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
+        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