progress, although all RTs pass using the V3 protocol on a 7.4 database and also pass using the V2 protocol on a 7.3 database.
SSL support is known not to work.
Modified Files:
jdbc/org/postgresql/PGConnection.java
jdbc/org/postgresql/errors.properties
jdbc/org/postgresql/core/BaseConnection.java
jdbc/org/postgresql/core/Encoding.java
jdbc/org/postgresql/core/Field.java
jdbc/org/postgresql/core/PGStream.java
jdbc/org/postgresql/core/QueryExecutor.java
jdbc/org/postgresql/core/StartupPacket.java
jdbc/org/postgresql/fastpath/Fastpath.java
jdbc/org/postgresql/fastpath/FastpathArg.java
jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java
jdbc/org/postgresql/test/jdbc2/BlobTest.java
jdbc/org/postgresql/test/jdbc2/CallableStmtTest.java
jdbc/org/postgresql/test/jdbc2/MiscTest.java
jdbc/org/postgresql/test/jdbc3/Jdbc3TestSuite.java
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/Attic/PGConnection.java,v 1.5 2003/04/14 10:39:51 davec Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/Attic/PGConnection.java,v 1.6 2003/05/29 03:21:32 barry Exp $
*
*-------------------------------------------------------------------------
*/
package org.postgresql;
import java.sql.*;
-import java.util.Properties;
-import java.util.Vector;
import org.postgresql.core.Encoding;
import org.postgresql.fastpath.Fastpath;
import org.postgresql.largeobject.LargeObjectManager;
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/BaseConnection.java,v 1.2 2003/04/13 04:10:07 barry Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/BaseConnection.java,v 1.3 2003/05/29 03:21:32 barry Exp $
*
*-------------------------------------------------------------------------
*/
package org.postgresql.core;
import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;
import org.postgresql.PGConnection;
public Encoding getEncoding() throws SQLException;
public DatabaseMetaData getMetaData() throws SQLException;
public Object getObject(String type, String value) throws SQLException;
+ public int getPGProtocolVersionMajor();
+ public int getPGProtocolVersionMinor();
public PGStream getPGStream();
public String getPGType(int oid) throws SQLException;
public int getPGType(String pgTypeName) throws SQLException;
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/Encoding.java,v 1.10 2003/03/07 18:39:41 barry Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/Encoding.java,v 1.11 2003/05/29 03:21:32 barry Exp $
*
*-------------------------------------------------------------------------
*/
*/
public byte[] encode(String s) throws SQLException
{
+ byte[] l_return;
try
{
if (encoding == null)
{
- return s.getBytes();
+ l_return = s.getBytes();
}
else
{
- return s.getBytes(encoding);
+ l_return = s.getBytes(encoding);
+ }
+ //Don't return null, return an empty byte[] instead
+ if (l_return == null) {
+ return new byte[0];
+ } else {
+ return l_return;
}
}
catch (UnsupportedEncodingException e)
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/Field.java,v 1.1 2003/03/07 18:39:41 barry Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/Field.java,v 1.2 2003/05/29 03:21:32 barry Exp $
*
*-------------------------------------------------------------------------
*/
package org.postgresql.core;
-import java.lang.*;
import java.sql.*;
-import java.util.*;
import org.postgresql.core.BaseConnection;
-import org.postgresql.util.PSQLException;
/*
*/
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/PGStream.java,v 1.1 2003/03/07 18:39:41 barry Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/PGStream.java,v 1.2 2003/05/29 03:21:32 barry Exp $
*
*-------------------------------------------------------------------------
*/
Send(buf);
}
+ /*
+ * Sends an integer to the back end
+ *
+ * @param val the integer to be sent
+ * @param siz the length of the integer in bytes (size of structure)
+ * @exception IOException if an I/O error occurs
+ */
+ public void SendIntegerR(int val, int siz) throws IOException
+ {
+ byte[] buf = new byte[siz];
+
+ for (int i = 0; i < siz; i++)
+ {
+ buf[i] = (byte)(val & 0xff);
+ val >>= 8;
+ }
+ Send(buf);
+ }
+
/*
* Send an array of bytes to the backend
*
* an array of strings
* @exception SQLException if a data I/O error occurs
*/
- public byte[][] ReceiveTuple(int nf, boolean bin) throws SQLException
+ public byte[][] ReceiveTupleV3(int nf, boolean bin) throws SQLException
+ {
+ //TODO: use l_msgSize
+ int l_msgSize = ReceiveIntegerR(4);
+ int i;
+ int l_nf = ReceiveIntegerR(2);
+ byte[][] answer = new byte[l_nf][0];
+
+ for (i = 0 ; i < l_nf ; ++i)
+ {
+ int l_size = ReceiveIntegerR(4);
+ boolean isNull = l_size == -1;
+ if (isNull)
+ answer[i] = null;
+ else
+ {
+ answer[i] = Receive(l_size);
+ }
+ }
+ return answer;
+ }
+
+ /*
+ * Read a tuple from the back end. A tuple is a two dimensional
+ * array of bytes
+ *
+ * @param nf the number of fields expected
+ * @param bin true if the tuple is a binary tuple
+ * @return null if the current response has no more tuples, otherwise
+ * an array of strings
+ * @exception SQLException if a data I/O error occurs
+ */
+ public byte[][] ReceiveTupleV2(int nf, boolean bin) throws SQLException
{
int i, bim = (nf + 7) / 8;
byte[] bitmask = Receive(bim);
* @return array of bytes received
* @exception SQLException if a data I/O error occurs
*/
- private byte[] Receive(int siz) throws SQLException
+ public byte[] Receive(int siz) throws SQLException
{
byte[] answer = new byte[siz];
Receive(answer, 0, siz);
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/QueryExecutor.java,v 1.21 2003/05/07 03:03:30 barry Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/core/Attic/QueryExecutor.java,v 1.22 2003/05/29 03:21:32 barry Exp $
*
*-------------------------------------------------------------------------
*/
import java.util.Vector;
import java.io.IOException;
import java.sql.*;
+import org.postgresql.Driver;
import org.postgresql.util.PSQLException;
-import org.postgresql.jdbc1.AbstractJdbc1Connection;
-import org.postgresql.jdbc1.AbstractJdbc1ResultSet;
-import org.postgresql.jdbc1.AbstractJdbc1Statement;
public class QueryExecutor
{
*
*/
private BaseResultSet execute() throws SQLException
+ {
+ if (connection.getPGProtocolVersionMajor() == 3) {
+ if (Driver.logDebug)
+ Driver.debug("Using Protocol Version3 to send query");
+ return executeV3();
+ } else {
+ if (Driver.logDebug)
+ Driver.debug("Using Protocol Version2 to send query");
+ return executeV2();
+ }
+ }
+
+ private BaseResultSet executeV3() throws SQLException
+ {
+
+ StringBuffer errorMessage = null;
+
+ if (pgStream == null)
+ {
+ throw new PSQLException("postgresql.con.closed");
+ }
+
+ synchronized (pgStream)
+ {
+
+ sendQueryV3();
+
+ int c;
+ boolean l_endQuery = false;
+ while (!l_endQuery)
+ {
+ c = pgStream.ReceiveChar();
+ switch (c)
+ {
+ case 'A': // Asynchronous Notify
+ int pid = pgStream.ReceiveInteger(4);
+ String msg = pgStream.ReceiveString(connection.getEncoding());
+ connection.addNotification(new org.postgresql.core.Notification(msg, pid));
+ break;
+ case 'B': // Binary Data Transfer
+ receiveTupleV3(true);
+ break;
+ case 'C': // Command Status
+ receiveCommandStatusV3();
+ break;
+ case 'D': // Text Data Transfer
+ receiveTupleV3(false);
+ break;
+ case 'E': // Error Message
+
+ // it's possible to get more than one error message for a query
+ // see libpq comments wrt backend closing a connection
+ // so, append messages to a string buffer and keep processing
+ // check at the bottom to see if we need to throw an exception
+
+ if ( errorMessage == null )
+ errorMessage = new StringBuffer();
+
+ int l_elen = pgStream.ReceiveIntegerR(4);
+ errorMessage.append(connection.getEncoding().decode(pgStream.Receive(l_elen-4)));
+ // keep processing
+ break;
+ case 'I': // Empty Query
+ int t = pgStream.ReceiveChar();
+ break;
+ case 'N': // Error Notification
+ int l_nlen = pgStream.ReceiveIntegerR(4);
+ statement.addWarning(connection.getEncoding().decode(pgStream.Receive(l_nlen-4)));
+ break;
+ case 'P': // Portal Name
+ String pname = pgStream.ReceiveString(connection.getEncoding());
+ break;
+ case 'S':
+ //TODO: handle parameter status messages
+ int l_len = pgStream.ReceiveIntegerR(4);
+ String l_pStatus = connection.getEncoding().decode(pgStream.Receive(l_len-4));
+ if (Driver.logDebug)
+ Driver.debug("ParameterStatus="+ l_pStatus);
+ break;
+ case 'T': // MetaData Field Description
+ receiveFieldsV3();
+ break;
+ case 'Z':
+ // read ReadyForQuery
+ //TODO: use size better
+ if (pgStream.ReceiveIntegerR(4) != 5) throw new PSQLException("postgresql.con.setup");
+ //TODO: handle transaction status
+ char l_tStatus = (char)pgStream.ReceiveChar();
+ l_endQuery = true;
+ break;
+ default:
+ throw new PSQLException("postgresql.con.type",
+ new Character((char) c));
+ }
+
+ }
+
+ // did we get an error during this query?
+ if ( errorMessage != null )
+ throw new SQLException( errorMessage.toString() );
+
+
+ //if an existing result set was passed in reuse it, else
+ //create a new one
+ if (rs != null)
+ {
+ rs.reInit(fields, tuples, status, update_count, insert_oid, binaryCursor);
+ }
+ else
+ {
+ rs = statement.createResultSet(fields, tuples, status, update_count, insert_oid, binaryCursor);
+ }
+ return rs;
+ }
+ }
+
+ private BaseResultSet executeV2() throws SQLException
{
StringBuffer errorMessage = null;
synchronized (pgStream)
{
- sendQuery();
+ sendQueryV2();
int c;
boolean l_endQuery = false;
connection.addNotification(new org.postgresql.core.Notification(msg, pid));
break;
case 'B': // Binary Data Transfer
- receiveTuple(true);
+ receiveTupleV2(true);
break;
case 'C': // Command Status
- receiveCommandStatus();
+ receiveCommandStatusV2();
break;
case 'D': // Text Data Transfer
- receiveTuple(false);
+ receiveTupleV2(false);
break;
case 'E': // Error Message
String pname = pgStream.ReceiveString(connection.getEncoding());
break;
case 'T': // MetaData Field Description
- receiveFields();
+ receiveFieldsV2();
break;
case 'Z':
l_endQuery = true;
/*
* Send a query to the backend.
*/
- private void sendQuery() throws SQLException
+ private void sendQueryV3() throws SQLException
+ {
+ for ( int i = 0; i < m_binds.length ; i++ )
+ {
+ if ( m_binds[i] == null )
+ throw new PSQLException("postgresql.prep.param", new Integer(i + 1));
+ }
+ try
+ {
+ byte[][] l_parts = new byte[(m_binds.length*2)+1][];
+ int j = 0;
+ int l_msgSize = 4;
+ Encoding l_encoding = connection.getEncoding();
+ pgStream.SendChar('Q');
+ for (int i = 0 ; i < m_binds.length ; ++i)
+ {
+ l_parts[j] = l_encoding.encode(m_sqlFrags[i]);
+ l_msgSize += l_parts[j].length;
+ j++;
+ l_parts[j] = l_encoding.encode(m_binds[i].toString());
+ l_msgSize += l_parts[j].length;
+ j++;
+ }
+ l_parts[j] = l_encoding.encode(m_sqlFrags[m_binds.length]);
+ l_msgSize += l_parts[j].length;
+ pgStream.SendInteger(l_msgSize+1,4);
+ for (int k = 0; k < l_parts.length; k++) {
+ pgStream.Send(l_parts[k]);
+ }
+ pgStream.SendChar(0);
+ pgStream.flush();
+ }
+ catch (IOException e)
+ {
+ throw new PSQLException("postgresql.con.ioerror", e);
+ }
+ }
+
+ /*
+ * Send a query to the backend.
+ */
+ private void sendQueryV2() throws SQLException
{
for ( int i = 0; i < m_binds.length ; i++ )
{
*
* @param isBinary set if the tuple should be treated as binary data
*/
- private void receiveTuple(boolean isBinary) throws SQLException
+ private void receiveTupleV3(boolean isBinary) throws SQLException
{
if (fields == null)
throw new PSQLException("postgresql.con.tuple");
- Object tuple = pgStream.ReceiveTuple(fields.length, isBinary);
+ Object tuple = pgStream.ReceiveTupleV3(fields.length, isBinary);
+ if (isBinary)
+ binaryCursor = true;
+ if (maxRows == 0 || tuples.size() < maxRows)
+ tuples.addElement(tuple);
+ }
+
+ /*
+ * Receive a tuple from the backend.
+ *
+ * @param isBinary set if the tuple should be treated as binary data
+ */
+ private void receiveTupleV2(boolean isBinary) throws SQLException
+ {
+ if (fields == null)
+ throw new PSQLException("postgresql.con.tuple");
+ Object tuple = pgStream.ReceiveTupleV2(fields.length, isBinary);
if (isBinary)
binaryCursor = true;
if (maxRows == 0 || tuples.size() < maxRows)
/*
* Receive command status from the backend.
*/
- private void receiveCommandStatus() throws SQLException
+ private void receiveCommandStatusV3() throws SQLException
+ {
+ //TODO: better handle the msg len
+ int l_len = pgStream.ReceiveIntegerR(4);
+ //read l_len -5 bytes (-4 for l_len and -1 for trailing \0)
+ status = connection.getEncoding().decode(pgStream.Receive(l_len-5));
+ //now read and discard the trailing \0
+ pgStream.Receive(1);
+ try
+ {
+ // Now handle the update count correctly.
+ if (status.startsWith("INSERT") || status.startsWith("UPDATE") || status.startsWith("DELETE") || status.startsWith("MOVE"))
+ {
+ update_count = Integer.parseInt(status.substring(1 + status.lastIndexOf(' ')));
+ }
+ if (status.startsWith("INSERT"))
+ {
+ insert_oid = Long.parseLong(status.substring(1 + status.indexOf(' '),
+ status.lastIndexOf(' ')));
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ throw new PSQLException("postgresql.con.fathom", status);
+ }
+ }
+ /*
+ * Receive command status from the backend.
+ */
+ private void receiveCommandStatusV2() throws SQLException
{
status = pgStream.ReceiveString(connection.getEncoding());
/*
* Receive the field descriptions from the back end.
*/
- private void receiveFields() throws SQLException
+ private void receiveFieldsV3() throws SQLException
+ {
+ //TODO: use the msgSize
+ //TODO: use the tableOid, and tablePosition
+ if (fields != null)
+ throw new PSQLException("postgresql.con.multres");
+ int l_msgSize = pgStream.ReceiveIntegerR(4);
+ int size = pgStream.ReceiveIntegerR(2);
+ fields = new Field[size];
+
+ for (int i = 0; i < fields.length; i++)
+ {
+ String typeName = pgStream.ReceiveString(connection.getEncoding());
+ int tableOid = pgStream.ReceiveIntegerR(4);
+ int tablePosition = pgStream.ReceiveIntegerR(2);
+ int typeOid = pgStream.ReceiveIntegerR(4);
+ int typeLength = pgStream.ReceiveIntegerR(2);
+ int typeModifier = pgStream.ReceiveIntegerR(4);
+ int formatType = pgStream.ReceiveIntegerR(2);
+ //TODO: use the extra values coming back
+ fields[i] = new Field(connection, typeName, typeOid, typeLength, typeModifier);
+ }
+ }
+ /*
+ * Receive the field descriptions from the back end.
+ */
+ private void receiveFieldsV2() throws SQLException
{
if (fields != null)
throw new PSQLException("postgresql.con.multres");
/**
* Sent to the backend to initialize a newly created connection.
*
- * $Id: StartupPacket.java,v 1.3 2003/03/07 18:39:42 barry Exp $
+ * $Id: StartupPacket.java,v 1.4 2003/05/29 03:21:32 barry Exp $
*/
public class StartupPacket
}
public void writeTo(PGStream stream) throws IOException
+ {
+ if (protocolMajor == 3) {
+ v3WriteTo(stream);
+ } else {
+ v2WriteTo(stream);
+ }
+ }
+
+ public void v3WriteTo(PGStream stream) throws IOException
+ {
+ stream.SendInteger(4 + 4 + "user".length() + 1 + user.length() + 1 + "database".length() +1 + database.length() + 1 + 1, 4);
+ stream.SendInteger(protocolMajor, 2);
+ stream.SendInteger(protocolMinor, 2);
+ stream.Send("user".getBytes());
+ stream.SendChar(0);
+ stream.Send(user.getBytes());
+ stream.SendChar(0);
+ stream.Send("database".getBytes());
+ stream.SendChar(0);
+ stream.Send(database.getBytes());
+ stream.SendChar(0);
+ stream.SendChar(0);
+ }
+
+ public void v2WriteTo(PGStream stream) throws IOException
{
stream.SendInteger(4 + 4 + SM_DATABASE + SM_USER + SM_OPTIONS + SM_UNUSED + SM_TTY, 4);
stream.SendInteger(protocolMajor, 2);
postgresql.con.multres:Cannot handle multiple result groups.
postgresql.con.pass:The password property is missing. It is mandatory.
postgresql.con.refused:Connection refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
+postgresql.con.scm:SCM credentials authentication is not supported by this driver.
postgresql.con.setup:Protocol error. Session setup failed.
postgresql.con.sslfail:An error occured while getting setting up the SSL connection.
postgresql.con.sslnotsupported:The server does not support SSL
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/fastpath/Attic/Fastpath.java,v 1.12 2003/03/07 18:39:42 barry Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/fastpath/Attic/Fastpath.java,v 1.13 2003/05/29 03:21:32 barry Exp $
*
*-------------------------------------------------------------------------
*/
* @exception SQLException if a database-access error occurs.
*/
public Object fastpath(int fnid, boolean resulttype, FastpathArg[] args) throws SQLException
+ {
+ if (conn.haveMinimumServerVersion("7.4")) {
+ return fastpathV3(fnid, resulttype, args);
+ } else {
+ return fastpathV2(fnid, resulttype, args);
+ }
+ }
+
+ private Object fastpathV3(int fnid, boolean resulttype, FastpathArg[] args) throws SQLException
+ {
+ // added Oct 7 1998 to give us thread safety
+ synchronized (stream)
+ {
+ // send the function call
+ try
+ {
+ int l_msgLen = 0;
+ l_msgLen += 16;
+ for (int i=0;i < args.length;i++)
+ l_msgLen += args[i].sendSize();
+
+ stream.SendChar('F');
+ stream.SendInteger(l_msgLen,4);
+ stream.SendInteger(fnid, 4);
+ stream.SendInteger(1,2);
+ stream.SendInteger(1,2);
+ stream.SendInteger(args.length,2);
+
+ for (int i = 0;i < args.length;i++)
+ args[i].send(stream);
+
+ stream.SendInteger(1,2);
+
+ // This is needed, otherwise data can be lost
+ stream.flush();
+
+ }
+ catch (IOException ioe)
+ {
+ throw new PSQLException("postgresql.fp.send", new Integer(fnid), ioe);
+ }
+
+ // Now handle the result
+
+ // Now loop, reading the results
+ Object result = null; // our result
+ StringBuffer errorMessage = null;
+ int c;
+ boolean l_endQuery = false;
+ while (!l_endQuery)
+ {
+ c = stream.ReceiveChar();
+
+ switch (c)
+ {
+ case 'A': // Asynchronous Notify
+ int pid = stream.ReceiveInteger(4);
+ String msg = stream.ReceiveString(conn.getEncoding());
+ conn.addNotification(new org.postgresql.core.Notification(msg, pid));
+ break;
+ //------------------------------
+ // Error message returned
+ case 'E':
+ if ( errorMessage == null )
+ errorMessage = new StringBuffer();
+
+ int l_elen = stream.ReceiveIntegerR(4);
+ errorMessage.append(conn.getEncoding().decode(stream.Receive(l_elen-4)));
+ break;
+ //------------------------------
+ // Notice from backend
+ case 'N':
+ int l_nlen = stream.ReceiveIntegerR(4);
+ conn.addWarning(conn.getEncoding().decode(stream.Receive(l_nlen-4)));
+ break;
+
+ case 'V':
+ int l_msgLen = stream.ReceiveIntegerR(4);
+ int l_valueLen = stream.ReceiveIntegerR(4);
+
+ if (l_valueLen == -1)
+ {
+ //null value
+ }
+ else if (l_valueLen == 0)
+ {
+ result = new byte[0];
+ }
+ else
+ {
+ // Return an Integer if
+ if (resulttype)
+ result = new Integer(stream.ReceiveIntegerR(l_valueLen));
+ else
+ {
+ byte buf[] = new byte[l_valueLen];
+ stream.Receive(buf, 0, l_valueLen);
+ result = buf;
+ }
+ }
+ break;
+
+ case 'Z':
+ //TODO: use size better
+ if (stream.ReceiveIntegerR(4) != 5) throw new PSQLException("postgresql.con.setup");
+ //TODO: handle transaction status
+ char l_tStatus = (char)stream.ReceiveChar();
+ l_endQuery = true;
+ break;
+
+ default:
+ throw new PSQLException("postgresql.fp.protocol", new Character((char)c));
+ }
+ }
+
+ if ( errorMessage != null )
+ throw new PSQLException("postgresql.fp.error", errorMessage.toString());
+
+ return result;
+ }
+ }
+
+ private Object fastpathV2(int fnid, boolean resulttype, FastpathArg[] args) throws SQLException
{
// added Oct 7 1998 to give us thread safety
synchronized (stream)
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/fastpath/Attic/FastpathArg.java,v 1.4 2003/03/07 18:39:42 barry Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/fastpath/Attic/FastpathArg.java,v 1.5 2003/05/29 03:21:32 barry Exp $
*
*-------------------------------------------------------------------------
*/
s.Send(bytes);
}
}
+
+ protected int sendSize()
+ {
+ if (type)
+ {
+ return 8;
+ }
+ else
+ {
+ return 4+bytes.length;
+ }
+ }
}
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Connection.java,v 1.18 2003/03/19 04:06:20 barry Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Connection.java,v 1.19 2003/05/29 03:21:32 barry Exp $
*
*-------------------------------------------------------------------------
*/
private String this_url;
private String cursor = null; // The positioned update cursor name
- // These are new for v6.3, they determine the current protocol versions
- // supported by this version of the driver. They are defined in
- // src/include/libpq/pqcomm.h
- protected static final int PG_PROTOCOL_LATEST_MAJOR = 2;
- protected static final int PG_PROTOCOL_LATEST_MINOR = 0;
+ private int PGProtocolVersionMajor = 2;
+ private int PGProtocolVersionMinor = 0;
+ public int getPGProtocolVersionMajor() { return PGProtocolVersionMajor; }
+ public int getPGProtocolVersionMinor() { return PGProtocolVersionMinor; }
private static final int AUTH_REQ_OK = 0;
private static final int AUTH_REQ_KRB4 = 1;
private static final int AUTH_REQ_PASSWORD = 3;
private static final int AUTH_REQ_CRYPT = 4;
private static final int AUTH_REQ_MD5 = 5;
+ private static final int AUTH_REQ_SCM = 6;
// These are used to cache oids, PGTypes and SQLTypes
PG_HOST = host;
PG_STATUS = CONNECTION_BAD;
- if (info.getProperty("ssl") != null && this_driver.sslEnabled())
+ if (info.getProperty("ssl") != null && Driver.sslEnabled())
{
useSSL = true;
}
throw new PSQLException ("postgresql.con.failed", e);
}
+ //Now do the protocol work
+ if (haveMinimumCompatibleVersion("7.4")) {
+ openConnectionV3(host,port,info,database,url,d,password);
+ } else {
+ openConnectionV2(host,port,info,database,url,d,password);
+ }
+ }
+
+ private void openConnectionV3(String p_host, int p_port, Properties p_info, String p_database, String p_url, Driver p_d, String p_password) throws SQLException
+ {
+ PGProtocolVersionMajor = 3;
+ if (Driver.logDebug)
+ Driver.debug("Using Protocol Version3");
+
+ // Now we need to construct and send an ssl startup packet
+ try
+ {
+ if (useSSL) {
+ if (Driver.logDebug)
+ Driver.debug("Asking server if it supports ssl");
+ pgStream.SendInteger(8,4);
+ pgStream.SendInteger(80877103,4);
+
+ // now flush the ssl packets to the backend
+ pgStream.flush();
+
+ // Now get the response from the backend, either an error message
+ // or an authentication request
+ int beresp = pgStream.ReceiveChar();
+ if (Driver.logDebug)
+ Driver.debug("Server response was (S=Yes,N=No): "+(char)beresp);
+ switch (beresp)
+ {
+ case 'E':
+ // An error occured, so pass the error message to the
+ // user.
+ //
+ // The most common one to be thrown here is:
+ // "User authentication failed"
+ //
+ throw new PSQLException("postgresql.con.misc", pgStream.ReceiveString(encoding));
+
+ case 'N':
+ // Server does not support ssl
+ throw new PSQLException("postgresql.con.sslnotsupported");
+
+ case 'S':
+ // Server supports ssl
+ if (Driver.logDebug)
+ Driver.debug("server does support ssl");
+ Driver.makeSSL(pgStream);
+ break;
+
+ default:
+ throw new PSQLException("postgresql.con.sslfail");
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ throw new PSQLException("postgresql.con.failed", e);
+ }
+
+
+ // Now we need to construct and send a startup packet
+ try
+ {
+ new StartupPacket(PGProtocolVersionMajor,
+ PGProtocolVersionMinor,
+ PG_USER,
+ p_database).writeTo(pgStream);
+
+ // now flush the startup packets to the backend
+ pgStream.flush();
+
+ // Now get the response from the backend, either an error message
+ // or an authentication request
+ int areq = -1; // must have a value here
+ do
+ {
+ int beresp = pgStream.ReceiveChar();
+ String salt = null;
+ byte [] md5Salt = new byte[4];
+ switch (beresp)
+ {
+ case 'E':
+ // An error occured, so pass the error message to the
+ // user.
+ //
+ // The most common one to be thrown here is:
+ // "User authentication failed"
+ //
+ int l_elen = pgStream.ReceiveIntegerR(4);
+ if (l_elen > 30000) {
+ //if the error length is > than 30000 we assume this is really a v2 protocol
+ //server so try again with a v2 connection
+ //need to create a new connection and try again
+ try
+ {
+ pgStream = new PGStream(p_host, p_port);
+ }
+ catch (ConnectException cex)
+ {
+ // Added by Peter Mount <peter@retep.org.uk>
+ // ConnectException is thrown when the connection cannot be made.
+ // we trap this an return a more meaningful message for the end user
+ throw new PSQLException ("postgresql.con.refused");
+ }
+ catch (IOException e)
+ {
+ throw new PSQLException ("postgresql.con.failed", e);
+ }
+ openConnectionV2(p_host, p_port, p_info, p_database, p_url, p_d, p_password);
+ return;
+ }
+ throw new PSQLException("postgresql.con.misc",encoding.decode(pgStream.Receive(l_elen-4)));
+
+ case 'R':
+ // Get the message length
+ int l_msgLen = pgStream.ReceiveIntegerR(4);
+ // Get the type of request
+ areq = pgStream.ReceiveIntegerR(4);
+ // Get the crypt password salt if there is one
+ if (areq == AUTH_REQ_CRYPT)
+ {
+ byte[] rst = new byte[2];
+ rst[0] = (byte)pgStream.ReceiveChar();
+ rst[1] = (byte)pgStream.ReceiveChar();
+ salt = new String(rst, 0, 2);
+ if (Driver.logDebug)
+ Driver.debug("Crypt salt=" + salt);
+ }
+
+ // Or get the md5 password salt if there is one
+ if (areq == AUTH_REQ_MD5)
+ {
+
+ md5Salt[0] = (byte)pgStream.ReceiveChar();
+ md5Salt[1] = (byte)pgStream.ReceiveChar();
+ md5Salt[2] = (byte)pgStream.ReceiveChar();
+ md5Salt[3] = (byte)pgStream.ReceiveChar();
+ salt = new String(md5Salt, 0, 4);
+ if (Driver.logDebug)
+ Driver.debug("MD5 salt=" + salt);
+ }
+
+ // now send the auth packet
+ switch (areq)
+ {
+ case AUTH_REQ_OK:
+ break;
+
+ case AUTH_REQ_KRB4:
+ if (Driver.logDebug)
+ Driver.debug("postgresql: KRB4");
+ throw new PSQLException("postgresql.con.kerb4");
+
+ case AUTH_REQ_KRB5:
+ if (Driver.logDebug)
+ Driver.debug("postgresql: KRB5");
+ throw new PSQLException("postgresql.con.kerb5");
+
+ case AUTH_REQ_SCM:
+ if (Driver.logDebug)
+ Driver.debug("postgresql: SCM");
+ throw new PSQLException("postgresql.con.scm");
+
+
+ case AUTH_REQ_PASSWORD:
+ if (Driver.logDebug)
+ Driver.debug("postgresql: PASSWORD");
+ pgStream.SendChar('p');
+ pgStream.SendInteger(5 + p_password.length(), 4);
+ pgStream.Send(p_password.getBytes());
+ pgStream.SendChar(0);
+ pgStream.flush();
+ break;
+
+ case AUTH_REQ_CRYPT:
+ if (Driver.logDebug)
+ Driver.debug("postgresql: CRYPT");
+ String crypted = UnixCrypt.crypt(salt, p_password);
+ pgStream.SendChar('p');
+ pgStream.SendInteger(5 + crypted.length(), 4);
+ pgStream.Send(crypted.getBytes());
+ pgStream.SendChar(0);
+ pgStream.flush();
+ break;
+
+ case AUTH_REQ_MD5:
+ if (Driver.logDebug)
+ Driver.debug("postgresql: MD5");
+ byte[] digest = MD5Digest.encode(PG_USER, p_password, md5Salt);
+ pgStream.SendChar('p');
+ pgStream.SendInteger(5 + digest.length, 4);
+ pgStream.Send(digest);
+ pgStream.SendChar(0);
+ pgStream.flush();
+ break;
+
+ default:
+ throw new PSQLException("postgresql.con.auth", new Integer(areq));
+ }
+ break;
+
+ default:
+ throw new PSQLException("postgresql.con.authfail");
+ }
+ }
+ while (areq != AUTH_REQ_OK);
+
+ }
+ catch (IOException e)
+ {
+ throw new PSQLException("postgresql.con.failed", e);
+ }
+
+ int beresp;
+ do
+ {
+ beresp = pgStream.ReceiveChar();
+ switch (beresp)
+ {
+ case 'Z':
+ //ready for query
+ break;
+ case 'K':
+ int l_msgLen = pgStream.ReceiveIntegerR(4);
+ if (l_msgLen != 12) throw new PSQLException("postgresql.con.setup");
+ pid = pgStream.ReceiveIntegerR(4);
+ ckey = pgStream.ReceiveIntegerR(4);
+ break;
+ case 'E':
+ int l_elen = pgStream.ReceiveIntegerR(4);
+ throw new PSQLException("postgresql.con.backend",encoding.decode(pgStream.Receive(l_elen-4)));
+ case 'N':
+ int l_nlen = pgStream.ReceiveIntegerR(4);
+ addWarning(encoding.decode(pgStream.Receive(l_nlen-4)));
+ break;
+ case 'S':
+ //TODO: handle parameter status messages
+ int l_len = pgStream.ReceiveIntegerR(4);
+ String l_pStatus = encoding.decode(pgStream.Receive(l_len-4));
+ if (Driver.logDebug)
+ Driver.debug("ParameterStatus="+ l_pStatus);
+ break;
+ default:
+ if (Driver.logDebug)
+ Driver.debug("invalid state="+ (char)beresp);
+ throw new PSQLException("postgresql.con.setup");
+ }
+ }
+ while (beresp != 'Z');
+ // read ReadyForQuery
+ if (pgStream.ReceiveIntegerR(4) != 5) throw new PSQLException("postgresql.con.setup");
+ //TODO: handle transaction status
+ char l_tStatus = (char)pgStream.ReceiveChar();
+
+ // "pg_encoding_to_char(1)" will return 'EUC_JP' for a backend compiled with multibyte,
+ // otherwise it's hardcoded to 'SQL_ASCII'.
+ // If the backend doesn't know about multibyte we can't assume anything about the encoding
+ // used, so we denote this with 'UNKNOWN'.
+ //Note: begining with 7.2 we should be using pg_client_encoding() which
+ //is new in 7.2. However it isn't easy to conditionally call this new
+ //function, since we don't yet have the information as to what server
+ //version we are talking to. Thus we will continue to call
+ //getdatabaseencoding() until we drop support for 7.1 and older versions
+ //or until someone comes up with a conditional way to run one or
+ //the other function depending on server version that doesn't require
+ //two round trips to the server per connection
+
+ final String encodingQuery =
+ "case when pg_encoding_to_char(1) = 'SQL_ASCII' then 'UNKNOWN' else getdatabaseencoding() end";
+
+ // Set datestyle and fetch db encoding in a single call, to avoid making
+ // more than one round trip to the backend during connection startup.
+
+
+ BaseResultSet resultSet
+ = execSQL("set datestyle to 'ISO'; select version(), " + encodingQuery + ";");
+
+ if (! resultSet.next())
+ {
+ throw new PSQLException("postgresql.con.failed", "failed getting backend encoding");
+ }
+ String version = resultSet.getString(1);
+ dbVersionNumber = extractVersionNumber(version);
+
+ String dbEncoding = resultSet.getString(2);
+ encoding = Encoding.getEncoding(dbEncoding, p_info.getProperty("charSet"));
+ //In 7.3 we are forced to do a second roundtrip to handle the case
+ //where a database may not be running in autocommit mode
+ //jdbc by default assumes autocommit is on until setAutoCommit(false)
+ //is called. Therefore we need to ensure a new connection is
+ //initialized to autocommit on.
+ //We also set the client encoding so that the driver only needs
+ //to deal with utf8. We can only do this in 7.3 because multibyte
+ //support is now always included
+ if (haveMinimumServerVersion("7.3"))
+ {
+ BaseResultSet acRset =
+ //TODO: if protocol V3 we can set the client encoding in startup
+ execSQL("set client_encoding = 'UNICODE'");
+ //set encoding to be unicode
+ encoding = Encoding.getEncoding("UNICODE", null);
+
+ }
+
+ // Initialise object handling
+ initObjectTypes();
+
+ // Mark the connection as ok, and cleanup
+ PG_STATUS = CONNECTION_OK;
+ }
+
+ private void openConnectionV2(String host, int port, Properties info, String database, String url, Driver d, String password) throws SQLException
+ {
+ PGProtocolVersionMajor = 2;
+ if (Driver.logDebug)
+ Driver.debug("Using Protocol Version2");
+
// Now we need to construct and send an ssl startup packet
try
{
// Now we need to construct and send a startup packet
try
{
- new StartupPacket(PG_PROTOCOL_LATEST_MAJOR,
- PG_PROTOCOL_LATEST_MINOR,
+ new StartupPacket(PGProtocolVersionMajor,
+ PGProtocolVersionMinor,
PG_USER,
database).writeTo(pgStream);
PG_STATUS = CONNECTION_OK;
}
-
/*
* Return the instance of org.postgresql.Driver
* that created this connection
* @exception SQLException if a database access error occurs
*/
public void close() throws SQLException
+ {
+ if (haveMinimumCompatibleVersion("7.4")) {
+ closeV3();
+ } else {
+ closeV2();
+ }
+ }
+
+ public void closeV3() throws SQLException
+ {
+ if (pgStream != null)
+ {
+ try
+ {
+ pgStream.SendChar('X');
+ pgStream.SendInteger(0,4);
+ pgStream.flush();
+ pgStream.close();
+ }
+ catch (IOException e)
+ {}
+ finally
+ {
+ pgStream = null;
+ }
+ }
+ }
+
+ public void closeV2() throws SQLException
{
if (pgStream != null)
{
return ;
if (autoCommit)
{
- if (haveMinimumServerVersion("7.3"))
- {
- //We do the select to ensure a transaction is in process
- //before we do the commit to avoid warning messages
- //from issuing a commit without a transaction in process
- //NOTE this is done in two network roundtrips to work around
- //a server bug in 7.3 where the select wouldn't actually start
- //a new transaction if in the same command as the commit
- execSQL("select 1;");
- execSQL("commit; set autocommit = on;");
- }
- else
- {
execSQL("end");
- }
}
else
{
- if (haveMinimumServerVersion("7.3"))
- {
- execSQL("set autocommit = off; " + getIsolationLevelSQL());
- }
- else if (haveMinimumServerVersion("7.1"))
+ if (haveMinimumServerVersion("7.1"))
{
execSQL("begin;" + getIsolationLevelSQL());
}
{
if (autoCommit)
return ;
- if (haveMinimumServerVersion("7.3"))
- {
- execSQL("commit; " + getIsolationLevelSQL());
- }
- else if (haveMinimumServerVersion("7.1"))
+ //TODO: delay starting new transaction until first command
+ if (haveMinimumServerVersion("7.1"))
{
execSQL("commit;begin;" + getIsolationLevelSQL());
}
{
if (autoCommit)
return ;
- if (haveMinimumServerVersion("7.3"))
- {
- //we don't automatically start a transaction
- //but let the server functionality automatically start
- //one when the first statement is executed
- execSQL("rollback; " + getIsolationLevelSQL());
- }
- else if (haveMinimumServerVersion("7.1"))
+ //TODO: delay starting transaction until first command
+ if (haveMinimumServerVersion("7.1"))
{
execSQL("rollback; begin;" + getIsolationLevelSQL());
}
import org.postgresql.largeobject.*;
/*
- * $Id: BlobTest.java,v 1.7 2002/08/14 20:35:40 barry Exp $
+ * $Id: BlobTest.java,v 1.8 2003/05/29 03:21:32 barry Exp $
*
* Some simple tests based on problems reported by users. Hopefully these will
* help prevent previous problems from re-occuring ;-)
result = result && f == -1 && b == -1;
if (!result)
- System.out.println("\nBlob compare failed at " + c + " of " + blob.size());
+ assertTrue("Blob compare failed at " + c + " of " + blob.size(), false);
blob.close();
fis.close();
public void testGetDouble () throws Throwable
{
- // System.out.println ("Testing CallableStmt Types.DOUBLE");
CallableStatement call = con.prepareCall (func + pkgName + "getDouble (?) }");
call.setDouble (2, (double)3.04);
call.registerOutParameter (1, Types.DOUBLE);
public void testGetInt () throws Throwable
{
- // System.out.println ("Testing CallableStmt Types.INTEGER");
CallableStatement call = con.prepareCall (func + pkgName + "getInt (?) }");
call.setInt (2, 4);
call.registerOutParameter (1, Types.INTEGER);
public void testGetNumeric () throws Throwable
{
- // System.out.println ("Testing CallableStmt Types.NUMERIC");
CallableStatement call = con.prepareCall (func + pkgName + "getNumeric (?) }");
call.setBigDecimal (2, new java.math.BigDecimal(4));
call.registerOutParameter (1, Types.NUMERIC);
public void testGetString () throws Throwable
{
- // System.out.println ("Testing CallableStmt Types.VARCHAR");
CallableStatement call = con.prepareCall (func + pkgName + "getString (?) }");
call.setString (2, "foo");
call.registerOutParameter (1, Types.VARCHAR);
import java.sql.*;
/*
- * $Id: MiscTest.java,v 1.8 2002/09/06 21:23:06 momjian Exp $
+ * $Id: MiscTest.java,v 1.9 2003/05/29 03:21:32 barry Exp $
*
* Some simple tests based on problems reported by users. Hopefully these will
* help prevent previous problems from re-occuring ;-)
public void xtestLocking()
{
- System.out.println("testing lock");
try
{
Connection con = TestUtil.openDB();