Bring in Adrian's JDBC driver as an interface
authorMarc G. Fournier
Sat, 16 Aug 1997 20:51:53 +0000 (20:51 +0000)
committerMarc G. Fournier
Sat, 16 Aug 1997 20:51:53 +0000 (20:51 +0000)
src/interfaces/jdbc/JDBC_Test.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/CallableStatement.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/Connection.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/DatabaseMetaData.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/Driver.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/Field.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/PG_Object.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/PreparedStatement.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/ResultSet.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/ResultSetMetaData.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/Statement.java [new file with mode: 0644]

diff --git a/src/interfaces/jdbc/JDBC_Test.java b/src/interfaces/jdbc/JDBC_Test.java
new file mode 100644 (file)
index 0000000..30cda33
--- /dev/null
@@ -0,0 +1,61 @@
+import java.io.*;
+import java.lang.*;
+import java.sql.*;
+
+class JDBC_Test
+{
+        public JDBC_Test() 
+   {
+   }
+
+   public static void main(String argv[])
+   {
+       String url = new String(argv[0]);
+       Connection db;
+       Statement s;
+       ResultSet rs;
+
+       // Load the driver
+       try
+       {
+           Class.forName("postgresql.Driver");
+       } catch (ClassNotFoundException e) {
+           System.err.println("Exception: " + e.toString());
+       }
+
+       // Lets do a few things -- it doesn't do everything, but
+       // it tests out basic functionality
+       try
+       {
+       System.out.println("Connecting to Database URL = " + url);
+       db = DriverManager.getConnection(url, "adrian", "");
+       System.out.println("Connected...Now creating a statement");
+       s = db.createStatement();
+       System.out.println("Ok...now we will create a table");
+       s.executeUpdate("create table test (a int2, b int2)");
+       System.out.println("Now we will insert some columns");
+       s.executeUpdate("insert into test values (1, 1)");
+       s.executeUpdate("insert into test values (2, 1)");
+       s.executeUpdate("insert into test values (3, 1)");
+       System.out.println("Inserted some data");
+       System.out.println("Now lets try a select");
+       rs = s.executeQuery("select a, b from test");
+       System.out.println("Back from the select...the following are results");
+       int i = 0;
+       while (rs.next())
+       {
+           int a = rs.getInt("a");
+           int b = rs.getInt("b");
+           System.out.println("row " + i + "   " + a + "   " + b);
+           i++;
+       }
+       System.out.println("Ok...dropping the table");
+       s.executeUpdate("drop table test");
+       System.out.println("Now closing the connection");
+       s.close();
+       db.close();
+       } catch (SQLException e) {
+       System.out.println("Exception: " + e.toString());
+       }
+   }
+}
diff --git a/src/interfaces/jdbc/postgresql/CallableStatement.java b/src/interfaces/jdbc/postgresql/CallableStatement.java
new file mode 100644 (file)
index 0000000..ff7ec7c
--- /dev/null
@@ -0,0 +1,126 @@
+package postgresql;
+
+import java.math.*;
+import java.sql.*;
+
+/**
+ * @version 1.0 15-APR-1997
+ * @author Adrian Hall
+ *
+ * CallableStatement is used to execute SQL stored procedures.
+ *
+ * JDBC provides a stored procedure SQL escape that allows stored procedures
+ * to be called in a standard way for all RDBMS's.  This escape syntax has
+ * one form that includes a result parameter and one that does not.  If used,
+ * the result parameter must be generated as an OUT parameter.  The other
+ * parameters may be used for input, output or both.  Parameters are refered
+ * to sequentially, by number.  The first parameter is 1.
+ *
+ * 
+ * {?= call [,, ...]}
+ * {call [,, ...]}
+ * 
+ *
+ * IN parameters are set using the set methods inherited from 
+ * PreparedStatement.  The type of all OUT parameters must be registered
+ * prior to executing the stored procedure; their values are retrieved
+ * after execution via the get methods provided here.
+ *
+ * A CallableStatement may return a ResultSet or multiple ResultSets.  Multiple
+ * ResultSets are handled using operations inherited from Statement.
+ *
+ * For maximum portability, a call's ResultSets and update counts should be
+ * processed prior to getting the values of output parameters.
+ *
+ * @see java.sql.Connection#prepareCall
+ * @see java.sql.ResultSet
+ * @see java.sql.CallableStatement
+ */
+public class CallableStatement implements java.sql.CallableStatement 
+{
+   public void registerOutParameter (int paramterIndex, int sqlType) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public void registerOutParameter (int parameterIndex, int sqlType, int scale) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public boolean wasNull () throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public String getString (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public boolean getBoolean (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public byte getByte (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public short getShort (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public int getInt (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public long getLong (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public float getFloat (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public double getDouble (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public BigDecimal getBigDecimal (int parameterIndex, int scale) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public byte[] getBytes (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public Date getDate (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public Time getTime (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public Timestamp getTimestamp (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public Object getObject (int parameterIndex) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+}
diff --git a/src/interfaces/jdbc/postgresql/Connection.java b/src/interfaces/jdbc/postgresql/Connection.java
new file mode 100644 (file)
index 0000000..aa354b6
--- /dev/null
@@ -0,0 +1,847 @@
+package postgresql;
+
+import java.io.*;
+import java.lang.*;
+import java.net.*;
+import java.util.*;
+import java.sql.*;
+import postgresql.*;
+
+/**
+ * @version 1.0 15-APR-1997
+ * @author Adrian Hall
+ *
+ * A Connection represents a session with a specific database.  Within the
+ * context of a Connection, SQL statements are executed and results are
+ * returned.
+ *
+ * A Connection's database is able to provide information describing
+ * its tables, its supported SQL grammar, its stored procedures, the
+ * capabilities of this connection, etc.  This information is obtained
+ * with the getMetaData method.
+ *
+ * Note: By default, the Connection automatically commits changes
+ * after executing each statement.  If auto-commit has been disabled, an
+ * explicit commit must be done or database changes will not be saved.
+ *
+ * @see java.sql.Connection
+ */
+public class Connection implements java.sql.Connection 
+{
+   private PG_Stream pg_stream;
+
+   private String PG_HOST;
+   private int PG_PORT;
+   private String PG_USER;
+   private String PG_PASSWORD;
+   private String PG_DATABASE;
+   private boolean PG_STATUS;
+
+   public boolean CONNECTION_OK = true;
+   public boolean CONNECTION_BAD = false;
+
+   private int STARTUP_CODE = 7;
+
+   private boolean autoCommit = true;
+   private boolean readOnly = false;
+   
+   private Driver this_driver;
+   private String this_url;
+   private String cursor = null;   // The positioned update cursor name
+
+   /**
+    * Connect to a PostgreSQL database back end.
+    *
+    * @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
+    */
+   public Connection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException
+   {
+       int len = 288;          // Length of a startup packet
+
+       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;
+
+       try
+       {
+           pg_stream = new PG_Stream(host, port);
+       } catch (IOException e) {
+           throw new SQLException ("Connection failed: " + e.toString());
+       }
+       
+       // Now we need to construct and send a startup packet
+       try
+       {
+           pg_stream.SendInteger(len, 4);          len -= 4;
+           pg_stream.SendInteger(STARTUP_CODE, 4);     len -= 4;
+           pg_stream.Send(database.getBytes(), 64);    len -= 64;
+           pg_stream.Send(PG_USER.getBytes(), len);
+       } catch (IOException e) {
+           throw new SQLException("Connection failed: " + e.toString());
+       }
+       ExecSQL(" ");               // Test connection
+       PG_STATUS = CONNECTION_OK;
+   }
+
+   /**
+    * SQL statements without parameters are normally executed using
+    * Statement objects.  If the same SQL statement is executed many
+    * times, it is more efficient to use a PreparedStatement
+    *
+    * @return a new Statement object
+    * @exception SQLException passed through from the constructor
+    */
+   public java.sql.Statement createStatement() throws SQLException
+   {
+       return new Statement(this);
+   }
+
+   /**
+    * A SQL statement with or without IN parameters can be pre-compiled
+    * and stored in a PreparedStatement object.  This object can then
+    * be used to efficiently execute this statement multiple times.
+    *
+    * Note: This method is optimized for handling parametric
+    * SQL statements that benefit from precompilation if the drivers
+    * supports precompilation.  PostgreSQL does not support precompilation.
+    * In this case, the statement is not sent to the database until the
+    * PreparedStatement is executed.  This has no direct effect on users;
+    * however it does affect which method throws certain SQLExceptions
+    *
+    * @param sql a SQL statement that may contain one or more '?' IN
+    *  parameter placeholders
+    * @return a new PreparedStatement object containing the pre-compiled
+    *  statement.
+    * @exception SQLException if a database access error occurs.
+    */
+   public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException
+   {
+       return new PreparedStatement(this, sql);
+   }
+
+   /**
+    * A SQL stored procedure call statement is handled by creating a
+    * CallableStatement for it.  The CallableStatement provides methods
+    * for setting up its IN and OUT parameters and methods for executing
+    * it.
+    *
+    * Note: This method is optimised for handling stored procedure
+    * call statements.  Some drivers may send the call statement to the
+    * database when the prepareCall is done; others may wait until the
+    * CallableStatement is executed.  This has no direct effect on users;
+    * however, it does affect which method throws certain SQLExceptions
+    *
+    * @param sql a SQL statement that may contain one or more '?' parameter
+    *  placeholders.  Typically this statement is a JDBC function call
+    *  escape string.
+    * @return a new CallableStatement object containing the pre-compiled
+    *  SQL statement
+    * @exception SQLException if a database access error occurs
+    */
+   public java.sql.CallableStatement prepareCall(String sql) throws SQLException
+   {
+       throw new SQLException("Callable Statements are not supported at this time");
+//     return new CallableStatement(this, sql);
+   }
+
+   /**
+    * A driver may convert the JDBC sql grammar into its system's
+    * native SQL grammar prior to sending it; nativeSQL returns the
+    * native form of the statement that the driver would have sent.
+    *
+    * @param sql a SQL statement that may contain one or more '?'
+    *  parameter placeholders
+    * @return the native form of this statement
+    * @exception SQLException if a database access error occurs
+    */
+   public String nativeSQL(String sql) throws SQLException
+   {
+       return sql;
+   }
+
+   /**
+    * If a connection is in auto-commit mode, than all its SQL
+    * statements will be executed and committed as individual
+    * transactions.  Otherwise, its SQL statements are grouped
+    * into transactions that are terminated by either commit()
+    * or rollback().  By default, new connections are in auto-
+    * commit mode.  The commit occurs when the statement completes
+    * or the next execute occurs, whichever comes first.  In the
+    * case of statements returning a ResultSet, the statement
+    * completes when the last row of the ResultSet has been retrieved
+    * or the ResultSet has been closed.  In advanced cases, a single
+    * statement may return multiple results as well as output parameter
+    * values.  Here the commit occurs when all results and output param
+    * values have been retrieved.
+    *
+    * @param autoCommit - true enables auto-commit; false disables it
+    * @exception SQLException if a database access error occurs
+    */
+   public void setAutoCommit(boolean autoCommit) throws SQLException
+   {
+       if (this.autoCommit == autoCommit)
+           return;
+       if (autoCommit)
+           ExecSQL("end");
+       else
+           ExecSQL("begin");
+       this.autoCommit = autoCommit;
+   }
+
+   /**
+    * gets the current auto-commit state
+    * 
+    * @return Current state of the auto-commit mode
+    * @exception SQLException (why?)
+    * @see setAutoCommit
+    */
+   public boolean getAutoCommit() throws SQLException
+   {
+       return this.autoCommit;
+   }
+
+   /**
+    * The method commit() makes all changes made since the previous
+    * commit/rollback permanent and releases any database locks currently
+    * held by the Connection.  This method should only be used when
+    * auto-commit has been disabled.  (If autoCommit == true, then we
+    * just return anyhow)
+    *
+    * @exception SQLException if a database access error occurs
+    * @see setAutoCommit
+    */
+   public void commit() throws SQLException
+   {
+       if (autoCommit)
+           return;
+       ExecSQL("commit");
+       autoCommit = true;
+       ExecSQL("begin");
+       autoCommit = false;
+   }
+
+   /**
+    * The method rollback() drops all changes made since the previous
+    * commit/rollback and releases any database locks currently held by
+    * the Connection. 
+    *
+    * @exception SQLException if a database access error occurs
+    * @see commit
+    */
+   public void rollback() throws SQLException
+   {
+       if (autoCommit)
+           return;
+       ExecSQL("rollback");
+       autoCommit = true;
+       ExecSQL("begin");
+       autoCommit = false;
+   }
+
+   /**
+    * In some cases, it is desirable to immediately release a Connection's
+    * database and JDBC resources instead of waiting for them to be
+    * automatically released (cant think why off the top of my head)
+    *
+    * Note: A Connection is automatically closed when it is
+    * garbage collected.  Certain fatal errors also result in a closed
+    * connection.
+    *
+    * @exception SQLException if a database access error occurs
+    */
+   public void close() throws SQLException
+   {
+       if (pg_stream != null)
+       {
+           try
+           {
+               pg_stream.close();
+           } catch (IOException e) {}
+           pg_stream = null;
+       }
+   }
+
+   /**
+    * Tests to see if a Connection is closed
+    *
+    * @return the status of the connection
+    * @exception SQLException (why?)
+    */
+   public boolean isClosed() throws SQLException
+   {
+       return (pg_stream == null);
+   }
+
+   /**
+    * A connection's database is able to provide information describing
+    * its tables, its supported SQL grammar, its stored procedures, the
+    * capabilities of this connection, etc.  This information is made
+    * available through a DatabaseMetaData object.
+    *
+    * @return a DatabaseMetaData object for this connection
+    * @exception SQLException if a database access error occurs
+    */
+   public java.sql.DatabaseMetaData getMetaData() throws SQLException
+   {
+//     return new DatabaseMetaData(this);
+       throw new SQLException("DatabaseMetaData not supported");
+   }
+
+   /**
+    * You can put a connection in read-only mode as a hunt to enable
+    * database optimizations
+    *
+    * Note: setReadOnly cannot be called while in the middle
+    * of a transaction
+    *
+    * @param readOnly - true enables read-only mode; false disables it
+    * @exception SQLException if a database access error occurs
+    */
+   public void setReadOnly (boolean readOnly) throws SQLException
+   {
+       this.readOnly = readOnly;
+   }
+
+   /**
+    * Tests to see if the connection is in Read Only Mode.  Note that
+    * we cannot really put the database in read only mode, but we pretend
+    * we can by returning the value of the readOnly flag
+    *
+    * @return true if the connection is read only
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean isReadOnly() throws SQLException
+   {
+       return readOnly;
+   }
+
+   /**
+    * A sub-space of this Connection's database may be selected by
+    * setting a catalog name.  If the driver does not support catalogs,
+    * it will silently ignore this request
+    *
+    * @exception SQLException if a database access error occurs
+    */
+   public void setCatalog(String catalog) throws SQLException
+   {
+       // No-op
+   }
+
+   /**
+    * Return the connections current catalog name, or null if no
+    * catalog name is set, or we dont support catalogs.
+    *
+    * @return the current catalog name or null
+    * @exception SQLException if a database access error occurs
+    */
+   public String getCatalog() throws SQLException
+   {
+       return null;
+   }
+
+   /**
+    * You can call this method to try to change the transaction
+    * isolation level using one of the TRANSACTION_* values.  
+    *
+    * Note: setTransactionIsolation cannot be called while
+    * in the middle of a transaction
+    *
+    * @param level one of the TRANSACTION_* isolation values with
+    *  the exception of TRANSACTION_NONE; some databases may
+    *  not support other values
+    * @exception SQLException if a database access error occurs
+    * @see java.sql.DatabaseMetaData#supportsTransactionIsolationLevel
+    */
+   public void setTransactionIsolation(int level) throws SQLException
+   {
+       throw new SQLException("Transaction Isolation Levels are not implemented");
+   }
+   
+   /**
+    * Get this Connection's current transaction isolation mode.
+    * 
+    * @return the current TRANSACTION_* mode value
+    * @exception SQLException if a database access error occurs
+    */
+   public int getTransactionIsolation() throws SQLException
+   {
+       return java.sql.Connection.TRANSACTION_SERIALIZABLE;
+   }
+
+   /**
+    * The first warning reported by calls on this Connection is
+    * returned.
+    *
+    * Note: Sebsequent warnings will be changed to this
+    * SQLWarning
+    *
+    * @return the first SQLWarning or null
+    * @exception SQLException if a database access error occurs
+    */
+   public SQLWarning getWarnings() throws SQLException
+   {
+       return null;    // We handle warnings as errors
+   }
+
+   /**
+    * After this call, getWarnings returns null until a new warning
+    * is reported for this connection.
+    *
+    * @exception SQLException if a database access error occurs
+    */
+   public void clearWarnings() throws SQLException
+   {
+       // Not handles since we handle wanrings as errors
+   }
+
+   // **********************************************************
+   //      END OF PUBLIC INTERFACE
+   // **********************************************************
+   
+   /**
+    * Send a query to the backend.  Returns one of the ResultSet
+    * objects.
+    *
+    * Note: 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 synchronized ResultSet ExecSQL(String sql) throws SQLException
+   {
+       Field[] fields = null;
+       Vector tuples = new Vector();
+       byte[] buf = new byte[sql.length()];
+       int fqp = 0;
+       boolean hfr = false;
+       String recv_status = null, msg;
+       SQLException final_error = null;
+
+       if (sql.length() > 8192)
+           throw new SQLException("SQL Statement too long: " + sql);
+       try
+       {
+           pg_stream.SendChar('Q');
+           buf = sql.getBytes();
+           pg_stream.Send(buf);
+           pg_stream.SendChar(0);
+       } catch (IOException e) {
+           throw new SQLException("I/O Error: " + e.toString());
+       }
+
+       while (!hfr || fqp > 0)
+       {
+           int c = pg_stream.ReceiveChar();
+       
+           switch (c)
+           {
+               case 'A':   // Asynchronous Notify
+                   int pid = pg_stream.ReceiveInteger(4);
+                   msg = pg_stream.ReceiveString(8192);
+                   break;
+               case 'B':   // Binary Data Transfer
+                   if (fields == null)
+                       throw new SQLException("Tuple received before MetaData");
+                   tuples.addElement(pg_stream.ReceiveTuple(fields.length, true));
+                   break;
+               case 'C':   // Command Status
+                   recv_status = pg_stream.ReceiveString(8192);
+                   if (fields != null)
+                       hfr = true;
+                   else
+                   {
+                       try
+                       {
+                           pg_stream.SendChar('Q');
+                           pg_stream.SendChar(' ');
+                           pg_stream.SendChar(0);
+                       } catch (IOException e) {
+                           throw new SQLException("I/O Error: " + e.toString());
+                       }
+                       fqp++;
+                   }
+                   break;
+               case 'D':   // Text Data Transfer
+                   if (fields == null)
+                       throw new SQLException("Tuple received before MetaData");
+                   tuples.addElement(pg_stream.ReceiveTuple(fields.length, false));
+                   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 SQLException("Garbled Data");
+                   if (fqp > 0)
+                       fqp--;
+                   if (fqp == 0)
+                       hfr = true;
+                   break;
+               case 'N':   // Error Notification
+                   msg = pg_stream.ReceiveString(4096);
+                   PrintStream log = DriverManager.getLogStream();
+                   log.println(msg);
+                   break;
+               case 'P':   // Portal Name
+                   String pname = pg_stream.ReceiveString(8192);
+                   break;
+               case 'T':   // MetaData Field Description
+                   if (fields != null)
+                       throw new SQLException("Cannot handle multiple result groups");
+                   fields = ReceiveFields();
+                   break;
+               default:
+                   throw new SQLException("Unknown Response Type: " + (char)c);
+           }
+       }
+       if (final_error != null)
+           throw final_error;
+       return new ResultSet(this, fields, tuples, recv_status, 1);
+   }
+
+   /**
+    * 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.ReceiveInteger(2), i;
+       Field[] fields = new Field[nf];
+       
+       for (i = 0 ; i < nf ; ++i)
+       {
+           String typname = pg_stream.ReceiveString(8192);
+           int typid = pg_stream.ReceiveInteger(4);
+           int typlen = pg_stream.ReceiveInteger(2);
+           fields[i] = new Field(this, typname, typid, typlen);
+       }
+       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 class handles all the Streamed I/O for a postgresql connection
+class PG_Stream
+{
+   private Socket connection;
+   private InputStream pg_input;
+   private OutputStream pg_output;
+
+   /**
+    * Constructor:  Connect to the PostgreSQL back end and return
+    * a stream connection.
+    *
+    * @param host the hostname to connect to
+    * @param port the port number that the postmaster is sitting on
+    * @exception IOException if an IOException occurs below it.
+    */
+   public PG_Stream(String host, int port) throws IOException
+   {
+       connection = new Socket(host, port);
+       pg_input = connection.getInputStream();
+       pg_output = connection.getOutputStream();   
+   }
+
+   /**
+    * Sends a single character to the back end
+    *
+    * @param val the character to be sent
+    * @exception IOException if an I/O error occurs
+    */
+   public void SendChar(int val) throws IOException
+   {
+       pg_output.write(val);
+   }
+
+   /**
+    * 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 SendInteger(int val, int siz) throws IOException
+   {
+       byte[] buf = new byte[siz];
+
+       while (siz-- > 0)
+       {
+           buf[siz] = (byte)(val & 0xff);
+           val >>= 8;
+       }
+       Send(buf);
+   }
+
+   /**
+    * Send an array of bytes to the backend
+    *
+    * @param buf The array of bytes to be sent
+    * @exception IOException if an I/O error occurs
+    */
+   public void Send(byte buf[]) throws IOException
+   {
+       pg_output.write(buf);
+   }
+
+   /**
+    * Send an exact array of bytes to the backend - if the length
+    * has not been reached, send nulls until it has.
+    *
+    * @param buf the array of bytes to be sent
+    * @param siz the number of bytes to be sent
+    * @exception IOException if an I/O error occurs
+    */
+   public void Send(byte buf[], int siz) throws IOException
+   {
+       int i;
+
+       pg_output.write(buf, 0, (buf.length < siz ? buf.length : siz));
+       if (buf.length < siz)
+       {
+           for (i = buf.length ; i < siz ; ++i)
+           {
+               pg_output.write(0);
+           }
+       }
+   }
+
+   /**
+    * Receives a single character from the backend
+    *
+    * @return the character received
+    * @exception SQLException if an I/O Error returns
+    */
+   public int ReceiveChar() throws SQLException
+   {
+       int c = 0;
+   
+       try
+       {
+           c = pg_input.read();
+           if (c < 0) throw new IOException("EOF");
+       } catch (IOException e) {
+           throw new SQLException("Error reading from backend: " + e.toString());
+       }
+       return c;
+   }
+
+   /**
+    * Receives an integer from the backend
+    *
+    * @param siz length of the integer in bytes
+    * @return the integer received from the backend
+    * @exception SQLException if an I/O error occurs
+    */
+   public int ReceiveInteger(int siz) throws SQLException
+   {
+       int n = 0;
+       
+       try
+       {
+           for (int i = 0 ; i < siz ; i++)
+           {
+               int b = pg_input.read();
+           
+               if (b < 0)
+                   throw new IOException("EOF");
+               n = n | (b >> (8 * i)) ;
+           }
+       } catch (IOException e) {
+           throw new SQLException("Error reading from backend: " + e.toString());
+       }
+       return n;
+   }
+
+   /**
+    * Receives a null-terminated string from the backend.  Maximum of
+    * maxsiz bytes - if we don't see a null, then we assume something
+    * has gone wrong.
+    *
+    * @param maxsiz maximum length of string
+    * @return string from back end
+    * @exception SQLException if an I/O error occurs
+    */
+   public String ReceiveString(int maxsiz) throws SQLException
+   {
+       byte[] rst = new byte[maxsiz];
+       int s = 0;
+
+       try
+       {
+           while (s < maxsiz)
+           {
+               int c = pg_input.read();
+               if (c < 0)
+                   throw new IOException("EOF");
+               else if (c == 0)
+                   break;
+               else
+                   rst[s++] = (byte)c;
+           }
+           if (s >= maxsiz)
+               throw new IOException("Too Much Data");
+       } catch (IOException e) {
+           throw new SQLException("Error reading from backend: " + e.toString());
+       }
+       String v = new String(rst, 0, s);
+       return v;
+   }
+
+   /**
+    * 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[][] ReceiveTuple(int nf, boolean bin) throws SQLException
+   {
+       int i, bim = (nf + 7)/8;
+       byte[] bitmask = Receive(bim);
+       byte[][] answer = new byte[nf][0];
+
+       int whichbit = 0x80;
+       int whichbyte = 0;
+       
+       for (i = 0 ; i < nf ; ++i)
+       {
+           boolean isNull = ((bitmask[whichbyte] & whichbit) == 0);
+           whichbit >>= 1;
+           if (whichbit == 0)
+           {
+               ++whichbyte;
+               whichbit = 0x80;
+           }
+           if (isNull) 
+               answer[i] = null;
+           else
+           {
+               int len = ReceiveInteger(4);
+               if (!bin) 
+                   len -= 4;
+               if (len < 0) 
+                   len = 0;
+               answer[i] = Receive(len);
+           }
+       }
+       return answer;
+   }
+
+   /**
+    * Reads in a given number of bytes from the backend
+    *
+    * @param siz number of bytes to read
+    * @return array of bytes received
+    * @exception SQLException if a data I/O error occurs
+    */
+   private byte[] Receive(int siz) throws SQLException
+   {
+       byte[] answer = new byte[siz];
+       int s = 0;
+
+       try 
+       {
+           while (s < siz)
+           {
+               int w = pg_input.read(answer, s, siz - s);
+               if (w < 0)
+                   throw new IOException("EOF");
+               s += w;
+           }
+       } catch (IOException e) {
+           throw new SQLException("Error reading from backend: " + e.toString());
+       }
+       return answer;
+   }
+   
+   /**
+    * Closes the connection
+    *
+    * @exception IOException if a IO Error occurs
+    */
+   public void close() throws IOException
+   {
+       pg_output.close();
+       pg_input.close();
+       connection.close();
+   }
+}
diff --git a/src/interfaces/jdbc/postgresql/DatabaseMetaData.java b/src/interfaces/jdbc/postgresql/DatabaseMetaData.java
new file mode 100644 (file)
index 0000000..259829c
--- /dev/null
@@ -0,0 +1,1556 @@
+package postgresql;
+
+import java.sql.*;
+
+/**
+ * @version 1.0 15-APR-1997
+ * @author Adrian Hall
+ *
+ * This class provides information about the database as a whole.
+ *
+ * Many of the methods here return lists of information in ResultSets.  You
+ * can use the normal ResultSet methods such as getString and getInt to 
+ * retrieve the data from these ResultSets.  If a given form of metadata is
+ * not available, these methods should throw a SQLException.
+ *
+ * Some of these methods take arguments that are String patterns.  These
+ * arguments all have names such as fooPattern.  Within a pattern String,
+ * "%" means match any substring of 0 or more characters, and "_" means
+ * match any one character.  Only metadata entries matching the search
+ * pattern are returned.  if a search pattern argument is set to a null
+ * ref, it means that argument's criteria should be dropped from the
+ * search.
+ *
+ * A SQLException will be throws if a driver does not support a meta
+ * data method.  In the case of methods that return a ResultSet, either
+ * a ResultSet (which may be empty) is returned or a SQLException is
+ * thrown.
+ *
+ * @see java.sql.DatabaseMetaData
+ */
+public class DatabaseMetaData implements java.sql.DatabaseMetaData 
+{
+   Connection connection;      // The connection association
+
+   public DatabaseMetaData(Connection conn)
+   {
+       this.connection = conn;
+   }
+
+   /**
+    * Can all the procedures returned by getProcedures be called
+    * by the current user?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean allProceduresAreCallable() throws SQLException
+   {
+       return true;        // For now...
+   }
+
+   /**
+    * Can all the tables returned by getTable be SELECTed by
+    * the current user?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean allTablesAreSelectable() throws SQLException
+   {
+       return true;        // For now...
+   }
+
+   /**
+    * What is the URL for this database?
+    *
+    * @return the url or null if it cannott be generated
+    * @exception SQLException if a database access error occurs
+    */
+   public String getURL() throws SQLException
+   {
+       return connection.getURL();
+   }
+
+   /**
+    * What is our user name as known to the database?
+    *
+    * @return our database user name
+    * @exception SQLException if a database access error occurs
+    */
+   public String getUserName() throws SQLException
+   {
+       return connection.getUserName();
+   }
+
+   /**
+    * Is the database in read-only mode?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean isReadOnly() throws SQLException
+   {
+       return connection.isReadOnly();
+   }
+
+   /**
+    * Are NULL values sorted high?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean nullsAreSortedHigh() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Are NULL values sorted low?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean nullsAreSortedLow() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Are NULL values sorted at the start regardless of sort order?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean nullsAreSortedAtStart() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Are NULL values sorted at the end regardless of sort order?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean nullsAreSortedAtEnd() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * What is the name of this database product - we hope that it is
+    * PostgreSQL, so we return that explicitly.
+    *
+    * @return the database product name
+    * @exception SQLException if a database access error occurs
+    */
+   public String getDatabaseProductName() throws SQLException
+   {
+       return new String("PostgreSQL");
+   }
+
+   /**
+    * What is the version of this database product.  Note that
+    * PostgreSQL 6.1 has a system catalog called pg_version - 
+    * however, select * from pg_version on any database retrieves
+    * no rows.  For now, we will return the version 6.1 (in the
+    * hopes that we change this driver as often as we change the
+    * database)
+    *
+    * @return the database version
+    * @exception SQLException if a database access error occurs
+    */
+   public String getDatabaseProductVersion() throws SQLException
+   {
+       return ("6.1");
+   }
+
+   /**
+    * What is the name of this JDBC driver?  If we don't know this
+    * we are doing something wrong!
+    *
+    * @return the JDBC driver name
+    * @exception SQLException why?
+    */
+   public String getDriverName() throws SQLException
+   {
+       return new String("PostgreSQL Native Driver");
+   }
+
+   /**
+    * What is the version string of this JDBC driver?  Again, this is
+    * static.
+    *
+    * @return the JDBC driver name.
+    * @exception SQLException why?
+    */
+   public String getDriverVersion() throws SQLException
+   {
+       return new String("1.0");
+   }
+
+   /**
+    * What is this JDBC driver's major version number?
+    *
+    * @return the JDBC driver major version
+    */
+   public int getDriverMajorVersion()
+   {
+       return 1;
+   }
+
+   /**
+    * What is this JDBC driver's minor version number?
+    *
+    * @return the JDBC driver minor version
+    */
+   public int getDriverMinorVersion()
+   {
+       return 0;
+   }
+
+   /**
+    * Does the database store tables in a local file?  No - it
+    * stores them in a file on the server.
+    * 
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean usesLocalFiles() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Does the database use a file for each table?  Well, not really,
+    * since it doesnt use local files. 
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean usesLocalFilePerTable() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Does the database treat mixed case unquoted SQL identifiers
+    * as case sensitive and as a result store them in mixed case?
+    * A JDBC-Compliant driver will always return false.
+    *
+    * Predicament - what do they mean by "SQL identifiers" - if it
+    * means the names of the tables and columns, then the answers
+    * given below are correct - otherwise I don't know.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsMixedCaseIdentifiers() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Does the database treat mixed case unquoted SQL identifiers as
+    * case insensitive and store them in upper case?
+    *
+    * @return true if so
+    */
+   public boolean storesUpperCaseIdentifiers() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Does the database treat mixed case unquoted SQL identifiers as
+    * case insensitive and store them in lower case?
+    *
+    * @return true if so
+    */
+   public boolean storesLowerCaseIdentifiers() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Does the database treat mixed case unquoted SQL identifiers as
+    * case insensitive and store them in mixed case?
+    *
+    * @return true if so
+    */
+   public boolean storesMixedCaseIdentifiers() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Does the database treat mixed case quoted SQL identifiers as
+    * case sensitive and as a result store them in mixed case?  A
+    * JDBC compliant driver will always return true. 
+    *
+    * Predicament - what do they mean by "SQL identifiers" - if it
+    * means the names of the tables and columns, then the answers
+    * given below are correct - otherwise I don't know.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Does the database treat mixed case quoted SQL identifiers as
+    * case insensitive and store them in upper case?
+    *
+    * @return true if so
+    */
+   public boolean storesUpperCaseQuotedIdentifiers() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Does the database treat mixed case quoted SQL identifiers as case
+    * insensitive and store them in lower case?
+    *
+    * @return true if so
+    */
+   public boolean storesLowerCaseQuotedIdentifiers() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Does the database treat mixed case quoted SQL identifiers as case
+    * insensitive and store them in mixed case?
+    *
+    * @return true if so
+    */
+   public boolean storesMixedCaseQuotedIdentifiers() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * What is the string used to quote SQL identifiers?  This returns
+    * a space if identifier quoting isn't supported.  A JDBC Compliant
+    * driver will always use a double quote character.
+    *
+    * If an SQL identifier is a table name, column name, etc. then
+    * we do not support it.
+    *
+    * @return the quoting string
+    * @exception SQLException if a database access error occurs
+    */
+   public String getIdentifierQuoteString() throws SQLException
+   {
+       return new String(" ");
+   }
+
+   /**
+    * Get a comma separated list of all a database's SQL keywords that
+    * are NOT also SQL92 keywords.
+    *
+    * Within PostgreSQL, the keywords are found in
+    *  src/backend/parser/keywords.c
+    * For SQL Keywords, I took the list provided at
+    *  http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt
+    * which is for SQL3, not SQL-92, but it is close enough for
+    * this purpose.
+    *
+    * @return a comma separated list of keywords we use
+    * @exception SQLException if a database access error occurs
+    */
+   public String getSQLKeywords() throws SQLException
+   {
+       return new String("abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version");
+   }
+
+   public String getNumericFunctions() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public String getStringFunctions() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public String getSystemFunctions() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public String getTimeDateFunctions() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   /**
+    * This is the string that can be used to escape '_' and '%' in
+    * a search string pattern style catalog search parameters
+    *
+    * @return the string used to escape wildcard characters
+    * @exception SQLException if a database access error occurs
+    */
+   public String getSearchStringEscape() throws SQLException
+   {
+       return new String("\\");
+   }
+
+   /**
+    * Get all the "extra" characters that can bew used in unquoted
+    * identifier names (those beyond a-zA-Z0-9 and _)
+    *
+    * From the file src/backend/parser/scan.l, an identifier is
+    * {letter}{letter_or_digit} which makes it just those listed
+    * above.
+    *
+    * @return a string containing the extra characters
+    * @exception SQLException if a database access error occurs
+    */
+   public String getExtraNameCharacters() throws SQLException
+   {
+       return new String("");
+   }
+
+   /**
+    * Is "ALTER TABLE" with an add column supported?
+    * Yes for PostgreSQL 6.1
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsAlterTableWithAddColumn() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Is "ALTER TABLE" with a drop column supported?
+    * Yes for PostgreSQL 6.1
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsAlterTableWithDropColumn() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Is column aliasing supported?
+    *
+    * If so, the SQL AS clause can be used to provide names for
+    * computed columns or to provide alias names for columns as
+    * required.  A JDBC Compliant driver always returns true.
+    *
+    * e.g.
+    *
+    * select count(C) as C_COUNT from T group by C;
+    *
+    * should return a column named as C_COUNT instead of count(C)
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsColumnAliasing() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Are concatenations between NULL and non-NULL values NULL?  A
+    * JDBC Compliant driver always returns true
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean nullPlusNonNullIsNull() throws SQLException
+   {
+       return true;
+   }
+
+   public boolean supportsConvert() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public boolean supportsConvert(int fromType, int toType) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public boolean supportsTableCorrelationNames() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public boolean supportsDifferentTableCorrelationNames() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   /**
+    * Are expressions in "ORCER BY" lists supported?
+    * 
+    * e.g. select * from t order by a + b;
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsExpressionsInOrderBy() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Can an "ORDER BY" clause use columns not in the SELECT?
+    * I checked it, and you can't.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsOrderByUnrelated() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Is some form of "GROUP BY" clause supported?
+    * I checked it, and yes it is.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsGroupBy() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Can a "GROUP BY" clause use columns not in the SELECT?
+    * I checked it - it seems to allow it
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsGroupByUnrelated() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Can a "GROUP BY" clause add columns not in the SELECT provided
+    * it specifies all the columns in the SELECT?  Does anyone actually
+    * understand what they mean here?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsGroupByBeyondSelect() throws SQLException
+   {
+       return true;        // For now...
+   }
+
+   /**
+    * Is the escape character in "LIKE" clauses supported?  A
+    * JDBC compliant driver always returns true.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsLikeEscapeClause() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Are multiple ResultSets from a single execute supported?
+    * Well, I implemented it, but I dont think this is possible from
+    * the back ends point of view.
+    * 
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsMultipleResultSets() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Can we have multiple transactions open at once (on different
+    * connections?)
+    * I guess we can have, since Im relying on it.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsMultipleTransactions() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Can columns be defined as non-nullable.  A JDBC Compliant driver
+    * always returns true.  We dont support NOT NULL, so we are not
+    * JDBC compliant.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsNonNullableColumns() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Does this driver support the minimum ODBC SQL grammar.  This
+    * grammar is defined at:
+    *
+    * http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm
+    *
+    * In Appendix C.  From this description, we seem to support the
+    * ODBC minimal (Level 0) grammar.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsMinimumSQLGrammar() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Does this driver support the Core ODBC SQL grammar.  We need
+    * SQL-92 conformance for this.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsCoreSQLGrammar() throws SQLException
+   {
+       return false;
+   }
+   
+   /**
+    * Does this driver support the Extended (Level 2) ODBC SQL
+    * grammar.  We don't conform to the Core (Level 1), so we can't
+    * conform to the Extended SQL Grammar.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsExtendedSQLGrammar() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Does this driver support the ANSI-92 entry level SQL grammar?
+    * All JDBC Compliant drivers must return true.  I think we have
+    * to support outer joins for this to be true.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsANSI92EntryLevelSQL() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Does this driver support the ANSI-92 intermediate level SQL
+    * grammar?  Anyone who does not support Entry level cannot support
+    * Intermediate level.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsANSI92IntermediateSQL() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Does this driver support the ANSI-92 full SQL grammar?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsANSI92FullSQL() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Is the SQL Integrity Enhancement Facility supported?
+    * I haven't seen this mentioned anywhere, so I guess not
+    * 
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsIntegrityEnhancementFacility() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Is some form of outer join supported?  From my knowledge, nope.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsOuterJoins() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Are full nexted outer joins supported?  Well, we dont support any
+    * form of outer join, so this is no as well
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsFullOuterJoins() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Is there limited support for outer joins?  (This will be true if
+    * supportFullOuterJoins is true)
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsLimitedOuterJoins() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * What is the database vendor's preferred term for "schema" - well,
+    * we do not provide support for schemas, so lets just use that
+    * term.
+    *
+    * @return the vendor term
+    * @exception SQLException if a database access error occurs
+    */
+   public String getSchemaTerm() throws SQLException
+   {
+       return new String("Schema");
+   }
+
+   /**
+    * What is the database vendor's preferred term for "procedure" - 
+    * I kind of like "Procedure" myself.
+    *
+    * @return the vendor term
+    * @exception SQLException if a database access error occurs
+    */
+   public String getProcedureTerm() throws SQLException
+   {
+       return new String("Procedure");
+   }
+
+   /**
+    * What is the database vendor's preferred term for "catalog"? -
+    * we dont have a preferred term, so just use Catalog
+    *
+    * @return the vendor term
+    * @exception SQLException if a database access error occurs
+    */
+   public String getCatalogTerm() throws SQLException
+   {
+       return new String("Catalog");
+   }
+
+   /**
+    * Does a catalog appear at the start of a qualified table name?
+    * (Otherwise it appears at the end).
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean isCatalogAtStart() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * What is the Catalog separator.  Hmmm....well, I kind of like
+    * a period (so we get catalog.table definitions). - I don't think
+    * PostgreSQL supports catalogs anyhow, so it makes no difference.
+    *
+    * @return the catalog separator string
+    * @exception SQLException if a database access error occurs
+    */
+   public String getCatalogSeparator() throws SQLException
+   {
+       return new String(".");
+   }
+
+   /**
+    * Can a schema name be used in a data manipulation statement?  Nope.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsSchemasInDataManipulation() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Can a schema name be used in a procedure call statement?  Nope.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsSchemasInProcedureCalls() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Can a schema be used in a table definition statement?  Nope.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsSchemasInTableDefinitions() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Can a schema name be used in an index definition statement?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsSchemasInIndexDefinitions() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Can a schema name be used in a privilege definition statement?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Can a catalog name be used in a data manipulation statement?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsCatalogsInDataManipulation() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Can a catalog name be used in a procedure call statement?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsCatalogsInProcedureCalls() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Can a catalog name be used in a table definition statement?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsCatalogsInTableDefinitions() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Can a catalog name be used in an index definition?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsCatalogsInIndexDefinitions() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Can a catalog name be used in a privilege definition statement?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * We support cursors for gets only it seems.  I dont see a method
+    * to get a positioned delete.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsPositionedDelete() throws SQLException
+   {
+       return false;           // For now...
+   }
+
+   /**
+    * Is positioned UPDATE supported?
+    * 
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsPositionedUpdate() throws SQLException
+   {
+       return false;           // For now...
+   }
+
+   public boolean supportsSelectForUpdate() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public boolean supportsStoredProcedures() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public boolean supportsSubqueriesInComparisons() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public boolean supportsSubqueriesInExists() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public boolean supportsSubqueriesInIns() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public boolean supportsSubqueriesInQuantifieds() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public boolean supportsCorrelatedSubqueries() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   /**
+    * Is SQL UNION supported?  Nope.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsUnion() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Is SQL UNION ALL supported?  Nope.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsUnionAll() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * In PostgreSQL, Cursors are only open within transactions.
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsOpenCursorsAcrossCommit() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Do we support open cursors across multiple transactions?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsOpenCursorsAcrossRollback() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Can statements remain open across commits?  They may, but
+    * this driver cannot guarentee that.  In further reflection.
+    * we are talking a Statement object jere, so the answer is
+    * yes, since the Statement is only a vehicle to ExecSQL()
+    *
+    * @return true if they always remain open; false otherwise
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsOpenStatementsAcrossCommit() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Can statements remain open across rollbacks?  They may, but
+    * this driver cannot guarentee that.  In further contemplation,
+    * we are talking a Statement object here, so the answer is yes,
+    * since the Statement is only a vehicle to ExecSQL() in Connection
+    *
+    * @return true if they always remain open; false otherwise
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsOpenStatementsAcrossRollback() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * How many hex characters can you have in an inline binary literal
+    *
+    * @return the max literal length
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxBinaryLiteralLength() throws SQLException
+   {
+       return 0;               // For now...
+   }
+
+   /**
+    * What is the maximum length for a character literal
+    * I suppose it is 8190 (8192 - 2 for the quotes)
+    *
+    * @return the max literal length
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxCharLiteralLength() throws SQLException
+   {
+       return 8190;
+   }
+
+   /**
+    * Whats the limit on column name length.  The description of
+    * pg_class would say '32' (length of pg_class.relname) - we
+    * should probably do a query for this....but....
+    *
+    * @return the maximum column name length
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxColumnNameLength() throws SQLException
+   {
+       return 32;
+   }
+
+   /**
+    * What is the maximum number of columns in a "GROUP BY" clause?
+    *
+    * @return the max number of columns
+    * @exception SQLException if a database access error occurs    
+    */
+   public int getMaxColumnsInGroupBy() throws SQLException
+   {
+       return getMaxColumnsInTable();
+   }
+
+   /**
+    * What's the maximum number of columns allowed in an index?
+    * 6.0 only allowed one column, but 6.1 introduced multi-column
+    * indices, so, theoretically, its all of them.
+    *
+    * @return max number of columns
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxColumnsInIndex() throws SQLException
+   {
+       return getMaxColumnsInTable();
+   }
+
+   /**
+    * What's the maximum number of columns in an "ORDER BY clause?
+    * Theoretically, all of them!
+    *
+    * @return the max columns
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxColumnsInOrderBy() throws SQLException
+   {
+       return getMaxColumnsInTable();
+   }
+
+   /**
+    * What is the maximum number of columns in a "SELECT" list?
+    * Theoretically, all of them!
+    *
+    * @return the max columns
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxColumnsInSelect() throws SQLException
+   {
+       return getMaxColumnsInTable();
+   }
+
+   /**
+    * What is the maximum number of columns in a table? From the
+    * create_table(l) manual page...
+    *
+    * "The new class is created as a heap with no initial data.  A
+    * class can have no more than 1600 attributes (realistically,
+    * this is limited by the fact that tuple sizes must be less than
+    * 8192 bytes)..."
+    *
+    * @return the max columns
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxColumnsInTable() throws SQLException
+   {
+       return 1600;
+   }
+
+   /**
+    * How many active connection can we have at a time to this
+    * database?  Well, since it depends on postmaster, which just
+    * does a listen() followed by an accept() and fork(), its
+    * basically very high.  Unless the system runs out of processes,
+    * it can be 65535 (the number of aux. ports on a TCP/IP system).
+    * I will return 8192 since that is what even the largest system
+    * can realistically handle,
+    *
+    * @return the maximum number of connections
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxConnections() throws SQLException
+   {
+       return 8192;
+   }
+
+   /**
+    * What is the maximum cursor name length (the same as all
+    * the other F***** identifiers!)
+    *
+    * @return max cursor name length in bytes
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxCursorNameLength() throws SQLException
+   {
+       return 32;
+   }
+
+   /**
+    * What is the maximum length of an index (in bytes)?  Now, does
+    * the spec. mean name of an index (in which case its 32, the 
+    * same as a table) or does it mean length of an index element
+    * (in which case its 8192, the size of a row) or does it mean
+    * the number of rows it can access (in which case it 2^32 - 
+    * a 4 byte OID number)?  I think its the length of an index
+    * element, personally, so Im setting it to 8192.
+    *
+    * @return max index length in bytes
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxIndexLength() throws SQLException
+   {
+       return 8192;
+   }
+
+   public int getMaxSchemaNameLength() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   /**
+    * What is the maximum length of a procedure name?
+    * (length of pg_proc.proname used) - again, I really
+    * should do a query here to get it.
+    *
+    * @return the max name length in bytes
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxProcedureNameLength() throws SQLException
+   {
+       return 32;
+   }
+
+   public int getMaxCatalogNameLength() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   /**
+    * What is the maximum length of a single row?  (not including
+    * blobs).  8192 is defined in PostgreSQL.
+    *
+    * @return max row size in bytes
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxRowSize() throws SQLException
+   {
+       return 8192;
+   }
+
+   /**
+    * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY
+    * blobs?  We don't handle blobs yet
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean doesMaxRowSizeIncludeBlobs() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * What is the maximum length of a SQL statement?
+    *
+    * @return max length in bytes
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxStatementLength() throws SQLException
+   {
+       return 8192;
+   }
+
+   /**
+    * How many active statements can we have open at one time to
+    * this database?  Basically, since each Statement downloads
+    * the results as the query is executed, we can have many.  However,
+    * we can only really have one statement per connection going
+    * at once (since they are executed serially) - so we return
+    * one.
+    *
+    * @return the maximum
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxStatements() throws SQLException
+   {
+       return 1;
+   }
+
+   /**
+    * What is the maximum length of a table name?  This was found
+    * from pg_class.relname length
+    *
+    * @return max name length in bytes
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxTableNameLength() throws SQLException
+   {
+       return 32;
+   }
+
+   /**
+    * What is the maximum number of tables that can be specified
+    * in a SELECT?  Theoretically, this is the same number as the
+    * number of tables allowable.  In practice tho, it is much smaller
+    * since the number of tables is limited by the statement, we
+    * return 1024 here - this is just a number I came up with (being
+    * the number of tables roughly of three characters each that you
+    * can fit inside a 8192 character buffer with comma separators).
+    *
+    * @return the maximum
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxTablesInSelect() throws SQLException
+   {
+       return 1024;
+   }
+
+   /**
+    * What is the maximum length of a user name?  Well, we generally
+    * use UNIX like user names in PostgreSQL, so I think this would
+    * be 8.  However, showing the schema for pg_user shows a length
+    * for username of 32.
+    *
+    * @return the max name length in bytes
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxUserNameLength() throws SQLException
+   {
+       return 32;
+   }
+
+   
+   /**
+    * What is the database's default transaction isolation level?  We
+    * do not support this, so all transactions are SERIALIZABLE.
+    *
+    * @return the default isolation level
+    * @exception SQLException if a database access error occurs
+    * @see Connection
+    */
+   public int getDefaultTransactionIsolation() throws SQLException
+   {
+       return Connection.TRANSACTION_SERIALIZABLE;
+   }
+
+   /**
+    * Are transactions supported?  If not, commit and rollback are noops
+    * and the isolation level is TRANSACTION_NONE.  We do support
+    * transactions.    
+    *
+    * @return true if transactions are supported
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsTransactions() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Does the database support the given transaction isolation level?
+    * We only support TRANSACTION_SERIALIZABLE
+    * 
+    * @param level the values are defined in java.sql.Connection
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    * @see Connection
+    */
+   public boolean supportsTransactionIsolationLevel(int level) throws SQLException
+   {
+       if (level == Connection.TRANSACTION_SERIALIZABLE)
+           return true;
+       else
+           return false;
+   }
+
+   /**
+    * Are both data definition and data manipulation transactions 
+    * supported?  I checked it, and could not do a CREATE TABLE
+    * within a transaction, so I am assuming that we don't
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Are only data manipulation statements withing a transaction
+    * supported?
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean supportsDataManipulationTransactionsOnly() throws SQLException
+   {
+       return true;
+   }
+
+   /**
+    * Does a data definition statement within a transaction force
+    * the transaction to commit?  I think this means something like:
+    *
+    * CREATE TABLE T (A INT);
+    * INSERT INTO T (A) VALUES (2);
+    * BEGIN;
+    * UPDATE T SET A = A + 1;
+    * CREATE TABLE X (A INT);
+    * SELECT A FROM T INTO X;
+    * COMMIT;
+    *
+    * does the CREATE TABLE call cause a commit?  The answer is no.  
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean dataDefinitionCausesTransactionCommit() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    *  Is a data definition statement within a transaction ignored?
+    * It seems to be (from experiment in previous method)
+    *
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean dataDefinitionIgnoredInTransactions() throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Get a description of stored procedures available in a catalog
+    * 
+    * Only procedure descriptions matching the schema and procedure
+    * name criteria are returned.  They are ordered by PROCEDURE_SCHEM
+    * and PROCEDURE_NAME
+    *
+    * Each procedure description has the following columns:
+    * PROCEDURE_CAT String => procedure catalog (may be null)
+    * PROCEDURE_SCHEM String => procedure schema (may be null)
+    * PROCEDURE_NAME String => procedure name
+    * Field 4 reserved (make it null)
+    * Field 5 reserved (make it null)
+    * Field 6 reserved (make it null)
+    * REMARKS String => explanatory comment on the procedure
+    * PROCEDURE_TYPE short => kind of procedure
+    *  * procedureResultUnknown - May return a result
+    *  * procedureNoResult - Does not return a result
+    *  * procedureReturnsResult - Returns a result
+    *
+    * @param catalog - a catalog name; "" retrieves those without a
+    *  catalog; null means drop catalog name from criteria
+    * @param schemaParrern - a schema name pattern; "" retrieves those
+    *  without a schema - we ignore this parameter
+    * @param procedureNamePattern - a procedure name pattern
+    * @return ResultSet - each row is a procedure description
+    * @exception SQLException if a database access error occurs
+    */
+   public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException
+   {
+       Field[] f = new Field[8];       // the field descriptors for the new ResultSet
+       static final int iVarcharOid = 1043;    // This is the OID for a varchar()
+       static final int iInt2Oid = 21;     // This is the OID for an int2
+       ResultSet r;                // ResultSet for the SQL query that we need to do
+       Vector v;               // The new ResultSet tuple stuff
+       String remarks = new String("no remarks");
+
+       Field[0] = new Field(conn, new String("PROCEDURE_CAT"), iVarcharOid, 32);
+       Field[1] = new Field(conn, new String("PROCEDURE_SCHEM"), iVarcharOid, 32);
+       Field[2] = new Field(conn, new String("PROCEDURE_NAME"), iVarcharOid, 32);
+       Field[3] = null;
+       Field[4] = null;
+       Field[5] = null;
+       Field[6] = new Field(conn, new String("REMARKS"), iVarcharOid, 8192);
+       Field[7] = new Field(conn, new String("PROCEDURE_TYPE"), iInt2Oid, 2);
+       r = conn.ExecSQL("select proname, proretset from pg_proc order by proname");
+       if (r.getColumnCount() != 2 || r.getTupleCount() <= 1)
+           throw new SQLException("Unexpected return from query for procedure list");
+       while (r.next())
+       {
+           byte[][] tuple = new byte[8][0];
+
+           String name = r.getString(1);
+           String remarks = new String("no remarks");
+           boolean retset = r.getBoolean(2);
+   
+           byte[0] = null;         // Catalog name
+           byte[1] = null;         // Schema name
+           byte[2] = name.getBytes();  // Procedure name
+           byte[3] = null;         // Reserved
+           byte[4] = null;         // Reserved
+           byte[5] = null;         // Reserved
+           byte[6] = remarks.getBytes();   // Remarks
+           if (retset)
+               byte[7] = procedureReturnsResult;
+           else
+               byte[7] = procedureNoResult;
+           v.addElement(byte);
+       }           
+       return new ResultSet(conn, f, v, "OK", 1);
+   }
+
+   public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getSchemas() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getCatalogs() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getTableTypes() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getTypeInfo() throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+
+   public java.sql.ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException
+   {
+       // XXX-Not Implemented
+   }
+}
diff --git a/src/interfaces/jdbc/postgresql/Driver.java b/src/interfaces/jdbc/postgresql/Driver.java
new file mode 100644 (file)
index 0000000..055a656
--- /dev/null
@@ -0,0 +1,269 @@
+package postgresql;
+
+import java.sql.*;
+import java.util.*;
+import postgresql.*;
+
+/**
+ * @version 1.0 15-APR-1997
+ * @author Adrian Hall
+ *
+ * The Java SQL framework allows for multiple database drivers.  Each
+ * driver should supply a class that implements the Driver interface
+ *
+ * The DriverManager will try to load as many drivers as it can find and then
+ * for any given connection request, it will ask each driver in turn to try
+ * to connect to the target URL.
+ *
+ * It is strongly recommended that each Driver class should be small and
+ * standalone so that the Driver class can be loaded and queried without
+ * bringing in vast quantities of supporting code.
+ *
+ * When a Driver class is loaded, it should create an instance of itself and
+ * register it with the DriverManager.  This means that a user can load and
+ * register a driver by doing Class.forName("foo.bah.Driver")
+ *
+ * @see postgresql.Connection
+ * @see java.sql.Driver
+ */
+public class Driver implements java.sql.Driver 
+{
+
+   static 
+   {
+       try
+       {
+           new Driver();
+       } catch (SQLException e) {
+           e.printStackTrace();
+       }
+   }
+
+   /**
+     * Construct a new driver and register it with DriverManager
+     *
+     * @exception SQLException for who knows what!
+     */
+   public Driver() throws SQLException
+   {
+       java.sql.DriverManager.registerDriver(this);
+   }
+   
+   /**
+    * Try to make a database connection to the given URL.  The driver
+    * should return "null" if it realizes it is the wrong kind of
+    * driver to connect to the given URL.  This will be common, as
+    * when the JDBC driverManager is asked to connect to a given URL,
+    * it passes the URL to each loaded driver in turn.
+    *
+    * The driver should raise an SQLException if it is the right driver
+    * to connect to the given URL, but has trouble connecting to the
+    * database.
+    *
+    * The java.util.Properties argument can be used to pass arbitrary
+    * string tag/value pairs as connection arguments.  Normally, at least
+    * "user" and "password" properties should be included in the 
+    * properties.
+    *
+    * Our protocol takes the form:
+    * 
+    *  jdbc:postgresql://host:port/database
+    * 
+    *
+    * @param url the URL of the database to connect to
+    * @param info a list of arbitrary tag/value pairs as connection
+    *  arguments
+    * @return a connection to the URL or null if it isnt us
+    * @exception SQLException if a database access error occurs
+    * @see java.sql.Driver#connect
+    */
+   public java.sql.Connection connect(String url, Properties info) throws SQLException
+   {
+       DriverURL dr = new DriverURL(url);
+       int port;
+
+       if (!(dr.protocol().equals("jdbc")))
+                        return null;
+       if (!(dr.subprotocol().equals("postgresql")))
+                        return null;
+       if (dr.host().equals("unknown"))
+                        return null;
+       port = dr.port();
+       if (port == -1)
+                        port = 5432;    // Default PostgreSQL port
+       return new Connection (dr.host(), port, info, dr.database(), url, this);
+   }
+
+   /**
+    * Returns true if the driver thinks it can open a connection to the
+    * given URL.  Typically, drivers will return true if they understand
+    * the subprotocol specified in the URL and false if they don't.  Our
+    * protocols start with jdbc:postgresql:
+    *
+    * @see java.sql.Driver#acceptsURL
+    * @param url the URL of the driver
+    * @return true if this driver accepts the given URL
+         * @exception SQLException if a database-access error occurs
+    *  (Dont know why it would *shrug*)
+    */
+   public boolean acceptsURL(String url) throws SQLException
+   {
+       DriverURL dr = new DriverURL(url);
+
+       if (dr.protocol().equals("jdbc"))
+           if (dr.subprotocol().equals("postgresql"))
+               return true;
+       return false;
+   }
+
+   /**
+    * The getPropertyInfo method is intended to allow a generic GUI
+    * tool to discover what properties it should prompt a human for
+    * in order to get enough information to connect to a database.
+    * Note that depending on the values the human has supplied so
+    * far, additional values may become necessary, so it may be necessary
+    * to iterate through several calls to getPropertyInfo
+    *
+    * @param url the Url of the database to connect to
+    * @param info a proposed list of tag/value pairs that will be sent on
+    *  connect open.
+    * @return An array of DriverPropertyInfo objects describing
+    *  possible properties.  This array may be an empty array if
+    *  no properties are required
+    * @exception SQLException if a database-access error occurs
+    * @see java.sql.Driver#getPropertyInfo
+    */
+   public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException
+   {
+       return null;        // We don't need anything except
+                   // the username, which is a default
+   }
+
+   /**
+    * Gets the drivers major version number
+    *
+    * @return the drivers major version number
+    */
+   public int getMajorVersion()
+   {
+       return 1;
+   }
+   
+   /**
+    * Get the drivers minor version number
+    *
+    * @return the drivers minor version number
+    */
+   public int getMinorVersion()
+   {
+       return 0;
+   }
+   
+   /**
+    * Report whether the driver is a genuine JDBC compliant driver.  A
+    * driver may only report "true" here if it passes the JDBC compliance
+    * tests, otherwise it is required to return false.  JDBC compliance
+    * requires full support for the JDBC API and full support for SQL 92
+    * Entry Level.  
+    */
+   public boolean jdbcCompliant()
+   {
+       return false;
+   }
+}
+
+/**
+ * The DriverURL class splits a JDBC URL into its subcomponents
+ *
+ * protocol:subprotocol:/[/host[:port]/][database]
+ */
+class DriverURL
+{
+   private String protocol, subprotocol, host, database;
+   private int port = -1;
+
+   /**
+    * Constructs a new DriverURL, splitting the specified URL into its
+    * component parts
+    */
+   public DriverURL(String url) throws SQLException
+   {
+       int a, b, c;
+       String tmp, hostport, dbportion;
+
+       a = url.indexOf(':');
+       if (a == -1)
+           throw new SQLException("Bad URL Protocol specifier");
+       b = url.indexOf(':', a+1);
+       if (b == -1)
+           throw new SQLException("Bad URL Subprotocol specifier");
+                protocol = new String(url.substring(0, a));
+       subprotocol = new String(url.substring(a+1, b));
+                tmp = new String(url.substring(b+1, url.length()));
+       if (tmp.length() < 2)
+           throw new SQLException("Bad URL Database specifier");
+                if (!tmp.substring(0, 2).equals("//"))
+       {
+           host = new String("unknown");
+           port = -1;
+           database = new String(tmp.substring(1, tmp.length()));
+           return;
+       }
+       dbportion = new String(tmp.substring(2, tmp.length()));
+       c = dbportion.indexOf('/');
+       if (c == -1)
+           throw new SQLException("Bad URL Database specifier");
+       a = dbportion.indexOf(':');
+       if (a == -1)
+       {
+           host = new String(dbportion.substring(0, c));
+           port = -1;
+           database = new String(dbportion.substring(c+1, dbportion.length()));
+       } else {
+           host = new String(dbportion.substring(0, a));
+           port = Integer.valueOf(dbportion.substring(a+1, c)).intValue();
+           database = new String(dbportion.substring(c+1, dbportion.length()));
+       }
+   }
+
+   /**
+    * Returns the protocol name of the DriverURL
+    */
+   public String protocol()
+   {
+       return protocol;
+   }
+
+   /**
+    * Returns the subprotocol name of the DriverURL
+    */
+   public String subprotocol()
+   {
+       return subprotocol;
+   }
+
+   /**
+    * Returns the hostname portion of the URL
+    */
+   public String host()
+   {
+       return host;
+   }
+
+   /**
+    * Returns the port number portion of the URL
+    * or -1 if no port was specified
+    */
+   public int port()
+   {
+       return port;
+   }
+
+   /**
+    * Returns the database name of the URL
+    */
+   public String database()
+   {
+       return database;
+   }
+}
diff --git a/src/interfaces/jdbc/postgresql/Field.java b/src/interfaces/jdbc/postgresql/Field.java
new file mode 100644 (file)
index 0000000..3d27dc6
--- /dev/null
@@ -0,0 +1,89 @@
+package postgresql;
+
+import java.lang.*;
+import java.sql.*;
+import java.util.*;
+import postgresql.*;
+
+/**
+ * postgresql.Field is a class used to describe fields in a PostgreSQL ResultSet
+ *
+ * @version 1.0 15-APR-1997
+ * @author Adrian Hall
+ */
+public class Field
+{
+   int length;     // Internal Length of this field
+   int oid;        // OID of the type
+   Connection conn;    // Connection Instantation
+   String name;        // Name of this field
+
+   int sql_type = -1;  // The entry in java.sql.Types for this field
+   String type_name = null;// The sql type name
+
+   /**
+    *  Construct a field based on the information fed to it.
+    *
+    * @param conn the connection this field came from
+    * @param name the name of the field
+    * @param oid the OID of the field
+    * @param len the length of the field
+    */
+   public Field(Connection conn, String name, int oid, int length)
+   {
+       this.conn = conn;
+       this.name = name;
+       this.oid = oid;
+       this.length = length;
+   }
+
+   /**
+    * the ResultSet and ResultMetaData both need to handle the SQL
+    * type, which is gained from another query.  Note that we cannot
+    * use getObject() in this, since getObject uses getSQLType().
+    *
+    * @return the entry in Types that refers to this field
+    * @exception SQLException if a database access error occurs
+    */
+   public int getSQLType() throws SQLException
+   {
+       if (sql_type == -1)
+       {
+           ResultSet result = (postgresql.ResultSet)conn.ExecSQL("select typname from pg_type where oid = " + oid);
+           if (result.getColumnCount() != 1 || result.getTupleCount() != 1)
+               throw new SQLException("Unexpected return from query for type");
+           result.next();
+           type_name = result.getString(1);
+           if (type_name.equals("int2"))               sql_type = Types.SMALLINT;
+           else if (type_name.equals("int4"))          sql_type = Types.INTEGER;
+           else if (type_name.equals("int8"))          sql_type = Types.BIGINT;
+           else if (type_name.equals("cash"))          sql_type = Types.DECIMAL;
+           else if (type_name.equals("money"))         sql_type = Types.DECIMAL;
+           else if (type_name.equals("float4"))            sql_type = Types.REAL;
+           else if (type_name.equals("float8"))            sql_type = Types.DOUBLE;
+           else if (type_name.equals("bpchar"))            sql_type = Types.CHAR;
+           else if (type_name.equals("varchar"))           sql_type = Types.VARCHAR;
+           else if (type_name.equals("bool"))          sql_type = Types.BIT;
+           else if (type_name.equals("date"))          sql_type = Types.DATE;
+           else if (type_name.equals("time"))          sql_type = Types.TIME;
+           else if (type_name.equals("abstime"))           sql_type = Types.TIMESTAMP;
+           else                            sql_type = Types.OTHER;
+       }   
+       return sql_type;
+   }
+
+   /**
+    * We also need to get the type name as returned by the back end.
+    * This is held in type_name AFTER a call to getSQLType.  Since
+    * we get this information within getSQLType (if it isn't already
+    * done), we can just call getSQLType and throw away the result.
+    *
+    * @return the String representation of the type of this field
+    * @exception SQLException if a database access error occurs
+    */
+   public String getTypeName() throws SQLException
+   {
+       int sql = getSQLType();
+       return type_name;
+   }
+}
diff --git a/src/interfaces/jdbc/postgresql/PG_Object.java b/src/interfaces/jdbc/postgresql/PG_Object.java
new file mode 100644 (file)
index 0000000..89518dc
--- /dev/null
@@ -0,0 +1,31 @@
+package postgresql;
+
+import java.lang.*;
+import java.sql.*;
+import java.util.*;
+import postgresql.*;
+
+/**
+ * postgresql.PG_Object is a class used to describe unknown types 
+ * An unknown type is any type that is unknown by JDBC Standards
+ *
+ * @version 1.0 15-APR-1997
+ * @author Adrian Hall
+ */
+public class PG_Object
+{
+   public String   type;
+   public String   value;
+
+   /**
+    *  Constructor for the PostgreSQL generic object
+    *
+    * @param type a string describing the type of the object
+    * @param value a string representation of the value of the object
+    */
+   public PG_Object(String type, String value)
+   {
+       this.type = type;
+       this.value = value;
+   }
+}
diff --git a/src/interfaces/jdbc/postgresql/PreparedStatement.java b/src/interfaces/jdbc/postgresql/PreparedStatement.java
new file mode 100644 (file)
index 0000000..98fdb6f
--- /dev/null
@@ -0,0 +1,538 @@
+package postgresql;
+
+import java.io.*;
+import java.math.*;
+import java.sql.*;
+import java.text.*;
+import java.util.*;
+
+/**
+ * @version 1.0 15-APR-1997
+ * @author Adrian Hall
+ *
+ * A SQL Statement is pre-compiled and stored in a PreparedStatement object.
+ * This object can then be used to efficiently execute this statement multiple
+ * times.
+ *
+ * Note: The setXXX methods for setting IN parameter values must
+ * specify types that are compatible with the defined SQL type of the input
+ * parameter.  For instance, if the IN parameter has SQL type Integer, then
+ * setInt should be used.
+ *
+ * If arbitrary parameter type conversions are required, then the setObject 
+ * method should be used with a target SQL type.
+ *
+ * @see ResultSet
+ * @see java.sql.PreparedStatement
+ */
+public class PreparedStatement extends Statement implements java.sql.PreparedStatement 
+{
+   String sql;
+   String[] templateStrings;
+   String[] inStrings;
+   Connection connection;
+
+   /**
+    * Constructor for the PreparedStatement class.  Split the SQL statement
+    * into segments - separated by the arguments.  When we rebuild the
+    * thing with the arguments, we can substitute the args and join the
+    * whole thing together.
+    *
+    * @param conn the instanatiating connection
+    * @param sql the SQL statement with ? for IN markers
+    * @exception SQLException if something bad occurs
+    */
+   public PreparedStatement(Connection connection, String sql) throws SQLException
+   {
+       super(connection);
+
+       Vector v = new Vector();
+       boolean inQuotes = false;
+       int lastParmEnd = 0, i;
+
+       this.sql = sql;
+       this.connection = connection;
+       for (i = 0; i < sql.length(); ++i)
+       {
+           int c = sql.charAt(i);
+
+           if (c == '\'')
+               inQuotes = !inQuotes;
+           if (c == '?' && !inQuotes)
+           {
+               v.addElement(sql.substring (lastParmEnd, i));
+               lastParmEnd = i + 1;
+           }
+       }
+       v.addElement(sql.substring (lastParmEnd, sql.length()));
+
+       templateStrings = new String[v.size()];
+       inStrings = new String[v.size() - 1];
+       clearParameters();
+
+       for (i = 0 ; i < templateStrings.length; ++i)
+           templateStrings[i] = (String)v.elementAt(i);
+   }
+
+   /**
+    * A Prepared SQL query is executed and its ResultSet is returned
+    *
+    * @return a ResultSet that contains the data produced by the
+    *  query - never null
+    * @exception SQLException if a database access error occurs
+    */
+   public java.sql.ResultSet executeQuery() throws SQLException
+   {
+       StringBuffer s = new StringBuffer();
+       int i;
+
+       for (i = 0 ; i < inStrings.length ; ++i)
+       {
+           if (inStrings[i] == null)
+               throw new SQLException("No value specified for parameter " + (i + 1));
+           s.append (templateStrings[i]);
+           s.append (inStrings[i]);
+       }
+       s.append(templateStrings[inStrings.length]);
+       return super.executeQuery(s.toString());    // in Statement class
+   }
+
+   /**
+    * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition,
+    * SQL statements that return nothing such as SQL DDL statements can
+    * be executed.
+    *
+    * @return either the row count for INSERT, UPDATE or DELETE; or
+    *  0 for SQL statements that return nothing.
+    * @exception SQLException if a database access error occurs
+    */
+   public int executeUpdate() throws SQLException
+   {
+       StringBuffer s = new StringBuffer();
+       int i;
+
+       for (i = 0 ; i < inStrings.length ; ++i)
+       {
+           if (inStrings[i] == null)
+               throw new SQLException("No value specified for parameter " + (i + 1));
+           s.append (templateStrings[i]);
+           s.append (inStrings[i]);
+       }
+       s.append(templateStrings[inStrings.length]);
+       return super.executeUpdate(s.toString());   // in Statement class
+   }   
+
+   /**
+    * Set a parameter to SQL NULL
+    *
+    * Note: You must specify the parameters SQL type (although
+    * PostgreSQL ignores it)
+    *
+    * @param parameterIndex the first parameter is 1, etc...
+    * @param sqlType the SQL type code defined in java.sql.Types
+    * @exception SQLException if a database access error occurs
+    */
+   public void setNull(int parameterIndex, int sqlType) throws SQLException
+   {
+       set(parameterIndex, "null");
+   }
+
+   /**
+    * Set a parameter to a Java boolean value.  The driver converts this
+    * to a SQL BIT value when it sends it to the database.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setBoolean(int parameterIndex, boolean x) throws SQLException
+   {
+       set(parameterIndex, x ? "'t'" : "'f'");
+   }
+
+   /**
+    * Set a parameter to a Java byte value.  The driver converts this to
+    * a SQL TINYINT value when it sends it to the database.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setByte(int parameterIndex, byte x) throws SQLException
+   {
+       set(parameterIndex, (new Integer(x)).toString());
+   }
+
+   /**
+    * Set a parameter to a Java short value.  The driver converts this
+    * to a SQL SMALLINT value when it sends it to the database.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setShort(int parameterIndex, short x) throws SQLException
+   {
+       set(parameterIndex, (new Integer(x)).toString());
+   }
+
+   /**
+    * Set a parameter to a Java int value.  The driver converts this to
+    * a SQL INTEGER value when it sends it to the database.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setInt(int parameterIndex, int x) throws SQLException
+   {
+       set(parameterIndex, (new Integer(x)).toString());
+   }
+
+   /**
+    * Set a parameter to a Java long value.  The driver converts this to
+    * a SQL BIGINT value when it sends it to the database.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setLong(int parameterIndex, long x) throws SQLException
+   {
+       set(parameterIndex, (new Long(x)).toString());
+   }
+
+   /**
+    * Set a parameter to a Java float value.  The driver converts this
+    * to a SQL FLOAT value when it sends it to the database.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setFloat(int parameterIndex, float x) throws SQLException
+   {
+       set(parameterIndex, (new Float(x)).toString());
+   }
+
+   /**
+    * Set a parameter to a Java double value.  The driver converts this
+    * to a SQL DOUBLE value when it sends it to the database
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setDouble(int parameterIndex, double x) throws SQLException
+   {
+       set(parameterIndex, (new Double(x)).toString());
+   }
+
+   /**
+    * Set a parameter to a java.lang.BigDecimal value.  The driver
+    * converts this to a SQL NUMERIC value when it sends it to the
+    * database.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException
+   {
+       set(parameterIndex, x.toString());
+   }
+
+   /**
+    * Set a parameter to a Java String value.  The driver converts this
+    * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments
+    * size relative to the driver's limits on VARCHARs) when it sends it
+    * to the database.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setString(int parameterIndex, String x) throws SQLException
+   {
+       StringBuffer b = new StringBuffer();
+       int i;
+
+       b.append('\'');
+       for (i = 0 ; i < x.length() ; ++i)
+       {
+           char c = x.charAt(i);
+           if (c == '\\' || c == '\'')
+               b.append((char)'\\');
+           b.append(c);
+       }
+       b.append('\'');
+       set(parameterIndex, b.toString());
+   }
+
+   /**
+    * Set a parameter to a Java array of bytes.  The driver converts this
+    * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's
+    * size relative to the driver's limits on VARBINARYs) when it sends
+    * it to the database.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setBytes(int parameterIndex, byte x[]) throws SQLException
+   {
+       throw new SQLException("Binary Data not supported");
+   }
+
+   /**
+    * Set a parameter to a java.sql.Date value.  The driver converts this
+    * to a SQL DATE value when it sends it to the database.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setDate(int parameterIndex, java.sql.Date x) throws SQLException
+   {
+       DateFormat df = DateFormat.getDateInstance();
+
+       set(parameterIndex, "'" + df.format(x) + "'");
+   }
+
+   /**
+    * Set a parameter to a java.sql.Time value.  The driver converts
+    * this to a SQL TIME value when it sends it to the database.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setTime(int parameterIndex, Time x) throws SQLException
+   {
+       set(parameterIndex, "'" + x.toString() + "'");
+   }
+
+   /**
+    * Set a parameter to a java.sql.Timestamp value.  The driver converts
+    * this to a SQL TIMESTAMP value when it sends it to the database.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException
+   {
+       set(parameterIndex, "'" + x.toString() + "'");
+   }
+
+   /**
+    * When a very large ASCII value is input to a LONGVARCHAR parameter,
+    * it may be more practical to send it via a java.io.InputStream.
+    * JDBC will read the data from the stream as needed, until it reaches
+    * end-of-file.  The JDBC driver will do any necessary conversion from
+    * ASCII to the database char format.
+    *
+    * Note: This stream object can either be a standard Java
+    * stream object or your own subclass that implements the standard
+    * interface.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @param length the number of bytes in the stream
+    * @exception SQLException if a database access error occurs
+    */
+   public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException
+   {
+       setBinaryStream(parameterIndex, x, length);
+   }
+
+   /**
+    * When a very large Unicode value is input to a LONGVARCHAR parameter,
+    * it may be more practical to send it via a java.io.InputStream.
+    * JDBC will read the data from the stream as needed, until it reaches
+    * end-of-file.  The JDBC driver will do any necessary conversion from
+    * UNICODE to the database char format.
+    *
+    * Note: This stream object can either be a standard Java
+    * stream object or your own subclass that implements the standard
+    * interface.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException
+   {
+       setBinaryStream(parameterIndex, x, length);
+   }
+
+   /**
+    * When a very large binary value is input to a LONGVARBINARY parameter,
+    * it may be more practical to send it via a java.io.InputStream.
+    * JDBC will read the data from the stream as needed, until it reaches
+    * end-of-file.  
+    *
+    * Note: This stream object can either be a standard Java
+    * stream object or your own subclass that implements the standard
+    * interface.
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the parameter value
+    * @exception SQLException if a database access error occurs
+    */
+   public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
+   {
+       throw new SQLException("InputStream as parameter not supported");
+   }
+
+   /**
+    * In general, parameter values remain in force for repeated used of a
+    * Statement.  Setting a parameter value automatically clears its
+    * previous value.  However, in coms cases, it is useful to immediately
+    * release the resources used by the current parameter values; this
+    * can be done by calling clearParameters
+    *
+    * @exception SQLException if a database access error occurs
+    */
+   public void clearParameters() throws SQLException
+   {
+       int i;
+
+       for (i = 0 ; i < inStrings.length ; i++)
+           inStrings[i] = null;
+   }
+
+   /**
+    * Set the value of a parameter using an object; use the java.lang
+    * equivalent objects for integral values.
+    *
+    * The given Java object will be converted to the targetSqlType before
+    * being sent to the database.
+    *
+    * note that this method may be used to pass database-specific
+    * abstract data types.  This is done by using a Driver-specific
+    * Java type and using a targetSqlType of java.sql.Types.OTHER
+    *
+    * @param parameterIndex the first parameter is 1...
+    * @param x the object containing the input parameter value
+    * @param targetSqlType The SQL type to be send to the database
+    * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC
+    *  types this is the number of digits after the decimal.  For 
+    *  all other types this value will be ignored.
+    * @exception SQLException if a database access error occurs
+    */
+   public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException
+   {
+       switch (targetSqlType)
+       {
+           case Types.TINYINT:
+           case Types.SMALLINT:
+           case Types.INTEGER:
+           case Types.BIGINT:
+           case Types.REAL:
+           case Types.FLOAT:
+           case Types.DOUBLE:
+           case Types.DECIMAL:
+           case Types.NUMERIC:
+               if (x instanceof Boolean)
+                   set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0");
+               else
+                   set(parameterIndex, x.toString());
+               break;
+           case Types.CHAR:
+           case Types.VARCHAR:
+           case Types.LONGVARCHAR:
+               setString(parameterIndex, x.toString());
+           case Types.DATE:
+               setDate(parameterIndex, (java.sql.Date)x);
+           case Types.TIME:
+               setTime(parameterIndex, (Time)x);
+           case Types.TIMESTAMP:
+               setTimestamp(parameterIndex, (Timestamp)x);
+           case Types.OTHER:
+               setString(parameterIndex, ((PG_Object)x).value);
+           default:
+               throw new SQLException("Unknown Types value");
+       }
+   }
+
+   public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException
+   {
+       setObject(parameterIndex, x, targetSqlType, 0);
+   }
+
+   public void setObject(int parameterIndex, Object x) throws SQLException
+   {
+       if (x instanceof String)
+           setString(parameterIndex, (String)x);
+       else if (x instanceof BigDecimal)
+           setBigDecimal(parameterIndex, (BigDecimal)x);
+       else if (x instanceof Integer)
+           setInt(parameterIndex, ((Integer)x).intValue());
+       else if (x instanceof Long)
+           setLong(parameterIndex, ((Long)x).longValue());
+       else if (x instanceof Float)
+           setFloat(parameterIndex, ((Float)x).floatValue());
+       else if (x instanceof Double)
+           setDouble(parameterIndex, ((Double)x).doubleValue());
+       else if (x instanceof byte[])
+           setBytes(parameterIndex, (byte[])x);
+       else if (x instanceof java.sql.Date)
+           setDate(parameterIndex, (java.sql.Date)x);
+       else if (x instanceof Time)
+           setTime(parameterIndex, (Time)x);
+       else if (x instanceof Timestamp)
+           setTimestamp(parameterIndex, (Timestamp)x);
+       else if (x instanceof Boolean)
+           setBoolean(parameterIndex, ((Boolean)x).booleanValue());
+       else if (x instanceof PG_Object)
+           setString(parameterIndex, ((PG_Object)x).value);
+       else
+           throw new SQLException("Unknown object type");
+   }
+
+   /**
+    * Some prepared statements return multiple results; the execute method
+    * handles these complex statements as well as the simpler form of 
+    * statements handled by executeQuery and executeUpdate
+    *
+    * @return true if the next result is a ResultSet; false if it is an
+    *  update count or there are no more results
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean execute() throws SQLException
+   {
+       StringBuffer s = new StringBuffer();
+       int i;
+
+       for (i = 0 ; i < inStrings.length ; ++i)
+       {
+           if (inStrings[i] == null)
+               throw new SQLException("No value specified for parameter " + (i + 1));
+           s.append (templateStrings[i]);
+           s.append (inStrings[i]);
+       }
+       s.append(templateStrings[inStrings.length]);
+       return super.execute(s.toString());     // in Statement class
+   }
+
+   // **************************************************************
+   //  END OF PUBLIC INTERFACE 
+   // **************************************************************
+   
+   /**
+    * There are a lot of setXXX classes which all basically do
+    * the same thing.  We need a method which actually does the
+    * set for us.
+    *
+    * @param paramIndex the index into the inString
+    * @param s a string to be stored
+    * @exception SQLException if something goes wrong
+    */
+   private void set(int paramIndex, String s) throws SQLException
+   {
+       if (paramIndex < 1 || paramIndex > inStrings.length)
+           throw new SQLException("Parameter index out of range");
+       inStrings[paramIndex - 1] = s;
+   }
+}
diff --git a/src/interfaces/jdbc/postgresql/ResultSet.java b/src/interfaces/jdbc/postgresql/ResultSet.java
new file mode 100644 (file)
index 0000000..c5894e7
--- /dev/null
@@ -0,0 +1,845 @@
+package postgresql;
+
+import java.lang.*;
+import java.io.*;
+import java.math.*;
+import java.text.*;
+import java.util.*;
+import java.sql.*;
+import postgresql.*;
+
+/**
+ * @version 1.0 15-APR-1997
+ * @author Adrian Hall
+ *
+ * A ResultSet provides access to a table of data generated by executing a
+ * Statement.  The table rows are retrieved in sequence.  Within a row its
+ * column values can be accessed in any order.
+ *
+ * A ResultSet maintains a cursor pointing to its current row of data.  
+ * Initially the cursor is positioned before the first row.  The 'next'
+ * method moves the cursor to the next row.
+ *
+ * The getXXX methods retrieve column values for the current row.  You can
+ * retrieve values either using the index number of the column, or by using
+ * the name of the column.  In general using the column index will be more
+ * efficient.  Columns are numbered from 1.
+ *
+ * For maximum portability, ResultSet columns within each row should be read
+ * in left-to-right order and each column should be read only once.
+ *
+ * For the getXXX methods, the JDBC driver attempts to convert the underlying
+ * data to the specified Java type and returns a suitable Java value.  See the
+ * JDBC specification for allowable mappings from SQL types to Java types with
+ * the ResultSet getXXX methods.
+ *
+ * Column names used as input to getXXX methods are case insenstive.  When
+ * performing a getXXX using a column name, if several columns have the same
+ * name, then the value of the first matching column will be returned.  The
+ * column name option is designed to be used when column names are used in the
+ * SQL Query.  For columns that are NOT explicitly named in the query, it is
+ * best to use column numbers.  If column names were used there is no way for
+ * the programmer to guarentee that they actually refer to the intended
+ * columns.
+ *
+ * A ResultSet is automatically closed by the Statement that generated it 
+ * when that Statement is closed, re-executed, or is used to retrieve the 
+ * next result from a sequence of multiple results.
+ *
+ * The number, types and properties of a ResultSet's columns are provided by
+ * the ResultSetMetaData object returned by the getMetaData method.
+ *
+ * @see ResultSetMetaData
+ * @see java.sql.ResultSet
+ */
+public class ResultSet implements java.sql.ResultSet 
+{
+   Vector rows;        // The results
+   Field fields[];     // The field descriptions
+   String status;      // Status of the result
+   int updateCount;    // How many rows did we get back?
+   int current_row;    // Our pointer to where we are at
+   byte[][] this_row;  // the current row result
+   Connection connection;  // the connection which we returned from
+   SQLWarning warnings = null; // The warning chain
+   boolean wasNullFlag = false;    // the flag for wasNull()
+
+   //  We can chain multiple resultSets together - this points to
+   // next resultSet in the chain.
+   private ResultSet next = null;
+
+   /**
+    * Create a new ResultSet - Note that we create ResultSets to
+    * represent the results of everything.
+    *
+    * @param fields an array of Field objects (basically, the
+    *  ResultSet MetaData)
+    * @param tuples Vector of the actual data
+    * @param status the status string returned from the back end
+    * @param updateCount the number of rows affected by the operation
+    * @param cursor the positioned update/delete cursor name
+    */
+   public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount)
+   {
+       this.connection = conn;
+       this.fields = fields;
+       this.rows = tuples;
+       this.status = status;
+       this.updateCount = updateCount;
+       this.this_row = null;
+       this.current_row = -1;
+   }
+
+   /**
+    * A ResultSet is initially positioned before its first row,
+    * the first call to next makes the first row the current row;
+    * the second call makes the second row the current row, etc.
+    *
+    * If an input stream from the previous row is open, it is
+    * implicitly closed.  The ResultSet's warning chain is cleared
+    * when a new row is read
+    *
+    * @return true if the new current is valid; false if there are no
+    *  more rows
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean next() throws SQLException
+   {
+       if (++current_row >= rows.size())
+           return false;
+       this_row = (byte [][])rows.elementAt(current_row);
+       return true;
+   }
+
+   /**
+    * In some cases, it is desirable to immediately release a ResultSet
+    * database and JDBC resources instead of waiting for this to happen
+    * when it is automatically closed.  The close method provides this
+    * immediate release.
+    *
+    * Note: A ResultSet is automatically closed by the Statement
+    * the Statement that generated it when that Statement is closed,
+    * re-executed, or is used to retrieve the next result from a sequence
+    * of multiple results.  A ResultSet is also automatically closed 
+    * when it is garbage collected.
+    *
+    * @exception SQLException if a database access error occurs
+    */
+   public void close() throws SQLException
+   {
+       // No-op
+   }
+
+   /**
+    * A column may have the value of SQL NULL; wasNull() reports whether
+    * the last column read had this special value.  Note that you must
+    * first call getXXX on a column to try to read its value and then
+    * call wasNull() to find if the value was SQL NULL
+    *
+    * @return true if the last column read was SQL NULL
+    * @exception SQLException if a database access error occurred
+    */
+   public boolean wasNull() throws SQLException
+   {
+       return wasNullFlag;
+   }
+
+   /**
+    * Get the value of a column in the current row as a Java String
+    *
+    * @param columnIndex the first column is 1, the second is 2...
+    * @return the column value, null for SQL NULL
+    * @exception SQLException if a database access error occurs
+    */
+   public String getString(int columnIndex) throws SQLException
+   {
+       byte[] bytes = getBytes(columnIndex);
+
+       if (bytes == null)
+           return null;
+       return new String(bytes);
+   }
+
+   /**
+    * Get the value of a column in the current row as a Java boolean
+    *
+    * @param columnIndex the first column is 1, the second is 2...
+    * @return the column value, false for SQL NULL
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean getBoolean(int columnIndex) throws SQLException
+   {
+       String s = getString(columnIndex);
+
+       if (s != null)
+       {
+           int c = s.charAt(0);
+           return ((c == 't') || (c == 'T'));
+       }
+       return false;       // SQL NULL
+   }
+
+   /**
+    * Get the value of a column in the current row as a Java byte.
+    *
+    * @param columnIndex the first column is 1, the second is 2,...
+    * @return the column value; 0 if SQL NULL
+    * @exception SQLException if a database access error occurs
+    */
+   public byte getByte(int columnIndex) throws SQLException
+   {
+       String s = getString(columnIndex);
+       
+       if (s != null)
+       {
+           try
+           {
+               return Byte.parseByte(s);
+           } catch (NumberFormatException e) {
+               throw new SQLException("Bad Byte Form: " + s);
+           }
+       }
+       return 0;       // SQL NULL
+   }
+
+   /**
+    * Get the value of a column in the current row as a Java short.
+    *
+    * @param columnIndex the first column is 1, the second is 2,...
+    * @return the column value; 0 if SQL NULL
+    * @exception SQLException if a database access error occurs
+    */
+   public short getShort(int columnIndex) throws SQLException
+   {
+       String s = getString(columnIndex);
+
+       if (s != null)
+       {
+           try
+           {
+               return Short.parseShort(s);
+           } catch (NumberFormatException e) {
+               throw new SQLException("Bad Short Form: " + s);
+           }
+       }
+       return 0;       // SQL NULL
+   }
+
+   /**
+    * Get the value of a column in the current row as a Java int.
+    *
+    * @param columnIndex the first column is 1, the second is 2,...
+    * @return the column value; 0 if SQL NULL
+    * @exception SQLException if a database access error occurs
+    */
+   public int getInt(int columnIndex) throws SQLException
+   {
+       String s = getString(columnIndex);
+
+       if (s != null)
+       {
+           try
+           {
+               return Integer.parseInt(s);
+           } catch (NumberFormatException e) {
+               throw new SQLException ("Bad Integer Form: " + s);
+           }
+       }
+       return 0;       // SQL NULL
+   }
+
+   /**
+    * Get the value of a column in the current row as a Java long.
+    *
+    * @param columnIndex the first column is 1, the second is 2,...
+    * @return the column value; 0 if SQL NULL
+    * @exception SQLException if a database access error occurs
+    */
+   public long getLong(int columnIndex) throws SQLException
+   {
+       String s = getString(columnIndex);
+
+       if (s != null)
+       {
+           try
+           {
+               return Long.parseLong(s);
+           } catch (NumberFormatException e) {
+               throw new SQLException ("Bad Long Form: " + s);
+           }
+       }
+       return 0;       // SQL NULL
+   }
+
+   /**
+    * Get the value of a column in the current row as a Java float.
+    *
+    * @param columnIndex the first column is 1, the second is 2,...
+    * @return the column value; 0 if SQL NULL
+    * @exception SQLException if a database access error occurs
+    */
+   public float getFloat(int columnIndex) throws SQLException
+   {
+       String s = getString(columnIndex);
+   
+       if (s != null)
+       {
+           try
+           {
+               return Float.valueOf(s).floatValue();
+           } catch (NumberFormatException e) {
+               throw new SQLException ("Bad Float Form: " + s);
+           }
+       }
+       return 0;       // SQL NULL
+   }
+
+   /**
+    * Get the value of a column in the current row as a Java double.
+    *
+    * @param columnIndex the first column is 1, the second is 2,...
+    * @return the column value; 0 if SQL NULL
+    * @exception SQLException if a database access error occurs
+    */
+   public double getDouble(int columnIndex) throws SQLException
+   {
+       String s = getString(columnIndex);
+   
+       if (s != null)
+       {
+           try
+           {
+               return Double.valueOf(s).doubleValue();
+           } catch (NumberFormatException e) {
+               throw new SQLException ("Bad Double Form: " + s);
+           }
+       }
+       return 0;       // SQL NULL
+   }
+
+   /**
+    * Get the value of a column in the current row as a 
+    * java.lang.BigDecimal object
+    *
+    * @param columnIndex  the first column is 1, the second is 2...
+    * @param scale the number of digits to the right of the decimal
+    * @return the column value; if the value is SQL NULL, null
+    * @exception SQLException if a database access error occurs
+    */
+   public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException
+   {
+       String s = getString(columnIndex);
+       BigDecimal val;
+
+       if (s != null)
+       {
+           try
+           {
+               val = new BigDecimal(s);
+           } catch (NumberFormatException e) {
+               throw new SQLException ("Bad BigDecimal Form: " + s);
+           }
+           try
+           {
+               return val.setScale(scale);
+           } catch (ArithmeticException e) {
+               throw new SQLException ("Bad BigDecimal Form: " + s);
+           }
+       }
+       return null;        // SQL NULL
+   }
+
+   /**
+    * Get the value of a column in the current row as a Java byte array
+    * The bytes represent the raw values returned by the driver.
+    *
+    * @param columnIndex the first column is 1, the second is 2, ...
+    * @return the column value; if the value is SQL NULL, the result
+    *  is null
+    * @exception SQLException if a database access error occurs
+    */
+   public byte[] getBytes(int columnIndex) throws SQLException
+   {
+       if (columnIndex < 1 || columnIndex > fields.length)
+           throw new SQLException("Column Index out of range");
+       wasNullFlag = (this_row[columnIndex - 1] == null);
+       return this_row[columnIndex - 1];
+   }
+
+   /**
+    * Get the value of a column in the current row as a java.sql.Date
+    * object
+    *
+    * @param columnIndex the first column is 1, the second is 2...
+    * @return the column value; null if SQL NULL
+    * @exception SQLException if a database access error occurs
+    */
+   public java.sql.Date getDate(int columnIndex) throws SQLException
+   {
+       String s = getString(columnIndex);
+   
+       if (s != null)
+       {
+           try
+           {
+               if (s.length() != 10)
+                   throw new NumberFormatException("Wrong Length!");
+               int mon = Integer.parseInt(s.substring(0,2));
+               int day = Integer.parseInt(s.substring(3,5));
+               int yr = Integer.parseInt(s.substring(6));
+               return new java.sql.Date(yr - 1900, mon -1, day);
+           } catch (NumberFormatException e) {
+               throw new SQLException("Bad Date Form: " + s);
+           }
+       }
+       return null;        // SQL NULL
+   }
+
+   /**
+    * Get the value of a column in the current row as a java.sql.Time
+    * object
+    *
+    * @param columnIndex the first column is 1, the second is 2...
+    * @return the column value; null if SQL NULL
+    * @exception SQLException if a database access error occurs
+    */
+   public Time getTime(int columnIndex) throws SQLException
+   {
+       String s = getString(columnIndex);
+
+       if (s != null)
+       {
+           try
+           {
+               if (s.length() != 5 && s.length() != 8)
+                   throw new NumberFormatException("Wrong Length!");
+               int hr = Integer.parseInt(s.substring(0,2));
+               int min = Integer.parseInt(s.substring(3,5));
+               int sec = (s.length() == 5) ? 0 : Integer.parseInt(s.substring(6));
+               return new Time(hr, min, sec);
+           } catch (NumberFormatException e) {
+               throw new SQLException ("Bad Time Form: " + s);
+           }
+       }
+       return null;        // SQL NULL
+   }
+
+   /**
+    * Get the value of a column in the current row as a 
+    * java.sql.Timestamp object
+    *
+    * @param columnIndex the first column is 1, the second is 2...
+    * @return the column value; null if SQL NULL
+    * @exception SQLException if a database access error occurs
+    */
+   public Timestamp getTimestamp(int columnIndex) throws SQLException
+   {
+       String s = getString(columnIndex);
+       DateFormat df = DateFormat.getDateInstance();
+
+       if (s != null)
+       {
+           try
+           {
+               java.sql.Date d = (java.sql.Date)df.parse(s);
+               return new Timestamp(d.getTime());
+           } catch (ParseException e) {
+               throw new SQLException("Bad Timestamp Format: " + s);
+           }
+       }
+       return null;        // SQL NULL
+   }
+
+   /**
+    * A column value can be retrieved as a stream of ASCII characters
+    * and then read in chunks from the stream.  This method is 
+    * particular suitable for retrieving large LONGVARCHAR values.
+    * The JDBC driver will do any necessary conversion from the
+    * database format into ASCII.
+    *
+    * Note: All the data in the returned stream must be read
+    * prior to getting the value of any other column.  The next call
+    * to a get method implicitly closes the stream.  Also, a stream
+    * may return 0 for available() whether there is data available
+    * or not.
+    *
+    * We implement an ASCII stream as a Binary stream - we should really
+    * do the data conversion, but I cannot be bothered to implement this
+    * right now.
+    *
+    * @param columnIndex the first column is 1, the second is 2, ...
+    * @return a Java InputStream that delivers the database column
+    *  value as a stream of one byte ASCII characters.  If the
+    *  value is SQL NULL then the result is null
+    * @exception SQLException if a database access error occurs
+    * @see getBinaryStream
+    */
+   public InputStream getAsciiStream(int columnIndex) throws SQLException
+   {
+       return getBinaryStream(columnIndex);
+   }
+
+   /**
+    * A column value can also be retrieved as a stream of Unicode
+    * characters. We implement this as a binary stream.
+    *
+    * @param columnIndex the first column is 1, the second is 2...
+    * @return a Java InputStream that delivers the database column value
+    *  as a stream of two byte Unicode characters.  If the value is
+    *  SQL NULL, then the result is null
+    * @exception SQLException if a database access error occurs
+    * @see getAsciiStream
+    * @see getBinaryStream
+    */
+   public InputStream getUnicodeStream(int columnIndex) throws SQLException
+   {
+       return getBinaryStream(columnIndex);
+   }
+
+   /**
+    * A column value can also be retrieved as a binary strea.  This
+    * method is suitable for retrieving LONGVARBINARY values.
+    *
+    * @param columnIndex the first column is 1, the second is 2...
+    * @return a Java InputStream that delivers the database column value
+    *  as a stream of two byte Unicode characters.  If the value is
+    *  SQL NULL, then the result is null
+    * @exception SQLException if a database access error occurs
+    * @see getAsciiStream
+    * @see getUnicodeStream
+    */
+   public InputStream getBinaryStream(int columnIndex) throws SQLException
+   {
+       byte b[] = getBytes(columnIndex);
+
+       if (b != null)
+           return new ByteArrayInputStream(b);
+       return null;        // SQL NULL
+   }
+
+   /**
+    * The following routines simply convert the columnName into
+    * a columnIndex and then call the appropriate routine above.
+    *
+    * @param columnName is the SQL name of the column
+    * @return the column value
+    * @exception SQLException if a database access error occurs
+    */
+   public String getString(String columnName) throws SQLException
+   {
+       return getString(findColumn(columnName));
+   }
+
+   public boolean getBoolean(String columnName) throws SQLException
+   {
+       return getBoolean(findColumn(columnName));
+   }
+
+   public byte getByte(String columnName) throws SQLException
+   {
+
+       return getByte(findColumn(columnName));
+   }
+
+   public short getShort(String columnName) throws SQLException
+   {
+       return getShort(findColumn(columnName));
+   }
+
+   public int getInt(String columnName) throws SQLException
+   {
+       return getInt(findColumn(columnName));
+   }
+
+   public long getLong(String columnName) throws SQLException
+   {
+       return getLong(findColumn(columnName));
+   }
+
+   public float getFloat(String columnName) throws SQLException
+   {
+       return getFloat(findColumn(columnName));
+   }
+
+   public double getDouble(String columnName) throws SQLException
+   {
+       return getDouble(findColumn(columnName));
+   }
+
+   public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException
+   {
+       return getBigDecimal(findColumn(columnName), scale);
+   }
+
+   public byte[] getBytes(String columnName) throws SQLException
+   {
+       return getBytes(findColumn(columnName));
+   }
+
+   public java.sql.Date getDate(String columnName) throws SQLException
+   {
+       return getDate(findColumn(columnName));
+   }
+
+   public Time getTime(String columnName) throws SQLException
+   {
+       return getTime(findColumn(columnName));
+   }
+
+   public Timestamp getTimestamp(String columnName) throws SQLException
+   {
+       return getTimestamp(findColumn(columnName));
+   }
+
+   public InputStream getAsciiStream(String columnName) throws SQLException
+   {
+       return getAsciiStream(findColumn(columnName));
+   }
+
+   public InputStream getUnicodeStream(String columnName) throws SQLException
+   {
+       return getUnicodeStream(findColumn(columnName));
+   }
+
+   public InputStream getBinaryStream(String columnName) throws SQLException
+   {
+       return getBinaryStream(findColumn(columnName));
+   }
+
+   /**
+    * The first warning reported by calls on this ResultSet is
+    * returned.  Subsequent ResultSet warnings will be chained
+    * to this SQLWarning.
+    *
+    * The warning chain is automatically cleared each time a new
+    * row is read.
+    *
+    * Note: This warning chain only covers warnings caused by
+    * ResultSet methods.  Any warnings caused by statement methods
+    * (such as reading OUT parameters) will be chained on the
+    * Statement object.
+    *
+    * @return the first SQLWarning or null;
+    * @exception SQLException if a database access error occurs.
+    */
+   public SQLWarning getWarnings() throws SQLException
+   {
+       return warnings;
+   }
+
+   /**
+    * After this call, getWarnings returns null until a new warning
+    * is reported for this ResultSet
+    *
+    * @exception SQLException if a database access error occurs
+    */
+   public void clearWarnings() throws SQLException
+   {
+       warnings = null;
+   }
+
+   /**
+    * Get the name of the SQL cursor used by this ResultSet
+    *
+    * In SQL, a result table is retrieved though 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.
+    *
+    * JDBC supports this SQL feature by providing the name of the
+    * SQL cursor used by a ResultSet.  The current row of a ResulSet
+    * is also the current row of this SQL cursor.
+    *
+    * Note: If positioned update is not supported, a SQLException
+    * is thrown.
+    *
+    * @return the ResultSet's SQL cursor name.
+    * @exception SQLException if a database access error occurs
+    */
+   public String getCursorName() throws SQLException
+   {
+       return connection.getCursorName();
+   }
+
+   /**
+    * The numbers, types and properties of a ResultSet's columns are
+    * provided by the getMetaData method
+    *
+    * @return a description of the ResultSet's columns
+    * @exception SQLException if a database access error occurs
+    */
+   public java.sql.ResultSetMetaData getMetaData() throws SQLException
+   {
+       return new ResultSetMetaData(rows, fields);
+   }
+
+   /**
+    * Get the value of a column in the current row as a Java object
+    *
+    * This method will return the value of the given column as a
+    * Java object.  The type of the Java object will be the default
+    * Java Object type corresponding to the column's SQL type, following
+    * the mapping specified in the JDBC specification.
+    *
+    * This method may also be used to read database specific abstract
+    * data types.
+    *
+    * @param columnIndex the first column is 1, the second is 2...
+    * @return a Object holding the column value
+    * @exception SQLException if a database access error occurs
+    */
+   public Object getObject(int columnIndex) throws SQLException
+   {
+       Field field;
+       
+       if (columnIndex < 1 || columnIndex > fields.length)
+           throw new SQLException("Column index out of range");
+       field = fields[columnIndex - 1];
+
+       switch (field.getSQLType())
+       {
+           case Types.BIT:
+               return new Boolean(getBoolean(columnIndex));
+           case Types.SMALLINT:
+               return new Integer(getInt(columnIndex));
+           case Types.INTEGER:
+               return new Integer(getInt(columnIndex));
+           case Types.BIGINT:
+               return new Long(getLong(columnIndex));
+           case Types.NUMERIC:
+               return getBigDecimal(columnIndex, 0);
+           case Types.REAL:
+               return new Float(getFloat(columnIndex));
+           case Types.DOUBLE:
+               return new Double(getDouble(columnIndex));
+           case Types.CHAR:
+           case Types.VARCHAR:
+               return getString(columnIndex);
+           case Types.DATE:
+               return getDate(columnIndex);
+           case Types.TIME:
+               return getTime(columnIndex);
+           case Types.TIMESTAMP:
+               return getTimestamp(columnIndex);
+           default:
+               return new PG_Object(field.getTypeName(), getString(columnIndex));
+       }
+   }
+
+   /**
+    * Get the value of a column in the current row as a Java object
+    *
+    * This method will return the value of the given column as a
+    * Java object.  The type of the Java object will be the default
+    * Java Object type corresponding to the column's SQL type, following
+    * the mapping specified in the JDBC specification.
+    *
+    * This method may also be used to read database specific abstract
+    * data types.
+    *
+    * @param columnName is the SQL name of the column
+    * @return a Object holding the column value
+    * @exception SQLException if a database access error occurs
+    */
+   public Object getObject(String columnName) throws SQLException
+   {
+       return getObject(findColumn(columnName));
+   }
+
+   /**
+    * Map a ResultSet column name to a ResultSet column index
+    *
+    * @param columnName the name of the column
+    * @return the column index
+    * @exception SQLException if a database access error occurs
+    */
+   public int findColumn(String columnName) throws SQLException
+   {
+       int i;
+
+       for (i = 0 ; i < fields.length; ++i)
+           if (fields[i].name.equalsIgnoreCase(columnName))
+               return (i+1);
+       throw new SQLException ("Column name not found");
+   }
+
+   // ************************************************************
+   //  END OF PUBLIC INTERFACE
+   // ************************************************************
+   
+   /**
+    * We at times need to know if the resultSet we are working
+    * with is the result of an UPDATE, DELETE or INSERT (in which
+    * case, we only have a row count), or of a SELECT operation
+    * (in which case, we have multiple fields) - this routine
+    * tells us.
+    *
+    * @return true if we have tuples available
+    */
+   public boolean reallyResultSet()
+   {
+       return (fields != null);
+   }
+
+   /**
+    * Since ResultSets can be chained, we need some method of
+    * finding the next one in the chain.  The method getNext()
+    * returns the next one in the chain.
+    *
+    * @return the next ResultSet, or null if there are none
+    */
+   public ResultSet getNext()
+   {
+       return next;
+   }
+
+   /**
+    * This following method allows us to add a ResultSet object
+    * to the end of the current chain.
+    *
+    * @param r the resultset to add to the end of the chain.
+    */
+   public void append(ResultSet r)
+   {
+       if (next == null)
+           next = r;
+       else
+           next.append(r);
+   }
+
+   /**
+    * If we are just a place holder for results, we still need
+    * to get an updateCount.  This method returns it.
+    *
+    * @return the updateCount
+    */
+   public int getResultCount()
+   {
+       return updateCount;
+   }
+
+   /**
+    * We also need to provide a couple of auxiliary functions for
+    * the implementation of the ResultMetaData functions.  In
+    * particular, we need to know the number of rows and the
+    * number of columns.  Rows are also known as Tuples
+    *
+    * getTupleCount returns the number of rows
+    *
+    * @return the number of rows
+    */
+   public int getTupleCount()
+   {
+       return rows.size();
+   }
+
+   /**
+    * getColumnCount returns the number of columns
+    *
+    * @return the number of columns
+    */
+   public int getColumnCount()
+   {
+       return fields.length;
+   }
+}
diff --git a/src/interfaces/jdbc/postgresql/ResultSetMetaData.java b/src/interfaces/jdbc/postgresql/ResultSetMetaData.java
new file mode 100644 (file)
index 0000000..a6974b3
--- /dev/null
@@ -0,0 +1,429 @@
+package postgresql;
+
+import java.lang.*;
+import java.sql.*;
+import java.util.*;
+import postgresql.*;
+
+/**
+ * @version 1.0 15-APR-1997
+ * @author Adrian Hall
+ *
+ * A ResultSetMetaData object can be used to find out about the types and
+ * properties of the columns in a ResultSet
+ *
+ * @see java.sql.ResultSetMetaData
+ */
+public class ResultSetMetaData implements java.sql.ResultSetMetaData 
+{
+   Vector rows;
+   Field[] fields;
+
+   /**
+    *  Initialise for a result with a tuple set and
+    *  a field descriptor set
+    *
+    * @param rows the Vector of rows returned by the ResultSet
+    * @param fields the array of field descriptors
+    */
+   public ResultSetMetaData(Vector rows, Field[] fields)
+   {
+       this.rows = rows;
+       this.fields = fields;
+   }
+   
+   /**
+    * Whats the number of columns in the ResultSet?
+    *
+    * @return the number
+    * @exception SQLException if a database access error occurs
+    */
+   public int getColumnCount() throws SQLException
+   {
+       return fields.length;
+   }
+
+   /**
+    * Is the column automatically numbered (and thus read-only)
+    * I believe that PostgreSQL does not support this feature.
+    *
+    * @param column the first column is 1, the second is 2...
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean isAutoIncrement(int column) throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Does a column's case matter? ASSUMPTION: Any field that is
+    * not obviously case insensitive is assumed to be case sensitive
+    *
+    * @param column the first column is 1, the second is 2...
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean isCaseSensitive(int column) throws SQLException
+   {
+       int sql_type = getField(column).getSQLType();
+
+       switch (sql_type)
+       {
+           case Types.SMALLINT:
+           case Types.INTEGER:
+           case Types.FLOAT:
+           case Types.REAL:
+           case Types.DOUBLE:
+           case Types.DATE:
+           case Types.TIME:
+           case Types.TIMESTAMP:
+               return false;
+           default:
+               return true;
+       }
+   }
+
+   /**
+    * Can the column be used in a WHERE clause?  Basically for
+    * this, I split the functions into two types: recognised
+    * types (which are always useable), and OTHER types (which
+    * may or may not be useable).  The OTHER types, for now, I
+    * will assume they are useable.  We should really query the
+    * catalog to see if they are useable.
+    *
+    * @param column the first column is 1, the second is 2...
+    * @return true if they can be used in a WHERE clause
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean isSearchable(int column) throws SQLException
+   {
+       int sql_type = getField(column).getSQLType();
+
+       // This switch is pointless, I know - but it is a set-up
+       // for further expansion.       
+       switch (sql_type)
+       {
+           case Types.OTHER:
+               return true;
+           default:
+               return true;
+       }
+   }
+
+   /**
+    * Is the column a cash value?  6.1 introduced the cash/money
+    * type, which haven't been incorporated as of 970414, so I
+    * just check the type name for both 'cash' and 'money'
+    *
+    * @param column the first column is 1, the second is 2...
+    * @return true if its a cash column
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean isCurrency(int column) throws SQLException
+   {
+       String type_name = getField(column).getTypeName();
+
+       if (type_name.equals("cash"))
+           return true;
+       if (type_name.equals("money"))
+           return true;
+       return false;
+   }
+
+   /**
+    * Can you put a NULL in this column?  I think this is always
+    * true in 6.1's case.  It would only be false if the field had
+    * been defined NOT NULL (system catalogs could be queried?)
+    *
+    * @param column the first column is 1, the second is 2...
+    * @return one of the columnNullable values
+    * @exception SQLException if a database access error occurs
+    */
+   public int isNullable(int column) throws SQLException
+   {
+       return columnNullable;  // We can always put NULL in
+   }
+
+   /**
+    * Is the column a signed number? In PostgreSQL, all numbers
+    * are signed, so this is trivial.  However, strings are not
+    * signed (duh!)
+    * 
+    * @param column the first column is 1, the second is 2...
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean isSigned(int column) throws SQLException
+   {
+       int sql_type = getField(column).getSQLType();
+   
+       switch (sql_type)
+       {
+           case Types.SMALLINT:
+           case Types.INTEGER:
+           case Types.FLOAT:
+           case Types.REAL:
+           case Types.DOUBLE:
+               return true;
+           case Types.DATE:
+           case Types.TIME:
+           case Types.TIMESTAMP:
+               return false;   // I don't know about these?
+           default:
+               return false;
+       }
+   }
+
+   /**
+    * What is the column's normal maximum width in characters?
+    *
+    * @param column the first column is 1, the second is 2, etc.
+    * @return the maximum width
+    * @exception SQLException if a database access error occurs
+    */
+   public int getColumnDisplaySize(int column) throws SQLException
+   {
+       int max = getColumnLabel(column).length();
+       int i;
+
+       for (i = 0 ; i < rows.size(); ++i)
+       {
+           byte[][] x = (byte[][])(rows.elementAt(i));
+           int xl = x[column - 1].length;
+           if (xl > max)
+               max = xl;
+       }
+       return max;
+   }
+
+   /**
+    * What is the suggested column title for use in printouts and
+    * displays?  We suggest the ColumnName!
+    *
+    * @param column the first column is 1, the second is 2, etc.
+    * @return the column label
+    * @exception SQLException if a database access error occurs
+    */
+   public String getColumnLabel(int column) throws SQLException
+   {
+       return getColumnName(column);
+   }
+
+   /**
+    * What's a column's name?
+    *
+    * @param column the first column is 1, the second is 2, etc.
+    * @return the column name
+    * @exception SQLException if a databvase access error occurs
+    */
+   public String getColumnName(int column) throws SQLException
+   {
+       return getField(column).name;
+   }
+
+   /**
+    * What is a column's table's schema?  This relies on us knowing
+    * the table name....which I don't know how to do as yet.  The 
+    * JDBC specification allows us to return "" if this is not
+    * applicable.
+    *
+    * @param column the first column is 1, the second is 2...
+    * @return the Schema
+    * @exception SQLException if a database access error occurs
+    */
+   public String getSchemaName(int column) throws SQLException
+   {
+       String table_name = getTableName(column);
+
+       // If the table name is invalid, so are we.
+       if (table_name.equals(""))
+           return "";  
+       return "";      // Ok, so I don't know how to
+                   // do this as yet.
+   }
+
+   /**
+    * What is a column's number of decimal digits.
+    *
+    * @param column the first column is 1, the second is 2...
+    * @return the precision
+    * @exception SQLException if a database access error occurs
+    */
+   public int getPrecision(int column) throws SQLException
+   {
+       int sql_type = getField(column).getSQLType();
+
+       switch (sql_type)
+       {
+           case Types.SMALLINT:
+               return 5;   
+           case Types.INTEGER:
+               return 10;
+           case Types.REAL:
+               return 8;
+           case Types.FLOAT:
+               return 16;
+           case Types.DOUBLE:
+               return 16;
+           default:
+               throw new SQLException("no precision for non-numeric data types.");
+       }
+   }
+
+   /**
+    * What is a column's number of digits to the right of the
+    * decimal point?
+    *
+    * @param column the first column is 1, the second is 2...
+    * @return the scale
+    * @exception SQLException if a database access error occurs
+    */
+   public int getScale(int column) throws SQLException
+   {
+       int sql_type = getField(column).getSQLType();
+
+       switch (sql_type)
+       {
+           case Types.SMALLINT:
+               return 0;
+           case Types.INTEGER:
+               return 0;
+           case Types.REAL:
+               return 8;
+           case Types.FLOAT:
+               return 16;
+           case Types.DOUBLE:
+               return 16;
+           default:
+               throw new SQLException("no scale for non-numeric data types");
+       }
+   }
+
+   /**
+    * Whats a column's table's name?  How do I find this out?  Both
+    * getSchemaName() and getCatalogName() rely on knowing the table
+    * Name, so we need this before we can work on them.
+    *
+    * @param column the first column is 1, the second is 2...
+    * @return column name, or "" if not applicable
+    * @exception SQLException if a database access error occurs
+    */
+   public String getTableName(int column) throws SQLException
+   {
+       return "";
+   }
+
+   /**
+    * What's a column's table's catalog name?  As with getSchemaName(),
+    * we can say that if getTableName() returns n/a, then we can too -
+    * otherwise, we need to work on it.
+    * 
+    * @param column the first column is 1, the second is 2...
+    * @return catalog name, or "" if not applicable
+    * @exception SQLException if a database access error occurs
+    */
+   public String getCatalogName(int column) throws SQLException
+   {
+       String table_name = getTableName(column);
+
+       if (table_name.equals(""))
+           return "";
+       return "";      // As with getSchemaName(), this
+                   // is just the start of it.
+   }
+   
+   /**
+    * What is a column's SQL Type? (java.sql.Type int)
+    *
+    * @param column the first column is 1, the second is 2, etc.
+    * @return the java.sql.Type value
+    * @exception SQLException if a database access error occurs
+    * @see postgresql.Field#getSQLType
+    * @see java.sql.Types
+    */
+   public int getColumnType(int column) throws SQLException
+   {
+       return getField(column).getSQLType();
+   }
+
+   /**
+    * Whats is the column's data source specific type name?
+    *
+    * @param column the first column is 1, the second is 2, etc.
+    * @return the type name
+    * @exception SQLException if a database access error occurs
+    */
+   public String getColumnTypeName(int column) throws SQLException
+   {
+       return getField(column).getTypeName();
+   }
+
+   /**
+    * Is the column definitely not writable?  In reality, we would
+    * have to check the GRANT/REVOKE stuff for this to be effective,
+    * and I haven't really looked into that yet, so this will get
+    * re-visited.
+    *
+    * @param column the first column is 1, the second is 2, etc.
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean isReadOnly(int column) throws SQLException
+   {
+       return false;
+   }
+
+   /**
+    * Is it possible for a write on the column to succeed?  Again, we
+    * would in reality have to check the GRANT/REVOKE stuff, which
+    * I haven't worked with as yet.  However, if it isn't ReadOnly, then
+    * it is obviously writable.
+    *
+    * @param column the first column is 1, the second is 2, etc.
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean isWritable(int column) throws SQLException
+   {
+       if (isReadOnly(column))
+           return true;
+       else
+           return false;
+   }
+
+   /**
+    * Will a write on this column definately succeed?  Hmmm...this
+    * is a bad one, since the two preceding functions have not been
+    * really defined.  I cannot tell is the short answer.  I thus
+    * return isWritable() just to give us an idea.
+    *
+    * @param column the first column is 1, the second is 2, etc..
+    * @return true if so
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean isDefinitelyWritable(int column) throws SQLException
+   {
+       return isWritable(column);
+   }
+
+   // ********************************************************
+   //  END OF PUBLIC INTERFACE
+   // ********************************************************
+   
+   /**
+    * For several routines in this package, we need to convert
+    * a columnIndex into a Field[] descriptor.  Rather than do
+    * the same code several times, here it is.
+    * 
+    * @param columnIndex the first column is 1, the second is 2...
+    * @return the Field description
+    * @exception SQLException if a database access error occurs
+    */
+   private Field getField(int columnIndex) throws SQLException
+   {
+       if (columnIndex < 1 || columnIndex > fields.length)
+           throw new SQLException("Column index out of range");
+       return fields[columnIndex - 1];
+   }
+}
diff --git a/src/interfaces/jdbc/postgresql/Statement.java b/src/interfaces/jdbc/postgresql/Statement.java
new file mode 100644 (file)
index 0000000..464a263
--- /dev/null
@@ -0,0 +1,306 @@
+package postgresql;
+
+import java.sql.*;
+
+/**
+ * @version 1.0 15-APR-1997
+ * @author Adrian Hall
+ *
+ * A Statement object is used for executing a static SQL statement and
+ * obtaining the results produced by it.
+ *
+ * Only one ResultSet per Statement can be open at any point in time.  
+ * Therefore, if the reading of one ResultSet is interleaved with the
+ * reading of another, each must have been generated by different
+ * Statements.  All statement execute methods implicitly close a
+ * statement's current ResultSet if an open one exists.
+ *
+ * @see java.sql.Statement
+ * @see ResultSet
+ */
+public class Statement implements java.sql.Statement
+{
+   Connection connection;      // The connection who created us
+   ResultSet result = null;    // The current results
+   SQLWarning warnings = null; // The warnings chain.
+   int maxrows = 0;        // maximum no. of rows; 0 = unlimited
+   int timeout = 0;        // The timeout for a query (not used)
+   boolean escapeProcessing = true;// escape processing flag
+
+   /**
+    * Constructor for a Statement.  It simply sets the connection
+    * that created us.
+    *
+    * @param c the Connection instantation that creates us
+    */
+   public Statement (Connection c)
+   {
+       connection = c;
+   }
+
+   /**
+    * Execute a SQL statement that retruns a single ResultSet
+    *
+    * @param sql typically a static SQL SELECT statement
+    * @return a ResulSet that contains the data produced by the query
+    * @exception SQLException if a database access error occurs
+    */
+   public java.sql.ResultSet executeQuery(String sql) throws SQLException
+   {
+       this.execute(sql);
+       while (result != null && !result.reallyResultSet())
+           result = result.getNext();
+       if (result == null)
+           throw new SQLException("no results returned");
+       return result;
+   }
+
+   /**
+    * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition
+    * SQL statements that return nothing such as SQL DDL statements
+    * can be executed
+    *
+    * @param sql a SQL statement
+    * @return either a row count, or 0 for SQL commands
+    * @exception SQLException if a database access error occurs
+    */
+   public int executeUpdate(String sql) throws SQLException
+   {
+       this.execute(sql);
+       if (result.reallyResultSet())
+           throw new SQLException("results returned");
+       return this.getUpdateCount();
+   }
+
+   /**
+    * In many cases, it is desirable to immediately release a
+    * Statement's database and JDBC resources instead of waiting
+    * for this to happen when it is automatically closed.  The
+    * close method provides this immediate release.
+    *
+    * Note: A Statement is automatically closed when it is 
+    * garbage collected.  When a Statement is closed, its current 
+    * ResultSet, if one exists, is also closed.
+    *
+    * @exception SQLException if a database access error occurs (why?)
+    */
+   public void close() throws SQLException
+   {
+       result = null;
+   }
+
+   /**
+    * The maxFieldSize limit (in bytes) is the maximum amount of
+    * data returned for any column value; it only applies to
+    * BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR
+    * columns.  If the limit is exceeded, the excess data is silently
+    * discarded.
+    *
+    * @return the current max column size limit; zero means unlimited
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxFieldSize() throws SQLException
+   {
+       return 8192;        // We cannot change this
+   }
+
+   /**
+    * Sets the maxFieldSize - NOT! - We throw an SQLException just
+    * to inform them to stop doing this.
+    *
+    * @param max the new max column size limit; zero means unlimited
+    * @exception SQLException if a database access error occurs
+    */
+   public void setMaxFieldSize(int max) throws SQLException
+   {
+       throw new SQLException("Attempt to setMaxFieldSize failed - compile time default");
+   }
+
+   /**
+    * The maxRows limit is set to limit the number of rows that
+    * any ResultSet can contain.  If the limit is exceeded, the
+    * excess rows are silently dropped.
+    *
+    * @return the current maximum row limit; zero means unlimited
+    * @exception SQLException if a database access error occurs
+    */
+   public int getMaxRows() throws SQLException
+   {
+       return maxrows;
+   }
+
+   /**
+    * Set the maximum number of rows
+    *
+    * @param max the new max rows limit; zero means unlimited
+    * @exception SQLException if a database access error occurs
+    * @see getMaxRows
+    */
+   public void setMaxRows(int max) throws SQLException
+   {
+       maxrows = max;
+   }
+
+   /**
+    * If escape scanning is on (the default), the driver will do escape
+    * substitution before sending the SQL to the database.  
+    *
+    * @param enable true to enable; false to disable
+    * @exception SQLException if a database access error occurs
+    */
+   public void setEscapeProcessing(boolean enable) throws SQLException
+   {
+       escapeProcessing = enable;
+   }
+
+   /**
+    * The queryTimeout limit is the number of seconds the driver
+    * will wait for a Statement to execute.  If the limit is
+    * exceeded, a SQLException is thrown.
+    *
+    * @return the current query timeout limit in seconds; 0 = unlimited
+    * @exception SQLException if a database access error occurs
+    */
+   public int getQueryTimeout() throws SQLException
+   {
+       return timeout;
+   }
+
+   /**
+    * Sets the queryTimeout limit
+    *
+    * @param seconds - the new query timeout limit in seconds
+    * @exception SQLException if a database access error occurs
+    */
+   public void setQueryTimeout(int seconds) throws SQLException
+   {
+       timeout = seconds;
+   }
+
+   /**
+    * Cancel can be used by one thread to cancel a statement that
+    * is being executed by another thread.  However, PostgreSQL is
+    * a sync. sort of thing, so this really has no meaning - we 
+    * define it as a no-op (i.e. you can't cancel, but there is no
+    * error if you try.)
+    *
+    * @exception SQLException only because thats the spec.
+    */
+   public void cancel() throws SQLException
+   {
+       // No-op
+   }
+
+   /**
+    * The first warning reported by calls on this Statement is
+    * returned.  A Statement's execute methods clear its SQLWarning
+    * chain.  Subsequent Statement warnings will be chained to this
+    * SQLWarning.
+    *
+    * The Warning chain is automatically cleared each time a statement
+    * is (re)executed.
+    *
+    * Note:  If you are processing a ResultSet then any warnings
+    * associated with ResultSet reads will be chained on the ResultSet
+    * object.
+    *
+    * @return the first SQLWarning on null
+    * @exception SQLException if a database access error occurs
+    */
+   public SQLWarning getWarnings() throws SQLException
+   {
+       return warnings;
+   }
+
+   /**
+    * After this call, getWarnings returns null until a new warning
+    * is reported for this Statement.
+    *
+    * @exception SQLException if a database access error occurs (why?)
+    */
+   public void clearWarnings() throws SQLException
+   {
+       warnings = null;
+   }
+
+   /**
+    * setCursorName defines the SQL cursor name that will be used by
+    * subsequent execute methods.  This name can then be used in SQL
+    * positioned update/delete statements to identify the current row
+    * in the ResultSet generated by this statement.  If a database
+    * doesn't support positioned update/delete, this method is a
+    * no-op.
+    *
+    * Note: By definition, positioned update/delete execution
+    * must be done by a different Statement than the one which
+    * generated the ResultSet being used for positioning.  Also, cursor
+    * names must be unique within a Connection.
+    *
+    * We throw an additional constriction.  There can only be one
+    * cursor active at any one time.
+    *
+    * @param name the new cursor name
+    * @exception SQLException if a database access error occurs
+    */
+   public void setCursorName(String name) throws SQLException
+   {
+       connection.setCursorName(name);
+   }
+
+   /**
+    * Execute a SQL statement that may return multiple results. We
+    * don't have to worry about this since we do not support multiple
+    * ResultSets.   You can use getResultSet or getUpdateCount to 
+    * retrieve the result.
+    *
+    * @param sql any SQL statement
+    * @return true if the next result is a ResulSet, false if it is
+    *  an update count or there are no more results
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean execute(String sql) throws SQLException
+   {
+       result = connection.ExecSQL(sql);
+       return (result != null && result.reallyResultSet());
+   }
+
+   /**
+    * getResultSet returns the current result as a ResultSet.  It
+    * should only be called once per result.
+    *
+    * @return the current result set; null if there are no more
+    * @exception SQLException if a database access error occurs (why?)
+    */
+   public java.sql.ResultSet getResultSet() throws SQLException
+   {
+       return result;
+   }
+
+   /**
+    * getUpdateCount returns the current result as an update count,
+    * if the result is a ResultSet or there are no more results, -1
+    * is returned.  It should only be called once per result.
+    *
+    * @return the current result as an update count.
+    * @exception SQLException if a database access error occurs
+    */
+   public int getUpdateCount() throws SQLException
+   {
+       if (result == null)         return -1;
+       if (result.reallyResultSet())   return -1;
+       return result.getResultCount();
+   }
+
+   /**
+    * getMoreResults moves to a Statement's next result.  If it returns
+    * true, this result is a ResulSet.
+    *
+    * @return true if the next ResultSet is valid
+    * @exception SQLException if a database access error occurs
+    */
+   public boolean getMoreResults() throws SQLException
+   {
+       result = result.getNext();
+       return (result != null && result.reallyResultSet());
+   }
+}