OSDN Git Service

Update patch from Peter <patches@maidast.demon.co.uk>
authorMarc G. Fournier <scrappy@hub.org>
Sun, 31 Aug 1997 08:15:13 +0000 (08:15 +0000)
committerMarc G. Fournier <scrappy@hub.org>
Sun, 31 Aug 1997 08:15:13 +0000 (08:15 +0000)
src/interfaces/jdbc/postgresql/CallableStatement.java
src/interfaces/jdbc/postgresql/Connection.java
src/interfaces/jdbc/postgresql/DatabaseMetaData.java
src/interfaces/jdbc/postgresql/Driver.java
src/interfaces/jdbc/postgresql/Field.java
src/interfaces/jdbc/postgresql/PG_Object.java
src/interfaces/jdbc/postgresql/ResultSet.java
src/interfaces/jdbc/postgresql/ResultSetMetaData.java

index ff7ec7c..ede69bb 100644 (file)
 package postgresql;
 
-import java.math.*;
 import java.sql.*;
+import java.math.*;
 
 /**
- * @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
+ * JDBC Interface to Postgres95 functions
  */
-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
-       }
+// Copy methods from the Result set object here.
 
+public class CallableStatement extends PreparedStatement implements java.sql.CallableStatement
+{
+  CallableStatement(Connection c,String q) throws SQLException
+  {
+    super(c,q);
+  }
+  
+  // Before executing a stored procedure call you must explicitly
+  // call registerOutParameter to register the java.sql.Type of each
+  // out parameter.
+  public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException {
+  }
+  
+  // You must also specify the scale for numeric/decimal types:        
+  public void registerOutParameter(int parameterIndex, int sqlType,
+                                  int scale) throws SQLException
+  {
+  }
+  
+  public boolean isNull(int parameterIndex) throws SQLException {
+    return true;
+  }
+  
+  // New API (JPM)
+  public boolean wasNull() throws SQLException {
+    // check to see if the last access threw an exception
+    return false; // fake it for now
+  }
+  
+  // Methods for retrieving OUT parameters from this statement.
+  public String getChar(int parameterIndex) throws SQLException {
+    return null;
+  }
+  
+  // New API (JPM)
+  public String getString(int parameterIndex) throws SQLException {
+    return null;
+  }
+  //public String getVarChar(int parameterIndex) throws SQLException {
+  //   return null;
+  //}
+  
+  public String getLongVarChar(int parameterIndex) throws SQLException {
+    return null;
+  }
+  
+  // New API (JPM) (getBit)
+  public boolean getBoolean(int parameterIndex) throws SQLException {
+    return false;
+  }
+  
+  // New API (JPM) (getTinyInt)
+  public byte getByte(int parameterIndex) throws SQLException {
+    return 0;
+  }
+  
+  // New API (JPM) (getSmallInt)
+  public short getShort(int parameterIndex) throws SQLException {
+    return 0;
+  }
+  
+  // New API (JPM) (getInteger)
+  public int getInt(int parameterIndex) throws SQLException {
+    return 0;
+  }
+  
+  // New API (JPM) (getBigInt)
+  public long getLong(int parameterIndex) throws SQLException {
+    return 0;
+  }
+  
+  public float getFloat(int parameterIndex) throws SQLException {
+    return (float) 0.0;
+  }
+  
+  public double getDouble(int parameterIndex) throws SQLException {
+    return 0.0;
+  }
+  
+  public BigDecimal getBigDecimal(int parameterIndex, int scale)
+       throws SQLException {
+        return null;
+  }
+  
+  // New API (JPM) (getBinary)
+  public byte[] getBytes(int parameterIndex) throws SQLException {
+    return null;
+  }
+  
+  // New API (JPM) (getLongVarBinary)
+  public byte[] getBinaryStream(int parameterIndex) throws SQLException {
+    return null;
+  }
+  
+  public java.sql.Date getDate(int parameterIndex) throws SQLException {
+    return null;
+  }
+  public java.sql.Time getTime(int parameterIndex) throws SQLException {
+    return null;
+  }
+  public java.sql.Timestamp getTimestamp(int parameterIndex)
+       throws SQLException {
+        return null;
+  }
+  
+  //----------------------------------------------------------------------
+  // Advanced features:
+  
+  // You can obtain a ParameterMetaData object to get information 
+  // about the parameters to this CallableStatement.
+  public DatabaseMetaData getMetaData() {
+    return null;
+  }
+  
+  // getObject returns a Java object for the parameter.
+  // See the JDBC spec's "Dynamic Programming" chapter for details.
+  public Object getObject(int parameterIndex)
+       throws SQLException {
+        return null;
+  }
 }
+
index aa354b6..a208970 100644 (file)
@@ -28,570 +28,569 @@ import postgresql.*;
  */
 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)
+  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;
+  
+  protected 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);
+  }
+  
+  /**
+   * 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
                {
-                       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);
+                 try
+                   {
+                     pg_stream.SendChar('Q');
+                     pg_stream.SendChar(' ');
+                     pg_stream.SendChar(0);
+                   } catch (IOException e) {
+                     throw new SQLException("I/O Error: " + e.toString());
+                   }
+                   fqp++;
                }
-               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;
-       }
+             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;
+  }
 }
 
 // ***********************************************************************
@@ -599,249 +598,249 @@ public class Connection implements java.sql.Connection
 //  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();
-       }
+  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();
+  }
 }
index 259829c..00485b2 100644 (file)
@@ -1,6 +1,7 @@
 package postgresql;
 
 import java.sql.*;
+import java.util.*;
 
 /**
  * @version 1.0 15-APR-1997
@@ -30,1527 +31,2053 @@ import java.sql.*;
  */
 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;
