OSDN Git Service

Bring in Adrian's JDBC driver as an interface
authorMarc G. Fournier <scrappy@hub.org>
Sat, 16 Aug 1997 20:51:53 +0000 (20:51 +0000)
committerMarc G. Fournier <scrappy@hub.org>
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 <A HREF="mailto:adrian@hottub.org">Adrian Hall</A>
+ *
+ * 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.
+ *
+ * <PRE>
+ *     {?= call <procedure-name>[<arg1>,<arg2>, ...]}
+ *     {call <procedure-name>[<arg1>,<arg2>, ...]}
+ * </PRE>
+ *
+ * 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 <A HREF="mailto:adrian@hottub.org">Adrian Hall</A>
+ *
+ * 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.
+ *
+ * <B>Note:</B> 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.
+        *
+        * <B>Note:</B> 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.
+        *
+        * <B>Note:</B> 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)
+        *
+        * <B>Note:</B> 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
+        *
+        * <B>Note:</B> 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.  
+        *
+        * <B>Note:</B> 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.
+        *
+        * <B>Note:</B> 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.
+        *
+        * <B>Note:</B> there does not seem to be any method currently
+        * in existance to return the update count.
+        *
+        * @param sql the SQL statement to be executed
+        * @return a ResultSet holding the results
+        * @exception SQLException if a database error occurs
+        */
+       public 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 <A HREF="mailto:adrian@hottub.org">Adrian Hall</A>
+ *
+ * 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 <A HREF="mailto:adrian@hottub.org">Adrian Hall</A>
+ *
+ * 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:
+        * <PRE>
+        *      jdbc:postgresql://host:port/database
+        * </PRE>
+        *
+        * @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 <A HREF="mailto:adrian@hottub.org">Adrian Hall</A>
+ */
+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 <A HREF="mailto:adrian@hottub.org">Adrian Hall</A>
+ */
+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 <A HREF="mailto:adrian@hottub.org">Adrian Hall</A>
+ *
+ * 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.
+ *
+ * <B>Note:</B> 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
+        *
+        * <B>Note:</B> 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.
+        *
+        * <B>Note:</B> 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.
+        *
+        * <B>Note:</B> 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.  
+        *
+        * <B>Note:</B> 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 <A HREF="mailto:adrian@hottub.org">Adrian Hall</A>
+ *
+ * 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.
+        *
+        * <B>Note:</B> 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.
+        *
+        * <B>Note:</B> 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.
+        *
+        * <B>Note:</B> 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.
+        *
+        * <B>Note:</B> 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 <A HREF="mailto:adrian@hottub.org">Adrian Hall</A>
+ *
+ * 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 <A HREF="mailto:adrian@hottub.org">Adrian Hall</A>
+ *
+ * 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.
+        *
+        * <B>Note:</B> 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.
+        *
+        * <B>Note:</B>  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.
+        *
+        * <B>Note:</B> 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());
+       }
+}