summaryrefslogtreecommitdiff
path: root/src/interfaces/jdbc/org/postgresql/Connection.java
diff options
context:
space:
mode:
authorPeter Mount <peter@retep.org.uk>2000-04-26 05:39:32 +0000
committerPeter Mount <peter@retep.org.uk>2000-04-26 05:39:32 +0000
commit4fc3690238d03b8d0e4cc73934b188f3a3943afe (patch)
tree878dbc3012fa7cb2d17a68e1d4fd8d15fa3d0c67 /src/interfaces/jdbc/org/postgresql/Connection.java
parent39116bfbfcf5d17956dca2a1a424e52488aa3f56 (diff)
Attempt III
Diffstat (limited to 'src/interfaces/jdbc/org/postgresql/Connection.java')
-rw-r--r--src/interfaces/jdbc/org/postgresql/Connection.java752
1 files changed, 752 insertions, 0 deletions
diff --git a/src/interfaces/jdbc/org/postgresql/Connection.java b/src/interfaces/jdbc/org/postgresql/Connection.java
new file mode 100644
index 00000000000..045ddec0fcf
--- /dev/null
+++ b/src/interfaces/jdbc/org/postgresql/Connection.java
@@ -0,0 +1,752 @@
+package org.postgresql;
+
+import java.io.*;
+import java.net.*;
+import java.sql.*;
+import java.util.*;
+import org.postgresql.Field;
+import org.postgresql.fastpath.*;
+import org.postgresql.largeobject.*;
+import org.postgresql.util.*;
+
+/**
+ * $Id: Connection.java,v 1.1 2000/04/26 05:39:32 peter Exp $
+ *
+ * This abstract class is used by org.postgresql.Driver to open either the JDBC1 or
+ * JDBC2 versions of the Connection class.
+ *
+ */
+public abstract class Connection
+{
+ // This is the network stream associated with this connection
+ public PG_Stream pg_stream;
+
+ // This is set by org.postgresql.Statement.setMaxRows()
+ public int maxrows = 0; // maximum no. of rows; 0 = unlimited
+
+ private String PG_HOST;
+ private int PG_PORT;
+ private String PG_USER;
+ private String PG_PASSWORD;
+ private String PG_DATABASE;
+ private boolean PG_STATUS;
+
+ public boolean CONNECTION_OK = true;
+ public boolean CONNECTION_BAD = false;
+
+ public boolean autoCommit = true;
+ public boolean readOnly = false;
+
+ public Driver this_driver;
+ private String this_url;
+ private String cursor = null; // The positioned update cursor name
+
+ // These are new for v6.3, they determine the current protocol versions
+ // supported by this version of the driver. They are defined in
+ // src/include/libpq/pqcomm.h
+ protected static final int PG_PROTOCOL_LATEST_MAJOR = 2;
+ protected static final int PG_PROTOCOL_LATEST_MINOR = 0;
+ private static final int SM_DATABASE = 64;
+ private static final int SM_USER = 32;
+ private static final int SM_OPTIONS = 64;
+ private static final int SM_UNUSED = 64;
+ private static final int SM_TTY = 64;
+
+ private static final int AUTH_REQ_OK = 0;
+ private static final int AUTH_REQ_KRB4 = 1;
+ private static final int AUTH_REQ_KRB5 = 2;
+ private static final int AUTH_REQ_PASSWORD = 3;
+ private static final int AUTH_REQ_CRYPT = 4;
+
+ // New for 6.3, salt value for crypt authorisation
+ private String salt;
+
+ // This is used by Field to cache oid -> names.
+ // It's here, because it's shared across this connection only.
+ // Hence it cannot be static within the Field class, because it would then
+ // be across all connections, which could be to different backends.
+ public Hashtable fieldCache = new Hashtable();
+
+ // Now handle notices as warnings, so things like "show" now work
+ public SQLWarning firstWarning = null;
+
+ // The PID an cancellation key we get from the backend process
+ public int pid;
+ public int ckey;
+
+ /**
+ * This is called by Class.forName() from within org.postgresql.Driver
+ */
+ public Connection()
+ {
+ }
+
+ /**
+ * This method actually opens the connection. It is called by Driver.
+ *
+ * @param host the hostname of the database back end
+ * @param port the port number of the postmaster process
+ * @param info a Properties[] thing of the user and password
+ * @param database the database to connect to
+ * @param u the URL of the connection
+ * @param d the Driver instantation of the connection
+ * @return a valid connection profile
+ * @exception SQLException if a database access error occurs
+ */
+ protected void openConnection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException
+ {
+ // Throw an exception if the user or password properties are missing
+ // This occasionally occurs when the client uses the properties version
+ // of getConnection(), and is a common question on the email lists
+ if(info.getProperty("user")==null)
+ throw new PSQLException("postgresql.con.user");
+ if(info.getProperty("password")==null)
+ throw new PSQLException("postgresql.con.pass");
+
+ this_driver = d;
+ this_url = new String(url);
+ PG_DATABASE = new String(database);
+ PG_PASSWORD = new String(info.getProperty("password"));
+ PG_USER = new String(info.getProperty("user"));
+ PG_PORT = port;
+ PG_HOST = new String(host);
+ PG_STATUS = CONNECTION_BAD;
+
+ // Now make the initial connection
+ try
+ {
+ pg_stream = new PG_Stream(host, port);
+ } catch (ConnectException cex) {
+ // Added by Peter Mount <peter@retep.org.uk>
+ // ConnectException is thrown when the connection cannot be made.
+ // we trap this an return a more meaningful message for the end user
+ throw new PSQLException ("postgresql.con.refused");
+ } catch (IOException e) {
+ throw new PSQLException ("postgresql.con.failed",e);
+ }
+
+ // Now we need to construct and send a startup packet
+ try
+ {
+ // Ver 6.3 code
+ pg_stream.SendInteger(4+4+SM_DATABASE+SM_USER+SM_OPTIONS+SM_UNUSED+SM_TTY,4);
+ pg_stream.SendInteger(PG_PROTOCOL_LATEST_MAJOR,2);
+ pg_stream.SendInteger(PG_PROTOCOL_LATEST_MINOR,2);
+ pg_stream.Send(database.getBytes(),SM_DATABASE);
+
+ // This last send includes the unused fields
+ pg_stream.Send(PG_USER.getBytes(),SM_USER+SM_OPTIONS+SM_UNUSED+SM_TTY);
+
+ // now flush the startup packets to the backend
+ pg_stream.flush();
+
+ // Now get the response from the backend, either an error message
+ // or an authentication request
+ int areq = -1; // must have a value here
+ do {
+ int beresp = pg_stream.ReceiveChar();
+ switch(beresp)
+ {
+ case 'E':
+ // An error occured, so pass the error message to the
+ // user.
+ //
+ // The most common one to be thrown here is:
+ // "User authentication failed"
+ //
+ throw new SQLException(pg_stream.ReceiveString(4096));
+
+ case 'R':
+ // Get the type of request
+ areq = pg_stream.ReceiveIntegerR(4);
+
+ // Get the password salt if there is one
+ if(areq == AUTH_REQ_CRYPT) {
+ byte[] rst = new byte[2];
+ rst[0] = (byte)pg_stream.ReceiveChar();
+ rst[1] = (byte)pg_stream.ReceiveChar();
+ salt = new String(rst,0,2);
+ DriverManager.println("Salt="+salt);
+ }
+
+ // now send the auth packet
+ switch(areq)
+ {
+ case AUTH_REQ_OK:
+ break;
+
+ case AUTH_REQ_KRB4:
+ DriverManager.println("postgresql: KRB4");
+ throw new PSQLException("postgresql.con.kerb4");
+
+ case AUTH_REQ_KRB5:
+ DriverManager.println("postgresql: KRB5");
+ throw new PSQLException("postgresql.con.kerb5");
+
+ case AUTH_REQ_PASSWORD:
+ DriverManager.println("postgresql: PASSWORD");
+ pg_stream.SendInteger(5+PG_PASSWORD.length(),4);
+ pg_stream.Send(PG_PASSWORD.getBytes());
+ pg_stream.SendInteger(0,1);
+ pg_stream.flush();
+ break;
+
+ case AUTH_REQ_CRYPT:
+ DriverManager.println("postgresql: CRYPT");
+ String crypted = UnixCrypt.crypt(salt,PG_PASSWORD);
+ pg_stream.SendInteger(5+crypted.length(),4);
+ pg_stream.Send(crypted.getBytes());
+ pg_stream.SendInteger(0,1);
+ pg_stream.flush();
+ break;
+
+ default:
+ throw new PSQLException("postgresql.con.auth",new Integer(areq));
+ }
+ break;
+
+ default:
+ throw new PSQLException("postgresql.con.authfail");
+ }
+ } while(areq != AUTH_REQ_OK);
+
+ } catch (IOException e) {
+ throw new PSQLException("postgresql.con.failed",e);
+ }
+
+
+ // As of protocol version 2.0, we should now receive the cancellation key and the pid
+ int beresp = pg_stream.ReceiveChar();
+ switch(beresp) {
+ case 'K':
+ pid = pg_stream.ReceiveInteger(4);
+ ckey = pg_stream.ReceiveInteger(4);
+ break;
+ case 'E':
+ case 'N':
+ throw new SQLException(pg_stream.ReceiveString(4096));
+ default:
+ throw new PSQLException("postgresql.con.setup");
+ }
+
+ // Expect ReadyForQuery packet
+ beresp = pg_stream.ReceiveChar();
+ switch(beresp) {
+ case 'Z':
+ break;
+ case 'E':
+ case 'N':
+ throw new SQLException(pg_stream.ReceiveString(4096));
+ default:
+ throw new PSQLException("postgresql.con.setup");
+ }
+
+ // Originally we issued a SHOW DATESTYLE statement to find the databases default
+ // datestyle. However, this caused some problems with timestamps, so in 6.5, we
+ // went the way of ODBC, and set the connection to ISO.
+ //
+ // This may cause some clients to break when they assume anything other than ISO,
+ // but then - they should be using the proper methods ;-)
+ //
+ //
+ firstWarning = null;
+
+ ExecSQL("set datestyle to 'ISO'");
+
+ // Initialise object handling
+ initObjectTypes();
+
+ // Mark the connection as ok, and cleanup
+ firstWarning = null;
+ PG_STATUS = CONNECTION_OK;
+ }
+
+ // These methods used to be in the main Connection implementation. As they
+ // are common to all implementations (JDBC1 or 2), they are placed here.
+ // This should make it easy to maintain the two specifications.
+
+ /**
+ * This adds a warning to the warning chain.
+ * @param msg message to add
+ */
+ public void addWarning(String msg)
+ {
+ DriverManager.println(msg);
+
+ // Add the warning to the chain
+ if(firstWarning!=null)
+ firstWarning.setNextWarning(new SQLWarning(msg));
+ else
+ firstWarning = new SQLWarning(msg);
+
+ // Now check for some specific messages
+
+ // This is obsolete in 6.5, but I've left it in here so if we need to use this
+ // technique again, we'll know where to place it.
+ //
+ // This is generated by the SQL "show datestyle"
+ //if(msg.startsWith("NOTICE:") && msg.indexOf("DateStyle")>0) {
+ //// 13 is the length off "DateStyle is "
+ //msg = msg.substring(msg.indexOf("DateStyle is ")+13);
+ //
+ //for(int i=0;i<dateStyles.length;i+=2)
+ //if(msg.startsWith(dateStyles[i]))
+ //currentDateStyle=i+1; // this is the index of the format
+ //}
+ }
+
+ /**
+ * Send a query to the backend. Returns one of the ResultSet
+ * objects.
+ *
+ * <B>Note:</B> there does not seem to be any method currently
+ * in existance to return the update count.
+ *
+ * @param sql the SQL statement to be executed
+ * @return a ResultSet holding the results
+ * @exception SQLException if a database error occurs
+ */
+ public java.sql.ResultSet ExecSQL(String sql) throws SQLException
+ {
+ // added Oct 7 1998 to give us thread safety.
+ synchronized(pg_stream) {
+
+ Field[] fields = null;
+ Vector tuples = new Vector();
+ byte[] buf = new byte[sql.length()];
+ int fqp = 0;
+ boolean hfr = false;
+ String recv_status = null, msg;
+ int update_count = 1;
+ SQLException final_error = null;
+
+ if (sql.length() > 8192)
+ throw new PSQLException("postgresql.con.toolong",sql);
+ try
+ {
+ pg_stream.SendChar('Q');
+ buf = sql.getBytes();
+ pg_stream.Send(buf);
+ pg_stream.SendChar(0);
+ pg_stream.flush();
+ } catch (IOException e) {
+ throw new PSQLException("postgresql.con.ioerror",e);
+ }
+
+ while (!hfr || fqp > 0)
+ {
+ Object tup=null; // holds rows as they are recieved
+
+ int c = pg_stream.ReceiveChar();
+
+ switch (c)
+ {
+ case 'A': // Asynchronous Notify
+ pid = pg_stream.ReceiveInteger(4);
+ msg = pg_stream.ReceiveString(8192);
+ break;
+ case 'B': // Binary Data Transfer
+ if (fields == null)
+ throw new PSQLException("postgresql.con.tuple");
+ tup = pg_stream.ReceiveTuple(fields.length, true);
+ // This implements Statement.setMaxRows()
+ if(maxrows==0 || tuples.size()<maxrows)
+ tuples.addElement(tup);
+ break;
+ case 'C': // Command Status
+ recv_status = pg_stream.ReceiveString(8192);
+
+ // Now handle the update count correctly.
+ if(recv_status.startsWith("INSERT") || recv_status.startsWith("UPDATE")) {
+ try {
+ update_count = Integer.parseInt(recv_status.substring(1+recv_status.lastIndexOf(' ')));
+ } catch(NumberFormatException nfe) {
+ throw new PSQLException("postgresql.con.fathom",recv_status);
+ }
+ }
+ if (fields != null)
+ hfr = true;
+ else
+ {
+ try
+ {
+ pg_stream.SendChar('Q');
+ pg_stream.SendChar(' ');
+ pg_stream.SendChar(0);
+ pg_stream.flush();
+ } catch (IOException e) {
+ throw new PSQLException("postgresql.con.ioerror",e);
+ }
+ fqp++;
+ }
+ break;
+ case 'D': // Text Data Transfer
+ if (fields == null)
+ throw new PSQLException("postgresql.con.tuple");
+ tup = pg_stream.ReceiveTuple(fields.length, false);
+ // This implements Statement.setMaxRows()
+ if(maxrows==0 || tuples.size()<maxrows)
+ tuples.addElement(tup);
+ break;
+ case 'E': // Error Message
+ msg = pg_stream.ReceiveString(4096);
+ final_error = new SQLException(msg);
+ hfr = true;
+ break;
+ case 'I': // Empty Query
+ int t = pg_stream.ReceiveChar();
+
+ if (t != 0)
+ throw new PSQLException("postgresql.con.garbled");
+ if (fqp > 0)
+ fqp--;
+ if (fqp == 0)
+ hfr = true;
+ break;
+ case 'N': // Error Notification
+ addWarning(pg_stream.ReceiveString(4096));
+ break;
+ case 'P': // Portal Name
+ String pname = pg_stream.ReceiveString(8192);
+ break;
+ case 'T': // MetaData Field Description
+ if (fields != null)
+ throw new PSQLException("postgresql.con.multres");
+ fields = ReceiveFields();
+ break;
+ case 'Z': // backend ready for query, ignore for now :-)
+ break;
+ default:
+ throw new PSQLException("postgresql.con.type",new Character((char)c));
+ }
+ }
+ if (final_error != null)
+ throw final_error;
+
+ return getResultSet(this, fields, tuples, recv_status, update_count);
+ }
+ }
+
+ /**
+ * Receive the field descriptions from the back end
+ *
+ * @return an array of the Field object describing the fields
+ * @exception SQLException if a database error occurs
+ */
+ private Field[] ReceiveFields() throws SQLException
+ {
+ int nf = pg_stream.ReceiveIntegerR(2), i;
+ Field[] fields = new Field[nf];
+
+ for (i = 0 ; i < nf ; ++i)
+ {
+ String typname = pg_stream.ReceiveString(8192);
+ int typid = pg_stream.ReceiveIntegerR(4);
+ int typlen = pg_stream.ReceiveIntegerR(2);
+ int typmod = pg_stream.ReceiveIntegerR(4);
+ fields[i] = new Field(this, typname, typid, typlen, typmod);
+ }
+ return fields;
+ }
+
+ /**
+ * In SQL, a result table can be retrieved through a cursor that
+ * is named. The current row of a result can be updated or deleted
+ * using a positioned update/delete statement that references the
+ * cursor name.
+ *
+ * We support one cursor per connection.
+ *
+ * setCursorName sets the cursor name.
+ *
+ * @param cursor the cursor name
+ * @exception SQLException if a database access error occurs
+ */
+ public void setCursorName(String cursor) throws SQLException
+ {
+ this.cursor = cursor;
+ }
+
+ /**
+ * getCursorName gets the cursor name.
+ *
+ * @return the current cursor name
+ * @exception SQLException if a database access error occurs
+ */
+ public String getCursorName() throws SQLException
+ {
+ return cursor;
+ }
+
+ /**
+ * We are required to bring back certain information by
+ * the DatabaseMetaData class. These functions do that.
+ *
+ * Method getURL() brings back the URL (good job we saved it)
+ *
+ * @return the url
+ * @exception SQLException just in case...
+ */
+ public String getURL() throws SQLException
+ {
+ return this_url;
+ }
+
+ /**
+ * Method getUserName() brings back the User Name (again, we
+ * saved it)
+ *
+ * @return the user name
+ * @exception SQLException just in case...
+ */
+ public String getUserName() throws SQLException
+ {
+ return PG_USER;
+ }
+
+ /**
+ * This returns the Fastpath API for the current connection.
+ *
+ * <p><b>NOTE:</b> This is not part of JDBC, but allows access to
+ * functions on the org.postgresql backend itself.
+ *
+ * <p>It is primarily used by the LargeObject API
+ *
+ * <p>The best way to use this is as follows:
+ *
+ * <p><pre>
+ * import org.postgresql.fastpath.*;
+ * ...
+ * Fastpath fp = ((org.postgresql.Connection)myconn).getFastpathAPI();
+ * </pre>
+ *
+ * <p>where myconn is an open Connection to org.postgresql.
+ *
+ * @return Fastpath object allowing access to functions on the org.postgresql
+ * backend.
+ * @exception SQLException by Fastpath when initialising for first time
+ */
+ public Fastpath getFastpathAPI() throws SQLException
+ {
+ if(fastpath==null)
+ fastpath = new Fastpath(this,pg_stream);
+ return fastpath;
+ }
+
+ // This holds a reference to the Fastpath API if already open
+ private Fastpath fastpath = null;
+
+ /**
+ * This returns the LargeObject API for the current connection.
+ *
+ * <p><b>NOTE:</b> This is not part of JDBC, but allows access to
+ * functions on the org.postgresql backend itself.
+ *
+ * <p>The best way to use this is as follows:
+ *
+ * <p><pre>
+ * import org.postgresql.largeobject.*;
+ * ...
+ * LargeObjectManager lo = ((org.postgresql.Connection)myconn).getLargeObjectAPI();
+ * </pre>
+ *
+ * <p>where myconn is an open Connection to org.postgresql.
+ *
+ * @return LargeObject object that implements the API
+ * @exception SQLException by LargeObject when initialising for first time
+ */
+ public LargeObjectManager getLargeObjectAPI() throws SQLException
+ {
+ if(largeobject==null)
+ largeobject = new LargeObjectManager(this);
+ return largeobject;
+ }
+
+ // This holds a reference to the LargeObject API if already open
+ private LargeObjectManager largeobject = null;
+
+ /**
+ * This method is used internally to return an object based around
+ * org.postgresql's more unique data types.
+ *
+ * <p>It uses an internal Hashtable to get the handling class. If the
+ * type is not supported, then an instance of org.postgresql.util.PGobject
+ * is returned.
+ *
+ * You can use the getValue() or setValue() methods to handle the returned
+ * object. Custom objects can have their own methods.
+ *
+ * In 6.4, this is extended to use the org.postgresql.util.Serialize class to
+ * allow the Serialization of Java Objects into the database without using
+ * Blobs. Refer to that class for details on how this new feature works.
+ *
+ * @return PGobject for this type, and set to value
+ * @exception SQLException if value is not correct for this type
+ * @see org.postgresql.util.Serialize
+ */
+ public Object getObject(String type,String value) throws SQLException
+ {
+ try {
+ Object o = objectTypes.get(type);
+
+ // If o is null, then the type is unknown, so check to see if type
+ // is an actual table name. If it does, see if a Class is known that
+ // can handle it
+ if(o == null) {
+ Serialize ser = new Serialize(this,type);
+ objectTypes.put(type,ser);
+ return ser.fetch(Integer.parseInt(value));
+ }
+
+ // If o is not null, and it is a String, then its a class name that
+ // extends PGobject.
+ //
+ // This is used to implement the org.postgresql unique types (like lseg,
+ // point, etc).
+ if(o instanceof String) {
+ // 6.3 style extending PG_Object
+ PGobject obj = null;
+ obj = (PGobject)(Class.forName((String)o).newInstance());
+ obj.setType(type);
+ obj.setValue(value);
+ return (Object)obj;
+ } else {
+ // If it's an object, it should be an instance of our Serialize class
+ // If so, then call it's fetch method.
+ if(o instanceof Serialize)
+ return ((Serialize)o).fetch(Integer.parseInt(value));
+ }
+ } catch(SQLException sx) {
+ // rethrow the exception. Done because we capture any others next
+ sx.fillInStackTrace();
+ throw sx;
+ } catch(Exception ex) {
+ throw new PSQLException("postgresql.con.creobj",type,ex);
+ }
+
+ // should never be reached
+ return null;
+ }
+
+ /**
+ * This stores an object into the database.
+ * @param o Object to store
+ * @return OID of the new rectord
+ * @exception SQLException if value is not correct for this type
+ * @see org.postgresql.util.Serialize
+ */
+ public int putObject(Object o) throws SQLException
+ {
+ try {
+ String type = o.getClass().getName();
+ Object x = objectTypes.get(type);
+
+ // If x is null, then the type is unknown, so check to see if type
+ // is an actual table name. If it does, see if a Class is known that
+ // can handle it
+ if(x == null) {
+ Serialize ser = new Serialize(this,type);
+ objectTypes.put(type,ser);
+ return ser.store(o);
+ }
+
+ // If it's an object, it should be an instance of our Serialize class
+ // If so, then call it's fetch method.
+ if(x instanceof Serialize)
+ return ((Serialize)x).store(o);
+
+ // Thow an exception because the type is unknown
+ throw new PSQLException("postgresql.con.strobj");
+
+ } catch(SQLException sx) {
+ // rethrow the exception. Done because we capture any others next
+ sx.fillInStackTrace();
+ throw sx;
+ } catch(Exception ex) {
+ throw new PSQLException("postgresql.con.strobjex",ex);
+ }
+ }
+
+ /**
+ * This allows client code to add a handler for one of org.postgresql's
+ * more unique data types.
+ *
+ * <p><b>NOTE:</b> This is not part of JDBC, but an extension.
+ *
+ * <p>The best way to use this is as follows:
+ *
+ * <p><pre>
+ * ...
+ * ((org.postgresql.Connection)myconn).addDataType("mytype","my.class.name");
+ * ...
+ * </pre>
+ *
+ * <p>where myconn is an open Connection to org.postgresql.
+ *
+ * <p>The handling class must extend org.postgresql.util.PGobject
+ *
+ * @see org.postgresql.util.PGobject
+ */
+ public void addDataType(String type,String name)
+ {
+ objectTypes.put(type,name);
+ }
+
+ // This holds the available types
+ private Hashtable objectTypes = new Hashtable();
+
+ // This array contains the types that are supported as standard.
+ //
+ // The first entry is the types name on the database, the second
+ // the full class name of the handling class.
+ //
+ private static final String defaultObjectTypes[][] = {
+ {"box", "postgresql.geometric.PGbox"},
+ {"circle", "postgresql.geometric.PGcircle"},
+ {"line", "postgresql.geometric.PGline"},
+ {"lseg", "postgresql.geometric.PGlseg"},
+ {"path", "postgresql.geometric.PGpath"},
+ {"point", "postgresql.geometric.PGpoint"},
+ {"polygon", "postgresql.geometric.PGpolygon"},
+ {"money", "postgresql.util.PGmoney"}
+ };
+
+ // This initialises the objectTypes hashtable
+ private void initObjectTypes()
+ {
+ for(int i=0;i<defaultObjectTypes.length;i++)
+ objectTypes.put(defaultObjectTypes[i][0],defaultObjectTypes[i][1]);
+ }
+
+ // These are required by other common classes
+ public abstract java.sql.Statement createStatement() throws SQLException;
+
+ /**
+ * This returns a resultset. It must be overridden, so that the correct
+ * version (from jdbc1 or jdbc2) are returned.
+ */
+ protected abstract java.sql.ResultSet getResultSet(org.postgresql.Connection conn, Field[] fields, Vector tuples, String status, int updateCount) throws SQLException;
+
+ public abstract void close() throws SQLException;
+
+ /**
+ * Overides finalize(). If called, it closes the connection.
+ *
+ * This was done at the request of Rachel Greenham
+ * <rachel@enlarion.demon.co.uk> who hit a problem where multiple
+ * clients didn't close the connection, and once a fortnight enough
+ * clients were open to kill the org.postgres server.
+ */
+ public void finalize() throws Throwable
+ {
+ close();
+ }
+
+ /**
+ * This is an attempt to implement SQL Escape clauses
+ */
+ public String EscapeSQL(String sql) {
+ return sql;
+ }
+
+}