-       }
-
+  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.2");
+  }
+  
+  /**
+   * 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(Integer.toString(connection.this_driver.getMajorVersion())+"."+Integer.toString(connection.this_driver.getMinorVersion()));
+  }
+  
+  /**
+   * What is this JDBC driver's major version number?
+   *
+   * @return the JDBC driver major version
+   */
+  public int getDriverMajorVersion()
+  {
+    return connection.this_driver.getMajorVersion();
+  }
+  
+  /**
+   * What is this JDBC driver's minor version number?
+   *
+   * @return the JDBC driver minor version
+   */
+  public int getDriverMinorVersion()
+  {
+    return connection.this_driver.getMinorVersion();
+  }
+  
+  /**
+   * 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
+    return "";
+  }
+  
+  public String getStringFunctions() throws SQLException
+  {
+    // XXX-Not Implemented
+    return "";
+  }
+  
+  public String getSystemFunctions() throws SQLException
+  {
+    // XXX-Not Implemented
+    return "";
+  }
+  
+  public String getTimeDateFunctions() throws SQLException
+  {
+    // XXX-Not Implemented
+    return "";
+  }
+  
+  /**
+   * 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
+    return false;
+  }
+  
+  public boolean supportsConvert(int fromType, int toType) throws SQLException
+  {
+    // XXX-Not Implemented
+    return false;
+  }
+  
+  public boolean supportsTableCorrelationNames() throws SQLException
+  {
+    // XXX-Not Implemented
+    return false;
+  }
+  
+  public boolean supportsDifferentTableCorrelationNames() throws SQLException
+  {
+    // XXX-Not Implemented
+    return false;
+  }
+  
+  /**
+   * 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 true;
+  }
+  
+  /**
+   * 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
+    return false;
+  }
+  
+  public boolean supportsStoredProcedures() throws SQLException
+  {
+    // XXX-Not Implemented
+    return false;
+  }
+  
+  public boolean supportsSubqueriesInComparisons() throws SQLException
+  {
+    // XXX-Not Implemented
+    return false;
+  }
+  
+  public boolean supportsSubqueriesInExists() throws SQLException
+  {
+    // XXX-Not Implemented
+    return false;
+  }
+  
+  public boolean supportsSubqueriesInIns() throws SQLException
+  {
+    // XXX-Not Implemented
+    return false;
+  }
+  
+  public boolean supportsSubqueriesInQuantifieds() throws SQLException
+  {
+    // XXX-Not Implemented
+    return false;
+  }
+  
+  public boolean supportsCorrelatedSubqueries() throws SQLException
+  {
+    // XXX-Not Implemented
+    return false;
+  }
+  
+  /**
+   * 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
+    return 0;
+  }
+  
+  /**
+   * 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
+    return 0;
+  }
+  
+  /**
+   * 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
+   */
+  static final int iVarcharOid = 1043; // This is the OID for a varchar()
+  static final int iInt2Oid = 21;       // This is the OID for an int2
+  public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException
+  {
+    // the field descriptors for the new ResultSet
+    Field f[] = new Field[8];
+    ResultSet r;       // ResultSet for the SQL query that we need to do
+    Vector v = new Vector();           // The new ResultSet tuple stuff
+    String remarks = new String("no remarks");
+    
+    f[0] = new Field(connection, new String("PROCEDURE_CAT"), iVarcharOid, 32);
+    f[1] = new Field(connection, new String("PROCEDURE_SCHEM"), iVarcharOid, 32);
+    f[2] = new Field(connection, new String("PROCEDURE_NAME"), iVarcharOid, 32);
+    f[3] = null;
+    f[4] = null;
+    f[5] = null;
+    f[6] = new Field(connection, new String("REMARKS"), iVarcharOid, 8192);
+    f[7] = new Field(connection, new String("PROCEDURE_TYPE"), iInt2Oid, 2);
+    r = connection.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];
        
