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
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
*/\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
- // 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
- // 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
+ // 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
+ // 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
- &