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 ff7ec7c26f2404fb0c4db65eeb7b468831c5464f..ede69bbb121e82e6d6794dab6e2577514a8c9d76 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 aa354b61fe1f2a8615d4d7986b177becb6a323a5..a208970ae24a81d043a37d024e8d7b7e76f4772c 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 259829c3fb404b7d4d385ae6c9a7a753babf0ca4..00485b258566ef6b59d271a9d9fecb6aa999d128 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 055a65601aa1f77f56f8ad1632f95235a29818f0..674c96fc66d4f13f8c19558e75b3bb8f049a0ce9 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 3d27dc6c786c7cd9f8fbb8fdde0b0a5df00dd245..2beb1d29411ae74bfff2979cd4fcb0728ac4f5d9 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 89518dc0d8da25a2e538f759795fc50e3dfed4a9..22c0c039c6196fabfdd4353054044029576d5894 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 c5894e7bffc23523d633827f5766afee2efe42aa..d9222ee8ea27a9ef7a623e3d8e84e4e70831a6dc 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 a6974b3076ec4841568dac0485ba1043bda6e85d..f058e2e0ba636c9185a9fbf5a85628cad5da9b43 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];
+  }
 }
+