-       /**
-        * 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);
+       String name = r.getString(1);
+       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
-       }
+       tuple[0] = null;                        // Catalog name
+       tuple[1] = null;                        // Schema name
+       tuple[2] = name.getBytes();     // Procedure name
+       tuple[3] = null;                        // Reserved
+       tuple[4] = null;                        // Reserved
+       tuple[5] = null;                        // Reserved
+       tuple[6] = remarks.getBytes();  // Remarks
+       tuple[7] = new byte[1];
+       if (retset)
+         tuple[7][0] = (byte)java.sql.DatabaseMetaData.procedureReturnsResult;
+       else
+         tuple[7][0] = (byte)java.sql.DatabaseMetaData.procedureNoResult;
+       v.addElement(tuple);
+      }
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+  
+  public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException
+  {
+    // XXX-Not Implemented
+    return null;
+  }
+  
+  public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException
+  {
+    return connection.createStatement().executeQuery("SELECT '' as TABLE_CAT,'' AS TABLE_SCHEM,relname AS TABLE_NAME,'TABLE' AS TABLE_TYPE,'' AS REMARKS FROM pg_class WHERE relkind = 'r' and relname !~ '^pg_' and relname !~ '^Inv' and relname ~ '"+tableNamePattern+"' ORDER BY TABLE_NAME");
+  }
+  
+  /**
+   * Get the schema names available in this database.  The results
+   * are ordered by schema name.
+   *
+   * <P>The schema column is:
+   *  <OL>
+   *   <LI><B>TABLE_SCHEM</B> String => schema name
+   *  </OL>
+   *
+   * @return ResultSet each row has a single String column that is a
+   * schema name
+   */
+  public java.sql.ResultSet getSchemas() throws SQLException
+  {
+    // XXX-Not Implemented
+    return null;
+  }
+  
+  /**
+   * Get the catalog names available in this database.  The results
+   * are ordered by catalog name.
+   *
+   * <P>The catalog column is:
+   *  <OL>
+   *   <LI><B>TABLE_CAT</B> String => catalog name
+   *  </OL>
+   *
+   * @return ResultSet each row has a single String column that is a
+   * catalog name
+   */
+  // We don't use catalog names, so this returns a single catalog
+  public java.sql.ResultSet getCatalogs() throws SQLException
+  {
+    return connection.createStatement().executeQuery("SELECT '' as TABLE_CAT");
+  }
+  
+  /**
+   * Get the table types available in this database.  The results
+   * are ordered by table type.
+   *
+   * <P>The table type is:
+   *  <OL>
+   *   <LI><B>TABLE_TYPE</B> String => table type.  Typical types are "TABLE",
+   *                   "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY",
+   *                   "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
+   *  </OL>
+   *
+   * @return ResultSet each row has a single String column that is a
+   * table type
+   */
+  public java.sql.ResultSet getTableTypes() throws SQLException
+  {
+    // XXX-Not Implemented
+    return null;
+  }
+  
+  /**
+   * Get a description of table columns available in a catalog.
+   *
+   * <P>Only column descriptions matching the catalog, schema, table
+   * and column name criteria are returned.  They are ordered by
+   * TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION.
+   *
+   * <P>Each column description has the following columns:
+   *  <OL>
+   *   <LI><B>TABLE_CAT</B> String => table catalog (may be null)
+   *   <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
+   *   <LI><B>TABLE_NAME</B> String => table name
+   *   <LI><B>COLUMN_NAME</B> String => column name
+   *   <LI><B>DATA_TYPE</B> short => SQL type from java.sql.Types
+   *   <LI><B>TYPE_NAME</B> String => Data source dependent type name
+   *   <LI><B>COLUMN_SIZE</B> int => column size.  For char or date
+   *       types this is the maximum number of characters, for numeric or
+   *       decimal types this is precision.
+   *   <LI><B>BUFFER_LENGTH</B> is not used.
+   *   <LI><B>DECIMAL_DIGITS</B> int => the number of fractional digits
+   *   <LI><B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2)
+   *   <LI><B>NULLABLE</B> int => is NULL allowed?
+   *      <UL>
+   *      <LI> columnNoNulls - might not allow NULL values
+   *      <LI> columnNullable - definitely allows NULL values
+   *      <LI> columnNullableUnknown - nullability unknown
+   *      </UL>
+   *   <LI><B>REMARKS</B> String => comment describing column (may be null)
+   *   <LI><B>COLUMN_DEF</B> String => default value (may be null)
+   *   <LI><B>SQL_DATA_TYPE</B> int => unused
+   *   <LI><B>SQL_DATETIME_SUB</B> int => unused
+   *   <LI><B>CHAR_OCTET_LENGTH</B> int => for char types the
+   *       maximum number of bytes in the column
+   *   <LI><B>ORDINAL_POSITION</B> int => index of column in table
+   *      (starting at 1)
+   *   <LI><B>IS_NULLABLE</B> String => "NO" means column definitely
+   *      does not allow NULL values; "YES" means the column might
+   *      allow NULL values.  An empty string means nobody knows.
+   *  </OL>
+   *
+   * @param catalog a catalog name; "" retrieves those without a catalog
+   * @param schemaPattern a schema name pattern; "" retrieves those
+   * without a schema
+   * @param tableNamePattern a table name pattern
+   * @param columnNamePattern a column name pattern
+   * @return ResultSet each row is a column description
+   * @see #getSearchStringEscape
+   */
+  public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException
+  {
+    // XXX-Not Implemented
+    // PM: this will be implemented, as soon as I sort out how to convert the
+    // code from the other driver (private note: look at getProcedures() )
+    return null;
+  }
+  
+  /**
+   * Get a description of the access rights for a table's columns.
+   *
+   * <P>Only privileges matching the column name criteria are
+   * returned.  They are ordered by COLUMN_NAME and PRIVILEGE.
+   *
+   * <P>Each privilige description has the following columns:
+   *  <OL>
+   *   <LI><B>TABLE_CAT</B> String => table catalog (may be null)
+   *   <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
+   *   <LI><B>TABLE_NAME</B> String => table name
+   *   <LI><B>COLUMN_NAME</B> String => column name
+   *   <LI><B>GRANTOR</B> => grantor of access (may be null)
+   *   <LI><B>GRANTEE</B> String => grantee of access
+   *   <LI><B>PRIVILEGE</B> String => name of access (SELECT,
+   *      INSERT, UPDATE, REFRENCES, ...)
+   *   <LI><B>IS_GRANTABLE</B> String => "YES" if grantee is permitted
+   *      to grant to others; "NO" if not; null if unknown
+   *  </OL>
+   *
+   * @param catalog a catalog name; "" retrieves those without a catalog
+   * @param schema a schema name; "" retrieves those without a schema
+   * @param table a table name
+   * @param columnNamePattern a column name pattern
+   * @return ResultSet each row is a column privilege description
+   * @see #getSearchStringEscape
+   */
+  public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException
+  {
+    // XXX-Not Implemented
+    return null;
+  }
+  
+  /**
+   * Get a description of the access rights for each table available
+   * in a catalog.
+   *
+   * <P>Only privileges matching the schema and table name
+   * criteria are returned.  They are ordered by TABLE_SCHEM,
+   * TABLE_NAME, and PRIVILEGE.
+   *
+   * <P>Each privilige description has the following columns:
+   *  <OL>
+   *   <LI><B>TABLE_CAT</B> String => table catalog (may be null)
+   *   <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
+   *   <LI><B>TABLE_NAME</B> String => table name
+   *   <LI><B>COLUMN_NAME</B> String => column name
+   *   <LI><B>GRANTOR</B> => grantor of access (may be null)
+   *   <LI><B>GRANTEE</B> String => grantee of access
+   *   <LI><B>PRIVILEGE</B> String => name of access (SELECT,
+   *      INSERT, UPDATE, REFRENCES, ...)
+   *   <LI><B>IS_GRANTABLE</B> String => "YES" if grantee is permitted
+   *      to grant to others; "NO" if not; null if unknown
+   *  </OL>
+   *
+   * @param catalog a catalog name; "" retrieves those without a catalog
+   * @param schemaPattern a schema name pattern; "" retrieves those
+   * without a schema
+   * @param tableNamePattern a table name pattern
+   * @return ResultSet each row is a table privilege description
+   * @see #getSearchStringEscape
+   */
+  public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException
+  {
+    // XXX-Not Implemented
+    return null;
+  }
+  
+  /**
+   * Get a description of a table's optimal set of columns that
+   * uniquely identifies a row. They are ordered by SCOPE.
+   *
+   * <P>Each column description has the following columns:
+   *  <OL>
+   *   <LI><B>SCOPE</B> short => actual scope of result
+   *      <UL>
+   *      <LI> bestRowTemporary - very temporary, while using row
+   *      <LI> bestRowTransaction - valid for remainder of current transaction
+   *      <LI> bestRowSession - valid for remainder of current session
+   *      </UL>
+   *   <LI><B>COLUMN_NAME</B> String => column name
+   *   <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types
+   *   <LI><B>TYPE_NAME</B> String => Data source dependent type name
+   *   <LI><B>COLUMN_SIZE</B> int => precision
+   *   <LI><B>BUFFER_LENGTH</B> int => not used
+   *   <LI><B>DECIMAL_DIGITS</B> short  => scale
+   *   <LI><B>PSEUDO_COLUMN</B> short => is this a pseudo column
+   *      like an Oracle ROWID
+   *      <UL>
+   *      <LI> bestRowUnknown - may or may not be pseudo column
+   *      <LI> bestRowNotPseudo - is NOT a pseudo column
+   *      <LI> bestRowPseudo - is a pseudo column
+   *      </UL>
+   *  </OL>
+   *
+   * @param catalog a catalog name; "" retrieves those without a catalog
+   * @param schema a schema name; "" retrieves those without a schema
+   * @param table a table name
+   * @param scope the scope of interest; use same values as SCOPE
+   * @param nullable include columns that are nullable?
+   * @return ResultSet each row is a column description
+   */
+  public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException
+  {
+    // XXX-Not Implemented
+    return null;
+  }
+  
+  /**
+   * Get a description of a table's columns that are automatically
+   * updated when any value in a row is updated.  They are
+   * unordered.
+   *
+   * <P>Each column description has the following columns:
+   *  <OL>
+   *   <LI><B>SCOPE</B> short => is not used
+   *   <LI><B>COLUMN_NAME</B> String => column name
+   *   <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types
+   *   <LI><B>TYPE_NAME</B> String => Data source dependent type name
+   *   <LI><B>COLUMN_SIZE</B> int => precision
+   *   <LI><B>BUFFER_LENGTH</B> int => length of column value in bytes
+   *   <LI><B>DECIMAL_DIGITS</B> short  => scale
+   *   <LI><B>PSEUDO_COLUMN</B> short => is this a pseudo column
+   *      like an Oracle ROWID
+   *      <UL>
+   *      <LI> versionColumnUnknown - may or may not be pseudo column
+   *      <LI> versionColumnNotPseudo - is NOT a pseudo column
+   *      <LI> versionColumnPseudo - is a pseudo column
+   *      </UL>
+   *  </OL>
+   *
+   * @param catalog a catalog name; "" retrieves those without a catalog
+   * @param schema a schema name; "" retrieves those without a schema
+   * @param table a table name
+   * @return ResultSet each row is a column description
+   */
+ public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException
+  {
+    // XXX-Not Implemented
+    return null;
+  }
+  
+  /**
+   * Get a description of a table's primary key columns.  They
+   * are ordered by COLUMN_NAME.
+   *
+   * <P>Each column description has the following columns:
+   *  <OL>
+   *   <LI><B>TABLE_CAT</B> String => table catalog (may be null)
+   *   <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
+   *   <LI><B>TABLE_NAME</B> String => table name
+   *   <LI><B>COLUMN_NAME</B> String => column name
+   *   <LI><B>KEY_SEQ</B> short => sequence number within primary key
+   *   <LI><B>PK_NAME</B> String => primary key name (may be null)
+   *  </OL>
+   *
+   * @param catalog a catalog name; "" retrieves those without a catalog
+   * @param schema a schema name pattern; "" retrieves those
+   * without a schema
+   * @param table a table name
+   * @return ResultSet each row is a primary key column description
+   */
+  public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException
+  {
+    return connection.createStatement().executeQuery("SELECT " +
+                                                    "'' as TABLE_CAT," +
+                                                    "'' AS TABLE_SCHEM," +
+                                                    "bc.relname AS TABLE_NAME," +
+                                                    "ic.relname AS COLUMN_NAME," +
+                                                    "'1' as KEY_SEQ,"+ // -- fake it as a String for now
+                                                    "t.typname as PK_NAME " +
+                                                    " FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a " +
+                                                    " WHERE relkind = 'r' " + //    -- not indices
+                                                    "  and bc.relname ~ '"+table+"'" +
+                                                    "  and i.indrelid = bc.oid" +
+                                                    "  and i.indexrelid = ic.oid" +
+                                                    "  and i.indkey[0] = a.attnum" +
+                                                    "  and i.indproc = '0'::oid" +
+                                                    "  and a.attrelid = bc.oid" +
+                                                    " ORDER BY TABLE_NAME, COLUMN_NAME;"
+                                                    );
+  }
+  
+  /**
+   * Get a description of the primary key columns that are
+   * referenced by a table's foreign key columns (the primary keys
+   * imported by a table).  They are ordered by PKTABLE_CAT,
+   * PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ.
+   *
+   * <P>Each primary key column description has the following columns:
+   *  <OL>
+   *   <LI><B>PKTABLE_CAT</B> String => primary key table catalog
+   *      being imported (may be null)
+   *   <LI><B>PKTABLE_SCHEM</B> String => primary key table schema
+   *      being imported (may be null)
+   *   <LI><B>PKTABLE_NAME</B> String => primary key table name
+   *      being imported
+   *   <LI><B>PKCOLUMN_NAME</B> String => primary key column name
+   *      being imported
+   *   <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
+   *   <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
+   *   <LI><B>FKTABLE_NAME</B> String => foreign key table name
+   *   <LI><B>FKCOLUMN_NAME</B> String => foreign key column name
+   *   <LI><B>KEY_SEQ</B> short => sequence number within foreign key
+   *   <LI><B>UPDATE_RULE</B> short => What happens to
+   *       foreign key when primary is updated:
+   *      <UL>
+   *      <LI> importedKeyCascade - change imported key to agree
+   *               with primary key update
+   *      <LI> importedKeyRestrict - do not allow update of primary
+   *               key if it has been imported
+   *      <LI> importedKeySetNull - change imported key to NULL if
+   *               its primary key has been updated
+   *      </UL>
+   *   <LI><B>DELETE_RULE</B> short => What happens to
+   *      the foreign key when primary is deleted.
+   *      <UL>
+   *      <LI> importedKeyCascade - delete rows that import a deleted key
+   *      <LI> importedKeyRestrict - do not allow delete of primary
+   *               key if it has been imported
+   *      <LI> importedKeySetNull - change imported key to NULL if
+   *               its primary key has been deleted
+   *      </UL>
+   *   <LI><B>FK_NAME</B> String => foreign key name (may be null)
+   *   <LI><B>PK_NAME</B> String => primary key name (may be null)
+   *  </OL>
+   *
+   * @param catalog a catalog name; "" retrieves those without a catalog
+   * @param schema a schema name pattern; "" retrieves those
+   * without a schema
+   * @param table a table name
+   * @return ResultSet each row is a primary key column description
+   * @see #getExportedKeys
+   */
+  public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException
+  {
+    // XXX-Not Implemented
+    return null;
+  }
+  
+  /**
+   * Get a description of a foreign key columns that reference a
+   * table's primary key columns (the foreign keys exported by a
+   * table).  They are ordered by FKTABLE_CAT, FKTABLE_SCHEM,
+   * FKTABLE_NAME, and KEY_SEQ.
+   *
+   * <P>Each foreign key column description has the following columns:
+   *  <OL>
+   *   <LI><B>PKTABLE_CAT</B> String => primary key table catalog (may be null)
+   *   <LI><B>PKTABLE_SCHEM</B> String => primary key table schema (may be null)
+   *   <LI><B>PKTABLE_NAME</B> String => primary key table name
+   *   <LI><B>PKCOLUMN_NAME</B> String => primary key column name
+   *   <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
+   *      being exported (may be null)
+   *   <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
+   *      being exported (may be null)
+   *   <LI><B>FKTABLE_NAME</B> String => foreign key table name
+   *      being exported
+   *   <LI><B>FKCOLUMN_NAME</B> String => foreign key column name
+   *      being exported
+   *   <LI><B>KEY_SEQ</B> short => sequence number within foreign key
+   *   <LI><B>UPDATE_RULE</B> short => What happens to
+   *       foreign key when primary is updated:
+   *      <UL>
+   *      <LI> importedKeyCascade - change imported key to agree
+   *               with primary key update
+   *      <LI> importedKeyRestrict - do not allow update of primary
+   *               key if it has been imported
+   *      <LI> importedKeySetNull - change imported key to NULL if
+   *               its primary key has been updated
+   *      </UL>
+   *   <LI><B>DELETE_RULE</B> short => What happens to
+   *      the foreign key when primary is deleted.
+   *      <UL>
+   *      <LI> importedKeyCascade - delete rows that import a deleted key
+   *      <LI> importedKeyRestrict - do not allow delete of primary
+   *               key if it has been imported
+   *      <LI> importedKeySetNull - change imported key to NULL if
+   *               its primary key has been deleted
+   *      </UL>
+   *   <LI><B>FK_NAME</B> String => foreign key identifier (may be null)
+   *   <LI><B>PK_NAME</B> String => primary key identifier (may be null)
+   *  </OL>
+   *
+   * @param catalog a catalog name; "" retrieves those without a catalog
+   * @param schema a schema name pattern; "" retrieves those
+   * without a schema
+   * @param table a table name
+   * @return ResultSet each row is a foreign key column description
+   * @see #getImportedKeys
+   */
+  public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException
+  {
+    // XXX-Not Implemented
+    return null;
+  }
+  
+  /**
+   * Get a description of the foreign key columns in the foreign key
+   * table that reference the primary key columns of the primary key
+   * table (describe how one table imports another's key.) This
+   * should normally return a single foreign key/primary key pair
+   * (most tables only import a foreign key from a table once.)  They
+   * are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and
+   * KEY_SEQ.
+   *
+   * <P>Each foreign key column description has the following columns:
+   *  <OL>
+   *   <LI><B>PKTABLE_CAT</B> String => primary key table catalog (may be null)
+   *   <LI><B>PKTABLE_SCHEM</B> String => primary key table schema (may be null)
+   *   <LI><B>PKTABLE_NAME</B> String => primary key table name
+   *   <LI><B>PKCOLUMN_NAME</B> String => primary key column name
+   *   <LI><B>FKTABLE_CAT</B> String => foreign key table catalog (may be null)
+   *      being exported (may be null)
+   *   <LI><B>FKTABLE_SCHEM</B> String => foreign key table schema (may be null)
+   *      being exported (may be null)
+   *   <LI><B>FKTABLE_NAME</B> String => foreign key table name
+   *      being exported
+   *   <LI><B>FKCOLUMN_NAME</B> String => foreign key column name
+   *      being exported
+   *   <LI><B>KEY_SEQ</B> short => sequence number within foreign key
+   *   <LI><B>UPDATE_RULE</B> short => What happens to
+   *       foreign key when primary is updated:
+   *      <UL>
+   *      <LI> importedKeyCascade - change imported key to agree
+   *               with primary key update
+   *      <LI> importedKeyRestrict - do not allow update of primary
+   *               key if it has been imported
+   *      <LI> importedKeySetNull - change imported key to NULL if
+   *               its primary key has been updated
+   *      </UL>
+   *   <LI><B>DELETE_RULE</B> short => What happens to
+   *      the foreign key when primary is deleted.
+   *      <UL>
+   *      <LI> importedKeyCascade - delete rows that import a deleted key
+   *      <LI> importedKeyRestrict - do not allow delete of primary
+   *               key if it has been imported
+   *      <LI> importedKeySetNull - change imported key to NULL if
+   *               its primary key has been deleted
+   *      </UL>
+   *   <LI><B>FK_NAME</B> String => foreign key identifier (may be null)
+   *   <LI><B>PK_NAME</B> String => primary key identifier (may be null)
+   *  </OL>
+   *
+   * @param catalog a catalog name; "" retrieves those without a catalog
+   * @param schema a schema name pattern; "" retrieves those
+   * without a schema
+   * @param table a table name
+   * @return ResultSet each row is a foreign key column description
+   * @see #getImportedKeys
+   */
+  public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException
+  {
+    // XXX-Not Implemented
+    return null;
+  }
+  
+  /**
+   * Get a description of all the standard SQL types supported by
+   * this database. They are ordered by DATA_TYPE and then by how
+   * closely the data type maps to the corresponding JDBC SQL type.
+   *
+   * <P>Each type description has the following columns:
+   *  <OL>
+   *   <LI><B>TYPE_NAME</B> String => Type name
+   *   <LI><B>DATA_TYPE</B> short => SQL data type from java.sql.Types
+   *   <LI><B>PRECISION</B> int => maximum precision
+   *   <LI><B>LITERAL_PREFIX</B> String => prefix used to quote a literal
+   *      (may be null)
+   *   <LI><B>LITERAL_SUFFIX</B> String => suffix used to quote a literal
+   (may be null)
+   *   <LI><B>CREATE_PARAMS</B> String => parameters used in creating
+   *      the type (may be null)
+   *   <LI><B>NULLABLE</B> short => can you use NULL for this type?
+   *      <UL>
+   *      <LI> typeNoNulls - does not allow NULL values
+   *      <LI> typeNullable - allows NULL values
+   *      <LI> typeNullableUnknown - nullability unknown
+   *      </UL>
+   *   <LI><B>CASE_SENSITIVE</B> boolean=> is it case sensitive?
+   *   <LI><B>SEARCHABLE</B> short => can you use "WHERE" based on this type:
+   *      <UL>
+   *      <LI> typePredNone - No support
+   *      <LI> typePredChar - Only supported with WHERE .. LIKE
+   *      <LI> typePredBasic - Supported except for WHERE .. LIKE
+   *      <LI> typeSearchable - Supported for all WHERE ..
+   *      </UL>
+   *   <LI><B>UNSIGNED_ATTRIBUTE</B> boolean => is it unsigned?
+   *   <LI><B>FIXED_PREC_SCALE</B> boolean => can it be a money value?
+   *   <LI><B>AUTO_INCREMENT</B> boolean => can it be used for an
+   *      auto-increment value?
+   *   <LI><B>LOCAL_TYPE_NAME</B> String => localized version of type name
+   *      (may be null)
+   *   <LI><B>MINIMUM_SCALE</B> short => minimum scale supported
+   *   <LI><B>MAXIMUM_SCALE</B> short => maximum scale supported
+   *   <LI><B>SQL_DATA_TYPE</B> int => unused
+   *   <LI><B>SQL_DATETIME_SUB</B> int => unused
+   *   <LI><B>NUM_PREC_RADIX</B> int => usually 2 or 10
+   *  </OL>
+   *
+   * @return ResultSet each row is a SQL type description
+   */
+  public java.sql.ResultSet getTypeInfo() throws SQLException
+  {
+    // XXX-Not Implemented
+    return null;
+  }
+  
+  /**
+   * Get a description of a table's indices and statistics. They are
+   * ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION.
+   *
+   * <P>Each index column description has the following columns:
+   *  <OL>
+   *   <LI><B>TABLE_CAT</B> String => table catalog (may be null)
+   *   <LI><B>TABLE_SCHEM</B> String => table schema (may be null)
+   *   <LI><B>TABLE_NAME</B> String => table name
+   *   <LI><B>NON_UNIQUE</B> boolean => Can index values be non-unique?
+   *      false when TYPE is tableIndexStatistic
+   *   <LI><B>INDEX_QUALIFIER</B> String => index catalog (may be null);
+   *      null when TYPE is tableIndexStatistic
+   *   <LI><B>INDEX_NAME</B> String => index name; null when TYPE is
+   *      tableIndexStatistic
+   *   <LI><B>TYPE</B> short => index type:
+   *      <UL>
+   *      <LI> tableIndexStatistic - this identifies table statistics that are
+   *           returned in conjuction with a table's index descriptions
+   *      <LI> tableIndexClustered - this is a clustered index
+   *      <LI> tableIndexHashed - this is a hashed index
+   *      <LI> tableIndexOther - this is some other style of index
+   *      </UL>
+   *   <LI><B>ORDINAL_POSITION</B> short => column sequence number
+   *      within index; zero when TYPE is tableIndexStatistic
+   *   <LI><B>COLUMN_NAME</B> String => column name; null when TYPE is
+   *      tableIndexStatistic
+   *   <LI><B>ASC_OR_DESC</B> String => column sort sequence, "A" => ascending,
+   *      "D" => descending, may be null if sort sequence is not supported;
+   *      null when TYPE is tableIndexStatistic
+   *   <LI><B>CARDINALITY</B> int => When TYPE is tableIndexStatisic then
+   *      this is the number of rows in the table; otherwise it is the
+   *      number of unique values in the index.
+   *   <LI><B>PAGES</B> int => When TYPE is  tableIndexStatisic then
+   *      this is the number of pages used for the table, otherwise it
+   *      is the number of pages used for the current index.
+   *   <LI><B>FILTER_CONDITION</B> String => Filter condition, if any.
+   *      (may be null)
+   *  </OL>
+   *
+   * @param catalog a catalog name; "" retrieves those without a catalog
+   * @param schema a schema name pattern; "" retrieves those without a schema
+   * @param table a table name
+   * @param unique when true, return only indices for unique values;
+   *     when false, return indices regardless of whether unique or not
+   * @param approximate when true, result is allowed to reflect approximate
+   *     or out of data values; when false, results are requested to be
+   *     accurate
+   * @return ResultSet each row is an index column description
+   */
+  public java.sql.ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException
+  {
+    // XXX-Not Implemented
+    return null;
+  }
 }
index 055a656..674c96f 100644 (file)
@@ -2,12 +2,8 @@ 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
  *
@@ -28,242 +24,274 @@ import postgresql.*;
  */
 public class Driver implements java.sql.Driver 
 {
-
-       static 
-       {
-               try
-               {
-                       new Driver();
-               } catch (SQLException e) {
-                       e.printStackTrace();
-               }
+  // These should be in sync with the backend that the driver was
+  // distributed with
+  static final int MAJORVERSION = 6;
+  static final int MINORVERSION = 2;
+  
+  static 
+  {
+    try {
+      // moved the registerDriver from the constructor to here
+      // because some clients call the driver themselves (I know, as
+      // my early jdbc work did - and that was based on other examples).
+      // Placing it here, means that the driver is registered once only.
+      java.sql.DriverManager.registerDriver(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
+  {
+  }
+  
+  /**
+   * 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
+  {
+    if((props = parseURL(url,info))==null)
+      return null;
+    
+    return new Connection (host(), port(), props, 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
+  {
+    if(parseURL(url,null)==null)
+      return false;
+    return true;
+  }
+  
+  /**
+   * 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 MAJORVERSION;
+  }
+  
+  /**
+   * Get the drivers minor version number
+   *
+   * @return the drivers minor version number
+   */
+  public int getMinorVersion()
+  {
+    return MINORVERSION;
+  }
+  
+  /**
+   * 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;
+  }
+  
+  private Properties props;
+  
+  static private String[] protocols = { "jdbc","postgresql" };
+  
+  /**
+   * Constructs a new DriverURL, splitting the specified URL into its
+   * component parts
+   */
+  Properties parseURL(String url,Properties defaults) throws SQLException
+  {
+    int state = -1;
+    Properties urlProps = new Properties(defaults);
+    String key = new String();
+    String value = new String();
+    
+    StringTokenizer st = new StringTokenizer(url, ":/;=&?", true);
+    for (int count = 0; (st.hasMoreTokens()); count++) {
+      String token = st.nextToken();
+      
+      // PM June 29 1997
+      // Added this, to help me understand how this works.
+      // Unless you want each token to be processed, leave this commented out
+      // but don't delete it.
+      //DriverManager.println("wellFormedURL: state="+state+" count="+count+" token='"+token+"'");
+      
+      // PM Aug 2 1997 - Modified to allow multiple backends
+      if (count <= 3) {
+       if ((count % 2) == 1 && token.equals(":"))
+         ;
+       else if((count % 2) == 0) {
+         boolean found=(count==0)?true:false;
+         for(int tmp=0;tmp<protocols.length;tmp++) {
+           if(token.equals(protocols[tmp])) {
+             // PM June 29 1997 Added this property to enable the driver
+             // to handle multiple backend protocols.
+             if(count == 2 && tmp > 0) {
+               urlProps.put("Protocol",token);
+               found=true;
+             }
+           }
+         }
+         
+         if(found == false)
+           return null;
+       } else return null;
+      }
+      else if (count > 3) {
+       if (count == 4 && token.equals("/")) state = 0;
+       else if (count == 4) {
+         urlProps.put("PGDBNAME", token);
+         state = -2;
        }
-
-       /**
-         * Construct a new driver and register it with DriverManager
-         *
-         * @exception SQLException for who knows what!
-         */
-       public Driver() throws SQLException
-       {
-               java.sql.DriverManager.registerDriver(this);
+       else if (count == 5 && state == 0 && token.equals("/"))
+         state = 1;
+       else if (count == 5 && state == 0)
+         return null;
+       else if (count == 6 && state == 1)
+         urlProps.put("PGHOST", token);
+       else if (count == 7 && token.equals(":")) state = 2;
+       else if (count == 8 && state == 2) {
+         try {
+           Integer portNumber = Integer.decode(token);
+           urlProps.put("PGPORT", portNumber.toString());
+         } catch (Exception e) {
+           return null;
+         }
        }
-       
-       /**
-        * 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);
+       else if ((count == 7 || count == 9) &&
+                (state == 1 || state == 2) && token.equals("/"))
+         state = -1;
+       else if (state == -1) {
+         urlProps.put("PGDBNAME", token);
+         state = -2;
        }
-
-       /**
-        * 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;
+       else if (state <= -2 && (count % 2) == 1) {
+         // PM Aug 2 1997 - added tests for ? and &
+         if (token.equals(";") || token.equals("?") || token.equals("&") ) state = -3;
+         else if (token.equals("=")) state = -5;
        }
-
-       /**
-        * 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;
+       else if (state <= -2 && (count % 2) == 0) {
+         if (state == -3) key = token;
+         else if (state == -5) {
+           value = token;
+           //DriverManager.println("put("+key+","+value+")");
+           urlProps.put(key, value);
+           state = -2;
+         }
        }
+      }
+    }
+    
+    // PM June 29 1997
+    // This now outputs the properties only if we are logging
+    if(DriverManager.getLogStream() != null)
+      urlProps.list(DriverManager.getLogStream());
+    
+    return urlProps;
+    
+  }
+  
+  /**
+   * Returns the hostname portion of the URL
+   */
+  public String host()
+  {
+    return props.getProperty("PGHOST","localhost");
+  }
+  
+  /**
+   * Returns the port number portion of the URL
+   * or -1 if no port was specified
+   */
+  public int port()
+  {
+    return Integer.parseInt(props.getProperty("PGPORT","5432"));
+  }
+  
+  /**
+   * Returns the database name of the URL
+   */
+  public String database()
+  {
+    return props.getProperty("PGDBNAME");
+  }
+  
+  /**
+   * Returns any property
+   */
+  public String property(String name)
+  {
+    return props.getProperty(name);
+  }
 }
 
-/**
- * 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;
-       }
-}
index 3d27dc6..2beb1d2 100644 (file)
@@ -13,77 +13,91 @@ import postgresql.*;
  */
 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;
-       }
+  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;
+  }
 }
index 89518dc..22c0c03 100644 (file)
@@ -14,18 +14,132 @@ import postgresql.*;
  */
 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;
-       }
+  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) throws SQLException
+  {
+    this.type = type;
+    this.value = value;
+  }
+  
+  /**
+   * This returns true if the object is a 'box'
+   */
+  public boolean isBox()
+  {
+    return type.equals("box");
+  }
+  
+  /**
+   * This returns a PGbox object, or null if it's not
+   * @return PGbox
+   */
+  public PGbox getBox() throws SQLException
+  {
+    if(isBox())
+      return new PGbox(value);
+    return null;
+  }
+  
+  /**
+   * This returns true if the object is a 'point'
+   */
+  public boolean isCircle()
+  {
+    return type.equals("circle");
+  }
+  
+  /**
+   * This returns a PGcircle object, or null if it's not
+   * @return PGcircle
+   */
+  public PGcircle getCircle() throws SQLException
+  {
+    if(isCircle())
+      return new PGcircle(value);
+    return null;
+  }
+  
+  /**
+   * This returns true if the object is a 'lseg' (line segment)
+   */
+  public boolean isLseg()
+  {
+    return type.equals("lseg");
+  }
+  
+  /**
+   * This returns a PGlsegobject, or null if it's not
+   * @return PGlseg
+   */
+  public PGlseg getLseg() throws SQLException
+  {
+    if(isLseg())
+      return new PGlseg(value);
+    return null;
+  }
+  
+  /**
+   * This returns true if the object is a 'path'
+   */
+  public boolean isPath()
+  {
+    return type.equals("path");
+  }
+  
+  /**
+   * This returns a PGpath object, or null if it's not
+   * @return PGpath
+   */
+  public PGpath getPath() throws SQLException
+  {
+    if(isPath())
+      return new PGpath(value);
+    return null;
+  }
+  
+  /**
+   * This returns true if the object is a 'point'
+   */
+  public boolean isPoint()
+  {
+    return type.equals("point");
+  }
+  
+  /**
+   * This returns a PGpoint object, or null if it's not
+   * @return PGpoint object
+   */
+  public PGpoint getPoint() throws SQLException
+  {
+    if(isPoint())
+      return new PGpoint(value);
+    return null;
+  }
+  
+  /**
+   * This returns true if the object is a 'polygon'
+   */
+  public boolean isPolygon()
+  {
+    return type.equals("polygon");
+  }
+  
+  /**
+   * This returns a PGpolygon object, or null if it's not
+   * @return PGpolygon
+   */
+  public PGpolygon getPolygon() throws SQLException
+  {
+    if(isPolygon())
+      return new PGpolygon(value);
+    return null;
+  }
 }
index c5894e7..d9222ee 100644 (file)
@@ -9,9 +9,6 @@ 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.
@@ -54,792 +51,793 @@ import postgresql.*;
  */
 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;
-       }
+  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;
+  }
 }
+
index a6974b3..f058e2e 100644 (file)
@@ -16,414 +16,415 @@ import postgresql.*;
  */
 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];
-       }
+  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];
+  }
 }
+