]> granicus.if.org Git - postgresql/commitdiff
As the email posted to the announce and interfaces list, attached is a tar
authorBruce Momjian <bruce@momjian.us>
Sun, 17 Jan 1999 04:51:59 +0000 (04:51 +0000)
committerBruce Momjian <bruce@momjian.us>
Sun, 17 Jan 1999 04:51:59 +0000 (04:51 +0000)
file containing the latest version of the JDBC driver, allowing it to be
compiled and used under JDK 1.2 and later.

NB: None (well almost none) of the new methods actually do anything. This
release only handles getting it to compile and run. Now this is done, I'll
start working on implementing the new stuff.

Now this tar file replaces everything under src/interfaces/jdbc. I had to
do it this way, rather than diffs, because most of the classes under the
postgresql subdirectory have moved to a new directory under that one, to
enable the support of the two JDBC standards.

Here's a list of files in the tar file. Any file not listed here (in the
postgresql directory) will have to be deleted, otherwise it could cause
the driver to fail:

Peter Mount

26 files changed:
src/interfaces/jdbc/CHANGELOG
src/interfaces/jdbc/Implementation [new file with mode: 0644]
src/interfaces/jdbc/Makefile
src/interfaces/jdbc/README
src/interfaces/jdbc/makeVersion.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/Connection.java
src/interfaces/jdbc/postgresql/Driver.java
src/interfaces/jdbc/postgresql/Field.java
src/interfaces/jdbc/postgresql/ResultSet.java
src/interfaces/jdbc/postgresql/fastpath/Fastpath.java
src/interfaces/jdbc/postgresql/jdbc1/CallableStatement.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc1/Connection.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc1/DatabaseMetaData.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc1/PreparedStatement.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc1/ResultSet.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc1/ResultSetMetaData.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc1/Statement.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc2/CallableStatement.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc2/Connection.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc2/DatabaseMetaData.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc2/PreparedStatement.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc2/ResultSet.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc2/ResultSetMetaData.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/jdbc2/Statement.java [new file with mode: 0644]
src/interfaces/jdbc/postgresql/largeobject/LargeObject.java
src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java

index a11df412b94e5f71664891122181acdc1bc5d141..2f3dfe2eb204145301a3f9183d962ef5614b8a66 100644 (file)
@@ -1,3 +1,42 @@
+Tue Dec 29 15:45:00 GMT 1998
+       - Refreshed the README (which was way out of date)
+
+Tue Dec 29 15:45:00 GMT 1998
+       - Finished adding the additional methods into the JDBC2 driver.
+       - Had to add some explicit package references for the JDK1.2 Javac to
+         cope with the driver
+
+Tue Dec 29 12:40:00 GMT 1998
+       - Fixed package imports and some references to java.sql.ResultSet in
+         various files. Compiled and tested the JDBC1 driver.
+
+Mon Dec 28 19:01:37 GMT 1998
+       - created a new package postgresql.jdbc2 which will contain the JDBC 2
+         specific classes. A similar new package (postgresql.jdbc1) has been
+         created to hold the JDBC 1 specific classes.
+       - modified Makefile to allow compilation of the JDBC 1 & 2 drivers,
+         with the possibility of building a dual-spec driver.
+       - changed the version number in postgresql.Driver to 6.5
+       - modified postgresql.Driver class to initiate the correct driver when
+         used under a 1.1 or 1.2+ JVM.
+       - postgresql.Connection and postgresql.jdbc2.Connection now extends the
+         new class postgresql.ConnectionStub, which allows us to dynamically
+         open the JDBC1 or JDBC2 drivers.
+       - enabled compilation of the driver under Win32 when using the Make
+         from the CygWin package (Cygnus B20.1 was used).
+       - To make future development easier (now we have 2 specifications to
+         work with) the following classes have moved from the postgresql to
+         the postgresql.jdbc1 package:
+               CallableStatement       Connection
+               DatabaseMetaData        PreparedStatement
+               ResultSet               ResultSetMetaData
+               Statement
+         Some of these classes have common code that is not dependent on
+         either JDBC specification. These common code are still in the
+         postgresql package.
+               Ie: postgresql.jdbc1.Connection extends postgresql.Connection
+               and postgresql.jdbc2.Connection extends postgresql.Connection
+
 Web Oct  7 22:00:00 BST 1998
        - removed syncronised from Connection.ExecSQL(). See next entry.
        - added new syncronised locking in the Connection.ExecSQL() and
@@ -82,4 +121,4 @@ Sun Aug 30 11:33:06 BST 1998
          and getSchemaName().
        - Created new class postgresql.util.PGmoney to map the money type
        - Created new class postgresql.geometric.PGline to map the line type
-       
\ No newline at end of file
+       
diff --git a/src/interfaces/jdbc/Implementation b/src/interfaces/jdbc/Implementation
new file mode 100644 (file)
index 0000000..05ceee2
--- /dev/null
@@ -0,0 +1,123 @@
+This short document is provided to help programmers through the internals of
+the PostgreSQL JDBC driver.
+
+Makefile
+--------
+
+All compilation must be done by using Make. This is because there are two
+versions of the driver, one for JDBC1 (for JDK 1.1.x) and the other for JDBC2
+(for JDK 1.2 or later). The makefile determines which version to compile by
+using a helper class makeVersion. This class is only used by make, and is not
+stored in the Jar file.
+
+Note:  It is not sufficient to simply call javac on postgresql/Driver.java as
+       some classes are dynamically loaded, so javac will not compile them.
+
+postgresql.jar
+--------------
+
+This jar file is produced by make, and contains the driver for your JDK
+platform.
+
+Note:  It is possible to compile the driver under say JDK1.1.7, then under
+       JDK 1.2. Because make doesn't remove the old classes before compiling,
+       jar will simply package both sets together. When the driver is loaded,
+       the postgresql.Driver class will sort out which set of classes to use.
+
+Importing packages
+------------------
+
+In user code, you may have to import one or more packages, if and only if you
+are using the non jdbc extensions (like FastPath, or LargeObject).
+
+DO NOT import the postgresql, postgresql.jdbc1 or postgresql.jdbc2 packages!
+
+Internally, some classes will import the packages when there is a link between
+them and the other packages. However, the above rule still applies. It's there
+because Javac becomes confused between the different places that similar class
+names are present.
+
+However, there are places where they need to refer to classes in the postgresql
+package. In this case, import the individual classes, and not the entire
+package.
+
+ie:    import postgresql.Field
+
+    NOT        import postgresql.*
+
+Package Layout
+--------------
+
+The driver is split into several packages:
+
+postgresql                     core classes, common to both JDBC 1 & 2
+postgresql.jdbc1               classes used only in implementing JDBC 1
+postgresql.jdbc2               classes used only in implementing JDBC 2
+postgresql.fastpath            FastPath to backend functions
+postgresql.geometric           2D Geometric types mapped to Java Objects
+postgresql.largeobject         Low level Large Object access
+postgresql.util                        Utility classes
+
+
+Package postgresql
+------------------
+
+This package holds the core classes.
+
+Driver         registers the driver when it's loaded, and determines which
+               Connection class (in jdbc1 or jdbc2 packages) to use when
+               connecting to a database.
+
+Field          Used internally to represent a Field
+PG_Stream      Used internally to manage the network stream.
+
+       These classes contains common code that is not dependent to the
+       two JDBC specifications.
+
+Connection     Common code used in Connections, mainly Network Protocol stuff.
+ResultSet      Common code used in ResultSet's
+
+Package postgresql.fastpath
+---------------------------
+
+Fastpath       Handles executing a function on the PostgreSQL Backend
+FastpathArg    Defines an argument for a function call
+
+Package postgresql.geometric
+----------------------------
+
+PGbox          Maps to postgresql type box
+PGcircle       Maps to postgresql type circle
+PGline         Maps to postgresql type line
+PGlseg         Maps to postgresql type lseg
+PGpath         Maps to postgresql type path
+PGpoint                Maps to postgresql type point
+PGpolygon      Maps to postgresql type polygon
+
+Package postgresql.jdbc1
+------------------------
+
+The classes in this package handle the JDBC 1 Specification, for JDK 1.1.x
+All interfaces in the java.sql package are present here.
+
+Package postgresql.jdbc2
+------------------------
+
+The classes in this package handle the JDBC 2 Specification, for JDK 1.2
+All interfaces in the java.sql, and javax.sql packages are present here.
+
+Package postgresql.largeobject
+------------------------------
+
+LargeObject            Represents an open LargeObject
+LargeObjectManager     Handles the opening and deleting of LargeObjects
+
+Package postgresql.util
+-----------------------
+
+PGmoney                Maps to postgresql type money
+PGobject       Used to represent postgresql types that have no Java equivalent
+PGtokenizer    Helper class for the geometric types
+Serialize      Used to serialise Java objects into tabes, rather than Blobs
+UnixCrypt      Used to handle crypt authentication
+
index bda001e8b0ea03698628aef771db40aab93cf85b..611687dc9a9c52466ffb9ecfeb93fff32c471d29 100644 (file)
@@ -4,12 +4,10 @@
 #    Makefile for Java JDBC interface
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/Makefile,v 1.10 1998/10/08 00:38:18 momjian Exp $
+#    $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/Makefile,v 1.11 1999/01/17 04:51:49 momjian Exp $
 #
 #-------------------------------------------------------------------------
 
-# These are commented out, but would be included in the postgresql source
-
 FIND           = find
 JAR            = jar
 JAVA           = java
@@ -24,7 +22,10 @@ RM           = rm -f
 .SUFFIXES:     .class .java
 .PHONY:                all clean doc examples
 
-all:     postgresql.jar
+# In 6.5, the all rule builds the makeVersion class which then calls make using
+# the jdbc1 or jdbc2 rules
+all:   makeVersion.class
+       make $$($(JAVA) makeVersion)
        @echo ------------------------------------------------------------
        @echo The JDBC driver has now been built. To make it available to
        @echo other applications, copy the postgresql.jar file to a public
@@ -37,7 +38,9 @@ all:    postgresql.jar
        @echo "under unix for HotJava), and add a line containing"
        @echo jdbc.drivers=postgresql.Driver
        @echo
-       @echo More details are in the README file.
+       @echo More details are in the README file and in the main postgresql
+       @echo documentation.
+       @echo
        @echo ------------------------------------------------------------
        @echo To build the examples, type:
        @echo "  make examples"
@@ -56,39 +59,83 @@ doc:
 
 # These classes form the driver. These, and only these are placed into
 # the jar file.
-OBJS=  postgresql/CallableStatement.class \
-       postgresql/Connection.class \
-       postgresql/DatabaseMetaData.class \
-       postgresql/Driver.class \
-       postgresql/Field.class \
-       postgresql/PG_Stream.class \
-       postgresql/PreparedStatement.class \
-       postgresql/ResultSet.class \
-       postgresql/ResultSetMetaData.class \
-       postgresql/Statement.class \
-       postgresql/fastpath/Fastpath.class \
-       postgresql/fastpath/FastpathArg.class \
-       postgresql/geometric/PGbox.class \
-       postgresql/geometric/PGcircle.class \
-       postgresql/geometric/PGline.class \
-       postgresql/geometric/PGlseg.class \
-       postgresql/geometric/PGpath.class \
-       postgresql/geometric/PGpoint.class \
-       postgresql/geometric/PGpolygon.class \
-       postgresql/largeobject/LargeObject.class \
-       postgresql/largeobject/LargeObjectManager.class \
-       postgresql/util/PGmoney.class \
-       postgresql/util/PGobject.class \
-       postgresql/util/PGtokenizer.class \
-       postgresql/util/Serialize.class \
-       postgresql/util/UnixCrypt.class
-
-# If you have problems with the first line, try the second one.
-# This is needed when compiling under Solaris, as the solaris sh doesn't
-# recognise $( )
-postgresql.jar: $(OBJS)
+OBJ_COMMON=    postgresql/Connection.class \
+               postgresql/Driver.class \
+               postgresql/Field.class \
+               postgresql/PG_Stream.class \
+               postgresql/ResultSet.class \
+               postgresql/fastpath/Fastpath.class \
+               postgresql/fastpath/FastpathArg.class \
+               postgresql/geometric/PGbox.class \
+               postgresql/geometric/PGcircle.class \
+               postgresql/geometric/PGline.class \
+               postgresql/geometric/PGlseg.class \
+               postgresql/geometric/PGpath.class \
+               postgresql/geometric/PGpoint.class \
+               postgresql/geometric/PGpolygon.class \
+               postgresql/largeobject/LargeObject.class \
+               postgresql/largeobject/LargeObjectManager.class \
+               postgresql/util/PGmoney.class \
+               postgresql/util/PGobject.class \
+               postgresql/util/PGtokenizer.class \
+               postgresql/util/Serialize.class \
+               postgresql/util/UnixCrypt.class
+
+# These files are unique to the JDBC 1 (JDK 1.1) driver
+OBJ_JDBC1=     postgresql/jdbc1/CallableStatement.class \
+               postgresql/jdbc1/Connection.class \
+               postgresql/jdbc1/DatabaseMetaData.class \
+               postgresql/jdbc1/PreparedStatement.class \
+               postgresql/jdbc1/ResultSet.class \
+               postgresql/jdbc1/ResultSetMetaData.class \
+               postgresql/jdbc1/Statement.class
+
+# These files are unique to the JDBC 2 (JDK 2 nee 1.2) driver
+OBJ_JDBC2=     postgresql/jdbc2/ResultSet.class \
+               postgresql/jdbc2/PreparedStatement.class \
+               postgresql/jdbc2/CallableStatement.class \
+               postgresql/jdbc2/Connection.class \
+               postgresql/jdbc2/DatabaseMetaData.class \
+               postgresql/jdbc2/ResultSetMetaData.class \
+               postgresql/jdbc2/Statement.class
+
+# This rule should never occur, but will be called when makeVersion fails to
+# understand the java.version property correctly.
+jdbc0:
+       @echo
+       @echo FATAL ERROR!
+       @echo
+       @echo makeVersion has not been able to determine what version of
+       @echo the JDK you are using, and hence what version of the driver
+       @echo to compile.
+       @echo
+       @echo There are two versions available, one that conforms to the
+       @echo JDBC 1 specification, and one to the JDBC 2 specification.
+       @echo
+       @echo To build the driver for JDBC 1 (usually for JDK 1.1 thru 1.1.7)
+       @echo then type: make jdbc1
+       @echo
+       @echo To build the driver for JDBC 2 (usually for JDK 1.2 and later)
+       @echo then type: make jdbc2
+       @echo
+       @echo If you still have problems, then please email the interfaces
+       @echo or bugs lists, or better still to me direct (peter@retep.org.uk)
+       @echo
+
+# This rule builds the JDBC1 compliant driver
+jdbc1: $(OBJ_COMMON) $(OBJ_JDBC1) postgresql.jar
+
+# This rule builds the JDBC2 compliant driver
+jdbc2: $(OBJ_COMMON) $(OBJ_JDBC2) postgresql.jar
+
+# If you have problems with this rule, replace the $( ) with ` ` as some
+# shells (mainly sh under Solaris) doesn't recognise $( )
+#
+# Note:        This works by storing all compiled classes under the postgresql
+#      directory. We use this later for compiling the dual-mode driver.
+#
+postgresql.jar: $(OBJ) $(OBJ_COMMON)
        $(JAR) -c0f $@ $$($(FIND) postgresql -name "*.class" -print)
-#      $(JAR) -c0f $@ `$(FIND) postgresql -name "*.class" -print`
 
 # This rule removes any temporary and compiled files from the source tree.
 clean:
index 035ad008bf3a830f694b47a3a2c935055c13580d..f1e3fb8c2a9bd01a3ce4b193c053ff4ddae50388 100644 (file)
@@ -15,8 +15,11 @@ list:
 
        http://www.postgresql.org
 
-By the time V6.3 is released, full documentation will be on the web, and in
-the distribution.
+When PostgreSQL V6.4 was released, full documentation for the driver was
+included in the main documentation tree (under the doc directory).
+
+This file was finally amended on December 29 1998 to account for the major
+changes made to the driver since V6.4 was released.
 
 ---------------------------------------------------------------------------
 
@@ -28,14 +31,35 @@ This will compile the driver, and build a .jar file (Java ARchive).
 REMEMBER: once you have compiled the driver, it will work on ALL platforms
 that support the JDK 1.1 api or later.
 
+The V6.5 driver introduced support for the JDBC2 specification (which is used
+with JDK 1.2 api and later). This caused us some problems because classes
+written for JDBC1 and JDBC2 are not compatible, so a large chunk of the
+driver had to be re-written to accomodate this.
+
+Running make will build a .jar file (postgresql.jar) which contains the driver.
+That jar file will contain the driver for _your_ version of the JDK. That is,
+if you run make using JDK 1.1.7, then you will get the JDBC1 driver. If you
+run using 1.2 then you will get the JDBC2 driver.
+
+Tip: If you want the driver to run on both JDBC1 or JDBC2, first compile under
+JDK 1.1.x, then recompile under JDK 1.2.
+
+In testing, I've done this using 1.1.6 (running under linux), and running make
+on my Win95 based Laptop (CygWin B20.1 was used to get a GNUMake - and a
+decent shell {bash}).
+
+When the .jar file is built, it includes all the classes under postgresql, and
+the driver automatically selects the correct classes.
+
 That means you don't have to compile it on every platform. Believe me, I
 still hear from people who ask me "I've compiled it ok under Solaris, but it
 won't compile under Linux" - there's no difference.
 
-PS: When you run make, don't worry if you see just one or two calls to javac.
-    If, while compiling a class, javac needs another class that's not compiled,
-    it will compile it automatically. This reduces the numer of calls to javac
-    that make has to do.
+PS: When you run make, don't worry if you see more than one or two calls to
+    javac. This is normal, because the driver dynamically loads classes, and
+    the Makefile ensures everything gets compiled.
+
+I advise you don't try running javac outside of make. You may miss something.
 
 Possible problems
 
@@ -47,6 +71,9 @@ postgresql/Driver.java:87: interface java.sql.Connection is an interface. It can
 This is caused by not having the current directory in your CLASSPATH. Under
 Linux/Solaris, unset the CLASSPATH environment variable, and rerun make.
 
+If you are still having problems, I keep a copy of the driver (for different
+versions of the backend) on my web site http://www.retep.org.uk/postgres/
+
 ---------------------------------------------------------------------------
 
 INSTALLING THE DRIVER
@@ -120,23 +147,11 @@ them to the URL. eg:
        jdbc:postgresql:database?user=me
        jdbc:postgresql:database?user=me&password=mypass
 
-By default, the driver doesn't use password authentication. You can enable
-this by adding the argument auth. ie:
-
-       jdbc:postgresql:database?user=me&password=mypass&auth=password
+Previous versions you had to use an auth argument to tell the driver what
+authentication scheme to use when connecting to the database.
 
-or if passing the user & password directly via DriverManager.getConnection():
-
-       jdbc:postgresql:database?auth=password
-
-PS: Password authentication is enabled if the value of auth starts with 'p'.
-    It is case insensitive.
-
-As of postgresql 6.3, Ident (RFC 1413) authentication is also supported.
-Simply use auth=ident in the url.
-
-Also, as of 6.3, a system property of postgresql.auth is supported. This
-defines the default authentication to use. The auth property overides this.
+However, this is no longer supported because the database tells the driver
+what scheme it's expecting.
 
 ---------------------------------------------------------------------------
 
@@ -148,15 +163,6 @@ POSTGRESQL SPECIFICS
 
 Date datatype:
 
-The driver now supports US and European date styles (although it is currently
-limited to postgres format).
-
-Basically the US like to format their dates as mm-dd-yyyy, while in Europe,
-we like to use dd-mm-yyyy. Postgres supports this by the DateStyle variable.
-From psql, you can issue "set datestyle='european';" to set european style,
-and "set datestyle='us';" to set the US format. You can see what the current
-value for this with "show datestyle;".
-
 The driver now issues the "show datestyle;" query when it first connects, so
 any call to ResultSet.getDate() how returns the correct date.
 
@@ -171,13 +177,16 @@ ie:
        s.executeUpdate("show datestyle");
        ..
        s.close();
-       
+
+Please note: This may change later, so that the driver uses the same format
+internally (similar to how the ODBC driver works).
+
                        ------------------
 
 JDBC supports database specific data types using the getObject() call. The
 following types have their own Java equivalents supplied by the driver:
 
-       box, circle, lseg, path, point, polygon
+       box, circle, line, lseg, path, point, polygon
 
 When using the getObject() method on a resultset, it returns a PG_Object,
 which holds the postgres type, and its value. This object also supports
@@ -194,10 +203,9 @@ syntax for writing these to the database.
 
 ---------------------------------------------------------------------------
 
-Peter T Mount, January 11 1998
-home email: pmount@maidast.demon.co.uk http://www.demon.co.uk/finder
-work email: peter@maidstone.gov.uk     http://www.maidstone.gov.uk
-
-Adrian Hall
-     email: adrian@hottub.org
+Peter T Mount, December 29 1998
+home email: pmount@retep.org.uk                http://www.retep.org.uk
+work email: petermount@it.maidstone.gov.uk or peter@taer.maidstone.gov.uk
 
+PS: Please use the home email whenever possible. If you must contact me at work
+then please cc my home one at the same time.
diff --git a/src/interfaces/jdbc/makeVersion.java b/src/interfaces/jdbc/makeVersion.java
new file mode 100644 (file)
index 0000000..3badfa9
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * This class is used by the makefile to determine which version of the
+ * JDK is currently in use, and if it's using JDK1.1.x then it returns JDBC1
+ * and if later, it returns JDBC2
+ *
+ * $Id: makeVersion.java,v 1.1 1999/01/17 04:51:49 momjian Exp $
+ */
+public class makeVersion
+{
+    public static void main(String[] args) {
+       String key     = "java.version";
+       String version = System.getProperty(key);
+       
+       //System.out.println(key+" = \""+version+"\"");
+       
+       // Tip: use print not println here as println breaks the make that
+       // comes with CygWin-B20.1
+       
+       if(version.startsWith("1.0")) {
+           // This will trigger the unknown rule in the makefile
+           System.out.print("jdbc0");
+       } else if(version.startsWith("1.1")) {
+           // This will trigger the building of the JDBC 1 driver
+           System.out.print("jdbc1");
+       } else {
+           // This will trigger the building of the JDBC 2 driver
+           System.out.print("jdbc2");
+       }
+    }
+}
index be15b38abe0979fa2c1e717696662061755f8d3a..df354776f7fc51f7645e8230b82af331e0a2c336 100644 (file)
@@ -1,41 +1,28 @@
 package postgresql;
 
 import java.io.*;
-import java.lang.*;
-import java.lang.reflect.*;
 import java.net.*;
-import java.util.*;
 import java.sql.*;
+import java.util.*;
+import postgresql.Field;
 import postgresql.fastpath.*;
 import postgresql.largeobject.*;
 import postgresql.util.*;
 
 /**
- * A Connection represents a session with a specific database.  Within the
- * context of a Connection, SQL statements are executed and results are
- * returned.
+ * $Id: Connection.java,v 1.14 1999/01/17 04:51:50 momjian Exp $
  *
- * <P>A Connection's database is able to provide information describing
- * its tables, its supported SQL grammar, its stored procedures, the
- * capabilities of this connection, etc.  This information is obtained
- * with the getMetaData method.
+ * This abstract class is used by postgresql.Driver to open either the JDBC1 or
+ * JDBC2 versions of the Connection class.
  *
- * <p><B>Note:</B> By default, the Connection automatically commits changes
- * after executing each statement.  If auto-commit has been disabled, an
- * explicit commit must be done or database changes will not be saved.
- *
- * @see java.sql.Connection
  */
-public class Connection implements java.sql.Connection 
+public abstract class Connection
 {
   // This is the network stream associated with this connection
-  protected PG_Stream pg_stream;
+  public PG_Stream pg_stream;
   
   // This is set by postgresql.Statement.setMaxRows()
-  protected int maxrows = 0;           // maximum no. of rows; 0 = unlimited
-  
-  // This is a cache of the DatabaseMetaData instance for this connection
-  protected DatabaseMetaData metadata;
+  public int maxrows = 0;              // maximum no. of rows; 0 = unlimited
   
   private String PG_HOST;
   private int PG_PORT;
@@ -47,10 +34,10 @@ public class Connection implements java.sql.Connection
   public boolean CONNECTION_OK = true;
   public boolean CONNECTION_BAD = false;
   
-  private boolean autoCommit = true;
-  private boolean readOnly = false;
+  public boolean autoCommit = true;
+  public boolean readOnly = false;
   
-  protected Driver this_driver;
+  public Driver this_driver;
   private String this_url;
   private String cursor = null;        // The positioned update cursor name
   
@@ -78,12 +65,12 @@ public class Connection implements java.sql.Connection
   // It's here, because it's shared across this connection only.
   // Hence it cannot be static within the Field class, because it would then
   // be across all connections, which could be to different backends.
-  protected Hashtable fieldCache = new Hashtable();
+  public Hashtable fieldCache = new Hashtable();
   
   /**
    * This is the current date style of the backend
    */
-  protected int currentDateStyle;
+  public int currentDateStyle;
   
   /**
    * This defines the formats for dates, according to the various date styles.
@@ -113,29 +100,29 @@ public class Connection implements java.sql.Connection
   };
   
   // Now handle notices as warnings, so things like "show" now work
-  protected SQLWarning firstWarning = null;
+  public SQLWarning firstWarning = null;
   
-  /**
-   * Connect to a PostgreSQL database back end.
-   *
-   * <p><b>Important Notice</b>
-   *
-   * <br>Although this will connect to the database, user code should open
-   * the connection via the DriverManager.getConnection() methods only.
-   *
-   * <br>This should only be called from the postgresql.Driver class.
-   *
-   * @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
-  {
+    /**
+     * This is called by Class.forName() from within postgresql.Driver
+     */
+    public Connection()
+    {
+    }
+    
+    /**
+     * This method actually opens the connection. It is called by Driver.
+     *
+     * @param host the hostname of the database back end
+     * @param port the port number of the postmaster process
+     * @param info a Properties[] thing of the user and password
+     * @param database the database to connect to
+     * @param u the URL of the connection
+     * @param d the Driver instantation of the connection
+     * @return a valid connection profile
+     * @exception SQLException if a database access error occurs
+     */
+    protected void openConnection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException
+    {
     // Throw an exception if the user or password properties are missing
     // This occasionally occurs when the client uses the properties version
     // of getConnection(), and is a common question on the email lists
@@ -262,777 +249,472 @@ public class Connection implements java.sql.Connection
        // Also, this query replaced the NULL query issued to test the
        // connection.
        //
-       clearWarnings();
+      firstWarning = null;
        ExecSQL("show datestyle");
        
        // Initialise object handling
        initObjectTypes();
        
        // Mark the connection as ok, and cleanup
-       clearWarnings();
+      firstWarning = null;
        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
-  {
-    if(metadata==null)
-      metadata = new DatabaseMetaData(this);
-    return metadata;
-  }
-  
-  /**
-   * 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 firstWarning;
-  }
-  
-  /**
-   * 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
-  {
-    firstWarning = null;
-  }
-  
-  // **********************************************************
-  //           END OF PUBLIC INTERFACE
-  // **********************************************************
-  
-  /**
-   * This adds a warning to the warning chain.
-   * @param msg message to add
-   */
-  public void addWarning(String msg)
-  {
-    DriverManager.println(msg);
+    }
     
-    // Add the warning to the chain
-    if(firstWarning!=null)
-      firstWarning.setNextWarning(new SQLWarning(msg));
-    else
-      firstWarning = new SQLWarning(msg);
+    // These methods used to be in the main Connection implementation. As they
+    // are common to all implementations (JDBC1 or 2), they are placed here.
+    // This should make it easy to maintain the two specifications.
     
-    // Now check for some specific messages
+    /**
+     * This adds a warning to the warning chain.
+     * @param msg message to add
+     */
+    public void addWarning(String msg)
+    {
+       DriverManager.println(msg);
+       
+       // Add the warning to the chain
+       if(firstWarning!=null)
+           firstWarning.setNextWarning(new SQLWarning(msg));
+       else
+           firstWarning = new SQLWarning(msg);
+       
+       // Now check for some specific messages
+       
+       // This is generated by the SQL "show datestyle"
+       if(msg.startsWith("NOTICE:") && msg.indexOf("DateStyle")>0) {
+           // 13 is the length off "DateStyle is "
+           msg = msg.substring(msg.indexOf("DateStyle is ")+13);
+           
+           for(int i=0;i<dateStyles.length;i+=2)
+               if(msg.startsWith(dateStyles[i]))
+                   currentDateStyle=i+1; // this is the index of the format
+       }
+    }
     
-    // This is generated by the SQL "show datestyle"
-    if(msg.startsWith("NOTICE:") && msg.indexOf("DateStyle")>0) {
-      // 13 is the length off "DateStyle is "
-      msg = msg.substring(msg.indexOf("DateStyle is ")+13);
-
-      for(int i=0;i<dateStyles.length;i+=2)
-       if(msg.startsWith(dateStyles[i]))
-         currentDateStyle=i+1; // this is the index of the format
+    /**
+     * @return the date format for the current date style of the backend
+     */
+    public String getDateStyle()
+    {
+       return dateStyles[currentDateStyle];
     }
-  }
-  
-  /**
-   * @return the date format for the current date style of the backend
-   */
-  public String getDateStyle()
-  {
-    return dateStyles[currentDateStyle];
-  }
-  
-  /**
-   * 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 ResultSet ExecSQL(String sql) throws SQLException
-  {
-    // added Oct 7 1998 to give us thread safety.
-    synchronized(pg_stream) {
-      
-    Field[] fields = null;
-    Vector tuples = new Vector();
-    byte[] buf = new byte[sql.length()];
-    int fqp = 0;
-    boolean hfr = false;
-    String recv_status = null, msg;
-    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);
-       pg_stream.flush();
-      } catch (IOException e) {
-       throw new SQLException("I/O Error: " + e.toString());
-      }
-      
-      while (!hfr || fqp > 0)
-       {
-         Object tup=null;      // holds rows as they are recieved
-         
-         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");
-             tup = pg_stream.ReceiveTuple(fields.length, true);
-             // This implements Statement.setMaxRows()
-             if(maxrows==0 || tuples.size()<maxrows)
-               tuples.addElement(tup);
-             break;
-           case 'C':   // Command Status
-             recv_status = pg_stream.ReceiveString(8192);
-             if (fields != null)
-               hfr = true;
-             else
+    /**
+     * Send a query to the backend.  Returns one of the ResultSet
+     * objects.
+     *
+     * <B>Note:</B> there does not seem to be any method currently
+     * in existance to return the update count.
+     *
+     * @param sql the SQL statement to be executed
+     * @return a ResultSet holding the results
+     * @exception SQLException if a database error occurs
+     */
+    public java.sql.ResultSet ExecSQL(String sql) throws SQLException
+    {
+       // added Oct 7 1998 to give us thread safety.
+       synchronized(pg_stream) {
+           
+           Field[] fields = null;
+           Vector tuples = new Vector();
+           byte[] buf = new byte[sql.length()];
+           int fqp = 0;
+           boolean hfr = false;
+           String recv_status = null, msg;
+           SQLException final_error = null;
+           
+           if (sql.length() > 8192)
+               throw new SQLException("SQL Statement too long: " + sql);
+           try
                {
-                 try
-                   {
-                     pg_stream.SendChar('Q');
-                     pg_stream.SendChar(' ');
-                     pg_stream.SendChar(0);
-                     pg_stream.flush();
-                   } catch (IOException e) {
-                     throw new SQLException("I/O Error: " + e.toString());
-                   }
-                   fqp++;
+                   pg_stream.SendChar('Q');
+                   buf = sql.getBytes();
+                   pg_stream.Send(buf);
+                   pg_stream.SendChar(0);
+                   pg_stream.flush();
+               } catch (IOException e) {
+                   throw new SQLException("I/O Error: " + e.toString());
                }
-             break;
-           case 'D':   // Text Data Transfer
-             if (fields == null)
-               throw new SQLException("Tuple received before MetaData");
-             tup = pg_stream.ReceiveTuple(fields.length, false);
-             // This implements Statement.setMaxRows()
-             if(maxrows==0 || tuples.size()<maxrows)
-               tuples.addElement(tup);
-             break;
-           case 'E':   // Error Message
-             msg = pg_stream.ReceiveString(4096);
-             final_error = new SQLException(msg);
-             hfr = true;
-             break;
-           case 'I':   // Empty Query
-             int t = pg_stream.ReceiveChar();
-             
-             if (t != 0)
-               throw new SQLException("Garbled Data");
-             if (fqp > 0)
-               fqp--;
-             if (fqp == 0)
-               hfr = true;
-             break;
-           case 'N':   // Error Notification
-             addWarning(pg_stream.ReceiveString(4096));
-             break;
-           case 'P':   // Portal Name
-             String pname = pg_stream.ReceiveString(8192);
-             break;
-           case 'T':   // MetaData Field Description
-             if (fields != null)
-               throw new SQLException("Cannot handle multiple result groups");
-             fields = ReceiveFields();
-             break;
-           default:
-             throw new SQLException("Unknown Response Type: " + (char)c);
+           
+           while (!hfr || fqp > 0)
+               {
+                   Object tup=null;    // holds rows as they are recieved
+                   
+                   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");
+                           tup = pg_stream.ReceiveTuple(fields.length, true);
+                           // This implements Statement.setMaxRows()
+                           if(maxrows==0 || tuples.size()<maxrows)
+                               tuples.addElement(tup);
+                           break;
+                       case 'C':       // Command Status
+                           recv_status = pg_stream.ReceiveString(8192);
+                           if (fields != null)
+                               hfr = true;
+                           else
+                               {
+                                   try
+                                       {
+                                           pg_stream.SendChar('Q');
+                                           pg_stream.SendChar(' ');
+                                           pg_stream.SendChar(0);
+                                           pg_stream.flush();
+                                       } catch (IOException e) {
+                                           throw new SQLException("I/O Error: " + e.toString());
+                                       }
+                                   fqp++;
+                               }
+                           break;
+                       case 'D':       // Text Data Transfer
+                           if (fields == null)
+                               throw new SQLException("Tuple received before MetaData");
+                           tup = pg_stream.ReceiveTuple(fields.length, false);
+                           // This implements Statement.setMaxRows()
+                           if(maxrows==0 || tuples.size()<maxrows)
+                               tuples.addElement(tup);
+                           break;
+                       case 'E':       // Error Message
+                           msg = pg_stream.ReceiveString(4096);
+                           final_error = new SQLException(msg);
+                           hfr = true;
+                           break;
+                       case 'I':       // Empty Query
+                           int t = pg_stream.ReceiveChar();
+                           
+                           if (t != 0)
+                               throw new SQLException("Garbled Data");
+                           if (fqp > 0)
+                               fqp--;
+                           if (fqp == 0)
+                               hfr = true;
+                           break;
+                       case 'N':       // Error Notification
+                           addWarning(pg_stream.ReceiveString(4096));
+                           break;
+                       case 'P':       // Portal Name
+                           String pname = pg_stream.ReceiveString(8192);
+                           break;
+                       case 'T':       // MetaData Field Description
+                           if (fields != null)
+                               throw new 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 getResultSet(this, fields, tuples, recv_status, 1);
+           //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.ReceiveIntegerR(2), i;
+       Field[] fields = new Field[nf];
+       
+       for (i = 0 ; i < nf ; ++i)
+           {
+               String typname = pg_stream.ReceiveString(8192);
+               int typid = pg_stream.ReceiveIntegerR(4);
+               int typlen = pg_stream.ReceiveIntegerR(2);
+               fields[i] = new Field(this, typname, typid, typlen);
+           }
+       return fields;
+    }
+    
+    /**
+     * In SQL, a result table can be retrieved through a cursor that
+     * is named.  The current row of a result can be updated or deleted
+     * using a positioned update/delete statement that references the
+     * cursor name.
+     *
+     * We support one cursor per connection.
+     *
+     * setCursorName sets the cursor name.
+     *
+     * @param cursor the cursor name
+     * @exception SQLException if a database access error occurs
+     */
+    public void setCursorName(String cursor) throws SQLException
+    {
+       this.cursor = cursor;
+    }
+    
+    /**
+     * getCursorName gets the cursor name.
+     *
+     * @return the current cursor name
+     * @exception SQLException if a database access error occurs
+     */
+    public String getCursorName() throws SQLException
+    {
+       return cursor;
+    }
+    
+    /**
+     * We are required to bring back certain information by
+     * the DatabaseMetaData class.  These functions do that.
+     *
+     * Method getURL() brings back the URL (good job we saved it)
+     *
+     * @return the url
+     * @exception SQLException just in case...
+     */
+    public String getURL() throws SQLException
+    {
+       return this_url;
+    }
+    
+    /**
+     * Method getUserName() brings back the User Name (again, we
+     * saved it)
+     *
+     * @return the user name
+     * @exception SQLException just in case...
+     */
+    public String getUserName() throws SQLException
+    {
+       return PG_USER;
+    }
+    
+    /**
+     * This returns the Fastpath API for the current connection.
+     *
+     * <p><b>NOTE:</b> This is not part of JDBC, but allows access to
+     * functions on the postgresql backend itself.
+     *
+     * <p>It is primarily used by the LargeObject API
+     *
+     * <p>The best way to use this is as follows:
+     *
+     * <p><pre>
+     * import postgresql.fastpath.*;
+     * ...
+     * Fastpath fp = ((postgresql.Connection)myconn).getFastpathAPI();
+     * </pre>
+     *
+     * <p>where myconn is an open Connection to postgresql.
+     *
+     * @return Fastpath object allowing access to functions on the postgresql
+     * backend.
+     * @exception SQLException by Fastpath when initialising for first time
+     */
+    public Fastpath getFastpathAPI() throws SQLException
+    {
+       if(fastpath==null)
+           fastpath = new Fastpath(this,pg_stream);
+       return fastpath;
+    }
+    
+    // This holds a reference to the Fastpath API if already open
+    private Fastpath fastpath = null;
+    
+    /**
+     * This returns the LargeObject API for the current connection.
+     *
+     * <p><b>NOTE:</b> This is not part of JDBC, but allows access to
+     * functions on the postgresql backend itself.
+     *
+     * <p>The best way to use this is as follows:
+     *
+     * <p><pre>
+     * import postgresql.largeobject.*;
+     * ...
+     * LargeObjectManager lo = ((postgresql.Connection)myconn).getLargeObjectAPI();
+     * </pre>
+     *
+     * <p>where myconn is an open Connection to postgresql.
+     *
+     * @return LargeObject object that implements the API
+     * @exception SQLException by LargeObject when initialising for first time
+     */
+    public LargeObjectManager getLargeObjectAPI() throws SQLException
+    {
+       if(largeobject==null)
+           largeobject = new LargeObjectManager(this);
+       return largeobject;
+    }
+    
+    // This holds a reference to the LargeObject API if already open
+    private LargeObjectManager largeobject = null;
+    
+    /**
+     * This method is used internally to return an object based around
+     * postgresql's more unique data types.
+     *
+     * <p>It uses an internal Hashtable to get the handling class. If the
+     * type is not supported, then an instance of postgresql.util.PGobject
+     * is returned.
+     *
+     * You can use the getValue() or setValue() methods to handle the returned
+     * object. Custom objects can have their own methods.
+     *
+     * In 6.4, this is extended to use the postgresql.util.Serialize class to
+     * allow the Serialization of Java Objects into the database without using
+     * Blobs. Refer to that class for details on how this new feature works.
+     *
+     * @return PGobject for this type, and set to value
+     * @exception SQLException if value is not correct for this type
+     * @see postgresql.util.Serialize
+     */
+    public Object getObject(String type,String value) throws SQLException
+    {
+       try {
+           Object o = objectTypes.get(type);
+           
+           // If o is null, then the type is unknown, so check to see if type
+           // is an actual table name. If it does, see if a Class is known that
+           // can handle it
+           if(o == null) {
+               Serialize ser = new Serialize(this,type);
+               objectTypes.put(type,ser);
+               return ser.fetch(Integer.parseInt(value));
+           }
+           
+           // If o is not null, and it is a String, then its a class name that
+           // extends PGobject.
+           //
+           // This is used to implement the postgresql unique types (like lseg,
+           // point, etc).
+           if(o instanceof String) {
+               // 6.3 style extending PG_Object
+               PGobject obj = null;
+               obj = (PGobject)(Class.forName((String)o).newInstance());
+               obj.setType(type);
+               obj.setValue(value);
+               return (Object)obj;
+           } else {
+               // If it's an object, it should be an instance of our Serialize class
+               // If so, then call it's fetch method.
+               if(o instanceof Serialize)
+                   return ((Serialize)o).fetch(Integer.parseInt(value));
            }
+       } catch(SQLException sx) {
+           // rethrow the exception. Done because we capture any others next
+           sx.fillInStackTrace();
+           throw sx;
+       } catch(Exception ex) {
+           throw new SQLException("Failed to create object for "+type+": "+ex);
        }
-      if (final_error != null)
-       throw final_error;
-      return new ResultSet(this, fields, tuples, recv_status, 1);
+       
+       // should never be reached
+       return null;
     }
-  }
-  
-  /**
-   * Receive the field descriptions from the back end
-   *
-   * @return an array of the Field object describing the fields
-   * @exception SQLException if a database error occurs
-   */
-  private Field[] ReceiveFields() throws SQLException
-  {
-    int nf = pg_stream.ReceiveIntegerR(2), i;
-    Field[] fields = new Field[nf];
     
-    for (i = 0 ; i < nf ; ++i)
-      {
-       String typname = pg_stream.ReceiveString(8192);
-       int typid = pg_stream.ReceiveIntegerR(4);
-       int typlen = pg_stream.ReceiveIntegerR(2);
-       fields[i] = new Field(this, typname, typid, typlen);
-      }
-    return fields;
-  }
-  
-  /**
-   * In SQL, a result table can be retrieved through a cursor that
-   * is named.  The current row of a result can be updated or deleted
-   * using a positioned update/delete statement that references the
-   * cursor name.
-   *
-   * We support one cursor per connection.
-   *
-   * setCursorName sets the cursor name.
-   *
-   * @param cursor the cursor name
-   * @exception SQLException if a database access error occurs
-   */
-  public void setCursorName(String cursor) throws SQLException
-  {
-    this.cursor = cursor;
-  }
-  
-  /**
-   * getCursorName gets the cursor name.
-   *
-   * @return the current cursor name
-   * @exception SQLException if a database access error occurs
-   */
-  public String getCursorName() throws SQLException
-  {
-    return cursor;
-  }
-  
-  /**
-   * We are required to bring back certain information by
-   * the DatabaseMetaData class.  These functions do that.
-   *
-   * Method getURL() brings back the URL (good job we saved it)
-   *
-   * @return the url
-   * @exception SQLException just in case...
-   */
-  public String getURL() throws SQLException
-  {
-    return this_url;
-  }
-  
-  /**
-   * Method getUserName() brings back the User Name (again, we
-   * saved it)
-   *
-   * @return the user name
-   * @exception SQLException just in case...
-   */
-  public String getUserName() throws SQLException
-  {
-    return PG_USER;
-  }
-  
-  /**
-   * This returns the Fastpath API for the current connection.
-   *
-   * <p><b>NOTE:</b> This is not part of JDBC, but allows access to
-   * functions on the postgresql backend itself.
-   *
-   * <p>It is primarily used by the LargeObject API
-   *
-   * <p>The best way to use this is as follows:
-   *
-   * <p><pre>
-   * import postgresql.fastpath.*;
-   * ...
-   * Fastpath fp = ((postgresql.Connection)myconn).getFastpathAPI();
-   * </pre>
-   *
-   * <p>where myconn is an open Connection to postgresql.
-   *
-   * @return Fastpath object allowing access to functions on the postgresql
-   * backend.
-   * @exception SQLException by Fastpath when initialising for first time
-   */
-  public Fastpath getFastpathAPI() throws SQLException
-  {
-    if(fastpath==null)
-      fastpath = new Fastpath(this,pg_stream);
-    return fastpath;
-  }
-  
-  // This holds a reference to the Fastpath API if already open
-  private Fastpath fastpath = null;
-  
-  /**
-   * This returns the LargeObject API for the current connection.
-   *
-   * <p><b>NOTE:</b> This is not part of JDBC, but allows access to
-   * functions on the postgresql backend itself.
-   *
-   * <p>The best way to use this is as follows:
-   *
-   * <p><pre>
-   * import postgresql.largeobject.*;
-   * ...
-   * LargeObjectManager lo = ((postgresql.Connection)myconn).getLargeObjectAPI();
-   * </pre>
-   *
-   * <p>where myconn is an open Connection to postgresql.
-   *
-   * @return LargeObject object that implements the API
-   * @exception SQLException by LargeObject when initialising for first time
-   */
-  public LargeObjectManager getLargeObjectAPI() throws SQLException
-  {
-    if(largeobject==null)
-      largeobject = new LargeObjectManager(this);
-    return largeobject;
-  }
-  
-  // This holds a reference to the LargeObject API if already open
-  private LargeObjectManager largeobject = null;
-  
-  /**
-   * This method is used internally to return an object based around
-   * postgresql's more unique data types.
-   *
-   * <p>It uses an internal Hashtable to get the handling class. If the
-   * type is not supported, then an instance of postgresql.util.PGobject
-   * is returned.
-   *
-   * You can use the getValue() or setValue() methods to handle the returned
-   * object. Custom objects can have their own methods.
-   *
-   * In 6.4, this is extended to use the postgresql.util.Serialize class to
-   * allow the Serialization of Java Objects into the database without using
-   * Blobs. Refer to that class for details on how this new feature works.
-   *
-   * @return PGobject for this type, and set to value
-   * @exception SQLException if value is not correct for this type
-   * @see postgresql.util.Serialize
-   */
-  protected Object getObject(String type,String value) throws SQLException
-  {
-    try {
-      Object o = objectTypes.get(type);
-      
-      // If o is null, then the type is unknown, so check to see if type
-      // is an actual table name. If it does, see if a Class is known that
-      // can handle it
-      if(o == null) {
-       Serialize ser = new Serialize(this,type);
-       objectTypes.put(type,ser);
-       return ser.fetch(Integer.parseInt(value));
-      }
-      
-      // If o is not null, and it is a String, then its a class name that
-      // extends PGobject.
-      //
-      // This is used to implement the postgresql unique types (like lseg,
-      // point, etc).
-      if(o instanceof String) {
-       // 6.3 style extending PG_Object
-       PGobject obj = null;
-       obj = (PGobject)(Class.forName((String)o).newInstance());
-       obj.setType(type);
-       obj.setValue(value);
-       return (Object)obj;
-      } else {
-       // If it's an object, it should be an instance of our Serialize class
-       // If so, then call it's fetch method.
-       if(o instanceof Serialize)
-         return ((Serialize)o).fetch(Integer.parseInt(value));
-      }
-    } catch(SQLException sx) {
-      // rethrow the exception. Done because we capture any others next
-      sx.fillInStackTrace();
-      throw sx;
-    } catch(Exception ex) {
-      throw new SQLException("Failed to create object for "+type+": "+ex);
+    /**
+     * This stores an object into the database.
+     * @param o Object to store
+     * @return OID of the new rectord
+     * @exception SQLException if value is not correct for this type
+     * @see postgresql.util.Serialize
+     */
+    public int putObject(Object o) throws SQLException
+    {
+       try {
+           String type = o.getClass().getName();
+           Object x = objectTypes.get(type);
+           
+           // If x is null, then the type is unknown, so check to see if type
+           // is an actual table name. If it does, see if a Class is known that
+           // can handle it
+           if(x == null) {
+               Serialize ser = new Serialize(this,type);
+               objectTypes.put(type,ser);
+               return ser.store(o);
+           }
+           
+           // If it's an object, it should be an instance of our Serialize class
+           // If so, then call it's fetch method.
+           if(x instanceof Serialize)
+               return ((Serialize)x).store(o);
+           
+           // Thow an exception because the type is unknown
+           throw new SQLException("The object could not be stored. Check that any tables required have already been created in the database.");
+           
+       } catch(SQLException sx) {
+           // rethrow the exception. Done because we capture any others next
+           sx.fillInStackTrace();
+           throw sx;
+       } catch(Exception ex) {
+           throw new SQLException("Failed to store object: "+ex);
+       }
     }
     
-    // should never be reached
-    return null;
-  }
-  
-  /**
-   * This stores an object into the database.
-   * @param o Object to store
-   * @return OID of the new rectord
-   * @exception SQLException if value is not correct for this type
-   * @see postgresql.util.Serialize
-   */
-  protected int putObject(Object o) throws SQLException
-  {
-    try {
-      String type = o.getClass().getName();
-      Object x = objectTypes.get(type);
-      
-      // If x is null, then the type is unknown, so check to see if type
-      // is an actual table name. If it does, see if a Class is known that
-      // can handle it
-      if(x == null) {
-       Serialize ser = new Serialize(this,type);
-       objectTypes.put(type,ser);
-       return ser.store(o);
-      }
-      
-      // If it's an object, it should be an instance of our Serialize class
-      // If so, then call it's fetch method.
-      if(x instanceof Serialize)
-       return ((Serialize)x).store(o);
-      
-      // Thow an exception because the type is unknown
-      throw new SQLException("The object could not be stored. Check that any tables required have already been created in the database.");
-      
-    } catch(SQLException sx) {
-      // rethrow the exception. Done because we capture any others next
-      sx.fillInStackTrace();
-      throw sx;
-    } catch(Exception ex) {
-      throw new SQLException("Failed to store object: "+ex);
+    /**
+     * This allows client code to add a handler for one of postgresql's
+     * more unique data types.
+     *
+     * <p><b>NOTE:</b> This is not part of JDBC, but an extension.
+     *
+     * <p>The best way to use this is as follows:
+     *
+     * <p><pre>
+     * ...
+     * ((postgresql.Connection)myconn).addDataType("mytype","my.class.name");
+     * ...
+     * </pre>
+     *
+     * <p>where myconn is an open Connection to postgresql.
+     *
+     * <p>The handling class must extend postgresql.util.PGobject
+     *
+     * @see postgresql.util.PGobject
+     */
+    public void addDataType(String type,String name)
+    {
+       objectTypes.put(type,name);
     }
-  }
-  
-  /**
-   * This allows client code to add a handler for one of postgresql's
-   * more unique data types.
-   *
-   * <p><b>NOTE:</b> This is not part of JDBC, but an extension.
-   *
-   * <p>The best way to use this is as follows:
-   *
-   * <p><pre>
-   * ...
-   * ((postgresql.Connection)myconn).addDataType("mytype","my.class.name");
-   * ...
-   * </pre>
-   *
-   * <p>where myconn is an open Connection to postgresql.
-   *
-   * <p>The handling class must extend postgresql.util.PGobject
-   *
-   * @see postgresql.util.PGobject
-   */
-  public void addDataType(String type,String name)
-  {
-    objectTypes.put(type,name);
-  }
-  
-  // This holds the available types
-  private Hashtable objectTypes = new Hashtable();
-  
-  // This array contains the types that are supported as standard.
-  //
-  // The first entry is the types name on the database, the second
-  // the full class name of the handling class.
-  //
-  private static final String defaultObjectTypes[][] = {
-    {"box",    "postgresql.geometric.PGbox"},
-    {"circle", "postgresql.geometric.PGcircle"},
-    {"line",   "postgresql.geometric.PGline"},
-    {"lseg",   "postgresql.geometric.PGlseg"},
-    {"path",   "postgresql.geometric.PGpath"},
-    {"point",  "postgresql.geometric.PGpoint"},
-    {"polygon",        "postgresql.geometric.PGpolygon"},
-    {"money",  "postgresql.util.PGmoney"}
-  };
-  
-  // This initialises the objectTypes hashtable
-  private void initObjectTypes()
-  {
-    for(int i=0;i<defaultObjectTypes.length;i++)
-      objectTypes.put(defaultObjectTypes[i][0],defaultObjectTypes[i][1]);
-  }
+    
+    // This holds the available types
+    private Hashtable objectTypes = new Hashtable();
+    
+    // This array contains the types that are supported as standard.
+    //
+    // The first entry is the types name on the database, the second
+    // the full class name of the handling class.
+    //
+    private static final String defaultObjectTypes[][] = {
+       {"box",         "postgresql.geometric.PGbox"},
+       {"circle",      "postgresql.geometric.PGcircle"},
+       {"line",        "postgresql.geometric.PGline"},
+       {"lseg",        "postgresql.geometric.PGlseg"},
+       {"path",        "postgresql.geometric.PGpath"},
+       {"point",       "postgresql.geometric.PGpoint"},
+       {"polygon",     "postgresql.geometric.PGpolygon"},
+       {"money",       "postgresql.util.PGmoney"}
+    };
+    
+    // This initialises the objectTypes hashtable
+    private void initObjectTypes()
+    {
+       for(int i=0;i<defaultObjectTypes.length;i++)
+           objectTypes.put(defaultObjectTypes[i][0],defaultObjectTypes[i][1]);
+    }
+    
+    // These are required by other common classes
+    public abstract java.sql.Statement createStatement() throws SQLException;
+    
+    /**
+     * This returns a resultset. It must be overridden, so that the correct
+     * version (from jdbc1 or jdbc2) are returned.
+     */
+    protected abstract java.sql.ResultSet getResultSet(postgresql.Connection conn, Field[] fields, Vector tuples, String status, int updateCount) throws SQLException;
 }
-
-// ***********************************************************************
-
index 90563d5460712084e9fc3f16800cd7632edac070..b3cc57f4f2f520f831d32683ca5f26a9043e99c7 100644 (file)
@@ -27,8 +27,11 @@ public class Driver implements java.sql.Driver
   // These should be in sync with the backend that the driver was
   // distributed with
   static final int MAJORVERSION = 6;
-  static final int MINORVERSION = 4;
-  
+  static final int MINORVERSION = 5;
+    
+  // Cache the version of the JDK in use
+  static String connectClass;
+    
   static 
   {
     try {
@@ -49,6 +52,13 @@ public class Driver implements java.sql.Driver
    */
   public Driver() throws SQLException
   {
+      // Set the connectClass variable so that future calls will handle the correct
+      // base class
+      if(System.getProperty("java.version").startsWith("1.1")) {
+         connectClass = "postgresql.jdbc1.Connection";
+      } else {
+         connectClass = "postgresql.jdbc2.Connection";
+      }
   }
   
   /**
@@ -84,7 +94,19 @@ public class Driver implements java.sql.Driver
     if((props = parseURL(url,info))==null)
       return null;
     
-    return new Connection (host(), port(), props, database(), url, this);
+    DriverManager.println("Using "+connectClass);
+    
+    try {
+       postgresql.Connection con = (postgresql.Connection)(Class.forName(connectClass).newInstance());
+       con.openConnection (host(), port(), props, database(), url, this);
+       return (java.sql.Connection)con;
+    } catch(ClassNotFoundException ex) {
+       throw new SQLException("The postgresql.jar file does not contain the correct JDBC classes for this JVM. Try rebuilding.\nException thrown was "+ex.toString());
+    } catch(Exception ex2) {
+       throw new SQLException("Something unusual has occured to cause the driver to fail. Please report this exception: "+ex2.toString());
+    }
+    // The old call - remove before posting
+    //return new Connection (host(), port(), props, database(), url, this);
   }
   
   /**
@@ -315,5 +337,16 @@ public class Driver implements java.sql.Driver
   {
     return props.getProperty(name);
   }
+    
+    /**
+     * This method was added in v6.5, and simply throws an SQLException
+     * for an unimplemented method. I decided to do it this way while
+     * implementing the JDBC2 extensions to JDBC, as it should help keep the
+     * overall driver size down.
+     */
+    public static SQLException notImplemented()
+    {
+       return new SQLException("This method is not yet implemented.");
+    }
 }
 
index b39aab20c1574ec84a15c25adf15cecfe0d9fd9d..416ddaa7e27c14acf5ec086dc4ee877eb7cbf501 100644 (file)
@@ -11,13 +11,14 @@ 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
+  public int length;           // Internal Length of this field
+  public int oid;              // OID of the type
+  public 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
+  protected Connection conn;   // Connection Instantation
+  
+  public int sql_type = -1;    // The entry in java.sql.Types for this field
+  public String type_name = null;// The sql type name
   
   /**
    * Construct a field based on the information fed to it.
index 12dcc476af43d117544253087e7859fe468012f4..69a00e67984ed437bac5c6c78e89e308dac7bfed 100644 (file)
@@ -10,61 +10,24 @@ import postgresql.largeobject.*;
 import postgresql.util.*;
 
 /**
- * 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.
- *
- * <P>A ResultSet maintains a cursor pointing to its current row of data.  
- * Initially the cursor is positioned before the first row.  The 'next'
- * method moves the cursor to the next row.
- *
- * <P>The getXXX methods retrieve column values for the current row.  You can
- * retrieve values either using the index number of the column, or by using
- * the name of the column.  In general using the column index will be more
- * efficient.  Columns are numbered from 1.
- *
- * <P>For maximum portability, ResultSet columns within each row should be read
- * in left-to-right order and each column should be read only once.
- *
- *<P> For the getXXX methods, the JDBC driver attempts to convert the
- * underlying data to the specified Java type and returns a suitable Java
- * value.  See the JDBC specification for allowable mappings from SQL types
- * to Java types with the ResultSet getXXX methods.
- *
- * <P>Column names used as input to getXXX methods are case insenstive.  When
- * performing a getXXX using a column name, if several columns have the same
- * name, then the value of the first matching column will be returned.  The
- * column name option is designed to be used when column names are used in the
- * SQL Query.  For columns that are NOT explicitly named in the query, it is
- * best to use column numbers.  If column names were used there is no way for
- * the programmer to guarentee that they actually refer to the intended
- * columns.
- *
- * <P>A ResultSet is automatically closed by the Statement that generated it 
- * when that Statement is closed, re-executed, or is used to retrieve the 
- * next result from a sequence of multiple results.
- *
- * <P>The number, types and properties of a ResultSet's columns are provided by
- * the ResultSetMetaData object returned by the getMetaData method.
- *
- * @see ResultSetMetaData
- * @see java.sql.ResultSet
+ * This class implements the common internal methods used by both JDBC 1 and
+ * JDBC 2 specifications.
  */
-public class ResultSet implements java.sql.ResultSet 
+public abstract class 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()
+  protected Vector rows;                       // The results
+  protected Field fields[];            // The field descriptions
+  protected String status;             // Status of the result
+  protected int updateCount;           // How many rows did we get back?
+  protected int current_row;           // Our pointer to where we are at
+  protected byte[][] this_row;         // the current row result
+  protected Connection connection;     // the connection which we returned from
+  protected SQLWarning warnings = null;        // The warning chain
+  protected 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;
+  protected ResultSet next = null;
   
   /**
    * Create a new ResultSet - Note that we create ResultSets to
@@ -87,710 +50,7 @@ public class ResultSet implements java.sql.ResultSet
     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.
-   *
-   * <p>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.
-   *
-   * <p><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);
-    if (columnIndex < 1 || columnIndex > fields.length)
-      throw new SQLException("Column Index out of range");
-    wasNullFlag = (this_row[columnIndex - 1] == null);
-    if(wasNullFlag)
-      return null;
-    return new String(this_row[columnIndex - 1]);
-  }
-  
-  /**
-   * 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.
-   *
-   * <p>In normal use, the bytes represent the raw values returned by the
-   * backend. However, if the column is an OID, then it is assumed to
-   * refer to a Large Object, and that object is returned as a byte array.
-   *
-   * <p><b>Be warned</b> If the large object is huge, then you may run out
-   * of memory.
-   *
-   * @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);
-    
-    // Handle OID's as BLOBS
-    if(!wasNullFlag)
-      if( fields[columnIndex - 1].getOID() == 26) {
-       LargeObjectManager lom = connection.getLargeObjectAPI();
-       LargeObject lob = lom.open(getInt(columnIndex));
-       byte buf[] = lob.read(lob.size());
-       lob.close();
-       return buf;
-      }
-    
-    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)
-      return null;
-    SimpleDateFormat df = new SimpleDateFormat(connection.getDateStyle());
-    try {
-      return new java.sql.Date(df.parse(s).getTime());
-    } catch (ParseException e) {
-      throw new SQLException("Bad Date Format: at " + e.getErrorOffset() + " in " + s);
-    }
-  }
-  
-  /**
-   * 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);
-    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzz");
-    
-    if (s != null)
-      {
-       int TZ = new Float(s.substring(19)).intValue();
-       TZ = TZ * 60 * 60 * 1000;
-       TimeZone zone = TimeZone.getDefault();
-       zone.setRawOffset(TZ);
-       String nm = zone.getID();
-       s = s.substring(0,18) + nm;
-       try {
-         java.util.Date d = df.parse(s);
-         return new Timestamp(d.getTime());
-       } catch (ParseException e) {
-         throw new SQLException("Bad Timestamp Format: at " + e.getErrorOffset() + " in " + 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.
-   *
-   * <p><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.
-   *
-   *<p> 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 bytes.  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.
-   *
-   * <p>The warning chain is automatically cleared each time a new
-   * row is read.
-   *
-   * <p><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
-   *
-   * <p>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.
-   *
-   * <p>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.
-   *
-   * <p><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
-   *
-   * <p>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.
-   *
-   * <p>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];
-    
-    // some fields can be null, mainly from those returned by MetaData methods
-    if(field==null) {
-      wasNullFlag=true;
-      return null;
-    }
-    
-    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 connection.getObject(field.getTypeName(), getString(columnIndex));
-      }
-  }
-  
-  /**
-   * Get the value of a column in the current row as a Java object
-   *
-   *<p> 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.
-   *
-   * <p>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
@@ -812,9 +72,9 @@ public class ResultSet implements java.sql.ResultSet
    *
    * @return the next ResultSet, or null if there are none
    */
-  public ResultSet getNext()
+  public java.sql.ResultSet getNext()
   {
-    return next;
+    return (java.sql.ResultSet)next;
   }
   
   /**
@@ -887,5 +147,12 @@ public class ResultSet implements java.sql.ResultSet
    {
      return fields[field-1].getOID();
    }
+  
+    /**
+     * This is part of the JDBC API, but is required by postgresql.Field
+     */
+    public abstract void close() throws SQLException;
+    public abstract boolean next() throws SQLException;
+    public abstract String getString(int i) throws SQLException;
 }
 
index ab702f457f2d7c0e91baa72ebbd401e418b203b5..232f8f0248581903f75f7b3e290d2aa227fd7e34 100644 (file)
@@ -7,6 +7,9 @@ import java.util.*;
 import java.sql.*;
 import postgresql.util.*;
 
+// Important: There are a lot of debug code commented out. Please do not
+// delete these.
+
 /**
  * This class implements the Fastpath api.
  *
@@ -54,7 +57,7 @@ public class Fastpath
   {
     this.conn=conn;
     this.stream=stream;
-    DriverManager.println("Fastpath initialised");
+    //DriverManager.println("Fastpath initialised");
   }
   
   /**
@@ -109,7 +112,7 @@ public class Fastpath
     Object result = null; // our result
     while(true) {
       int in = stream.ReceiveChar();
-      DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'");
+      //DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'");
       switch(in)
        {
        case 'V':
@@ -120,7 +123,7 @@ public class Fastpath
          //
        case 'G':
          int sz = stream.ReceiveIntegerR(4);
-         DriverManager.println("G: size="+sz);  //debug
+         //DriverManager.println("G: size="+sz);  //debug
          
          // Return an Integer if
          if(resulttype)
@@ -149,7 +152,7 @@ public class Fastpath
          // Here we simply return res, which would contain the result
          // processed earlier. If no result, this already contains null
        case '0':
-         DriverManager.println("returning "+result);
+         //DriverManager.println("returning "+result);
          return result;
          
        default:
@@ -181,7 +184,7 @@ public class Fastpath
    */
   public Object fastpath(String name,boolean resulttype,FastpathArg[] args) throws SQLException
   {
-    DriverManager.println("Fastpath: calling "+name);
+    //DriverManager.println("Fastpath: calling "+name);
     return fastpath(getID(name),resulttype,args);
   }
   
diff --git a/src/interfaces/jdbc/postgresql/jdbc1/CallableStatement.java b/src/interfaces/jdbc/postgresql/jdbc1/CallableStatement.java
new file mode 100644 (file)
index 0000000..2a90157
--- /dev/null
@@ -0,0 +1,308 @@
+package postgresql.jdbc1;
+
+// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 2 class in the
+// postgresql.jdbc2 package.
+
+import java.sql.*;
+import java.math.*;
+
+/**
+ * CallableStatement is used to execute SQL stored procedures.
+ *
+ * <p>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 registered as an OUT
+ * parameter. The other parameters may be used for input, output or both.
+ * Parameters are refered to sequentially, by number. The first parameter
+ * is 1.
+ *
+ * {?= call <procedure-name>[<arg1>,<arg2>, ...]}                 
+ * {call <procedure-name>[<arg1>,<arg2>, ...]}       
+ *
+ *
+ * <p>IN parameter values 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.
+ *
+ * <p>A Callable statement may return a ResultSet or multiple ResultSets.
+ * Multiple ResultSets are handled using operations inherited from
+ * Statement.
+ *
+ * <p>For maximum portability, a call's ResultSets and update counts should 
+ * be processed prior to getting the values of output parameters.        
+ *
+ * @see Connection#prepareCall
+ * @see ResultSet
+ */
+
+public class CallableStatement extends PreparedStatement implements java.sql.CallableStatement
+{
+  /**
+   * @exception SQLException on failure
+   */
+  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.
+   *
+   * <p>Note: When reading the value of an out parameter, you must use
+   * the getXXX method whose Java type XXX corresponds to the
+   * parameter's registered SQL type.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @param sqlType SQL type code defined by java.sql.Types; for
+   * parameters of type Numeric or Decimal use the version of
+   * registerOutParameter that accepts a scale value
+   * @exception SQLException if a database-access error occurs.
+   */
+  public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException {
+  }
+  
+  /**
+   * You must also specify the scale for numeric/decimal types:
+   *
+   * <p>Note: When reading the value of an out parameter, you must use
+   * the getXXX method whose Java type XXX corresponds to the
+   * parameter's registered SQL type.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @param sqlType use either java.sql.Type.NUMERIC or java.sql.Type.DECIMAL
+   * @param scale a value greater than or equal to zero representing the
+   * desired number of digits to the right of the decimal point
+   * @exception SQLException if a database-access error occurs.
+   */
+  public void registerOutParameter(int parameterIndex, int sqlType,
+                                  int scale) throws SQLException
+  {
+  }
+  
+  // Old api?
+  //public boolean isNull(int parameterIndex) throws SQLException {
+  //return true;
+  //}
+  
+  /**
+   * An OUT parameter may have the value of SQL NULL; wasNull
+   * reports whether the last value read has this special value.
+   *
+   * <p>Note: You must first call getXXX on a parameter to read its
+   * value and then call wasNull() to see if the value was SQL NULL.
+   * @return true if the last parameter read was SQL NULL
+   * @exception SQLException if a database-access error occurs.
+   */
+  public boolean wasNull() throws SQLException {
+    // check to see if the last access threw an exception
+    return false; // fake it for now
+  }
+  
+  // Old api?
+  //public String getChar(int parameterIndex) throws SQLException {
+  //return null;
+  //}
+  
+  /**
+   * Get the value of a CHAR, VARCHAR, or LONGVARCHAR parameter as a
+   * Java String.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is null
+   * @exception SQLException if a database-access error occurs.
+   */
+  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;
+  //}
+  
+  /**
+   * Get the value of a BIT parameter as a Java boolean.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is false
+   * @exception SQLException if a database-access error occurs.
+   */
+  public boolean getBoolean(int parameterIndex) throws SQLException {
+    return false;
+  }
+  
+  /**
+   * Get the value of a TINYINT parameter as a Java byte.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is 0
+   * @exception SQLException if a database-access error occurs.
+   */
+  public byte getByte(int parameterIndex) throws SQLException {
+    return 0;
+  }
+  
+  /**
+   * Get the value of a SMALLINT parameter as a Java short.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is 0
+   * @exception SQLException if a database-access error occurs.
+   */
+  public short getShort(int parameterIndex) throws SQLException {
+    return 0;
+  }
+  
+  /**
+   * Get the value of an INTEGER parameter as a Java int.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is 0
+   * @exception SQLException if a database-access error occurs.
+   */
+public int getInt(int parameterIndex) throws SQLException {
+    return 0;
+  }
+  
+  /**
+   * Get the value of a BIGINT parameter as a Java long.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is 0
+   * @exception SQLException if a database-access error occurs.
+   */
+  public long getLong(int parameterIndex) throws SQLException {
+    return 0;
+  }
+  
+  /**
+   * Get the value of a FLOAT parameter as a Java float.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is 0
+   * @exception SQLException if a database-access error occurs.
+   */
+  public float getFloat(int parameterIndex) throws SQLException {
+    return (float) 0.0;
+  }
+  
+  /**
+   * Get the value of a DOUBLE parameter as a Java double.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is 0
+   * @exception SQLException if a database-access error occurs.
+   */
+  public double getDouble(int parameterIndex) throws SQLException {
+    return 0.0;
+  }
+  
+  /**
+   * Get the value of a NUMERIC parameter as a java.math.BigDecimal
+   * object.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @param scale a value greater than or equal to zero representing the
+   * desired number of digits to the right of the decimal point
+   * @return the parameter value; if the value is SQL NULL, the result is null
+   * @exception SQLException if a database-access error occurs.
+   */
+  public BigDecimal getBigDecimal(int parameterIndex, int scale)
+       throws SQLException {
+        return null;
+  }
+  
+  /**
+   * Get the value of a SQL BINARY or VARBINARY parameter as a Java
+   * byte[]
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is null
+   * @exception SQLException if a database-access error occurs.
+   */
+  public byte[] getBytes(int parameterIndex) throws SQLException {
+    return null;
+  }
+  
+  // New API (JPM) (getLongVarBinary)
+  //public byte[] getBinaryStream(int parameterIndex) throws SQLException {
+  //return null;
+  //}
+  
+  /**
+   * Get the value of a SQL DATE parameter as a java.sql.Date object
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is null
+   * @exception SQLException if a database-access error occurs.
+   */
+  public java.sql.Date getDate(int parameterIndex) throws SQLException {
+    return null;
+  }
+  
+  /**
+   * Get the value of a SQL TIME parameter as a java.sql.Time object.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is null
+   * @exception SQLException if a database-access error occurs.
+   */
+  public java.sql.Time getTime(int parameterIndex) throws SQLException {
+    return null;
+  }
+  
+  /**
+   * Get the value of a SQL TIMESTAMP parameter as a java.sql.Timestamp object.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is null
+   * @exception SQLException if a database-access error occurs.
+   */
+  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.
+  /**
+   * Get the value of a parameter as a Java object.
+   *
+   * <p>This method returns a Java object whose type coresponds to the
+   * SQL type that was registered for this parameter using
+   * registerOutParameter.
+   *
+   * <P>Note that this method may be used to read datatabase-specific,
+   * abstract data types. This is done by specifying a targetSqlType
+   * of java.sql.types.OTHER, which allows the driver to return a
+   * database-specific Java type.
+   *
+   * <p>See the JDBC spec's "Dynamic Programming" chapter for details.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return A java.lang.Object holding the OUT parameter value.
+   * @exception SQLException if a database-access error occurs.
+   */
+  public Object getObject(int parameterIndex)
+       throws SQLException {
+        return null;
+  }
+}
+
diff --git a/src/interfaces/jdbc/postgresql/jdbc1/Connection.java b/src/interfaces/jdbc/postgresql/jdbc1/Connection.java
new file mode 100644 (file)
index 0000000..790dfa6
--- /dev/null
@@ -0,0 +1,366 @@
+package postgresql.jdbc1;
+
+// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 2 class in the
+// postgresql.jdbc2 package.
+
+import java.io.*;
+import java.lang.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.util.*;
+import java.sql.*;
+import postgresql.Field;
+import postgresql.fastpath.*;
+import postgresql.largeobject.*;
+import postgresql.util.*;
+
+/**
+ * $Id: Connection.java,v 1.1 1999/01/17 04:51:53 momjian Exp $
+ *
+ * A Connection represents a session with a specific database.  Within the
+ * context of a Connection, SQL statements are executed and results are
+ * returned.
+ *
+ * <P>A Connection's database is able to provide information describing
+ * its tables, its supported SQL grammar, its stored procedures, the
+ * capabilities of this connection, etc.  This information is obtained
+ * with the getMetaData method.
+ *
+ * <p><B>Note:</B> By default, the Connection automatically commits changes
+ * after executing each statement.  If auto-commit has been disabled, an
+ * explicit commit must be done or database changes will not be saved.
+ *
+ * @see java.sql.Connection
+ */
+public class Connection extends postgresql.Connection implements java.sql.Connection 
+{
+  // This is a cache of the DatabaseMetaData instance for this connection
+  protected DatabaseMetaData metadata;
+  
+  /**
+   * 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
+  {
+    if(metadata==null)
+      metadata = new DatabaseMetaData(this);
+    return metadata;
+  }
+  
+  /**
+   * 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 firstWarning;
+  }
+  
+  /**
+   * 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
+  {
+    firstWarning = null;
+  }
+    
+    /**
+     * This overides the method in postgresql.Connection and returns a
+     * ResultSet.
+     */
+    protected java.sql.ResultSet getResultSet(postgresql.Connection conn, Field[] fields, Vector tuples, String status, int updateCount) throws SQLException
+    {
+       return new postgresql.jdbc1.ResultSet((postgresql.jdbc1.Connection)conn,fields,tuples,status,updateCount);
+    }
+    
+}
+
+// ***********************************************************************
+
diff --git a/src/interfaces/jdbc/postgresql/jdbc1/DatabaseMetaData.java b/src/interfaces/jdbc/postgresql/jdbc1/DatabaseMetaData.java
new file mode 100644 (file)
index 0000000..b7e90e8
--- /dev/null
@@ -0,0 +1,2526 @@
+package postgresql.jdbc1;
+
+// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 2 class in the
+// postgresql.jdbc2 package.
+
+import java.sql.*;
+import java.util.*;
+import postgresql.Field;
+
+/**
+ * This class provides information about the database as a whole.
+ *
+ * <p>Many of the methods here return lists of information in ResultSets.  You
+ * can use the normal ResultSet methods such as getString and getInt to 
+ * retrieve the data from these ResultSets.  If a given form of metadata is
+ * not available, these methods should throw a SQLException.
+ *
+ * <p>Some of these methods take arguments that are String patterns.  These
+ * arguments all have names such as fooPattern.  Within a pattern String,
+ * "%" means match any substring of 0 or more characters, and "_" means
+ * match any one character.  Only metadata entries matching the search
+ * pattern are returned.  if a search pattern argument is set to a null
+ * ref, it means that argument's criteria should be dropped from the
+ * search.
+ *
+ * <p>A SQLException will be throws if a driver does not support a meta
+ * data method.  In the case of methods that return a ResultSet, either
+ * a ResultSet (which may be empty) is returned or a SQLException is
+ * thrown.
+ *
+ * @see java.sql.DatabaseMetaData
+ */
+public class DatabaseMetaData implements java.sql.DatabaseMetaData 
+{
+  Connection connection;               // The connection association
+  
+  // These define various OID's. Hopefully they will stay constant.
+  static final int iVarcharOid = 1043; // OID for varchar
+  static final int iBoolOid = 16;      // OID for bool
+  static final int iInt2Oid = 21;      // OID for int2
+  static final int iInt4Oid = 23;      // OID for int4
+  static final int VARHDRSZ =  4;      // length for int4
+  
+  // This is a default value for remarks
+  private static final byte defaultRemarks[]="no remarks".getBytes();
+  
+  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.
+   *
+   * <p>Note that PostgreSQL 6.3 has a system catalog called pg_version - 
+   * however, select * from pg_version on any database retrieves
+   * no rows.
+   *
+   * <p>For now, we will return the version 6.3 (in the hope 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.4");
+  }
+  
+  /**
+   * 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.
+   *
+   * <p>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 false;
+  }
+  
+  /**
+   * 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 true;
+  }
+  
+  /**
+   * 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. 
+   *
+   * <p>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.
+   *
+   * <p>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 null;
+  }
+  
+  /**
+   * Get a comma separated list of all a database's SQL keywords that
+   * are NOT also SQL92 keywords.
+   *
+   * <p>Within PostgreSQL, the keywords are found in
+   *   src/backend/parser/keywords.c
+   *
+   * <p>For SQL Keywords, I took the list provided at
+   *   <a href="http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt">
+   * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt</a>
+   * 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 _)
+   *
+   * <p>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?
+   *
+   * <p>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.
+   *
+   * <p>e.g.
+   *
+   * <br><pre>
+   * select count(C) as C_COUNT from T group by C;
+   *
+   * </pre><br>
+   * 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?
+   * 
+   * <br>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.
+   *
+   * <p>This changed from false to true in v6.2 of the driver, as this
+   * support was added to the backend.
+   *
+   * @return true if so
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean supportsNonNullableColumns() throws SQLException
+  {
+    return true;
+  }
+  
+  /**
+   * Does this driver support the minimum ODBC SQL grammar.  This
+   * grammar is defined at:
+   *
+   * <p><a href="http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm">http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm</a>
+   *
+   * <p>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
+  {
+    // PM Sep 29 97 - changed from "." as we don't support catalogs.
+    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...
+   *
+   * <p>"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:
+   *
+   * <p><pre>
+   * 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;
+   * </pre><p>
+   *
+   * 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 true;
+  }
+  
+  /**
+   * Get a description of stored procedures available in a catalog
+   * 
+   * <p>Only procedure descriptions matching the schema and procedure
+   * name criteria are returned.  They are ordered by PROCEDURE_SCHEM
+   * and PROCEDURE_NAME
+   *
+   * <p>Each procedure description has the following columns:
+   * <ol>
+   * <li><b>PROCEDURE_CAT</b> String => procedure catalog (may be null)
+   * <li><b>PROCEDURE_SCHEM</b> String => procedure schema (may be null)
+   * <li><b>PROCEDURE_NAME</b> String => procedure name
+   * <li><b>Field 4</b> reserved (make it null)
+   * <li><b>Field 5</b> reserved (make it null)
+   * <li><b>Field 6</b> reserved (make it null)
+   * <li><b>REMARKS</b> String => explanatory comment on the procedure
+   * <li><b>PROCEDURE_TYPE</b> short => kind of procedure
+   *   <ul>
+   *    <li> procedureResultUnknown - May return a result
+   *   <li> procedureNoResult - Does not return a result
+   *   <li> procedureReturnsResult - Returns a result
+   *    </ul>
+   * </ol>
+   *
+   * @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
+  {
+    // the field descriptors for the new ResultSet
+    Field f[] = new Field[8];
+    java.sql.ResultSet r;      // ResultSet for the SQL query that we need to do
+    Vector v = new Vector();           // The new ResultSet tuple stuff
+    
+    byte remarks[] = defaultRemarks;
+    
+    f[0] = new Field(connection, "PROCEDURE_CAT",   iVarcharOid, 32);
+    f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32);
+    f[2] = new Field(connection, "PROCEDURE_NAME",  iVarcharOid, 32);
+    f[3] = f[4] = f[5] = null; // reserved, must be null for now
+    f[6] = new Field(connection, "REMARKS",       iVarcharOid, 8192);
+    f[7] = new Field(connection, "PROCEDURE_TYPE", iInt2Oid,   2);
+    
+    // If the pattern is null, then set it to the default
+    if(procedureNamePattern==null)
+      procedureNamePattern="%";
+    
+    r = connection.ExecSQL("select proname, proretset from pg_proc where proname like '"+procedureNamePattern.toLowerCase()+"' order by proname");
+    
+    while (r.next())
+      {
+       byte[][] tuple = new byte[8][0];
+       
+       tuple[0] = null;                        // Catalog name
+       tuple[1] = null;                        // Schema name
+       tuple[2] = r.getBytes(1);               // Procedure name
+       tuple[3] = tuple[4] = tuple[5] = null;  // Reserved
+       tuple[6] = remarks;                     // Remarks
+       
+       if (r.getBoolean(2))
+         tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureReturnsResult).getBytes();
+       else
+         tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureNoResult).getBytes();
+       
+       v.addElement(tuple);
+      }
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+  
+  /**
+   * Get a description of a catalog's stored procedure parameters
+   * and result columns.
+   *
+   * <p>Only descriptions matching the schema, procedure and parameter
+   * name criteria are returned. They are ordered by PROCEDURE_SCHEM
+   * and PROCEDURE_NAME. Within this, the return value, if any, is
+   * first. Next are the parameter descriptions in call order. The
+   * column descriptions follow in column number order.
+   *
+   * <p>Each row in the ResultSet is a parameter description or column 
+   * description with the following fields:
+   * <ol>
+   * <li><b>PROCEDURE_CAT</b> String => procedure catalog (may be null)
+   * <li><b>PROCEDURE_SCHE</b>M String => procedure schema (may be null)
+   * <li><b>PROCEDURE_NAME</b> String => procedure name
+   * <li><b>COLUMN_NAME</b> String => column/parameter name
+   * <li><b>COLUMN_TYPE</b> Short => kind of column/parameter:
+   * <ul><li>procedureColumnUnknown - nobody knows
+   * <li>procedureColumnIn - IN parameter
+   * <li>procedureColumnInOut - INOUT parameter
+   * <li>procedureColumnOut - OUT parameter
+   * <li>procedureColumnReturn - procedure return value
+   * <li>procedureColumnResult - result column in ResultSet
+   * </ul>
+   * <li><b>DATA_TYPE</b> short => SQL type from java.sql.Types
+   * <li><b>TYPE_NAME</b> String => SQL type name
+   * <li><b>PRECISION</b> int => precision
+   * <li><b>LENGTH</b> int => length in bytes of data
+   * <li><b>SCALE</b> short => scale
+   * <li><b>RADIX</b> short => radix
+   * <li><b>NULLABLE</b> short => can it contain NULL?
+   * <ul><li>procedureNoNulls - does not allow NULL values
+   * <li>procedureNullable - allows NULL values
+   * <li>procedureNullableUnknown - nullability unknown
+   * <li><b>REMARKS</b> String => comment describing parameter/column
+   * </ol>
+   * @param catalog This is ignored in postgresql, advise this is set to null
+   * @param schemaPattern This is ignored in postgresql, advise this is set to null
+   * @param procedureNamePattern a procedure name pattern
+   * @param columnNamePattern a column name pattern
+   * @return each row is a stored procedure parameter or column description
+   * @exception SQLException if a database-access error occurs
+   * @see #getSearchStringEscape
+   */
+  // Implementation note: This is required for Borland's JBuilder to work
+  public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException
+  {
+    if(procedureNamePattern==null)
+      procedureNamePattern="%";
+    
+    if(columnNamePattern==null)
+      columnNamePattern="%";
+    
+    // for now, this returns an empty result set.
+    Field f[] = new Field[13];
+    ResultSet r;       // ResultSet for the SQL query that we need to do
+    Vector v = new Vector();           // The new ResultSet tuple stuff
+    
+    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] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32);
+    f[4] = new Field(connection, new String("COLUMN_TYPE"), iInt2Oid, 2);
+    f[5] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2);
+    f[6] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32);
+    f[7] = new Field(connection, new String("PRECISION"), iInt4Oid, 4);
+    f[8] = new Field(connection, new String("LENGTH"), iInt4Oid, 4);
+    f[9] = new Field(connection, new String("SCALE"), iInt2Oid, 2);
+    f[10] = new Field(connection, new String("RADIX"), iInt2Oid, 2);
+    f[11] = new Field(connection, new String("NULLABLE"), iInt2Oid, 2);
+    f[12] = new Field(connection, new String("REMARKS"), iVarcharOid, 32);
+    
+    // add query loop here
+    
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+  
+  /**
+   * Get a description of tables available in a catalog.              
+   *
+   * <p>Only table descriptions matching the catalog, schema, table
+   * name and type criteria are returned. They are ordered by
+   * TABLE_TYPE, TABLE_SCHEM and TABLE_NAME.                      
+   * 
+   * <p>Each table 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>TABLE_TYPE</b> String => table type. Typical types are "TABLE",
+   * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL
+   * TEMPORARY", "ALIAS", "SYNONYM".                             
+   * <li><b>REMARKS</b> String => explanatory comment on the table
+   * </ol>
+   *
+   * <p>The valid values for the types parameter are:
+   * "TABLE", "INDEX", "LARGE OBJECT", "SEQUENCE", "SYSTEM TABLE" and
+   * "SYSTEM INDEX"
+   *
+   * @param catalog a catalog name; For postgresql, this is ignored, and
+   * should be set to null
+   * @param schemaPattern a schema name pattern; For postgresql, this is ignored, and
+   * should be set to null
+   * @param tableNamePattern a table name pattern. For all tables this should be "%"
+   * @param types a list of table types to include; null returns
+   * all types
+   * @return each row is a table description      
+   * @exception SQLException if a database-access error occurs.
+   */
+  public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException
+  {
+    // Handle default value for types
+    if(types==null)
+      types = defaultTableTypes;
+    
+    if(tableNamePattern==null)
+      tableNamePattern="%";
+    
+    // the field descriptors for the new ResultSet
+    Field f[] = new Field[5];
+    java.sql.ResultSet r;      // ResultSet for the SQL query that we need to do
+    Vector v = new Vector();           // The new ResultSet tuple stuff
+    
+    f[0] = new Field(connection, new String("TABLE_CAT"), iVarcharOid, 32);
+    f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32);
+    f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32);
+    f[3] = new Field(connection, new String("TABLE_TYPE"), iVarcharOid, 32);
+    f[4] = new Field(connection, new String("REMARKS"), iVarcharOid, 32);
+    
+    // Now form the query
+    StringBuffer sql = new StringBuffer("select relname,oid from pg_class where (");
+    boolean notFirst=false;
+    for(int i=0;i<types.length;i++) {
+      if(notFirst)
+       sql.append(" or ");
+      for(int j=0;j<getTableTypes.length;j++)
+       if(getTableTypes[j][0].equals(types[i])) {
+         sql.append(getTableTypes[j][1]);
+         notFirst=true;
+       }
+    }
+    
+    // Added by Stefan Andreasen <stefan@linux.kapow.dk>
+    // Now take the pattern into account
+    sql.append(") and relname like '");
+    sql.append(tableNamePattern.toLowerCase());
+    sql.append("'");
+    
+    // Now run the query
+    r = connection.ExecSQL(sql.toString());
+    
+    byte remarks[];
+    
+    while (r.next())
+      {
+       byte[][] tuple = new byte[5][0];
+       
+       // Fetch the description for the table (if any)
+       java.sql.ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(2));
+       if(((postgresql.ResultSet)dr).getTupleCount()==1) {
+         dr.next();
+         remarks = dr.getBytes(1);
+       } else
+         remarks = defaultRemarks;
+       dr.close();
+       
+       tuple[0] = null;                // Catalog name
+       tuple[1] = null;                // Schema name
+       tuple[2] = r.getBytes(1);       // Table name
+       tuple[3] = null;                // Table type
+       tuple[4] = remarks;             // Remarks
+       v.addElement(tuple);
+      }
+    r.close();
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+  
+  // This array contains the valid values for the types argument
+  // in getTables().
+  //
+  // Each supported type consists of it's name, and the sql where
+  // clause to retrieve that value.
+  //
+  // IMPORTANT: the query must be enclosed in ( )
+  private static final String getTableTypes[][] = {
+    {"TABLE",          "(relkind='r' and relname !~ '^pg_' and relname !~ '^xinv')"},
+    {"INDEX",          "(relkind='i' and relname !~ '^pg_' and relname !~ '^xinx')"},
+    {"LARGE OBJECT",   "(relkind='r' and relname ~ '^xinv')"},
+    {"SEQUENCE",       "(relkind='S' and relname !~ '^pg_')"},
+    {"SYSTEM TABLE",   "(relkind='r' and relname ~ '^pg_')"},
+    {"SYSTEM INDEX",   "(relkind='i' and relname ~ '^pg_')"}
+  };
+  
+  // These are the default tables, used when NULL is passed to getTables
+  // The choice of these provide the same behaviour as psql's \d
+  private static final String defaultTableTypes[] = {
+    "TABLE","INDEX","SEQUENCE"
+  };
+  
+  /**
+   * 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
+  {
+    // We don't use schemas, so we simply return a single schema name "".
+    //
+    Field f[] = new Field[1];
+    Vector v = new Vector();
+    byte[][] tuple = new byte[1][0];
+    f[0] = new Field(connection,new String("TABLE_SCHEM"),iVarcharOid,32);
+    tuple[0] = "".getBytes();
+    v.addElement(tuple);
+    return new ResultSet(connection,f,v,"OK",1);
+  }
+  
+  /**
+   * 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
+   */
+  public java.sql.ResultSet getCatalogs() throws SQLException
+  {
+    // We don't use catalogs, so we simply return a single catalog name "".
+    Field f[] = new Field[1];
+    Vector v = new Vector();
+    byte[][] tuple = new byte[1][0];
+    f[0] = new Field(connection,new String("TABLE_CAT"),iVarcharOid,32);
+    tuple[0] = "".getBytes();
+    v.addElement(tuple);
+    return new ResultSet(connection,f,v,"OK",1);
+  }
+  
+  /**
+   * 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
+  {
+    Field f[] = new Field[1];
+    Vector v = new Vector();
+    byte[][] tuple = new byte[1][0];
+    f[0] = new Field(connection,new String("TABLE_TYPE"),iVarcharOid,32);
+    for(int i=0;i<getTableTypes.length;i++) {
+      tuple[0] = getTableTypes[i][0].getBytes();
+      v.addElement(tuple);
+    }
+    return new ResultSet(connection,f,v,"OK",1);
+  }
+  
+  /**
+   * 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
+  {
+    // the field descriptors for the new ResultSet
+    Field f[] = new Field[18];
+    java.sql.ResultSet r;      // ResultSet for the SQL query that we need to do
+    Vector v = new Vector();           // The new ResultSet tuple stuff
+    
+    f[0] = new Field(connection, new String("TABLE_CAT"), iVarcharOid, 32);
+    f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32);
+    f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32);
+    f[3] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32);
+    f[4] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2);
+    f[5] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32);
+    f[6] = new Field(connection, new String("COLUMN_SIZE"), iInt4Oid, 4);
+    f[7] = new Field(connection, new String("BUFFER_LENGTH"), iVarcharOid, 32);
+    f[8] = new Field(connection, new String("DECIMAL_DIGITS"), iInt4Oid, 4);
+    f[9] = new Field(connection, new String("NUM_PREC_RADIX"), iInt4Oid, 4);
+    f[10] = new Field(connection, new String("NULLABLE"), iInt4Oid, 4);
+    f[11] = new Field(connection, new String("REMARKS"), iVarcharOid, 32);
+    f[12] = new Field(connection, new String("COLUMN_DEF"), iVarcharOid, 32);
+    f[13] = new Field(connection, new String("SQL_DATA_TYPE"), iInt4Oid, 4);
+    f[14] = new Field(connection, new String("SQL_DATETIME_SUB"), iInt4Oid, 4);
+    f[15] = new Field(connection, new String("CHAR_OCTET_LENGTH"), iVarcharOid, 32);
+    f[16] = new Field(connection, new String("ORDINAL_POSITION"), iInt4Oid,4);
+    f[17] = new Field(connection, new String("IS_NULLABLE"), iVarcharOid, 32);
+    
+    // Added by Stefan Andreasen <stefan@linux.kapow.dk>
+    // If the pattern are  null then set them to %
+    if (tableNamePattern == null) tableNamePattern="%";
+    if (columnNamePattern == null) columnNamePattern="%";
+    
+    // Now form the query
+    // Modified by Stefan Andreasen <stefan@linux.kapow.dk>
+    r = connection.ExecSQL("select a.oid,c.relname,a.attname,a.atttypid,a.attnum,a.attnotnull,a.attlen,a.atttypmod from pg_class c, pg_attribute a where a.attrelid=c.oid and c.relname like '"+tableNamePattern.toLowerCase()+"' and a.attname like '"+columnNamePattern.toLowerCase()+"' and a.attnum>0 order by c.relname,a.attnum");
+    
+    byte remarks[];
+    
+    while(r.next()) {
+       byte[][] tuple = new byte[18][0];
+       
+       // Fetch the description for the table (if any)
+       java.sql.ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(1));
+       if(((postgresql.ResultSet)dr).getTupleCount()==1) {
+         dr.next();
+         tuple[11] = dr.getBytes(1);
+       } else
+         tuple[11] = defaultRemarks;
+       
+       dr.close();
+       
+       tuple[0] = "".getBytes();       // Catalog name
+       tuple[1] = "".getBytes();       // Schema name
+       tuple[2] = r.getBytes(2);       // Table name
+       tuple[3] = r.getBytes(3);       // Column name
+       
+       dr = connection.ExecSQL("select typname from pg_type where oid = "+r.getString(4));
+       dr.next();
+       String typname=dr.getString(1);
+       dr.close();
+       tuple[4] = Integer.toString(Field.getSQLType(typname)).getBytes();      // Data type
+       tuple[5] = typname.getBytes();  // Type name
+       
+       // Column size
+       // Looking at the psql source,
+       // I think the length of a varchar as specified when the table was created
+       // should be extracted from atttypmod which contains this length + sizeof(int32)
+       if (typname.equals("bpchar") || typname.equals("varchar")) {
+         int atttypmod = r.getInt(8);
+         tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes();
+       } else
+         tuple[6] = r.getBytes(7);
+       
+       tuple[7] = null;        // Buffer length
+       
+       tuple[8] = "0".getBytes();      // Decimal Digits - how to get this?
+       tuple[9] = "10".getBytes();     // Num Prec Radix - assume decimal
+       
+       // tuple[10] is below
+       // tuple[11] is above
+       
+       tuple[12] = null;       // column default
+       
+       tuple[13] = null;       // sql data type (unused)
+       tuple[14] = null;       // sql datetime sub (unused)
+       
+       tuple[15] = tuple[6];   // char octet length
+       
+       tuple[16] = r.getBytes(5);      // ordinal position
+       
+       String nullFlag = r.getString(6);
+       tuple[10] = Integer.toString(nullFlag.equals("f")?java.sql.DatabaseMetaData.columnNullable:java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable
+       tuple[17] = (nullFlag.equals("f")?"YES":"NO").getBytes();       // is nullable
+       
+       v.addElement(tuple);
+      }
+    r.close();
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+  
+  /**
+   * 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
+  {
+    Field f[] = new Field[8];
+    Vector v = new Vector();
+    
+    if(table==null)
+      table="%";
+    
+    if(columnNamePattern==null)
+      columnNamePattern="%";
+    else
+      columnNamePattern=columnNamePattern.toLowerCase();
+    
+    f[0] = new Field(connection,new String("TABLE_CAT"),iVarcharOid,32);
+    f[1] = new Field(connection,new String("TABLE_SCHEM"),iVarcharOid,32);
+    f[2] = new Field(connection,new String("TABLE_NAME"),iVarcharOid,32);
+    f[3] = new Field(connection,new String("COLUMN_NAME"),iVarcharOid,32);
+    f[4] = new Field(connection,new String("GRANTOR"),iVarcharOid,32);
+    f[5] = new Field(connection,new String("GRANTEE"),iVarcharOid,32);
+    f[6] = new Field(connection,new String("PRIVILEGE"),iVarcharOid,32);
+    f[7] = new Field(connection,new String("IS_GRANTABLE"),iVarcharOid,32);
+    
+    // This is taken direct from the psql source
+    java.sql.ResultSet r = connection.ExecSQL("SELECT relname, relacl FROM pg_class, pg_user WHERE ( relkind = 'r' OR relkind = 'i') and relname !~ '^pg_' and relname !~ '^xin[vx][0-9]+' and usesysid = relowner and relname like '"+table.toLowerCase()+"' ORDER BY relname");
+    while(r.next()) {
+      byte[][] tuple = new byte[8][0];
+      tuple[0] = tuple[1]= "".getBytes();
+      DriverManager.println("relname=\""+r.getString(1)+"\" relacl=\""+r.getString(2)+"\"");
+      
+      // For now, don't add to the result as relacl needs to be processed.
+      //v.addElement(tuple);
+    }
+    
+    return new ResultSet(connection,f,v,"OK",1);
+  }
+  
+  /**
+   * 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
+   */
+  // Implementation note: This is required for Borland's JBuilder to work
+  public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException
+  {
+    // for now, this returns an empty result set.
+    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
+    
+    f[0] = new Field(connection, new String("SCOPE"), iInt2Oid, 2);
+    f[1] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32);
+    f[2] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2);
+    f[3] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32);
+    f[4] = new Field(connection, new String("COLUMN_SIZE"), iInt4Oid, 4);
+    f[5] = new Field(connection, new String("BUFFER_LENGTH"), iInt4Oid, 4);
+    f[6] = new Field(connection, new String("DECIMAL_DIGITS"), iInt2Oid, 2);
+    f[7] = new Field(connection, new String("PSEUDO_COLUMN"), iInt2Oid, 2);
+    
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+  
+  /**
+   * 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, pg_type t " +
+                                                    " WHERE bc.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
+  {
+    java.sql.ResultSet rs = connection.ExecSQL("select typname from pg_type");
+    if(rs!=null) {
+      Field f[] = new Field[18];
+      ResultSet r;     // ResultSet for the SQL query that we need to do
+      Vector v = new Vector();         // The new ResultSet tuple stuff
+      
+      f[0] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32);
+      f[1] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2);
+      f[2] = new Field(connection, new String("PRECISION"), iInt4Oid, 4);
+      f[3] = new Field(connection, new String("LITERAL_PREFIX"), iVarcharOid, 32);
+      f[4] = new Field(connection, new String("LITERAL_SUFFIX"), iVarcharOid, 32);
+      f[5] = new Field(connection, new String("CREATE_PARAMS"), iVarcharOid, 32);
+      f[6] = new Field(connection, new String("NULLABLE"), iInt2Oid, 2);
+      f[7] = new Field(connection, new String("CASE_SENSITIVE"), iBoolOid, 1);
+      f[8] = new Field(connection, new String("SEARCHABLE"), iInt2Oid, 2);
+      f[9] = new Field(connection, new String("UNSIGNED_ATTRIBUTE"), iBoolOid, 1);
+      f[10] = new Field(connection, new String("FIXED_PREC_SCALE"), iBoolOid, 1);
+      f[11] = new Field(connection, new String("AUTO_INCREMENT"), iBoolOid, 1);
+      f[12] = new Field(connection, new String("LOCAL_TYPE_NAME"), iVarcharOid, 32);
+      f[13] = new Field(connection, new String("MINIMUM_SCALE"), iInt2Oid, 2);
+      f[14] = new Field(connection, new String("MAXIMUM_SCALE"), iInt2Oid, 2);
+      f[15] = new Field(connection, new String("SQL_DATA_TYPE"), iInt4Oid, 4);
+      f[16] = new Field(connection, new String("SQL_DATETIME_SUB"), iInt4Oid, 4);
+      f[17] = new Field(connection, new String("NUM_PREC_RADIX"), iInt4Oid, 4);
+      
+      // cache some results, this will keep memory useage down, and speed
+      // things up a little.
+      byte b9[]  = "9".getBytes();
+      byte b10[] = "10".getBytes();
+      byte bf[]  = "f".getBytes();
+      byte bnn[] = Integer.toString(typeNoNulls).getBytes();
+      byte bts[] = Integer.toString(typeSearchable).getBytes();
+      
+      while(rs.next()) {
+       byte[][] tuple = new byte[18][];
+       String typname=rs.getString(1);
+       tuple[0] = typname.getBytes();
+       tuple[1] = Integer.toString(Field.getSQLType(typname)).getBytes();
+       tuple[2] = b9;  // for now
+       tuple[6] = bnn; // for now
+       tuple[7] = bf; // false for now - not case sensitive
+       tuple[8] = bts;
+       tuple[9] = bf; // false for now - it's signed
+       tuple[10] = bf; // false for now - must handle money
+       tuple[11] = bf; // false for now - handle autoincrement
+       // 12 - LOCAL_TYPE_NAME is null
+       // 13 & 14 ?
+       // 15 & 16 are unused so we return null
+       tuple[17] = b10; // everything is base 10
+       v.addElement(tuple);
+      }
+      rs.close();
+      return new ResultSet(connection, f, v, "OK", 1);
+    }
+    
+    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
+   */
+  // Implementation note: This is required for Borland's JBuilder to work
+  public java.sql.ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException
+  {
+    // for now, this returns an empty result set.
+    Field f[] = new Field[13];
+    ResultSet r;       // ResultSet for the SQL query that we need to do
+    Vector v = new Vector();           // The new ResultSet tuple stuff
+    
+    f[0] = new Field(connection, new String("TABLE_CAT"), iVarcharOid, 32);
+    f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32);
+    f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32);
+    f[3] = new Field(connection, new String("NON_UNIQUE"), iBoolOid, 1);
+    f[4] = new Field(connection, new String("INDEX_QUALIFIER"), iVarcharOid, 32);
+    f[5] = new Field(connection, new String("INDEX_NAME"), iVarcharOid, 32);
+    f[6] = new Field(connection, new String("TYPE"), iInt2Oid, 2);
+    f[7] = new Field(connection, new String("ORDINAL_POSITION"), iInt2Oid, 2);
+    f[8] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32);
+    f[9] = new Field(connection, new String("ASC_OR_DESC"), iVarcharOid, 32);
+    f[10] = new Field(connection, new String("CARDINALITY"), iInt4Oid, 4);
+    f[11] = new Field(connection, new String("PAGES"), iInt4Oid, 4);
+    f[12] = new Field(connection, new String("FILTER_CONDITION"), iVarcharOid, 32);
+    
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+}
+
diff --git a/src/interfaces/jdbc/postgresql/jdbc1/PreparedStatement.java b/src/interfaces/jdbc/postgresql/jdbc1/PreparedStatement.java
new file mode 100644 (file)
index 0000000..78eaf3d
--- /dev/null
@@ -0,0 +1,600 @@
+package postgresql.jdbc1;
+
+// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 2 class in the
+// postgresql.jdbc2 package.
+
+import java.io.*;
+import java.math.*;
+import java.sql.*;
+import java.text.*;
+import java.util.*;
+import postgresql.largeobject.*;
+import postgresql.util.*;
+
+/**
+ * A SQL Statement is pre-compiled and stored in a PreparedStatement object.
+ * This object can then be used to efficiently execute this statement multiple
+ * times.
+ *
+ * <p><B>Note:</B> The setXXX methods for setting IN parameter values must
+ * specify types that are compatible with the defined SQL type of the input
+ * parameter.  For instance, if the IN parameter has SQL type Integer, then
+ * setInt should be used.
+ *
+ * <p>If arbitrary parameter type conversions are required, then the setObject 
+ * method should be used with a target SQL type.
+ *
+ * @see ResultSet
+ * @see java.sql.PreparedStatement
+ */
+public class PreparedStatement extends Statement implements java.sql.PreparedStatement 
+{
+       String sql;
+       String[] templateStrings;
+       String[] inStrings;
+       Connection connection;
+
+       /**
+        * Constructor for the PreparedStatement class.
+        * Split the SQL statement into segments - separated by the arguments.
+        * When we rebuild the thing with the arguments, we can substitute the
+        * args and join the whole thing together.
+        *
+        * @param conn the instanatiating connection
+        * @param sql the SQL statement with ? for IN markers
+        * @exception SQLException if something bad occurs
+        */
+       public PreparedStatement(Connection connection, String sql) throws SQLException
+       {
+               super(connection);
+
+               Vector v = new Vector();
+               boolean inQuotes = false;
+               int lastParmEnd = 0, i;
+
+               this.sql = sql;
+               this.connection = connection;
+               for (i = 0; i < sql.length(); ++i)
+               {
+                       int c = sql.charAt(i);
+
+                       if (c == '\'')
+                               inQuotes = !inQuotes;
+                       if (c == '?' && !inQuotes)
+                       {
+                               v.addElement(sql.substring (lastParmEnd, i));
+                               lastParmEnd = i + 1;
+                       }
+               }
+               v.addElement(sql.substring (lastParmEnd, sql.length()));
+
+               templateStrings = new String[v.size()];
+               inStrings = new String[v.size() - 1];
+               clearParameters();
+
+               for (i = 0 ; i < templateStrings.length; ++i)
+                       templateStrings[i] = (String)v.elementAt(i);
+       }
+
+       /**
+        * A Prepared SQL query is executed and its ResultSet is returned
+        *
+        * @return a ResultSet that contains the data produced by the
+        *      query - never null
+        * @exception SQLException if a database access error occurs
+        */
+       public java.sql.ResultSet executeQuery() throws SQLException
+       {
+               StringBuffer s = new StringBuffer();
+               int i;
+
+               for (i = 0 ; i < inStrings.length ; ++i)
+               {
+                       if (inStrings[i] == null)
+                               throw new SQLException("No value specified for parameter " + (i + 1));
+                       s.append (templateStrings[i]);
+                       s.append (inStrings[i]);
+               }
+               s.append(templateStrings[inStrings.length]);
+               return super.executeQuery(s.toString());        // in Statement class
+       }
+
+       /**
+        * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition,
+        * SQL statements that return nothing such as SQL DDL statements can
+        * be executed.
+        *
+        * @return either the row count for INSERT, UPDATE or DELETE; or
+        *      0 for SQL statements that return nothing.
+        * @exception SQLException if a database access error occurs
+        */
+       public int executeUpdate() throws SQLException
+       {
+               StringBuffer s = new StringBuffer();
+               int i;
+
+               for (i = 0 ; i < inStrings.length ; ++i)
+               {
+                       if (inStrings[i] == null)
+                               throw new SQLException("No value specified for parameter " + (i + 1));
+                       s.append (templateStrings[i]);
+                       s.append (inStrings[i]);
+               }
+               s.append(templateStrings[inStrings.length]);
+               return super.executeUpdate(s.toString());       // in Statement class
+       }       
+
+       /**
+        * Set a parameter to SQL NULL
+        *
+        * <p><B>Note:</B> You must specify the parameters SQL type (although
+        * PostgreSQL ignores it)
+        *
+        * @param parameterIndex the first parameter is 1, etc...
+        * @param sqlType the SQL type code defined in java.sql.Types
+        * @exception SQLException if a database access error occurs
+        */
+       public void setNull(int parameterIndex, int sqlType) throws SQLException
+       {
+               set(parameterIndex, "null");
+       }
+
+       /**
+        * Set a parameter to a Java boolean value.  The driver converts this
+        * to a SQL BIT value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setBoolean(int parameterIndex, boolean x) throws SQLException
+       {
+               set(parameterIndex, x ? "'t'" : "'f'");
+       }
+
+       /**
+        * Set a parameter to a Java byte value.  The driver converts this to
+        * a SQL TINYINT value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setByte(int parameterIndex, byte x) throws SQLException
+       {
+               set(parameterIndex, (new Integer(x)).toString());
+       }
+
+       /**
+        * Set a parameter to a Java short value.  The driver converts this
+        * to a SQL SMALLINT value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setShort(int parameterIndex, short x) throws SQLException
+       {
+               set(parameterIndex, (new Integer(x)).toString());
+       }
+
+       /**
+        * Set a parameter to a Java int value.  The driver converts this to
+        * a SQL INTEGER value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setInt(int parameterIndex, int x) throws SQLException
+       {
+               set(parameterIndex, (new Integer(x)).toString());
+       }
+
+       /**
+        * Set a parameter to a Java long value.  The driver converts this to
+        * a SQL BIGINT value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setLong(int parameterIndex, long x) throws SQLException
+       {
+               set(parameterIndex, (new Long(x)).toString());
+       }
+
+       /**
+        * Set a parameter to a Java float value.  The driver converts this
+        * to a SQL FLOAT value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setFloat(int parameterIndex, float x) throws SQLException
+       {
+               set(parameterIndex, (new Float(x)).toString());
+       }
+
+       /**
+        * Set a parameter to a Java double value.  The driver converts this
+        * to a SQL DOUBLE value when it sends it to the database
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setDouble(int parameterIndex, double x) throws SQLException
+       {
+               set(parameterIndex, (new Double(x)).toString());
+       }
+
+       /**
+        * Set a parameter to a java.lang.BigDecimal value.  The driver
+        * converts this to a SQL NUMERIC value when it sends it to the
+        * database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException
+       {
+               set(parameterIndex, x.toString());
+       }
+
+       /**
+        * Set a parameter to a Java String value.  The driver converts this
+        * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments
+        * size relative to the driver's limits on VARCHARs) when it sends it
+        * to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setString(int parameterIndex, String x) throws SQLException
+       {
+         // if the passed string is null, then set this column to null
+         if(x==null)
+           set(parameterIndex,"null");
+         else {
+           StringBuffer b = new StringBuffer();
+           int i;
+           
+           b.append('\'');
+           for (i = 0 ; i < x.length() ; ++i)
+             {
+               char c = x.charAt(i);
+               if (c == '\\' || c == '\'')
+                 b.append((char)'\\');
+               b.append(c);
+             }
+           b.append('\'');
+           set(parameterIndex, b.toString());
+         }
+       }
+
+  /**
+   * Set a parameter to a Java array of bytes.  The driver converts this
+   * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's
+   * size relative to the driver's limits on VARBINARYs) when it sends
+   * it to the database.
+   *
+   * <p>Implementation note:
+   * <br>With postgresql, this creates a large object, and stores the
+   * objects oid in this column.
+   *
+   * @param parameterIndex the first parameter is 1...
+   * @param x the parameter value
+   * @exception SQLException if a database access error occurs
+   */
+  public void setBytes(int parameterIndex, byte x[]) throws SQLException
+  {
+    LargeObjectManager lom = connection.getLargeObjectAPI();
+    int oid = lom.create();
+    LargeObject lob = lom.open(oid);
+    lob.write(x);
+    lob.close();
+    setInt(parameterIndex,oid);
+  }
+
+       /**
+        * Set a parameter to a java.sql.Date value.  The driver converts this
+        * to a SQL DATE value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setDate(int parameterIndex, java.sql.Date x) throws SQLException
+       {
+         SimpleDateFormat df = new SimpleDateFormat("''"+connection.getDateStyle()+"''");
+         
+         set(parameterIndex, df.format(x));
+         
+         // The above is how the date should be handled.
+         //
+         // However, in JDK's prior to 1.1.6 (confirmed with the
+         // Linux jdk1.1.3 and the Win95 JRE1.1.5), SimpleDateFormat seems
+         // to format a date to the previous day. So the fix is to add a day
+         // before formatting.
+         //
+         // PS: 86400000 is one day
+         //
+         //set(parameterIndex, df.format(new java.util.Date(x.getTime()+86400000)));
+       }
+  
+       /**
+        * Set a parameter to a java.sql.Time value.  The driver converts
+        * this to a SQL TIME value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...));
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setTime(int parameterIndex, Time x) throws SQLException
+       {
+               set(parameterIndex, "'" + x.toString() + "'");
+       }
+
+       /**
+        * Set a parameter to a java.sql.Timestamp value.  The driver converts
+        * this to a SQL TIMESTAMP value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException
+       {
+               set(parameterIndex, "'" + x.toString() + "'");
+       }
+
+       /**
+        * When a very large ASCII value is input to a LONGVARCHAR parameter,
+        * it may be more practical to send it via a java.io.InputStream.
+        * JDBC will read the data from the stream as needed, until it reaches
+        * end-of-file.  The JDBC driver will do any necessary conversion from
+        * ASCII to the database char format.
+        *
+        * <P><B>Note:</B> This stream object can either be a standard Java
+        * stream object or your own subclass that implements the standard
+        * interface.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @param length the number of bytes in the stream
+        * @exception SQLException if a database access error occurs
+        */
+       public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException
+       {
+               setBinaryStream(parameterIndex, x, length);
+       }
+
+       /**
+        * When a very large Unicode value is input to a LONGVARCHAR parameter,
+        * it may be more practical to send it via a java.io.InputStream.
+        * JDBC will read the data from the stream as needed, until it reaches
+        * end-of-file.  The JDBC driver will do any necessary conversion from
+        * UNICODE to the database char format.
+        *
+        * <P><B>Note:</B> This stream object can either be a standard Java
+        * stream object or your own subclass that implements the standard
+        * interface.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException
+       {
+               setBinaryStream(parameterIndex, x, length);
+       }
+
+       /**
+        * When a very large binary value is input to a LONGVARBINARY parameter,
+        * it may be more practical to send it via a java.io.InputStream.
+        * JDBC will read the data from the stream as needed, until it reaches
+        * end-of-file.  
+        *
+        * <P><B>Note:</B> This stream object can either be a standard Java
+        * stream object or your own subclass that implements the standard
+        * interface.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
+       {
+               throw new SQLException("InputStream as parameter not supported");
+       }
+
+       /**
+        * In general, parameter values remain in force for repeated used of a
+        * Statement.  Setting a parameter value automatically clears its
+        * previous value.  However, in coms cases, it is useful to immediately
+        * release the resources used by the current parameter values; this
+        * can be done by calling clearParameters
+        *
+        * @exception SQLException if a database access error occurs
+        */
+       public void clearParameters() throws SQLException
+       {
+               int i;
+
+               for (i = 0 ; i < inStrings.length ; i++)
+                       inStrings[i] = null;
+       }
+
+       /**
+        * Set the value of a parameter using an object; use the java.lang
+        * equivalent objects for integral values.
+        *
+        * <P>The given Java object will be converted to the targetSqlType before
+        * being sent to the database.
+        *
+        * <P>note that this method may be used to pass database-specific
+        * abstract data types.  This is done by using a Driver-specific
+        * Java type and using a targetSqlType of java.sql.Types.OTHER
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the object containing the input parameter value
+        * @param targetSqlType The SQL type to be send to the database
+        * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC
+        *      types this is the number of digits after the decimal.  For 
+        *      all other types this value will be ignored.
+        * @exception SQLException if a database access error occurs
+        */
+       public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException
+       {
+               switch (targetSqlType)
+               {
+                       case Types.TINYINT:
+                       case Types.SMALLINT:
+                       case Types.INTEGER:
+                       case Types.BIGINT:
+                       case Types.REAL:
+                       case Types.FLOAT:
+                       case Types.DOUBLE:
+                       case Types.DECIMAL:
+                       case Types.NUMERIC:
+                               if (x instanceof Boolean)
+                                       set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0");
+                               else
+                                       set(parameterIndex, x.toString());
+                               break;
+                       case Types.CHAR:
+                       case Types.VARCHAR:
+                       case Types.LONGVARCHAR:
+                               setString(parameterIndex, x.toString());
+                               break;
+                       case Types.DATE:
+                               setDate(parameterIndex, (java.sql.Date)x);
+                               break;
+                       case Types.TIME:
+                               setTime(parameterIndex, (Time)x);
+                               break;
+                       case Types.TIMESTAMP:
+                               setTimestamp(parameterIndex, (Timestamp)x);
+                               break;
+                       case Types.OTHER:
+                               setString(parameterIndex, ((PGobject)x).getValue());
+                               break;
+                       default:
+                               throw new SQLException("Unknown Types value");
+               }
+       }
+
+       public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException
+       {
+               setObject(parameterIndex, x, targetSqlType, 0);
+       }
+       
+  /**
+   * This stores an Object into a parameter.
+   * <p>New for 6.4, if the object is not recognised, but it is
+   * Serializable, then the object is serialised using the
+   * postgresql.util.Serialize class.
+   */
+       public void setObject(int parameterIndex, Object x) throws SQLException
+       {
+               if (x instanceof String)
+                       setString(parameterIndex, (String)x);
+               else if (x instanceof BigDecimal)
+                       setBigDecimal(parameterIndex, (BigDecimal)x);
+               else if (x instanceof Short)
+                       setShort(parameterIndex, ((Short)x).shortValue());
+               else if (x instanceof Integer)
+                       setInt(parameterIndex, ((Integer)x).intValue());
+               else if (x instanceof Long)
+                       setLong(parameterIndex, ((Long)x).longValue());
+               else if (x instanceof Float)
+                       setFloat(parameterIndex, ((Float)x).floatValue());
+               else if (x instanceof Double)
+                       setDouble(parameterIndex, ((Double)x).doubleValue());
+               else if (x instanceof byte[])
+                       setBytes(parameterIndex, (byte[])x);
+               else if (x instanceof java.sql.Date)
+                       setDate(parameterIndex, (java.sql.Date)x);
+               else if (x instanceof Time)
+                       setTime(parameterIndex, (Time)x);
+               else if (x instanceof Timestamp)
+                       setTimestamp(parameterIndex, (Timestamp)x);
+               else if (x instanceof Boolean)
+                       setBoolean(parameterIndex, ((Boolean)x).booleanValue());
+               else if (x instanceof PGobject)
+                       setString(parameterIndex, ((PGobject)x).getValue());
+               else
+                       setLong(parameterIndex, connection.putObject(x));
+       }
+
+       /**
+        * Some prepared statements return multiple results; the execute method
+        * handles these complex statements as well as the simpler form of 
+        * statements handled by executeQuery and executeUpdate
+        *
+        * @return true if the next result is a ResultSet; false if it is an
+        *      update count or there are no more results
+        * @exception SQLException if a database access error occurs
+        */
+       public boolean execute() throws SQLException
+       {
+               StringBuffer s = new StringBuffer();
+               int i;
+
+               for (i = 0 ; i < inStrings.length ; ++i)
+               {
+                       if (inStrings[i] == null)
+                               throw new SQLException("No value specified for parameter " + (i + 1));
+                       s.append (templateStrings[i]);
+                       s.append (inStrings[i]);
+               }
+               s.append(templateStrings[inStrings.length]);
+               return super.execute(s.toString());     // in Statement class
+       }
+
+       /**
+        * Returns the SQL statement with the current template values
+        * substituted.
+        */
+       public String toString() {
+               StringBuffer s = new StringBuffer();
+               int i;
+
+               for (i = 0 ; i < inStrings.length ; ++i)
+               {
+                       if (inStrings[i] == null)
+                               s.append( '?' );
+                       else
+                               s.append (templateStrings[i]);
+                       s.append (inStrings[i]);
+               }
+               s.append(templateStrings[inStrings.length]);
+               return s.toString();
+       }
+       
+       // **************************************************************
+       //      END OF PUBLIC INTERFACE 
+       // **************************************************************
+       
+       /**
+        * There are a lot of setXXX classes which all basically do
+        * the same thing.  We need a method which actually does the
+        * set for us.
+        *
+        * @param paramIndex the index into the inString
+        * @param s a string to be stored
+        * @exception SQLException if something goes wrong
+        */
+       private void set(int paramIndex, String s) throws SQLException
+       {
+               if (paramIndex < 1 || paramIndex > inStrings.length)
+                       throw new SQLException("Parameter index out of range");
+               inStrings[paramIndex - 1] = s;
+       }
+}
diff --git a/src/interfaces/jdbc/postgresql/jdbc1/ResultSet.java b/src/interfaces/jdbc/postgresql/jdbc1/ResultSet.java
new file mode 100644 (file)
index 0000000..5d5e033
--- /dev/null
@@ -0,0 +1,776 @@
+package postgresql.jdbc1;
+
+// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 2 class in the
+// postgresql.jdbc2 package.
+
+import java.lang.*;
+import java.io.*;
+import java.math.*;
+import java.text.*;
+import java.util.*;
+import java.sql.*;
+import postgresql.Field;
+import postgresql.largeobject.*;
+import postgresql.util.*;
+
+/**
+ * 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.
+ *
+ * <P>A ResultSet maintains a cursor pointing to its current row of data.  
+ * Initially the cursor is positioned before the first row.  The 'next'
+ * method moves the cursor to the next row.
+ *
+ * <P>The getXXX methods retrieve column values for the current row.  You can
+ * retrieve values either using the index number of the column, or by using
+ * the name of the column.  In general using the column index will be more
+ * efficient.  Columns are numbered from 1.
+ *
+ * <P>For maximum portability, ResultSet columns within each row should be read
+ * in left-to-right order and each column should be read only once.
+ *
+ *<P> For the getXXX methods, the JDBC driver attempts to convert the
+ * underlying data to the specified Java type and returns a suitable Java
+ * value.  See the JDBC specification for allowable mappings from SQL types
+ * to Java types with the ResultSet getXXX methods.
+ *
+ * <P>Column names used as input to getXXX methods are case insenstive.  When
+ * performing a getXXX using a column name, if several columns have the same
+ * name, then the value of the first matching column will be returned.  The
+ * column name option is designed to be used when column names are used in the
+ * SQL Query.  For columns that are NOT explicitly named in the query, it is
+ * best to use column numbers.  If column names were used there is no way for
+ * the programmer to guarentee that they actually refer to the intended
+ * columns.
+ *
+ * <P>A ResultSet is automatically closed by the Statement that generated it 
+ * when that Statement is closed, re-executed, or is used to retrieve the 
+ * next result from a sequence of multiple results.
+ *
+ * <P>The number, types and properties of a ResultSet's columns are provided by
+ * the ResultSetMetaData object returned by the getMetaData method.
+ *
+ * @see ResultSetMetaData
+ * @see java.sql.ResultSet
+ */
+public class ResultSet extends postgresql.ResultSet implements java.sql.ResultSet 
+{
+  /**
+   * 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)
+  {
+      super(conn,fields,tuples,status,updateCount);
+  }
+  
+  /**
+   * 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.
+   *
+   * <p>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.
+   *
+   * <p><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);
+    if (columnIndex < 1 || columnIndex > fields.length)
+      throw new SQLException("Column Index out of range");
+    wasNullFlag = (this_row[columnIndex - 1] == null);
+    if(wasNullFlag)
+      return null;
+    return new String(this_row[columnIndex - 1]);
+  }
+  
+  /**
+   * 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.
+   *
+   * <p>In normal use, the bytes represent the raw values returned by the
+   * backend. However, if the column is an OID, then it is assumed to
+   * refer to a Large Object, and that object is returned as a byte array.
+   *
+   * <p><b>Be warned</b> If the large object is huge, then you may run out
+   * of memory.
+   *
+   * @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);
+    
+    // Handle OID's as BLOBS
+    if(!wasNullFlag)
+      if( fields[columnIndex - 1].getOID() == 26) {
+       LargeObjectManager lom = connection.getLargeObjectAPI();
+       LargeObject lob = lom.open(getInt(columnIndex));
+       byte buf[] = lob.read(lob.size());
+       lob.close();
+       return buf;
+      }
+    
+    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)
+      return null;
+    SimpleDateFormat df = new SimpleDateFormat(connection.getDateStyle());
+    try {
+      return new java.sql.Date(df.parse(s).getTime());
+    } catch (ParseException e) {
+      throw new SQLException("Bad Date Format: at " + e.getErrorOffset() + " in " + s);
+    }
+  }
+  
+  /**
+   * 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);
+    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzz");
+    
+    if (s != null)
+      {
+       int TZ = new Float(s.substring(19)).intValue();
+       TZ = TZ * 60 * 60 * 1000;
+       TimeZone zone = TimeZone.getDefault();
+       zone.setRawOffset(TZ);
+       String nm = zone.getID();
+       s = s.substring(0,18) + nm;
+       try {
+         java.util.Date d = df.parse(s);
+         return new Timestamp(d.getTime());
+       } catch (ParseException e) {
+         throw new SQLException("Bad Timestamp Format: at " + e.getErrorOffset() + " in " + 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.
+   *
+   * <p><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.
+   *
+   *<p> 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 bytes.  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.
+   *
+   * <p>The warning chain is automatically cleared each time a new
+   * row is read.
+   *
+   * <p><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
+   *
+   * <p>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.
+   *
+   * <p>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.
+   *
+   * <p><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
+   *
+   * <p>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.
+   *
+   * <p>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];
+    
+    // some fields can be null, mainly from those returned by MetaData methods
+    if(field==null) {
+      wasNullFlag=true;
+      return null;
+    }
+    
+    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 connection.getObject(field.getTypeName(), getString(columnIndex));
+      }
+  }
+  
+  /**
+   * Get the value of a column in the current row as a Java object
+   *
+   *<p> 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.
+   *
+   * <p>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");
+  }
+}
+
diff --git a/src/interfaces/jdbc/postgresql/jdbc1/ResultSetMetaData.java b/src/interfaces/jdbc/postgresql/jdbc1/ResultSetMetaData.java
new file mode 100644 (file)
index 0000000..95492e8
--- /dev/null
@@ -0,0 +1,426 @@
+package postgresql.jdbc1;
+
+// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 2 class in the
+// postgresql.jdbc2 package.
+
+import java.lang.*;
+import java.sql.*;
+import java.util.*;
+import postgresql.*;
+
+/**
+ * A ResultSetMetaData object can be used to find out about the types and
+ * properties of the columns in a ResultSet
+ *
+ * @see java.sql.ResultSetMetaData
+ */
+public class ResultSetMetaData implements java.sql.ResultSetMetaData 
+{
+  Vector rows;
+  Field[] fields;
+  
+  /**
+   *   Initialise for a result with a tuple set and
+   *   a field descriptor set
+   *
+   * @param rows the Vector of rows returned by the ResultSet
+   * @param fields the array of field descriptors
+   */
+  public ResultSetMetaData(Vector rows, Field[] fields)
+  {
+    this.rows = rows;
+    this.fields = fields;
+  }
+  
+  /**
+   * Whats the number of columns in the ResultSet?
+   *
+   * @return the number
+   * @exception SQLException if a database access error occurs
+   */
+  public int getColumnCount() throws SQLException
+  {
+    return fields.length;
+  }
+  
+  /**
+   * Is the column automatically numbered (and thus read-only)
+   * I believe that PostgreSQL does not support this feature.
+   *
+   * @param column the first column is 1, the second is 2...
+   * @return true if so
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean isAutoIncrement(int column) throws SQLException
+  {
+    return false;
+  }
+  
+  /**
+   * Does a column's case matter? ASSUMPTION: Any field that is
+   * not obviously case insensitive is assumed to be case sensitive
+   *
+   * @param column the first column is 1, the second is 2...
+   * @return true if so
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean isCaseSensitive(int column) throws SQLException
+  {
+    int sql_type = getField(column).getSQLType();
+    
+    switch (sql_type)
+      {
+      case Types.SMALLINT:
+      case Types.INTEGER:
+      case Types.FLOAT:
+      case Types.REAL:
+      case Types.DOUBLE:
+      case Types.DATE:
+      case Types.TIME:
+      case Types.TIMESTAMP:
+       return false;
+      default:
+       return true;
+      }
+  }
+  
+  /**
+   * Can the column be used in a WHERE clause?  Basically for
+   * this, I split the functions into two types: recognised
+   * types (which are always useable), and OTHER types (which
+   * may or may not be useable).  The OTHER types, for now, I
+   * will assume they are useable.  We should really query the
+   * catalog to see if they are useable.
+   *
+   * @param column the first column is 1, the second is 2...
+   * @return true if they can be used in a WHERE clause
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean isSearchable(int column) throws SQLException
+  {
+    int sql_type = getField(column).getSQLType();
+    
+    // This switch is pointless, I know - but it is a set-up
+    // for further expansion.          
+    switch (sql_type)
+      {
+      case Types.OTHER:
+       return true;
+      default:
+       return true;
+      }
+  }
+  
+  /**
+   * Is the column a cash value?  6.1 introduced the cash/money
+   * type, which haven't been incorporated as of 970414, so I
+   * just check the type name for both 'cash' and 'money'
+   *
+   * @param column the first column is 1, the second is 2...
+   * @return true if its a cash column
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean isCurrency(int column) throws SQLException
+  {
+    String type_name = getField(column).getTypeName();
+    
+    return type_name.equals("cash") || type_name.equals("money");
+  }
+  
+  /**
+   * 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));
+       if(x[column-1]!=null) {
+         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 database access error occurs
+   */
+  public String getColumnName(int column) throws SQLException
+  {
+    Field f = getField(column);
+    if(f!=null)
+      return f.name;
+    return "field"+column;
+  }
+  
+  /**
+   * 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
+  {
+    return "";
+  }
+  
+  /**
+   * 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;
+      case Types.VARCHAR:
+       return 0;
+      default:
+       return 0;
+      }
+  }
+  
+  /**
+   * 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;
+      case Types.VARCHAR:
+       return 0;
+      default:
+       return 0;
+      }
+  }
+  
+  /**
+   * 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
+  {
+    return "";
+  }
+  
+  /**
+   * What is a column's SQL Type? (java.sql.Type int)
+   *
+   * @param column the first column is 1, the second is 2, etc.
+   * @return the java.sql.Type value
+   * @exception SQLException if a database access error occurs
+   * @see postgresql.Field#getSQLType
+   * @see java.sql.Types
+   */
+  public int getColumnType(int column) throws SQLException
+  {
+    return getField(column).getSQLType();
+  }
+  
+  /**
+   * Whats is the column's data source specific type name?
+   *
+   * @param column the first column is 1, the second is 2, etc.
+   * @return the type name
+   * @exception SQLException if a database access error occurs
+   */
+  public String getColumnTypeName(int column) throws SQLException
+  {
+    return getField(column).getTypeName();
+  }
+  
+  /**
+   * Is the column definitely not writable?  In reality, we would
+   * have to check the GRANT/REVOKE stuff for this to be effective,
+   * and I haven't really looked into that yet, so this will get
+   * re-visited.
+   *
+   * @param column the first column is 1, the second is 2, etc.
+   * @return true if so
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean isReadOnly(int column) throws SQLException
+  {
+    return false;
+  }
+  
+  /**
+   * Is it possible for a write on the column to succeed?  Again, we
+   * would in reality have to check the GRANT/REVOKE stuff, which
+   * I haven't worked with as yet.  However, if it isn't ReadOnly, then
+   * it is obviously writable.
+   *
+   * @param column the first column is 1, the second is 2, etc.
+   * @return true if so
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean isWritable(int column) throws SQLException
+  {
+    if (isReadOnly(column))
+      return true;
+    else
+      return false;
+  }
+  
+  /**
+   * Will a write on this column definately succeed?  Hmmm...this
+   * is a bad one, since the two preceding functions have not been
+   * really defined.  I cannot tell is the short answer.  I thus
+   * return isWritable() just to give us an idea.
+   *
+   * @param column the first column is 1, the second is 2, etc..
+   * @return true if so
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean isDefinitelyWritable(int column) throws SQLException
+  {
+    return isWritable(column);
+  }
+  
+  // ********************************************************
+  //   END OF PUBLIC INTERFACE
+  // ********************************************************
+  
+  /**
+   * For several routines in this package, we need to convert
+   * a columnIndex into a Field[] descriptor.  Rather than do
+   * the same code several times, here it is.
+   * 
+   * @param columnIndex the first column is 1, the second is 2...
+   * @return the Field description
+   * @exception SQLException if a database access error occurs
+   */
+  private Field getField(int columnIndex) throws SQLException
+  {
+    if (columnIndex < 1 || columnIndex > fields.length)
+      throw new SQLException("Column index out of range");
+    return fields[columnIndex - 1];
+  }
+}
+
diff --git a/src/interfaces/jdbc/postgresql/jdbc1/Statement.java b/src/interfaces/jdbc/postgresql/jdbc1/Statement.java
new file mode 100644 (file)
index 0000000..4bc0263
--- /dev/null
@@ -0,0 +1,323 @@
+package postgresql.jdbc1;
+
+// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 2 class in the
+// postgresql.jdbc2 package.
+
+import java.sql.*;
+
+/**
+ * A Statement object is used for executing a static SQL statement and
+ * obtaining the results produced by it.
+ *
+ * <p>Only one ResultSet per Statement can be open at any point in time.  
+ * Therefore, if the reading of one ResultSet is interleaved with the
+ * reading of another, each must have been generated by different
+ * Statements.  All statement execute methods implicitly close a
+ * statement's current ResultSet if an open one exists.
+ *
+ * @see java.sql.Statement
+ * @see ResultSet
+ */
+public class Statement implements java.sql.Statement
+{
+    Connection connection;             // The connection who created us
+    java.sql.ResultSet result = null;  // The current results
+    SQLWarning warnings = null;        // The warnings chain.
+    int timeout = 0;           // The timeout for a query (not used)
+    boolean escapeProcessing = true;// escape processing flag
+    
+       /**
+        * Constructor for a Statement.  It simply sets the connection
+        * that created us.
+        *
+        * @param c the Connection instantation that creates us
+        */
+       public Statement (Connection c)
+       {
+               connection = c;
+       }
+
+       /**
+        * Execute a SQL statement that retruns a single ResultSet
+        *
+        * @param sql typically a static SQL SELECT statement
+        * @return a ResulSet that contains the data produced by the query
+        * @exception SQLException if a database access error occurs
+        */
+       public java.sql.ResultSet executeQuery(String sql) throws SQLException
+       {
+               this.execute(sql);
+               while (result != null && !((postgresql.ResultSet)result).reallyResultSet())
+                       result = ((postgresql.ResultSet)result).getNext();
+               if (result == null)
+                       throw new SQLException("no results returned");
+               return result;
+       }
+
+       /**
+        * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition
+        * SQL statements that return nothing such as SQL DDL statements
+        * can be executed
+        *
+        * @param sql a SQL statement
+        * @return either a row count, or 0 for SQL commands
+        * @exception SQLException if a database access error occurs
+        */
+       public int executeUpdate(String sql) throws SQLException
+       {
+               this.execute(sql);
+               if (((postgresql.ResultSet)result).reallyResultSet())
+                       throw new SQLException("results returned");
+               return this.getUpdateCount();
+       }
+
+       /**
+        * In many cases, it is desirable to immediately release a
+        * Statement's database and JDBC resources instead of waiting
+        * for this to happen when it is automatically closed.  The
+        * close method provides this immediate release.
+        *
+        * <p><B>Note:</B> A Statement is automatically closed when it is 
+        * garbage collected.  When a Statement is closed, its current 
+        * ResultSet, if one exists, is also closed.
+        *
+        * @exception SQLException if a database access error occurs (why?)
+        */
+       public void close() throws SQLException
+       {
+               result = null;
+       }
+
+       /**
+        * The maxFieldSize limit (in bytes) is the maximum amount of
+        * data returned for any column value; it only applies to
+        * BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR
+        * columns.  If the limit is exceeded, the excess data is silently
+        * discarded.
+        *
+        * @return the current max column size limit; zero means unlimited
+        * @exception SQLException if a database access error occurs
+        */
+       public int getMaxFieldSize() throws SQLException
+       {
+               return 8192;            // We cannot change this
+       }
+
+       /**
+        * Sets the maxFieldSize - NOT! - We throw an SQLException just
+        * to inform them to stop doing this.
+        *
+        * @param max the new max column size limit; zero means unlimited
+        * @exception SQLException if a database access error occurs
+        */
+       public void setMaxFieldSize(int max) throws SQLException
+       {
+               throw new SQLException("Attempt to setMaxFieldSize failed - compile time default");
+       }
+
+       /**
+        * The maxRows limit is set to limit the number of rows that
+        * any ResultSet can contain.  If the limit is exceeded, the
+        * excess rows are silently dropped.
+        *
+        * @return the current maximum row limit; zero means unlimited
+        * @exception SQLException if a database access error occurs
+        */
+       public int getMaxRows() throws SQLException
+       {
+               return connection.maxrows;
+       }
+
+       /**
+        * Set the maximum number of rows
+        *
+        * @param max the new max rows limit; zero means unlimited
+        * @exception SQLException if a database access error occurs
+        * @see getMaxRows
+        */
+       public void setMaxRows(int max) throws SQLException
+       {
+         connection.maxrows = max;
+       }
+
+       /**
+        * If escape scanning is on (the default), the driver will do escape
+        * substitution before sending the SQL to the database.  
+        *
+        * @param enable true to enable; false to disable
+        * @exception SQLException if a database access error occurs
+        */
+       public void setEscapeProcessing(boolean enable) throws SQLException
+       {
+               escapeProcessing = enable;
+       }
+
+       /**
+        * The queryTimeout limit is the number of seconds the driver
+        * will wait for a Statement to execute.  If the limit is
+        * exceeded, a SQLException is thrown.
+        *
+        * @return the current query timeout limit in seconds; 0 = unlimited
+        * @exception SQLException if a database access error occurs
+        */
+       public int getQueryTimeout() throws SQLException
+       {
+               return timeout;
+       }
+
+       /**
+        * Sets the queryTimeout limit
+        *
+        * @param seconds - the new query timeout limit in seconds
+        * @exception SQLException if a database access error occurs
+        */
+       public void setQueryTimeout(int seconds) throws SQLException
+       {
+               timeout = seconds;
+       }
+
+       /**
+        * Cancel can be used by one thread to cancel a statement that
+        * is being executed by another thread.  However, PostgreSQL is
+        * a sync. sort of thing, so this really has no meaning - we 
+        * define it as a no-op (i.e. you can't cancel, but there is no
+        * error if you try.)
+        *
+        * 6.4 introduced a cancel operation, but we have not implemented it
+        * yet. Sometime before 6.5, this method will be implemented.
+        *
+        * @exception SQLException only because thats the spec.
+        */
+       public void cancel() throws SQLException
+       {
+               // No-op
+       }
+
+       /**
+        * The first warning reported by calls on this Statement is
+        * returned.  A Statement's execute methods clear its SQLWarning
+        * chain.  Subsequent Statement warnings will be chained to this
+        * SQLWarning.
+        *
+        * <p>The Warning chain is automatically cleared each time a statement
+        * is (re)executed.
+        *
+        * <p><B>Note:</B>  If you are processing a ResultSet then any warnings
+        * associated with ResultSet reads will be chained on the ResultSet
+        * object.
+        *
+        * @return the first SQLWarning on null
+        * @exception SQLException if a database access error occurs
+        */
+       public SQLWarning getWarnings() throws SQLException
+       {
+               return warnings;
+       }
+
+       /**
+        * After this call, getWarnings returns null until a new warning
+        * is reported for this Statement.
+        *
+        * @exception SQLException if a database access error occurs (why?)
+        */
+       public void clearWarnings() throws SQLException
+       {
+               warnings = null;
+       }
+
+       /**
+        * setCursorName defines the SQL cursor name that will be used by
+        * subsequent execute methods.  This name can then be used in SQL
+        * positioned update/delete statements to identify the current row
+        * in the ResultSet generated by this statement.  If a database
+        * doesn't support positioned update/delete, this method is a
+        * no-op.
+        *
+        * <p><B>Note:</B> By definition, positioned update/delete execution
+        * must be done by a different Statement than the one which
+        * generated the ResultSet being used for positioning.  Also, cursor
+        * names must be unique within a Connection.
+        *
+        * <p>We throw an additional constriction.  There can only be one
+        * cursor active at any one time.
+        *
+        * @param name the new cursor name
+        * @exception SQLException if a database access error occurs
+        */
+       public void setCursorName(String name) throws SQLException
+       {
+               connection.setCursorName(name);
+       }
+
+       /**
+        * Execute a SQL statement that may return multiple results. We
+        * don't have to worry about this since we do not support multiple
+        * ResultSets.   You can use getResultSet or getUpdateCount to 
+        * retrieve the result.
+        *
+        * @param sql any SQL statement
+        * @return true if the next result is a ResulSet, false if it is
+        *      an update count or there are no more results
+        * @exception SQLException if a database access error occurs
+        */
+       public boolean execute(String sql) throws SQLException
+       {
+               result = connection.ExecSQL(sql);
+               return (result != null && ((postgresql.ResultSet)result).reallyResultSet());
+       }
+
+       /**
+        * getResultSet returns the current result as a ResultSet.  It
+        * should only be called once per result.
+        *
+        * @return the current result set; null if there are no more
+        * @exception SQLException if a database access error occurs (why?)
+        */
+       public java.sql.ResultSet getResultSet() throws SQLException
+       {
+               return result;
+       }
+
+       /**
+        * getUpdateCount returns the current result as an update count,
+        * if the result is a ResultSet or there are no more results, -1
+        * is returned.  It should only be called once per result.
+        *
+        * @return the current result as an update count.
+        * @exception SQLException if a database access error occurs
+        */
+       public int getUpdateCount() throws SQLException
+       {
+               if (result == null)             return -1;
+               if (((postgresql.ResultSet)result).reallyResultSet())   return -1;
+               return ((postgresql.ResultSet)result).getResultCount();
+       }
+
+       /**
+        * getMoreResults moves to a Statement's next result.  If it returns
+        * true, this result is a ResulSet.
+        *
+        * @return true if the next ResultSet is valid
+        * @exception SQLException if a database access error occurs
+        */
+       public boolean getMoreResults() throws SQLException
+       {
+               result = ((postgresql.ResultSet)result).getNext();
+               return (result != null && ((postgresql.ResultSet)result).reallyResultSet());
+       }
+   
+   /**
+    * Returns the status message from the current Result.<p>
+    * This is used internally by the driver.
+    *
+    * @return status message from backend
+    */
+   public String getResultStatusString()
+   {
+     if(result == null)
+       return null;
+     return ((postgresql.ResultSet)result).getStatusString();
+   }
+}
diff --git a/src/interfaces/jdbc/postgresql/jdbc2/CallableStatement.java b/src/interfaces/jdbc/postgresql/jdbc2/CallableStatement.java
new file mode 100644 (file)
index 0000000..6cbc2ef
--- /dev/null
@@ -0,0 +1,361 @@
+package postgresql.jdbc2;
+
+// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 1 class in the
+// postgresql.jdbc1 package.
+
+import java.sql.*;
+import java.math.*;
+
+/**
+ * CallableStatement is used to execute SQL stored procedures.
+ *
+ * <p>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 registered as an OUT
+ * parameter. The other parameters may be used for input, output or both.
+ * Parameters are refered to sequentially, by number. The first parameter
+ * is 1.
+ *
+ * {?= call <procedure-name>[<arg1>,<arg2>, ...]}                 
+ * {call <procedure-name>[<arg1>,<arg2>, ...]}       
+ *
+ *
+ * <p>IN parameter values 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.
+ *
+ * <p>A Callable statement may return a ResultSet or multiple ResultSets.
+ * Multiple ResultSets are handled using operations inherited from
+ * Statement.
+ *
+ * <p>For maximum portability, a call's ResultSets and update counts should 
+ * be processed prior to getting the values of output parameters.        
+ *
+ * @see Connection#prepareCall
+ * @see ResultSet
+ */
+
+public class CallableStatement extends postgresql.jdbc2.PreparedStatement implements java.sql.CallableStatement
+{
+  /**
+   * @exception SQLException on failure
+   */
+  public 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.
+   *
+   * <p>Note: When reading the value of an out parameter, you must use
+   * the getXXX method whose Java type XXX corresponds to the
+   * parameter's registered SQL type.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @param sqlType SQL type code defined by java.sql.Types; for
+   * parameters of type Numeric or Decimal use the version of
+   * registerOutParameter that accepts a scale value
+   * @exception SQLException if a database-access error occurs.
+   */
+  public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException {
+  }
+  
+  /**
+   * You must also specify the scale for numeric/decimal types:
+   *
+   * <p>Note: When reading the value of an out parameter, you must use
+   * the getXXX method whose Java type XXX corresponds to the
+   * parameter's registered SQL type.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @param sqlType use either java.sql.Type.NUMERIC or java.sql.Type.DECIMAL
+   * @param scale a value greater than or equal to zero representing the
+   * desired number of digits to the right of the decimal point
+   * @exception SQLException if a database-access error occurs.
+   */
+  public void registerOutParameter(int parameterIndex, int sqlType,
+                                  int scale) throws SQLException
+  {
+  }
+  
+  // Old api?
+  //public boolean isNull(int parameterIndex) throws SQLException {
+  //return true;
+  //}
+  
+  /**
+   * An OUT parameter may have the value of SQL NULL; wasNull
+   * reports whether the last value read has this special value.
+   *
+   * <p>Note: You must first call getXXX on a parameter to read its
+   * value and then call wasNull() to see if the value was SQL NULL.
+   * @return true if the last parameter read was SQL NULL
+   * @exception SQLException if a database-access error occurs.
+   */
+  public boolean wasNull() throws SQLException {
+    // check to see if the last access threw an exception
+    return false; // fake it for now
+  }
+  
+  // Old api?
+  //public String getChar(int parameterIndex) throws SQLException {
+  //return null;
+  //}
+  
+  /**
+   * Get the value of a CHAR, VARCHAR, or LONGVARCHAR parameter as a
+   * Java String.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is null
+   * @exception SQLException if a database-access error occurs.
+   */
+  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;
+  //}
+  
+  /**
+   * Get the value of a BIT parameter as a Java boolean.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is false
+   * @exception SQLException if a database-access error occurs.
+   */
+  public boolean getBoolean(int parameterIndex) throws SQLException {
+    return false;
+  }
+  
+  /**
+   * Get the value of a TINYINT parameter as a Java byte.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is 0
+   * @exception SQLException if a database-access error occurs.
+   */
+  public byte getByte(int parameterIndex) throws SQLException {
+    return 0;
+  }
+  
+  /**
+   * Get the value of a SMALLINT parameter as a Java short.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is 0
+   * @exception SQLException if a database-access error occurs.
+   */
+  public short getShort(int parameterIndex) throws SQLException {
+    return 0;
+  }
+  
+  /**
+   * Get the value of an INTEGER parameter as a Java int.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is 0
+   * @exception SQLException if a database-access error occurs.
+   */
+public int getInt(int parameterIndex) throws SQLException {
+    return 0;
+  }
+  
+  /**
+   * Get the value of a BIGINT parameter as a Java long.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is 0
+   * @exception SQLException if a database-access error occurs.
+   */
+  public long getLong(int parameterIndex) throws SQLException {
+    return 0;
+  }
+  
+  /**
+   * Get the value of a FLOAT parameter as a Java float.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is 0
+   * @exception SQLException if a database-access error occurs.
+   */
+  public float getFloat(int parameterIndex) throws SQLException {
+    return (float) 0.0;
+  }
+  
+  /**
+   * Get the value of a DOUBLE parameter as a Java double.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is 0
+   * @exception SQLException if a database-access error occurs.
+   */
+  public double getDouble(int parameterIndex) throws SQLException {
+    return 0.0;
+  }
+  
+  /**
+   * Get the value of a NUMERIC parameter as a java.math.BigDecimal
+   * object.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @param scale a value greater than or equal to zero representing the
+   * desired number of digits to the right of the decimal point
+   * @return the parameter value; if the value is SQL NULL, the result is null
+   * @exception SQLException if a database-access error occurs.
+   */
+  public BigDecimal getBigDecimal(int parameterIndex, int scale)
+       throws SQLException {
+        return null;
+  }
+  
+  /**
+   * Get the value of a SQL BINARY or VARBINARY parameter as a Java
+   * byte[]
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is null
+   * @exception SQLException if a database-access error occurs.
+   */
+  public byte[] getBytes(int parameterIndex) throws SQLException {
+    return null;
+  }
+  
+  // New API (JPM) (getLongVarBinary)
+  //public byte[] getBinaryStream(int parameterIndex) throws SQLException {
+  //return null;
+  //}
+  
+  /**
+   * Get the value of a SQL DATE parameter as a java.sql.Date object
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is null
+   * @exception SQLException if a database-access error occurs.
+   */
+  public java.sql.Date getDate(int parameterIndex) throws SQLException {
+    return null;
+  }
+  
+  /**
+   * Get the value of a SQL TIME parameter as a java.sql.Time object.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is null
+   * @exception SQLException if a database-access error occurs.
+   */
+  public java.sql.Time getTime(int parameterIndex) throws SQLException {
+    return null;
+  }
+  
+  /**
+   * Get the value of a SQL TIMESTAMP parameter as a java.sql.Timestamp object.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return the parameter value; if the value is SQL NULL, the result is null
+   * @exception SQLException if a database-access error occurs.
+   */
+  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.
+  /**
+   * Get the value of a parameter as a Java object.
+   *
+   * <p>This method returns a Java object whose type coresponds to the
+   * SQL type that was registered for this parameter using
+   * registerOutParameter.
+   *
+   * <P>Note that this method may be used to read datatabase-specific,
+   * abstract data types. This is done by specifying a targetSqlType
+   * of java.sql.types.OTHER, which allows the driver to return a
+   * database-specific Java type.
+   *
+   * <p>See the JDBC spec's "Dynamic Programming" chapter for details.
+   *
+   * @param parameterIndex the first parameter is 1, the second is 2,...
+   * @return A java.lang.Object holding the OUT parameter value.
+   * @exception SQLException if a database-access error occurs.
+   */
+  public Object getObject(int parameterIndex)
+       throws SQLException {
+        return null;
+  }
+    
+    // ** JDBC 2 Extensions **
+    
+    public Array getArray(int i) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.math.BigDecimal getBigDecimal(int i) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Blob getBlob(int i) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Clob getClob(int i) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Object getObject(int i,java.util.Map map) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Ref getRef(int i) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.sql.Date getDate(int i,java.util.Calendar cal) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Time getTime(int i,java.util.Calendar cal) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Timestamp getTimestamp(int i,java.util.Calendar cal) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void registerOutParameter(int parameterIndex, int sqlType,String typeName) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+  
+}
+
diff --git a/src/interfaces/jdbc/postgresql/jdbc2/Connection.java b/src/interfaces/jdbc/postgresql/jdbc2/Connection.java
new file mode 100644 (file)
index 0000000..53e1bd6
--- /dev/null
@@ -0,0 +1,418 @@
+package postgresql.jdbc2;
+
+// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 1 class in the
+// postgresql.jdbc1 package.
+
+import java.io.*;
+import java.lang.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.util.*;
+import java.sql.*;
+import postgresql.Field;
+import postgresql.fastpath.*;
+import postgresql.largeobject.*;
+import postgresql.util.*;
+
+/**
+ * $Id: Connection.java,v 1.1 1999/01/17 04:51:56 momjian Exp $
+ *
+ * A Connection represents a session with a specific database.  Within the
+ * context of a Connection, SQL statements are executed and results are
+ * returned.
+ *
+ * <P>A Connection's database is able to provide information describing
+ * its tables, its supported SQL grammar, its stored procedures, the
+ * capabilities of this connection, etc.  This information is obtained
+ * with the getMetaData method.
+ *
+ * <p><B>Note:</B> By default, the Connection automatically commits changes
+ * after executing each statement.  If auto-commit has been disabled, an
+ * explicit commit must be done or database changes will not be saved.
+ *
+ * @see java.sql.Connection
+ */
+public class Connection extends postgresql.Connection implements java.sql.Connection 
+{
+  // This is a cache of the DatabaseMetaData instance for this connection
+  protected DatabaseMetaData metadata;
+  
+  /**
+   * 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
+  {
+    if(metadata==null)
+      metadata = new DatabaseMetaData(this);
+    return metadata;
+  }
+  
+  /**
+   * 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 firstWarning;
+  }
+  
+  /**
+   * 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
+  {
+    firstWarning = null;
+  }
+    
+    /**
+     * This overides the method in postgresql.Connection and returns a
+     * ResultSet.
+     */
+    protected java.sql.ResultSet getResultSet(postgresql.Connection conn, Field[] fields, Vector tuples, String status, int updateCount) throws SQLException
+    {
+       return new postgresql.jdbc2.ResultSet((postgresql.jdbc2.Connection)conn,fields,tuples,status,updateCount);
+    }
+    
+    // *****************
+    // JDBC 2 extensions
+    // *****************
+    
+    public java.sql.Statement createStatement(int resultSetType,int resultSetConcurrency) throws SQLException
+    {
+       // normal create followed by 2 sets?
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.sql.PreparedStatement prepareStatement(String sql,int resultSetType,int resultSetConcurrency) throws SQLException
+    {
+       // normal prepare followed by 2 sets?
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.sql.CallableStatement prepareCall(String sql,int resultSetType,int resultSetConcurrency) throws SQLException
+    {
+       // normal prepare followed by 2 sets?
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public int getResultSetConcurrency() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public int getResultSetType() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.util.Map getTypeMap() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setResultSetConcurrency(int value) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setResultSetType(int type) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setTypeMap(java.util.Map map) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+}
+
+// ***********************************************************************
+
diff --git a/src/interfaces/jdbc/postgresql/jdbc2/DatabaseMetaData.java b/src/interfaces/jdbc/postgresql/jdbc2/DatabaseMetaData.java
new file mode 100644 (file)
index 0000000..9a73f22
--- /dev/null
@@ -0,0 +1,2623 @@
+package postgresql.jdbc2;
+
+// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 1 class in the
+// postgresql.jdbc1 package.
+
+import java.sql.*;
+import java.util.*;
+import postgresql.Field;
+
+/**
+ * This class provides information about the database as a whole.
+ *
+ * <p>Many of the methods here return lists of information in ResultSets.  You
+ * can use the normal ResultSet methods such as getString and getInt to 
+ * retrieve the data from these ResultSets.  If a given form of metadata is
+ * not available, these methods should throw a SQLException.
+ *
+ * <p>Some of these methods take arguments that are String patterns.  These
+ * arguments all have names such as fooPattern.  Within a pattern String,
+ * "%" means match any substring of 0 or more characters, and "_" means
+ * match any one character.  Only metadata entries matching the search
+ * pattern are returned.  if a search pattern argument is set to a null
+ * ref, it means that argument's criteria should be dropped from the
+ * search.
+ *
+ * <p>A SQLException will be throws if a driver does not support a meta
+ * data method.  In the case of methods that return a ResultSet, either
+ * a ResultSet (which may be empty) is returned or a SQLException is
+ * thrown.
+ *
+ * @see java.sql.DatabaseMetaData
+ */
+public class DatabaseMetaData implements java.sql.DatabaseMetaData 
+{
+  Connection connection;               // The connection association
+  
+  // These define various OID's. Hopefully they will stay constant.
+  static final int iVarcharOid = 1043; // OID for varchar
+  static final int iBoolOid = 16;      // OID for bool
+  static final int iInt2Oid = 21;      // OID for int2
+  static final int iInt4Oid = 23;      // OID for int4
+  static final int VARHDRSZ =  4;      // length for int4
+  
+  // This is a default value for remarks
+  private static final byte defaultRemarks[]="no remarks".getBytes();
+  
+  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.
+   *
+   * <p>Note that PostgreSQL 6.3 has a system catalog called pg_version - 
+   * however, select * from pg_version on any database retrieves
+   * no rows.
+   *
+   * <p>For now, we will return the version 6.3 (in the hope 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.4");
+  }
+  
+  /**
+   * 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.
+   *
+   * <p>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 false;
+  }
+  
+  /**
+   * 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 true;
+  }
+  
+  /**
+   * 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. 
+   *
+   * <p>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.
+   *
+   * <p>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 null;
+  }
+  
+  /**
+   * Get a comma separated list of all a database's SQL keywords that
+   * are NOT also SQL92 keywords.
+   *
+   * <p>Within PostgreSQL, the keywords are found in
+   *   src/backend/parser/keywords.c
+   *
+   * <p>For SQL Keywords, I took the list provided at
+   *   <a href="http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt">
+   * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt</a>
+   * 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 _)
+   *
+   * <p>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?
+   *
+   * <p>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.
+   *
+   * <p>e.g.
+   *
+   * <br><pre>
+   * select count(C) as C_COUNT from T group by C;
+   *
+   * </pre><br>
+   * 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?
+   * 
+   * <br>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.
+   *
+   * <p>This changed from false to true in v6.2 of the driver, as this
+   * support was added to the backend.
+   *
+   * @return true if so
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean supportsNonNullableColumns() throws SQLException
+  {
+    return true;
+  }
+  
+  /**
+   * Does this driver support the minimum ODBC SQL grammar.  This
+   * grammar is defined at:
+   *
+   * <p><a href="http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm">http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm</a>
+   *
+   * <p>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
+  {
+    // PM Sep 29 97 - changed from "." as we don't support catalogs.
+    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...
+   *
+   * <p>"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:
+   *
+   * <p><pre>
+   * 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;
+   * </pre><p>
+   *
+   * 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 true;
+  }
+  
+  /**
+   * Get a description of stored procedures available in a catalog
+   * 
+   * <p>Only procedure descriptions matching the schema and procedure
+   * name criteria are returned.  They are ordered by PROCEDURE_SCHEM
+   * and PROCEDURE_NAME
+   *
+   * <p>Each procedure description has the following columns:
+   * <ol>
+   * <li><b>PROCEDURE_CAT</b> String => procedure catalog (may be null)
+   * <li><b>PROCEDURE_SCHEM</b> String => procedure schema (may be null)
+   * <li><b>PROCEDURE_NAME</b> String => procedure name
+   * <li><b>Field 4</b> reserved (make it null)
+   * <li><b>Field 5</b> reserved (make it null)
+   * <li><b>Field 6</b> reserved (make it null)
+   * <li><b>REMARKS</b> String => explanatory comment on the procedure
+   * <li><b>PROCEDURE_TYPE</b> short => kind of procedure
+   *   <ul>
+   *    <li> procedureResultUnknown - May return a result
+   *   <li> procedureNoResult - Does not return a result
+   *   <li> procedureReturnsResult - Returns a result
+   *    </ul>
+   * </ol>
+   *
+   * @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
+  {
+    // the field descriptors for the new ResultSet
+    Field f[] = new Field[8];
+    java.sql.ResultSet r;      // ResultSet for the SQL query that we need to do
+    Vector v = new Vector();           // The new ResultSet tuple stuff
+    
+    byte remarks[] = defaultRemarks;
+    
+    f[0] = new Field(connection, "PROCEDURE_CAT",   iVarcharOid, 32);
+    f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32);
+    f[2] = new Field(connection, "PROCEDURE_NAME",  iVarcharOid, 32);
+    f[3] = f[4] = f[5] = null; // reserved, must be null for now
+    f[6] = new Field(connection, "REMARKS",       iVarcharOid, 8192);
+    f[7] = new Field(connection, "PROCEDURE_TYPE", iInt2Oid,   2);
+    
+    // If the pattern is null, then set it to the default
+    if(procedureNamePattern==null)
+      procedureNamePattern="%";
+    
+    r = connection.ExecSQL("select proname, proretset from pg_proc where proname like '"+procedureNamePattern.toLowerCase()+"' order by proname");
+    
+    while (r.next())
+      {
+       byte[][] tuple = new byte[8][0];
+       
+       tuple[0] = null;                        // Catalog name
+       tuple[1] = null;                        // Schema name
+       tuple[2] = r.getBytes(1);               // Procedure name
+       tuple[3] = tuple[4] = tuple[5] = null;  // Reserved
+       tuple[6] = remarks;                     // Remarks
+       
+       if (r.getBoolean(2))
+         tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureReturnsResult).getBytes();
+       else
+         tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureNoResult).getBytes();
+       
+       v.addElement(tuple);
+      }
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+  
+  /**
+   * Get a description of a catalog's stored procedure parameters
+   * and result columns.
+   *
+   * <p>Only descriptions matching the schema, procedure and parameter
+   * name criteria are returned. They are ordered by PROCEDURE_SCHEM
+   * and PROCEDURE_NAME. Within this, the return value, if any, is
+   * first. Next are the parameter descriptions in call order. The
+   * column descriptions follow in column number order.
+   *
+   * <p>Each row in the ResultSet is a parameter description or column 
+   * description with the following fields:
+   * <ol>
+   * <li><b>PROCEDURE_CAT</b> String => procedure catalog (may be null)
+   * <li><b>PROCEDURE_SCHE</b>M String => procedure schema (may be null)
+   * <li><b>PROCEDURE_NAME</b> String => procedure name
+   * <li><b>COLUMN_NAME</b> String => column/parameter name
+   * <li><b>COLUMN_TYPE</b> Short => kind of column/parameter:
+   * <ul><li>procedureColumnUnknown - nobody knows
+   * <li>procedureColumnIn - IN parameter
+   * <li>procedureColumnInOut - INOUT parameter
+   * <li>procedureColumnOut - OUT parameter
+   * <li>procedureColumnReturn - procedure return value
+   * <li>procedureColumnResult - result column in ResultSet
+   * </ul>
+   * <li><b>DATA_TYPE</b> short => SQL type from java.sql.Types
+   * <li><b>TYPE_NAME</b> String => SQL type name
+   * <li><b>PRECISION</b> int => precision
+   * <li><b>LENGTH</b> int => length in bytes of data
+   * <li><b>SCALE</b> short => scale
+   * <li><b>RADIX</b> short => radix
+   * <li><b>NULLABLE</b> short => can it contain NULL?
+   * <ul><li>procedureNoNulls - does not allow NULL values
+   * <li>procedureNullable - allows NULL values
+   * <li>procedureNullableUnknown - nullability unknown
+   * <li><b>REMARKS</b> String => comment describing parameter/column
+   * </ol>
+   * @param catalog This is ignored in postgresql, advise this is set to null
+   * @param schemaPattern This is ignored in postgresql, advise this is set to null
+   * @param procedureNamePattern a procedure name pattern
+   * @param columnNamePattern a column name pattern
+   * @return each row is a stored procedure parameter or column description
+   * @exception SQLException if a database-access error occurs
+   * @see #getSearchStringEscape
+   */
+  // Implementation note: This is required for Borland's JBuilder to work
+  public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException
+  {
+    if(procedureNamePattern==null)
+      procedureNamePattern="%";
+    
+    if(columnNamePattern==null)
+      columnNamePattern="%";
+    
+    // for now, this returns an empty result set.
+    Field f[] = new Field[13];
+    ResultSet r;       // ResultSet for the SQL query that we need to do
+    Vector v = new Vector();           // The new ResultSet tuple stuff
+    
+    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] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32);
+    f[4] = new Field(connection, new String("COLUMN_TYPE"), iInt2Oid, 2);
+    f[5] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2);
+    f[6] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32);
+    f[7] = new Field(connection, new String("PRECISION"), iInt4Oid, 4);
+    f[8] = new Field(connection, new String("LENGTH"), iInt4Oid, 4);
+    f[9] = new Field(connection, new String("SCALE"), iInt2Oid, 2);
+    f[10] = new Field(connection, new String("RADIX"), iInt2Oid, 2);
+    f[11] = new Field(connection, new String("NULLABLE"), iInt2Oid, 2);
+    f[12] = new Field(connection, new String("REMARKS"), iVarcharOid, 32);
+    
+    // add query loop here
+    
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+  
+  /**
+   * Get a description of tables available in a catalog.              
+   *
+   * <p>Only table descriptions matching the catalog, schema, table
+   * name and type criteria are returned. They are ordered by
+   * TABLE_TYPE, TABLE_SCHEM and TABLE_NAME.                      
+   * 
+   * <p>Each table 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>TABLE_TYPE</b> String => table type. Typical types are "TABLE",
+   * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL
+   * TEMPORARY", "ALIAS", "SYNONYM".                             
+   * <li><b>REMARKS</b> String => explanatory comment on the table
+   * </ol>
+   *
+   * <p>The valid values for the types parameter are:
+   * "TABLE", "INDEX", "LARGE OBJECT", "SEQUENCE", "SYSTEM TABLE" and
+   * "SYSTEM INDEX"
+   *
+   * @param catalog a catalog name; For postgresql, this is ignored, and
+   * should be set to null
+   * @param schemaPattern a schema name pattern; For postgresql, this is ignored, and
+   * should be set to null
+   * @param tableNamePattern a table name pattern. For all tables this should be "%"
+   * @param types a list of table types to include; null returns
+   * all types
+   * @return each row is a table description      
+   * @exception SQLException if a database-access error occurs.
+   */
+  public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException
+  {
+    // Handle default value for types
+    if(types==null)
+      types = defaultTableTypes;
+    
+    if(tableNamePattern==null)
+      tableNamePattern="%";
+    
+    // the field descriptors for the new ResultSet
+    Field f[] = new Field[5];
+    java.sql.ResultSet r;      // ResultSet for the SQL query that we need to do
+    Vector v = new Vector();           // The new ResultSet tuple stuff
+    
+    f[0] = new Field(connection, new String("TABLE_CAT"), iVarcharOid, 32);
+    f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32);
+    f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32);
+    f[3] = new Field(connection, new String("TABLE_TYPE"), iVarcharOid, 32);
+    f[4] = new Field(connection, new String("REMARKS"), iVarcharOid, 32);
+    
+    // Now form the query
+    StringBuffer sql = new StringBuffer("select relname,oid from pg_class where (");
+    boolean notFirst=false;
+    for(int i=0;i<types.length;i++) {
+      if(notFirst)
+       sql.append(" or ");
+      for(int j=0;j<getTableTypes.length;j++)
+       if(getTableTypes[j][0].equals(types[i])) {
+         sql.append(getTableTypes[j][1]);
+         notFirst=true;
+       }
+    }
+    
+    // Added by Stefan Andreasen <stefan@linux.kapow.dk>
+    // Now take the pattern into account
+    sql.append(") and relname like '");
+    sql.append(tableNamePattern.toLowerCase());
+    sql.append("'");
+    
+    // Now run the query
+    r = connection.ExecSQL(sql.toString());
+    
+    byte remarks[];
+    
+    while (r.next())
+      {
+       byte[][] tuple = new byte[5][0];
+       
+       // Fetch the description for the table (if any)
+       java.sql.ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(2));
+       if(((postgresql.ResultSet)dr).getTupleCount()==1) {
+         dr.next();
+         remarks = dr.getBytes(1);
+       } else
+         remarks = defaultRemarks;
+       dr.close();
+       
+       tuple[0] = null;                // Catalog name
+       tuple[1] = null;                // Schema name
+       tuple[2] = r.getBytes(1);       // Table name
+       tuple[3] = null;                // Table type
+       tuple[4] = remarks;             // Remarks
+       v.addElement(tuple);
+      }
+    r.close();
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+  
+  // This array contains the valid values for the types argument
+  // in getTables().
+  //
+  // Each supported type consists of it's name, and the sql where
+  // clause to retrieve that value.
+  //
+  // IMPORTANT: the query must be enclosed in ( )
+  private static final String getTableTypes[][] = {
+    {"TABLE",          "(relkind='r' and relname !~ '^pg_' and relname !~ '^xinv')"},
+    {"INDEX",          "(relkind='i' and relname !~ '^pg_' and relname !~ '^xinx')"},
+    {"LARGE OBJECT",   "(relkind='r' and relname ~ '^xinv')"},
+    {"SEQUENCE",       "(relkind='S' and relname !~ '^pg_')"},
+    {"SYSTEM TABLE",   "(relkind='r' and relname ~ '^pg_')"},
+    {"SYSTEM INDEX",   "(relkind='i' and relname ~ '^pg_')"}
+  };
+  
+  // These are the default tables, used when NULL is passed to getTables
+  // The choice of these provide the same behaviour as psql's \d
+  private static final String defaultTableTypes[] = {
+    "TABLE","INDEX","SEQUENCE"
+  };
+  
+  /**
+   * 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
+  {
+    // We don't use schemas, so we simply return a single schema name "".
+    //
+    Field f[] = new Field[1];
+    Vector v = new Vector();
+    byte[][] tuple = new byte[1][0];
+    f[0] = new Field(connection,new String("TABLE_SCHEM"),iVarcharOid,32);
+    tuple[0] = "".getBytes();
+    v.addElement(tuple);
+    return new ResultSet(connection,f,v,"OK",1);
+  }
+  
+  /**
+   * 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
+   */
+  public java.sql.ResultSet getCatalogs() throws SQLException
+  {
+    // We don't use catalogs, so we simply return a single catalog name "".
+    Field f[] = new Field[1];
+    Vector v = new Vector();
+    byte[][] tuple = new byte[1][0];
+    f[0] = new Field(connection,new String("TABLE_CAT"),iVarcharOid,32);
+    tuple[0] = "".getBytes();
+    v.addElement(tuple);
+    return new ResultSet(connection,f,v,"OK",1);
+  }
+  
+  /**
+   * 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
+  {
+    Field f[] = new Field[1];
+    Vector v = new Vector();
+    byte[][] tuple = new byte[1][0];
+    f[0] = new Field(connection,new String("TABLE_TYPE"),iVarcharOid,32);
+    for(int i=0;i<getTableTypes.length;i++) {
+      tuple[0] = getTableTypes[i][0].getBytes();
+      v.addElement(tuple);
+    }
+    return new ResultSet(connection,f,v,"OK",1);
+  }
+  
+  /**
+   * 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
+  {
+    // the field descriptors for the new ResultSet
+    Field f[] = new Field[18];
+    java.sql.ResultSet r;      // ResultSet for the SQL query that we need to do
+    Vector v = new Vector();           // The new ResultSet tuple stuff
+    
+    f[0] = new Field(connection, new String("TABLE_CAT"), iVarcharOid, 32);
+    f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32);
+    f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32);
+    f[3] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32);
+    f[4] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2);
+    f[5] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32);
+    f[6] = new Field(connection, new String("COLUMN_SIZE"), iInt4Oid, 4);
+    f[7] = new Field(connection, new String("BUFFER_LENGTH"), iVarcharOid, 32);
+    f[8] = new Field(connection, new String("DECIMAL_DIGITS"), iInt4Oid, 4);
+    f[9] = new Field(connection, new String("NUM_PREC_RADIX"), iInt4Oid, 4);
+    f[10] = new Field(connection, new String("NULLABLE"), iInt4Oid, 4);
+    f[11] = new Field(connection, new String("REMARKS"), iVarcharOid, 32);
+    f[12] = new Field(connection, new String("COLUMN_DEF"), iVarcharOid, 32);
+    f[13] = new Field(connection, new String("SQL_DATA_TYPE"), iInt4Oid, 4);
+    f[14] = new Field(connection, new String("SQL_DATETIME_SUB"), iInt4Oid, 4);
+    f[15] = new Field(connection, new String("CHAR_OCTET_LENGTH"), iVarcharOid, 32);
+    f[16] = new Field(connection, new String("ORDINAL_POSITION"), iInt4Oid,4);
+    f[17] = new Field(connection, new String("IS_NULLABLE"), iVarcharOid, 32);
+    
+    // Added by Stefan Andreasen <stefan@linux.kapow.dk>
+    // If the pattern are  null then set them to %
+    if (tableNamePattern == null) tableNamePattern="%";
+    if (columnNamePattern == null) columnNamePattern="%";
+    
+    // Now form the query
+    // Modified by Stefan Andreasen <stefan@linux.kapow.dk>
+    r = connection.ExecSQL("select a.oid,c.relname,a.attname,a.atttypid,a.attnum,a.attnotnull,a.attlen,a.atttypmod from pg_class c, pg_attribute a where a.attrelid=c.oid and c.relname like '"+tableNamePattern.toLowerCase()+"' and a.attname like '"+columnNamePattern.toLowerCase()+"' and a.attnum>0 order by c.relname,a.attnum");
+    
+    byte remarks[];
+    
+    while(r.next()) {
+       byte[][] tuple = new byte[18][0];
+       
+       // Fetch the description for the table (if any)
+       java.sql.ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(1));
+       if(((postgresql.ResultSet)dr).getTupleCount()==1) {
+         dr.next();
+         tuple[11] = dr.getBytes(1);
+       } else
+         tuple[11] = defaultRemarks;
+       
+       dr.close();
+       
+       tuple[0] = "".getBytes();       // Catalog name
+       tuple[1] = "".getBytes();       // Schema name
+       tuple[2] = r.getBytes(2);       // Table name
+       tuple[3] = r.getBytes(3);       // Column name
+       
+       dr = connection.ExecSQL("select typname from pg_type where oid = "+r.getString(4));
+       dr.next();
+       String typname=dr.getString(1);
+       dr.close();
+       tuple[4] = Integer.toString(Field.getSQLType(typname)).getBytes();      // Data type
+       tuple[5] = typname.getBytes();  // Type name
+       
+       // Column size
+       // Looking at the psql source,
+       // I think the length of a varchar as specified when the table was created
+       // should be extracted from atttypmod which contains this length + sizeof(int32)
+       if (typname.equals("bpchar") || typname.equals("varchar")) {
+         int atttypmod = r.getInt(8);
+         tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes();
+       } else
+         tuple[6] = r.getBytes(7);
+       
+       tuple[7] = null;        // Buffer length
+       
+       tuple[8] = "0".getBytes();      // Decimal Digits - how to get this?
+       tuple[9] = "10".getBytes();     // Num Prec Radix - assume decimal
+       
+       // tuple[10] is below
+       // tuple[11] is above
+       
+       tuple[12] = null;       // column default
+       
+       tuple[13] = null;       // sql data type (unused)
+       tuple[14] = null;       // sql datetime sub (unused)
+       
+       tuple[15] = tuple[6];   // char octet length
+       
+       tuple[16] = r.getBytes(5);      // ordinal position
+       
+       String nullFlag = r.getString(6);
+       tuple[10] = Integer.toString(nullFlag.equals("f")?java.sql.DatabaseMetaData.columnNullable:java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable
+       tuple[17] = (nullFlag.equals("f")?"YES":"NO").getBytes();       // is nullable
+       
+       v.addElement(tuple);
+      }
+    r.close();
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+  
+  /**
+   * 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
+  {
+    Field f[] = new Field[8];
+    Vector v = new Vector();
+    
+    if(table==null)
+      table="%";
+    
+    if(columnNamePattern==null)
+      columnNamePattern="%";
+    else
+      columnNamePattern=columnNamePattern.toLowerCase();
+    
+    f[0] = new Field(connection,new String("TABLE_CAT"),iVarcharOid,32);
+    f[1] = new Field(connection,new String("TABLE_SCHEM"),iVarcharOid,32);
+    f[2] = new Field(connection,new String("TABLE_NAME"),iVarcharOid,32);
+    f[3] = new Field(connection,new String("COLUMN_NAME"),iVarcharOid,32);
+    f[4] = new Field(connection,new String("GRANTOR"),iVarcharOid,32);
+    f[5] = new Field(connection,new String("GRANTEE"),iVarcharOid,32);
+    f[6] = new Field(connection,new String("PRIVILEGE"),iVarcharOid,32);
+    f[7] = new Field(connection,new String("IS_GRANTABLE"),iVarcharOid,32);
+    
+    // This is taken direct from the psql source
+    java.sql.ResultSet r = connection.ExecSQL("SELECT relname, relacl FROM pg_class, pg_user WHERE ( relkind = 'r' OR relkind = 'i') and relname !~ '^pg_' and relname !~ '^xin[vx][0-9]+' and usesysid = relowner and relname like '"+table.toLowerCase()+"' ORDER BY relname");
+    while(r.next()) {
+      byte[][] tuple = new byte[8][0];
+      tuple[0] = tuple[1]= "".getBytes();
+      DriverManager.println("relname=\""+r.getString(1)+"\" relacl=\""+r.getString(2)+"\"");
+      
+      // For now, don't add to the result as relacl needs to be processed.
+      //v.addElement(tuple);
+    }
+    
+    return new ResultSet(connection,f,v,"OK",1);
+  }
+  
+  /**
+   * 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
+   */
+  // Implementation note: This is required for Borland's JBuilder to work
+  public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException
+  {
+    // for now, this returns an empty result set.
+    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
+    
+    f[0] = new Field(connection, new String("SCOPE"), iInt2Oid, 2);
+    f[1] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32);
+    f[2] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2);
+    f[3] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32);
+    f[4] = new Field(connection, new String("COLUMN_SIZE"), iInt4Oid, 4);
+    f[5] = new Field(connection, new String("BUFFER_LENGTH"), iInt4Oid, 4);
+    f[6] = new Field(connection, new String("DECIMAL_DIGITS"), iInt2Oid, 2);
+    f[7] = new Field(connection, new String("PSEUDO_COLUMN"), iInt2Oid, 2);
+    
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+  
+  /**
+   * 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, pg_type t " +
+                                                    " WHERE bc.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
+  {
+    java.sql.ResultSet rs = connection.ExecSQL("select typname from pg_type");
+    if(rs!=null) {
+      Field f[] = new Field[18];
+      ResultSet r;     // ResultSet for the SQL query that we need to do
+      Vector v = new Vector();         // The new ResultSet tuple stuff
+      
+      f[0] = new Field(connection, new String("TYPE_NAME"), iVarcharOid, 32);
+      f[1] = new Field(connection, new String("DATA_TYPE"), iInt2Oid, 2);
+      f[2] = new Field(connection, new String("PRECISION"), iInt4Oid, 4);
+      f[3] = new Field(connection, new String("LITERAL_PREFIX"), iVarcharOid, 32);
+      f[4] = new Field(connection, new String("LITERAL_SUFFIX"), iVarcharOid, 32);
+      f[5] = new Field(connection, new String("CREATE_PARAMS"), iVarcharOid, 32);
+      f[6] = new Field(connection, new String("NULLABLE"), iInt2Oid, 2);
+      f[7] = new Field(connection, new String("CASE_SENSITIVE"), iBoolOid, 1);
+      f[8] = new Field(connection, new String("SEARCHABLE"), iInt2Oid, 2);
+      f[9] = new Field(connection, new String("UNSIGNED_ATTRIBUTE"), iBoolOid, 1);
+      f[10] = new Field(connection, new String("FIXED_PREC_SCALE"), iBoolOid, 1);
+      f[11] = new Field(connection, new String("AUTO_INCREMENT"), iBoolOid, 1);
+      f[12] = new Field(connection, new String("LOCAL_TYPE_NAME"), iVarcharOid, 32);
+      f[13] = new Field(connection, new String("MINIMUM_SCALE"), iInt2Oid, 2);
+      f[14] = new Field(connection, new String("MAXIMUM_SCALE"), iInt2Oid, 2);
+      f[15] = new Field(connection, new String("SQL_DATA_TYPE"), iInt4Oid, 4);
+      f[16] = new Field(connection, new String("SQL_DATETIME_SUB"), iInt4Oid, 4);
+      f[17] = new Field(connection, new String("NUM_PREC_RADIX"), iInt4Oid, 4);
+      
+      // cache some results, this will keep memory useage down, and speed
+      // things up a little.
+      byte b9[]  = "9".getBytes();
+      byte b10[] = "10".getBytes();
+      byte bf[]  = "f".getBytes();
+      byte bnn[] = Integer.toString(typeNoNulls).getBytes();
+      byte bts[] = Integer.toString(typeSearchable).getBytes();
+      
+      while(rs.next()) {
+       byte[][] tuple = new byte[18][];
+       String typname=rs.getString(1);
+       tuple[0] = typname.getBytes();
+       tuple[1] = Integer.toString(Field.getSQLType(typname)).getBytes();
+       tuple[2] = b9;  // for now
+       tuple[6] = bnn; // for now
+       tuple[7] = bf; // false for now - not case sensitive
+       tuple[8] = bts;
+       tuple[9] = bf; // false for now - it's signed
+       tuple[10] = bf; // false for now - must handle money
+       tuple[11] = bf; // false for now - handle autoincrement
+       // 12 - LOCAL_TYPE_NAME is null
+       // 13 & 14 ?
+       // 15 & 16 are unused so we return null
+       tuple[17] = b10; // everything is base 10
+       v.addElement(tuple);
+      }
+      rs.close();
+      return new ResultSet(connection, f, v, "OK", 1);
+    }
+    
+    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
+   */
+  // Implementation note: This is required for Borland's JBuilder to work
+  public java.sql.ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException
+  {
+    // for now, this returns an empty result set.
+    Field f[] = new Field[13];
+    ResultSet r;       // ResultSet for the SQL query that we need to do
+    Vector v = new Vector();           // The new ResultSet tuple stuff
+    
+    f[0] = new Field(connection, new String("TABLE_CAT"), iVarcharOid, 32);
+    f[1] = new Field(connection, new String("TABLE_SCHEM"), iVarcharOid, 32);
+    f[2] = new Field(connection, new String("TABLE_NAME"), iVarcharOid, 32);
+    f[3] = new Field(connection, new String("NON_UNIQUE"), iBoolOid, 1);
+    f[4] = new Field(connection, new String("INDEX_QUALIFIER"), iVarcharOid, 32);
+    f[5] = new Field(connection, new String("INDEX_NAME"), iVarcharOid, 32);
+    f[6] = new Field(connection, new String("TYPE"), iInt2Oid, 2);
+    f[7] = new Field(connection, new String("ORDINAL_POSITION"), iInt2Oid, 2);
+    f[8] = new Field(connection, new String("COLUMN_NAME"), iVarcharOid, 32);
+    f[9] = new Field(connection, new String("ASC_OR_DESC"), iVarcharOid, 32);
+    f[10] = new Field(connection, new String("CARDINALITY"), iInt4Oid, 4);
+    f[11] = new Field(connection, new String("PAGES"), iInt4Oid, 4);
+    f[12] = new Field(connection, new String("FILTER_CONDITION"), iVarcharOid, 32);
+    
+    return new ResultSet(connection, f, v, "OK", 1);
+  }
+    
+    // ** JDBC 2 Extensions **
+    
+    public boolean deletesAreDetected(int i) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean othersDeletesAreVisible(int i) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Class getClass(String catalog,
+                         String schema,
+                         String table,
+                         String columnNamePattern
+                         ) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.sql.Connection getConnection() throws SQLException
+    {
+       return (java.sql.Connection)connection;
+    }
+    
+    public java.sql.ResultSet getUDTs(String catalog,
+                                     String schemaPattern,
+                                     String typeNamePattern,
+                                     int[] types
+                                     ) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean othersInsertsAreVisible(int type) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean updatesAreDetected(int type) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean othersUpdatesAreVisible(int type) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean ownUpdatesAreVisible(int type) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean ownInsertsAreVisible(int type) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean insertsAreDetected(int type) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean ownDeletesAreVisible(int type) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean rowChangesAreDetected(int type) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean rowChangesAreVisible(int type) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean supportsBatchUpdates() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean supportsResultSetConcurrency(int type,int concurrency) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean supportsResultSetType(int type) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    
+}
+
diff --git a/src/interfaces/jdbc/postgresql/jdbc2/PreparedStatement.java b/src/interfaces/jdbc/postgresql/jdbc2/PreparedStatement.java
new file mode 100644 (file)
index 0000000..7a835a8
--- /dev/null
@@ -0,0 +1,661 @@
+package postgresql.jdbc2;
+
+// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 1 class in the
+// postgresql.jdbc1 package.
+
+import java.io.*;
+import java.math.*;
+import java.sql.*;
+import java.text.*;
+import java.util.*;
+import postgresql.largeobject.*;
+import postgresql.util.*;
+
+/**
+ * A SQL Statement is pre-compiled and stored in a PreparedStatement object.
+ * This object can then be used to efficiently execute this statement multiple
+ * times.
+ *
+ * <p><B>Note:</B> The setXXX methods for setting IN parameter values must
+ * specify types that are compatible with the defined SQL type of the input
+ * parameter.  For instance, if the IN parameter has SQL type Integer, then
+ * setInt should be used.
+ *
+ * <p>If arbitrary parameter type conversions are required, then the setObject 
+ * method should be used with a target SQL type.
+ *
+ * @see ResultSet
+ * @see java.sql.PreparedStatement
+ */
+public class PreparedStatement extends Statement implements java.sql.PreparedStatement 
+{
+       String sql;
+       String[] templateStrings;
+       String[] inStrings;
+       Connection connection;
+
+       /**
+        * Constructor for the PreparedStatement class.
+        * Split the SQL statement into segments - separated by the arguments.
+        * When we rebuild the thing with the arguments, we can substitute the
+        * args and join the whole thing together.
+        *
+        * @param conn the instanatiating connection
+        * @param sql the SQL statement with ? for IN markers
+        * @exception SQLException if something bad occurs
+        */
+       public PreparedStatement(Connection connection, String sql) throws SQLException
+       {
+               super(connection);
+
+               Vector v = new Vector();
+               boolean inQuotes = false;
+               int lastParmEnd = 0, i;
+
+               this.sql = sql;
+               this.connection = connection;
+               for (i = 0; i < sql.length(); ++i)
+               {
+                       int c = sql.charAt(i);
+
+                       if (c == '\'')
+                               inQuotes = !inQuotes;
+                       if (c == '?' && !inQuotes)
+                       {
+                               v.addElement(sql.substring (lastParmEnd, i));
+                               lastParmEnd = i + 1;
+                       }
+               }
+               v.addElement(sql.substring (lastParmEnd, sql.length()));
+
+               templateStrings = new String[v.size()];
+               inStrings = new String[v.size() - 1];
+               clearParameters();
+
+               for (i = 0 ; i < templateStrings.length; ++i)
+                       templateStrings[i] = (String)v.elementAt(i);
+       }
+
+       /**
+        * A Prepared SQL query is executed and its ResultSet is returned
+        *
+        * @return a ResultSet that contains the data produced by the
+        *      query - never null
+        * @exception SQLException if a database access error occurs
+        */
+       public java.sql.ResultSet executeQuery() throws SQLException
+       {
+               StringBuffer s = new StringBuffer();
+               int i;
+
+               for (i = 0 ; i < inStrings.length ; ++i)
+               {
+                       if (inStrings[i] == null)
+                               throw new SQLException("No value specified for parameter " + (i + 1));
+                       s.append (templateStrings[i]);
+                       s.append (inStrings[i]);
+               }
+               s.append(templateStrings[inStrings.length]);
+               return super.executeQuery(s.toString());        // in Statement class
+       }
+
+       /**
+        * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition,
+        * SQL statements that return nothing such as SQL DDL statements can
+        * be executed.
+        *
+        * @return either the row count for INSERT, UPDATE or DELETE; or
+        *      0 for SQL statements that return nothing.
+        * @exception SQLException if a database access error occurs
+        */
+       public int executeUpdate() throws SQLException
+       {
+               StringBuffer s = new StringBuffer();
+               int i;
+
+               for (i = 0 ; i < inStrings.length ; ++i)
+               {
+                       if (inStrings[i] == null)
+                               throw new SQLException("No value specified for parameter " + (i + 1));
+                       s.append (templateStrings[i]);
+                       s.append (inStrings[i]);
+               }
+               s.append(templateStrings[inStrings.length]);
+               return super.executeUpdate(s.toString());       // in Statement class
+       }       
+
+       /**
+        * Set a parameter to SQL NULL
+        *
+        * <p><B>Note:</B> You must specify the parameters SQL type (although
+        * PostgreSQL ignores it)
+        *
+        * @param parameterIndex the first parameter is 1, etc...
+        * @param sqlType the SQL type code defined in java.sql.Types
+        * @exception SQLException if a database access error occurs
+        */
+       public void setNull(int parameterIndex, int sqlType) throws SQLException
+       {
+               set(parameterIndex, "null");
+       }
+
+       /**
+        * Set a parameter to a Java boolean value.  The driver converts this
+        * to a SQL BIT value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setBoolean(int parameterIndex, boolean x) throws SQLException
+       {
+               set(parameterIndex, x ? "'t'" : "'f'");
+       }
+
+       /**
+        * Set a parameter to a Java byte value.  The driver converts this to
+        * a SQL TINYINT value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setByte(int parameterIndex, byte x) throws SQLException
+       {
+               set(parameterIndex, (new Integer(x)).toString());
+       }
+
+       /**
+        * Set a parameter to a Java short value.  The driver converts this
+        * to a SQL SMALLINT value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setShort(int parameterIndex, short x) throws SQLException
+       {
+               set(parameterIndex, (new Integer(x)).toString());
+       }
+
+       /**
+        * Set a parameter to a Java int value.  The driver converts this to
+        * a SQL INTEGER value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setInt(int parameterIndex, int x) throws SQLException
+       {
+               set(parameterIndex, (new Integer(x)).toString());
+       }
+
+       /**
+        * Set a parameter to a Java long value.  The driver converts this to
+        * a SQL BIGINT value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setLong(int parameterIndex, long x) throws SQLException
+       {
+               set(parameterIndex, (new Long(x)).toString());
+       }
+
+       /**
+        * Set a parameter to a Java float value.  The driver converts this
+        * to a SQL FLOAT value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setFloat(int parameterIndex, float x) throws SQLException
+       {
+               set(parameterIndex, (new Float(x)).toString());
+       }
+
+       /**
+        * Set a parameter to a Java double value.  The driver converts this
+        * to a SQL DOUBLE value when it sends it to the database
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setDouble(int parameterIndex, double x) throws SQLException
+       {
+               set(parameterIndex, (new Double(x)).toString());
+       }
+
+       /**
+        * Set a parameter to a java.lang.BigDecimal value.  The driver
+        * converts this to a SQL NUMERIC value when it sends it to the
+        * database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException
+       {
+               set(parameterIndex, x.toString());
+       }
+
+       /**
+        * Set a parameter to a Java String value.  The driver converts this
+        * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments
+        * size relative to the driver's limits on VARCHARs) when it sends it
+        * to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setString(int parameterIndex, String x) throws SQLException
+       {
+         // if the passed string is null, then set this column to null
+         if(x==null)
+           set(parameterIndex,"null");
+         else {
+           StringBuffer b = new StringBuffer();
+           int i;
+           
+           b.append('\'');
+           for (i = 0 ; i < x.length() ; ++i)
+             {
+               char c = x.charAt(i);
+               if (c == '\\' || c == '\'')
+                 b.append((char)'\\');
+               b.append(c);
+             }
+           b.append('\'');
+           set(parameterIndex, b.toString());
+         }
+       }
+
+  /**
+   * Set a parameter to a Java array of bytes.  The driver converts this
+   * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's
+   * size relative to the driver's limits on VARBINARYs) when it sends
+   * it to the database.
+   *
+   * <p>Implementation note:
+   * <br>With postgresql, this creates a large object, and stores the
+   * objects oid in this column.
+   *
+   * @param parameterIndex the first parameter is 1...
+   * @param x the parameter value
+   * @exception SQLException if a database access error occurs
+   */
+  public void setBytes(int parameterIndex, byte x[]) throws SQLException
+  {
+    LargeObjectManager lom = connection.getLargeObjectAPI();
+    int oid = lom.create();
+    LargeObject lob = lom.open(oid);
+    lob.write(x);
+    lob.close();
+    setInt(parameterIndex,oid);
+  }
+
+       /**
+        * Set a parameter to a java.sql.Date value.  The driver converts this
+        * to a SQL DATE value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setDate(int parameterIndex, java.sql.Date x) throws SQLException
+       {
+         SimpleDateFormat df = new SimpleDateFormat("''"+connection.getDateStyle()+"''");
+         
+         set(parameterIndex, df.format(x));
+         
+         // The above is how the date should be handled.
+         //
+         // However, in JDK's prior to 1.1.6 (confirmed with the
+         // Linux jdk1.1.3 and the Win95 JRE1.1.5), SimpleDateFormat seems
+         // to format a date to the previous day. So the fix is to add a day
+         // before formatting.
+         //
+         // PS: 86400000 is one day
+         //
+         //set(parameterIndex, df.format(new java.util.Date(x.getTime()+86400000)));
+       }
+  
+       /**
+        * Set a parameter to a java.sql.Time value.  The driver converts
+        * this to a SQL TIME value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...));
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setTime(int parameterIndex, Time x) throws SQLException
+       {
+               set(parameterIndex, "'" + x.toString() + "'");
+       }
+
+       /**
+        * Set a parameter to a java.sql.Timestamp value.  The driver converts
+        * this to a SQL TIMESTAMP value when it sends it to the database.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException
+       {
+               set(parameterIndex, "'" + x.toString() + "'");
+       }
+
+       /**
+        * When a very large ASCII value is input to a LONGVARCHAR parameter,
+        * it may be more practical to send it via a java.io.InputStream.
+        * JDBC will read the data from the stream as needed, until it reaches
+        * end-of-file.  The JDBC driver will do any necessary conversion from
+        * ASCII to the database char format.
+        *
+        * <P><B>Note:</B> This stream object can either be a standard Java
+        * stream object or your own subclass that implements the standard
+        * interface.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @param length the number of bytes in the stream
+        * @exception SQLException if a database access error occurs
+        */
+       public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException
+       {
+               setBinaryStream(parameterIndex, x, length);
+       }
+
+       /**
+        * When a very large Unicode value is input to a LONGVARCHAR parameter,
+        * it may be more practical to send it via a java.io.InputStream.
+        * JDBC will read the data from the stream as needed, until it reaches
+        * end-of-file.  The JDBC driver will do any necessary conversion from
+        * UNICODE to the database char format.
+        *
+        * ** DEPRECIATED IN JDBC 2 **
+        *
+        * <P><B>Note:</B> This stream object can either be a standard Java
+        * stream object or your own subclass that implements the standard
+        * interface.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        * @deprecated
+        */
+       public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException
+       {
+               setBinaryStream(parameterIndex, x, length);
+       }
+
+       /**
+        * When a very large binary value is input to a LONGVARBINARY parameter,
+        * it may be more practical to send it via a java.io.InputStream.
+        * JDBC will read the data from the stream as needed, until it reaches
+        * end-of-file.  
+        *
+        * <P><B>Note:</B> This stream object can either be a standard Java
+        * stream object or your own subclass that implements the standard
+        * interface.
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the parameter value
+        * @exception SQLException if a database access error occurs
+        */
+       public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
+       {
+               throw new SQLException("InputStream as parameter not supported");
+       }
+
+       /**
+        * In general, parameter values remain in force for repeated used of a
+        * Statement.  Setting a parameter value automatically clears its
+        * previous value.  However, in coms cases, it is useful to immediately
+        * release the resources used by the current parameter values; this
+        * can be done by calling clearParameters
+        *
+        * @exception SQLException if a database access error occurs
+        */
+       public void clearParameters() throws SQLException
+       {
+               int i;
+
+               for (i = 0 ; i < inStrings.length ; i++)
+                       inStrings[i] = null;
+       }
+
+       /**
+        * Set the value of a parameter using an object; use the java.lang
+        * equivalent objects for integral values.
+        *
+        * <P>The given Java object will be converted to the targetSqlType before
+        * being sent to the database.
+        *
+        * <P>note that this method may be used to pass database-specific
+        * abstract data types.  This is done by using a Driver-specific
+        * Java type and using a targetSqlType of java.sql.Types.OTHER
+        *
+        * @param parameterIndex the first parameter is 1...
+        * @param x the object containing the input parameter value
+        * @param targetSqlType The SQL type to be send to the database
+        * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC
+        *      types this is the number of digits after the decimal.  For 
+        *      all other types this value will be ignored.
+        * @exception SQLException if a database access error occurs
+        */
+       public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException
+       {
+               switch (targetSqlType)
+               {
+                       case Types.TINYINT:
+                       case Types.SMALLINT:
+                       case Types.INTEGER:
+                       case Types.BIGINT:
+                       case Types.REAL:
+                       case Types.FLOAT:
+                       case Types.DOUBLE:
+                       case Types.DECIMAL:
+                       case Types.NUMERIC:
+                               if (x instanceof Boolean)
+                                       set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0");
+                               else
+                                       set(parameterIndex, x.toString());
+                               break;
+                       case Types.CHAR:
+                       case Types.VARCHAR:
+                       case Types.LONGVARCHAR:
+                               setString(parameterIndex, x.toString());
+                               break;
+                       case Types.DATE:
+                               setDate(parameterIndex, (java.sql.Date)x);
+                               break;
+                       case Types.TIME:
+                               setTime(parameterIndex, (Time)x);
+                               break;
+                       case Types.TIMESTAMP:
+                               setTimestamp(parameterIndex, (Timestamp)x);
+                               break;
+                       case Types.OTHER:
+                               setString(parameterIndex, ((PGobject)x).getValue());
+                               break;
+                       default:
+                               throw new SQLException("Unknown Types value");
+               }
+       }
+
+       public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException
+       {
+               setObject(parameterIndex, x, targetSqlType, 0);
+       }
+       
+  /**
+   * This stores an Object into a parameter.
+   * <p>New for 6.4, if the object is not recognised, but it is
+   * Serializable, then the object is serialised using the
+   * postgresql.util.Serialize class.
+   */
+       public void setObject(int parameterIndex, Object x) throws SQLException
+       {
+               if (x instanceof String)
+                       setString(parameterIndex, (String)x);
+               else if (x instanceof BigDecimal)
+                       setBigDecimal(parameterIndex, (BigDecimal)x);
+               else if (x instanceof Short)
+                       setShort(parameterIndex, ((Short)x).shortValue());
+               else if (x instanceof Integer)
+                       setInt(parameterIndex, ((Integer)x).intValue());
+               else if (x instanceof Long)
+                       setLong(parameterIndex, ((Long)x).longValue());
+               else if (x instanceof Float)
+                       setFloat(parameterIndex, ((Float)x).floatValue());
+               else if (x instanceof Double)
+                       setDouble(parameterIndex, ((Double)x).doubleValue());
+               else if (x instanceof byte[])
+                       setBytes(parameterIndex, (byte[])x);
+               else if (x instanceof java.sql.Date)
+                       setDate(parameterIndex, (java.sql.Date)x);
+               else if (x instanceof Time)
+                       setTime(parameterIndex, (Time)x);
+               else if (x instanceof Timestamp)
+                       setTimestamp(parameterIndex, (Timestamp)x);
+               else if (x instanceof Boolean)
+                       setBoolean(parameterIndex, ((Boolean)x).booleanValue());
+               else if (x instanceof PGobject)
+                       setString(parameterIndex, ((PGobject)x).getValue());
+               else
+                       setLong(parameterIndex, connection.putObject(x));
+       }
+
+       /**
+        * Some prepared statements return multiple results; the execute method
+        * handles these complex statements as well as the simpler form of 
+        * statements handled by executeQuery and executeUpdate
+        *
+        * @return true if the next result is a ResultSet; false if it is an
+        *      update count or there are no more results
+        * @exception SQLException if a database access error occurs
+        */
+       public boolean execute() throws SQLException
+       {
+               StringBuffer s = new StringBuffer();
+               int i;
+
+               for (i = 0 ; i < inStrings.length ; ++i)
+               {
+                       if (inStrings[i] == null)
+                               throw new SQLException("No value specified for parameter " + (i + 1));
+                       s.append (templateStrings[i]);
+                       s.append (inStrings[i]);
+               }
+               s.append(templateStrings[inStrings.length]);
+               return super.execute(s.toString());     // in Statement class
+       }
+
+       /**
+        * Returns the SQL statement with the current template values
+        * substituted.
+        */
+       public String toString() {
+               StringBuffer s = new StringBuffer();
+               int i;
+
+               for (i = 0 ; i < inStrings.length ; ++i)
+               {
+                       if (inStrings[i] == null)
+                               s.append( '?' );
+                       else
+                               s.append (templateStrings[i]);
+                       s.append (inStrings[i]);
+               }
+               s.append(templateStrings[inStrings.length]);
+               return s.toString();
+       }
+       
+       // **************************************************************
+       //      END OF PUBLIC INTERFACE 
+       // **************************************************************
+       
+       /**
+        * There are a lot of setXXX classes which all basically do
+        * the same thing.  We need a method which actually does the
+        * set for us.
+        *
+        * @param paramIndex the index into the inString
+        * @param s a string to be stored
+        * @exception SQLException if something goes wrong
+        */
+       private void set(int paramIndex, String s) throws SQLException
+       {
+               if (paramIndex < 1 || paramIndex > inStrings.length)
+                       throw new SQLException("Parameter index out of range");
+               inStrings[paramIndex - 1] = s;
+       }
+    
+    // ** JDBC 2 Extensions **
+    
+    public void addBatch() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.sql.ResultSetMetaData getMetaData() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setArray(int i,Array x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setBlob(int i,Blob x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setCharacterStream(int i,java.io.Reader x,int length) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setClob(int i,Clob x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setNull(int i,int t,String s) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setRef(int i,Ref x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setDate(int i,java.sql.Date d,java.util.Calendar cal) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setTime(int i,Time t,java.util.Calendar cal) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setTimestamp(int i,Timestamp t,java.util.Calendar cal) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+}
diff --git a/src/interfaces/jdbc/postgresql/jdbc2/ResultSet.java b/src/interfaces/jdbc/postgresql/jdbc2/ResultSet.java
new file mode 100644 (file)
index 0000000..9790dac
--- /dev/null
@@ -0,0 +1,1261 @@
+package postgresql.jdbc2;
+
+// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 1 class in the
+// postgresql.jdbc1 package.
+
+
+import java.lang.*;
+import java.io.*;
+import java.math.*;
+import java.text.*;
+import java.util.*;
+import java.sql.*;
+import postgresql.Field;
+import postgresql.largeobject.*;
+import postgresql.util.*;
+
+/**
+ * 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.
+ *
+ * <P>A ResultSet maintains a cursor pointing to its current row of data.  
+ * Initially the cursor is positioned before the first row.  The 'next'
+ * method moves the cursor to the next row.
+ *
+ * <P>The getXXX methods retrieve column values for the current row.  You can
+ * retrieve values either using the index number of the column, or by using
+ * the name of the column.  In general using the column index will be more
+ * efficient.  Columns are numbered from 1.
+ *
+ * <P>For maximum portability, ResultSet columns within each row should be read
+ * in left-to-right order and each column should be read only once.
+ *
+ *<P> For the getXXX methods, the JDBC driver attempts to convert the
+ * underlying data to the specified Java type and returns a suitable Java
+ * value.  See the JDBC specification for allowable mappings from SQL types
+ * to Java types with the ResultSet getXXX methods.
+ *
+ * <P>Column names used as input to getXXX methods are case insenstive.  When
+ * performing a getXXX using a column name, if several columns have the same
+ * name, then the value of the first matching column will be returned.  The
+ * column name option is designed to be used when column names are used in the
+ * SQL Query.  For columns that are NOT explicitly named in the query, it is
+ * best to use column numbers.  If column names were used there is no way for
+ * the programmer to guarentee that they actually refer to the intended
+ * columns.
+ *
+ * <P>A ResultSet is automatically closed by the Statement that generated it 
+ * when that Statement is closed, re-executed, or is used to retrieve the 
+ * next result from a sequence of multiple results.
+ *
+ * <P>The number, types and properties of a ResultSet's columns are provided by
+ * the ResultSetMetaData object returned by the getMetaData method.
+ *
+ * @see ResultSetMetaData
+ * @see java.sql.ResultSet
+ */
+public class ResultSet extends postgresql.ResultSet implements java.sql.ResultSet 
+{
+  /**
+   * 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)
+  {
+      super(conn,fields,tuples,status,updateCount);
+  }
+  
+  /**
+   * 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.
+   *
+   * <p>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.
+   *
+   * <p><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);
+    if (columnIndex < 1 || columnIndex > fields.length)
+      throw new SQLException("Column Index out of range");
+    wasNullFlag = (this_row[columnIndex - 1] == null);
+    if(wasNullFlag)
+      return null;
+    return new String(this_row[columnIndex - 1]);
+  }
+  
+  /**
+   * 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
+   * @deprecated
+   */
+  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.
+   *
+   * <p>In normal use, the bytes represent the raw values returned by the
+   * backend. However, if the column is an OID, then it is assumed to
+   * refer to a Large Object, and that object is returned as a byte array.
+   *
+   * <p><b>Be warned</b> If the large object is huge, then you may run out
+   * of memory.
+   *
+   * @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);
+    
+    // Handle OID's as BLOBS
+    if(!wasNullFlag)
+      if( fields[columnIndex - 1].getOID() == 26) {
+       LargeObjectManager lom = connection.getLargeObjectAPI();
+       LargeObject lob = lom.open(getInt(columnIndex));
+       byte buf[] = lob.read(lob.size());
+       lob.close();
+       return buf;
+      }
+    
+    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)
+      return null;
+    SimpleDateFormat df = new SimpleDateFormat(connection.getDateStyle());
+    try {
+      return new java.sql.Date(df.parse(s).getTime());
+    } catch (ParseException e) {
+      throw new SQLException("Bad Date Format: at " + e.getErrorOffset() + " in " + s);
+    }
+  }
+  
+  /**
+   * 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);
+    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzz");
+    
+    if (s != null)
+      {
+       int TZ = new Float(s.substring(19)).intValue();
+       TZ = TZ * 60 * 60 * 1000;
+       TimeZone zone = TimeZone.getDefault();
+       zone.setRawOffset(TZ);
+       String nm = zone.getID();
+       s = s.substring(0,18) + nm;
+       try {
+         java.util.Date d = df.parse(s);
+         return new Timestamp(d.getTime());
+       } catch (ParseException e) {
+         throw new SQLException("Bad Timestamp Format: at " + e.getErrorOffset() + " in " + 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.
+   *
+   * <p><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.
+   *
+   *<p> 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.
+   *
+   * ** DEPRECATED IN JDBC 2 **
+   *
+   * @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
+   * @deprecated
+   */
+  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 bytes.  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));
+  }
+  
+    /**
+     * @deprecated
+     */
+  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));
+  }
+  
+    /**
+     *
+     * ** DEPRECATED IN JDBC 2 **
+     *
+     * @deprecated
+     */
+  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.
+   *
+   * <p>The warning chain is automatically cleared each time a new
+   * row is read.
+   *
+   * <p><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
+   *
+   * <p>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.
+   *
+   * <p>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.
+   *
+   * <p><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
+   *
+   * <p>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.
+   *
+   * <p>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];
+    
+    // some fields can be null, mainly from those returned by MetaData methods
+    if(field==null) {
+      wasNullFlag=true;
+      return null;
+    }
+    
+    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 connection.getObject(field.getTypeName(), getString(columnIndex));
+      }
+  }
+  
+  /**
+   * Get the value of a column in the current row as a Java object
+   *
+   *<p> 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.
+   *
+   * <p>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");
+  }
+    
+    // ** JDBC 2 Extensions **
+    
+    public boolean absolute(int row) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void afterLast() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void beforeFirst() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void cancelRowUpdates() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void deleteRow() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean first() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Array getArray(String colName) throws SQLException
+    {
+       return getArray(findColumn(colName));
+    }
+    
+    public Array getArray(int i) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.math.BigDecimal getBigDecimal(String columnName) throws SQLException
+    {
+       return getBigDecimal(findColumn(columnName));
+    }
+    
+    public Blob getBlob(String columnName) throws SQLException
+    {
+       return getBlob(findColumn(columnName));
+    }
+    
+    public Blob getBlob(int i) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.io.Reader getCharacterStream(String columnName) throws SQLException
+    {
+       return getCharacterStream(findColumn(columnName));
+    }
+    
+    public java.io.Reader getCharacterStream(int i) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Clob getClob(String columnName) throws SQLException
+    {
+       return getClob(findColumn(columnName));
+    }
+    
+    public Clob getClob(int i) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public int getConcurrency() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.sql.Date getDate(int i,java.util.Calendar cal) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Time getTime(int i,java.util.Calendar cal) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Timestamp getTimestamp(int i,java.util.Calendar cal) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.sql.Date getDate(String c,java.util.Calendar cal) throws SQLException
+    {
+       return getDate(findColumn(c),cal);
+    }
+    
+    public Time getTime(String c,java.util.Calendar cal) throws SQLException
+    {
+       return getTime(findColumn(c),cal);
+    }
+    
+    public Timestamp getTimestamp(String c,java.util.Calendar cal) throws SQLException
+    {
+       return getTimestamp(findColumn(c),cal);
+    }
+    
+    public int getFetchDirection() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public int getFetchSize() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public int getKeysetSize() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Object getObject(String columnName,java.util.Map map) throws SQLException
+    {
+       return getObject(findColumn(columnName),map);
+    }
+    
+    public Object getObject(int i,java.util.Map map) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public Ref getRef(String columnName) throws SQLException
+    {
+       return getRef(findColumn(columnName));
+    }
+    
+    public Ref getRef(int i) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public int getRow() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    // This one needs some thought, as not all ResultSets come from a statement
+    public java.sql.Statement getStatement() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public int getType() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void insertRow() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean isAfterLast() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean isBeforeFirst() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean isFirst() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean isLast() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean last() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void moveToCurrentRow() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void moveToInsertRow() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean previous() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void refreshRow() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean relative(int rows) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean rowDeleted() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean rowInserted() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public boolean rowUpdated() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setFetchDirection(int direction) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setFetchSize(int rows) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setKeysetSize(int keys) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateAsciiStream(int columnIndex,
+                                 java.io.InputStream x,
+                                 int length
+                                 ) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateAsciiStream(String columnName,
+                                 java.io.InputStream x,
+                                 int length
+                                 ) throws SQLException
+    {
+       updateAsciiStream(findColumn(columnName),x,length);
+    }
+    
+    public void updateBigDecimal(int columnIndex,
+                                 java.math.BigDecimal x
+                                 ) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateBigDecimal(String columnName,
+                                 java.math.BigDecimal x
+                                 ) throws SQLException
+    {
+       updateBigDecimal(findColumn(columnName),x);
+    }
+    
+    public void updateBinaryStream(int columnIndex,
+                                 java.io.InputStream x,
+                                 int length
+                                 ) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateBinaryStream(String columnName,
+                                 java.io.InputStream x,
+                                 int length
+                                 ) throws SQLException
+    {
+       updateBinaryStream(findColumn(columnName),x,length);
+    }
+    
+    public void updateBoolean(int columnIndex,boolean x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateBoolean(String columnName,boolean x) throws SQLException
+    {
+       updateBoolean(findColumn(columnName),x);
+    }
+    
+    public void updateByte(int columnIndex,byte x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateByte(String columnName,byte x) throws SQLException
+    {
+       updateByte(findColumn(columnName),x);
+    }
+    
+    public void updateBytes(String columnName,byte[] x) throws SQLException
+    {
+       updateBytes(findColumn(columnName),x);
+    }
+    
+    public void updateBytes(int columnIndex,byte[] x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateCharacterStream(int columnIndex,
+                                     java.io.Reader x,
+                                     int length
+                                     ) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateCharacterStream(String columnName,
+                                     java.io.Reader x,
+                                     int length
+                                     ) throws SQLException
+    {
+       updateCharacterStream(findColumn(columnName),x,length);
+    }
+    
+    public void updateDate(int columnIndex,java.sql.Date x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateDate(String columnName,java.sql.Date x) throws SQLException
+    {
+       updateDate(findColumn(columnName),x);
+    }
+    
+    public void updateDouble(int columnIndex,double x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateDouble(String columnName,double x) throws SQLException
+    {
+       updateDouble(findColumn(columnName),x);
+    }
+    
+    public void updateFloat(int columnIndex,float x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateFloat(String columnName,float x) throws SQLException
+    {
+       updateFloat(findColumn(columnName),x);
+    }
+    
+    public void updateInt(int columnIndex,int x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateInt(String columnName,int x) throws SQLException
+    {
+       updateInt(findColumn(columnName),x);
+    }
+    
+    public void updateLong(int columnIndex,long x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateLong(String columnName,long x) throws SQLException
+    {
+       updateLong(findColumn(columnName),x);
+    }
+    
+    public void updateNull(int columnIndex) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateNull(String columnName) throws SQLException
+    {
+       updateNull(findColumn(columnName));
+    }
+    
+    public void updateObject(int columnIndex,Object x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateObject(String columnName,Object x) throws SQLException
+    {
+       updateObject(findColumn(columnName),x);
+    }
+    
+    public void updateObject(int columnIndex,Object x,int scale) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateObject(String columnName,Object x,int scale) throws SQLException
+    {
+       updateObject(findColumn(columnName),x,scale);
+    }
+    
+    public void updateRow() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateShort(int columnIndex,short x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateShort(String columnName,short x) throws SQLException
+    {
+       updateShort(findColumn(columnName),x);
+    }
+    
+    public void updateString(int columnIndex,String x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateString(String columnName,String x) throws SQLException
+    {
+       updateString(findColumn(columnName),x);
+    }
+    
+    public void updateTime(int columnIndex,Time x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateTime(String columnName,Time x) throws SQLException
+    {
+       updateTime(findColumn(columnName),x);
+    }
+    
+    public void updateTimestamp(int columnIndex,Timestamp x) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void updateTimestamp(String columnName,Timestamp x) throws SQLException
+    {
+       updateTimestamp(findColumn(columnName),x);
+    }
+    
+}
+
diff --git a/src/interfaces/jdbc/postgresql/jdbc2/ResultSetMetaData.java b/src/interfaces/jdbc/postgresql/jdbc2/ResultSetMetaData.java
new file mode 100644 (file)
index 0000000..8ac88b9
--- /dev/null
@@ -0,0 +1,435 @@
+package postgresql.jdbc2;
+
+// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 1 class in the
+// postgresql.jdbc1 package.
+
+import java.lang.*;
+import java.sql.*;
+import java.util.*;
+import postgresql.*;
+
+/**
+ * A ResultSetMetaData object can be used to find out about the types and
+ * properties of the columns in a ResultSet
+ *
+ * @see java.sql.ResultSetMetaData
+ */
+public class ResultSetMetaData implements java.sql.ResultSetMetaData 
+{
+  Vector rows;
+  Field[] fields;
+  
+  /**
+   *   Initialise for a result with a tuple set and
+   *   a field descriptor set
+   *
+   * @param rows the Vector of rows returned by the ResultSet
+   * @param fields the array of field descriptors
+   */
+  public ResultSetMetaData(Vector rows, Field[] fields)
+  {
+    this.rows = rows;
+    this.fields = fields;
+  }
+  
+  /**
+   * Whats the number of columns in the ResultSet?
+   *
+   * @return the number
+   * @exception SQLException if a database access error occurs
+   */
+  public int getColumnCount() throws SQLException
+  {
+    return fields.length;
+  }
+  
+  /**
+   * Is the column automatically numbered (and thus read-only)
+   * I believe that PostgreSQL does not support this feature.
+   *
+   * @param column the first column is 1, the second is 2...
+   * @return true if so
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean isAutoIncrement(int column) throws SQLException
+  {
+    return false;
+  }
+  
+  /**
+   * Does a column's case matter? ASSUMPTION: Any field that is
+   * not obviously case insensitive is assumed to be case sensitive
+   *
+   * @param column the first column is 1, the second is 2...
+   * @return true if so
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean isCaseSensitive(int column) throws SQLException
+  {
+    int sql_type = getField(column).getSQLType();
+    
+    switch (sql_type)
+      {
+      case Types.SMALLINT:
+      case Types.INTEGER:
+      case Types.FLOAT:
+      case Types.REAL:
+      case Types.DOUBLE:
+      case Types.DATE:
+      case Types.TIME:
+      case Types.TIMESTAMP:
+       return false;
+      default:
+       return true;
+      }
+  }
+  
+  /**
+   * Can the column be used in a WHERE clause?  Basically for
+   * this, I split the functions into two types: recognised
+   * types (which are always useable), and OTHER types (which
+   * may or may not be useable).  The OTHER types, for now, I
+   * will assume they are useable.  We should really query the
+   * catalog to see if they are useable.
+   *
+   * @param column the first column is 1, the second is 2...
+   * @return true if they can be used in a WHERE clause
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean isSearchable(int column) throws SQLException
+  {
+    int sql_type = getField(column).getSQLType();
+    
+    // This switch is pointless, I know - but it is a set-up
+    // for further expansion.          
+    switch (sql_type)
+      {
+      case Types.OTHER:
+       return true;
+      default:
+       return true;
+      }
+  }
+  
+  /**
+   * Is the column a cash value?  6.1 introduced the cash/money
+   * type, which haven't been incorporated as of 970414, so I
+   * just check the type name for both 'cash' and 'money'
+   *
+   * @param column the first column is 1, the second is 2...
+   * @return true if its a cash column
+   * @exception SQLException if a database access error occurs
+   */
+  public boolean isCurrency(int column) throws SQLException
+  {
+    String type_name = getField(column).getTypeName();
+    
+    return type_name.equals("cash") || type_name.equals("money");
+  }
+  
+  /**
+   * 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));
+       if(x[column-1]!=null) {
+         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 database access error occurs
+   */
+  public String getColumnName(int column) throws SQLException
+  {
+    Field f = getField(column);
+    if(f!=null)
+      return f.name;
+    return "field"+column;
+  }
+  
+  /**
+   * 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
+  {
+    return "";
+  }
+  
+  /**
+   * 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;
+      case Types.VARCHAR:
+       return 0;
+      default:
+       return 0;
+      }
+  }
+  
+  /**
+   * 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;
+      case Types.VARCHAR:
+       return 0;
+      default:
+       return 0;
+      }
+  }
+  
+  /**
+   * 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
+  {
+    return "";
+  }
+  
+  /**
+   * 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];
+  }
+    
+    // ** JDBC 2 Extensions **
+    
+    // This can hook into our PG_Object mechanism
+    public String getColumnClassName(int column) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+}
+
diff --git a/src/interfaces/jdbc/postgresql/jdbc2/Statement.java b/src/interfaces/jdbc/postgresql/jdbc2/Statement.java
new file mode 100644 (file)
index 0000000..427efe1
--- /dev/null
@@ -0,0 +1,397 @@
+package postgresql.jdbc2;
+
+// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver.
+// If you make any modifications to this file, you must make sure that the
+// changes are also made (if relevent) to the related JDBC 1 class in the
+// postgresql.jdbc1 package.
+
+import java.sql.*;
+
+/**
+ * A Statement object is used for executing a static SQL statement and
+ * obtaining the results produced by it.
+ *
+ * <p>Only one ResultSet per Statement can be open at any point in time.  
+ * Therefore, if the reading of one ResultSet is interleaved with the
+ * reading of another, each must have been generated by different
+ * Statements.  All statement execute methods implicitly close a
+ * statement's current ResultSet if an open one exists.
+ *
+ * @see java.sql.Statement
+ * @see ResultSet
+ */
+public class Statement implements java.sql.Statement
+{
+    Connection connection;             // The connection who created us
+    java.sql.ResultSet result = null;  // The current results
+    SQLWarning warnings = null;        // The warnings chain.
+    int timeout = 0;           // The timeout for a query (not used)
+    boolean escapeProcessing = true;// escape processing flag
+    
+       /**
+        * Constructor for a Statement.  It simply sets the connection
+        * that created us.
+        *
+        * @param c the Connection instantation that creates us
+        */
+       public Statement (Connection c)
+       {
+               connection = c;
+       }
+
+       /**
+        * Execute a SQL statement that retruns a single ResultSet
+        *
+        * @param sql typically a static SQL SELECT statement
+        * @return a ResulSet that contains the data produced by the query
+        * @exception SQLException if a database access error occurs
+        */
+       public java.sql.ResultSet executeQuery(String sql) throws SQLException
+       {
+               this.execute(sql);
+               while (result != null && !((postgresql.ResultSet)result).reallyResultSet())
+                       result = ((postgresql.ResultSet)result).getNext();
+               if (result == null)
+                       throw new SQLException("no results returned");
+               return result;
+       }
+
+       /**
+        * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition
+        * SQL statements that return nothing such as SQL DDL statements
+        * can be executed
+        *
+        * @param sql a SQL statement
+        * @return either a row count, or 0 for SQL commands
+        * @exception SQLException if a database access error occurs
+        */
+       public int executeUpdate(String sql) throws SQLException
+       {
+               this.execute(sql);
+               if (((postgresql.ResultSet)result).reallyResultSet())
+                       throw new SQLException("results returned");
+               return this.getUpdateCount();
+       }
+
+       /**
+        * In many cases, it is desirable to immediately release a
+        * Statement's database and JDBC resources instead of waiting
+        * for this to happen when it is automatically closed.  The
+        * close method provides this immediate release.
+        *
+        * <p><B>Note:</B> A Statement is automatically closed when it is 
+        * garbage collected.  When a Statement is closed, its current 
+        * ResultSet, if one exists, is also closed.
+        *
+        * @exception SQLException if a database access error occurs (why?)
+        */
+       public void close() throws SQLException
+       {
+               result = null;
+       }
+
+       /**
+        * The maxFieldSize limit (in bytes) is the maximum amount of
+        * data returned for any column value; it only applies to
+        * BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR
+        * columns.  If the limit is exceeded, the excess data is silently
+        * discarded.
+        *
+        * @return the current max column size limit; zero means unlimited
+        * @exception SQLException if a database access error occurs
+        */
+       public int getMaxFieldSize() throws SQLException
+       {
+               return 8192;            // We cannot change this
+       }
+
+       /**
+        * Sets the maxFieldSize - NOT! - We throw an SQLException just
+        * to inform them to stop doing this.
+        *
+        * @param max the new max column size limit; zero means unlimited
+        * @exception SQLException if a database access error occurs
+        */
+       public void setMaxFieldSize(int max) throws SQLException
+       {
+               throw new SQLException("Attempt to setMaxFieldSize failed - compile time default");
+       }
+
+       /**
+        * The maxRows limit is set to limit the number of rows that
+        * any ResultSet can contain.  If the limit is exceeded, the
+        * excess rows are silently dropped.
+        *
+        * @return the current maximum row limit; zero means unlimited
+        * @exception SQLException if a database access error occurs
+        */
+       public int getMaxRows() throws SQLException
+       {
+               return connection.maxrows;
+       }
+
+       /**
+        * Set the maximum number of rows
+        *
+        * @param max the new max rows limit; zero means unlimited
+        * @exception SQLException if a database access error occurs
+        * @see getMaxRows
+        */
+       public void setMaxRows(int max) throws SQLException
+       {
+         connection.maxrows = max;
+       }
+
+       /**
+        * If escape scanning is on (the default), the driver will do escape
+        * substitution before sending the SQL to the database.  
+        *
+        * @param enable true to enable; false to disable
+        * @exception SQLException if a database access error occurs
+        */
+       public void setEscapeProcessing(boolean enable) throws SQLException
+       {
+               escapeProcessing = enable;
+       }
+
+       /**
+        * The queryTimeout limit is the number of seconds the driver
+        * will wait for a Statement to execute.  If the limit is
+        * exceeded, a SQLException is thrown.
+        *
+        * @return the current query timeout limit in seconds; 0 = unlimited
+        * @exception SQLException if a database access error occurs
+        */
+       public int getQueryTimeout() throws SQLException
+       {
+               return timeout;
+       }
+
+       /**
+        * Sets the queryTimeout limit
+        *
+        * @param seconds - the new query timeout limit in seconds
+        * @exception SQLException if a database access error occurs
+        */
+       public void setQueryTimeout(int seconds) throws SQLException
+       {
+               timeout = seconds;
+       }
+
+       /**
+        * Cancel can be used by one thread to cancel a statement that
+        * is being executed by another thread.  However, PostgreSQL is
+        * a sync. sort of thing, so this really has no meaning - we 
+        * define it as a no-op (i.e. you can't cancel, but there is no
+        * error if you try.)
+        *
+        * 6.4 introduced a cancel operation, but we have not implemented it
+        * yet. Sometime before 6.5, this method will be implemented.
+        *
+        * @exception SQLException only because thats the spec.
+        */
+       public void cancel() throws SQLException
+       {
+               // No-op
+       }
+
+       /**
+        * The first warning reported by calls on this Statement is
+        * returned.  A Statement's execute methods clear its SQLWarning
+        * chain.  Subsequent Statement warnings will be chained to this
+        * SQLWarning.
+        *
+        * <p>The Warning chain is automatically cleared each time a statement
+        * is (re)executed.
+        *
+        * <p><B>Note:</B>  If you are processing a ResultSet then any warnings
+        * associated with ResultSet reads will be chained on the ResultSet
+        * object.
+        *
+        * @return the first SQLWarning on null
+        * @exception SQLException if a database access error occurs
+        */
+       public SQLWarning getWarnings() throws SQLException
+       {
+               return warnings;
+       }
+
+       /**
+        * After this call, getWarnings returns null until a new warning
+        * is reported for this Statement.
+        *
+        * @exception SQLException if a database access error occurs (why?)
+        */
+       public void clearWarnings() throws SQLException
+       {
+               warnings = null;
+       }
+
+       /**
+        * setCursorName defines the SQL cursor name that will be used by
+        * subsequent execute methods.  This name can then be used in SQL
+        * positioned update/delete statements to identify the current row
+        * in the ResultSet generated by this statement.  If a database
+        * doesn't support positioned update/delete, this method is a
+        * no-op.
+        *
+        * <p><B>Note:</B> By definition, positioned update/delete execution
+        * must be done by a different Statement than the one which
+        * generated the ResultSet being used for positioning.  Also, cursor
+        * names must be unique within a Connection.
+        *
+        * <p>We throw an additional constriction.  There can only be one
+        * cursor active at any one time.
+        *
+        * @param name the new cursor name
+        * @exception SQLException if a database access error occurs
+        */
+       public void setCursorName(String name) throws SQLException
+       {
+               connection.setCursorName(name);
+       }
+
+       /**
+        * Execute a SQL statement that may return multiple results. We
+        * don't have to worry about this since we do not support multiple
+        * ResultSets.   You can use getResultSet or getUpdateCount to 
+        * retrieve the result.
+        *
+        * @param sql any SQL statement
+        * @return true if the next result is a ResulSet, false if it is
+        *      an update count or there are no more results
+        * @exception SQLException if a database access error occurs
+        */
+       public boolean execute(String sql) throws SQLException
+       {
+               result = connection.ExecSQL(sql);
+               return (result != null && ((postgresql.ResultSet)result).reallyResultSet());
+       }
+
+       /**
+        * getResultSet returns the current result as a ResultSet.  It
+        * should only be called once per result.
+        *
+        * @return the current result set; null if there are no more
+        * @exception SQLException if a database access error occurs (why?)
+        */
+       public java.sql.ResultSet getResultSet() throws SQLException
+       {
+               return result;
+       }
+
+       /**
+        * getUpdateCount returns the current result as an update count,
+        * if the result is a ResultSet or there are no more results, -1
+        * is returned.  It should only be called once per result.
+        *
+        * @return the current result as an update count.
+        * @exception SQLException if a database access error occurs
+        */
+       public int getUpdateCount() throws SQLException
+       {
+               if (result == null)             return -1;
+               if (((postgresql.ResultSet)result).reallyResultSet())   return -1;
+               return ((postgresql.ResultSet)result).getResultCount();
+       }
+
+       /**
+        * getMoreResults moves to a Statement's next result.  If it returns
+        * true, this result is a ResulSet.
+        *
+        * @return true if the next ResultSet is valid
+        * @exception SQLException if a database access error occurs
+        */
+       public boolean getMoreResults() throws SQLException
+       {
+               result = ((postgresql.ResultSet)result).getNext();
+               return (result != null && ((postgresql.ResultSet)result).reallyResultSet());
+       }
+   
+   /**
+    * Returns the status message from the current Result.<p>
+    * This is used internally by the driver.
+    *
+    * @return status message from backend
+    */
+   public String getResultStatusString()
+   {
+     if(result == null)
+       return null;
+     return ((postgresql.ResultSet)result).getStatusString();
+   }
+    
+    // ** JDBC 2 Extensions **
+    
+    public void addBatch(String sql) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void clearBatch() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public int[] executeBatch() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public java.sql.Connection getConnection() throws SQLException
+    {
+       return (java.sql.Connection)connection;
+    }
+    
+    public int getFetchDirection() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public int getFetchSize() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public int getKeysetSize() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public int getResultSetConcurrency() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public int getResultSetType() throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setFetchDirection(int direction) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setFetchSize(int rows) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setKeysetSize(int keys) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setResultSetConcurrency(int value) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    public void setResultSetType(int value) throws SQLException
+    {
+       throw postgresql.Driver.notImplemented();
+    }
+    
+    
+}
index 0cab20f8e337f2eb317a690ac874aee845d8c737..e2d394bf0510797fa12bad6e46c74a4f817f7e33 100644 (file)
@@ -112,14 +112,40 @@ public class LargeObject
    * @return byte[] array containing data read
    * @exception SQLException if a database-access error occurs.
    */
-  public byte[] read(int len) throws SQLException
-  {
-    FastpathArg args[] = new FastpathArg[2];
-    args[0] = new FastpathArg(fd);
-    args[1] = new FastpathArg(len);
-    return fp.getData("loread",args);
-  }
-  
+    public byte[] read(int len) throws SQLException
+    {
+       // This is the original method, where the entire block (len bytes)
+       // is retrieved in one go.
+       FastpathArg args[] = new FastpathArg[2];
+       args[0] = new FastpathArg(fd);
+       args[1] = new FastpathArg(len);
+       return fp.getData("loread",args);
+       
+       // This version allows us to break this down into 4k blocks
+       //if(len<=4048) {
+       //// handle as before, return the whole block in one go
+       //FastpathArg args[] = new FastpathArg[2];
+       //args[0] = new FastpathArg(fd);
+       //args[1] = new FastpathArg(len);
+       //return fp.getData("loread",args);
+       //} else {
+       //// return in 4k blocks
+       //byte[] buf=new byte[len];
+       //int off=0;
+       //while(len>0) {
+       //int bs=4048;
+       //len-=bs;
+       //if(len<0) {
+       //bs+=len;
+       //len=0;
+       //}
+       //read(buf,off,bs);
+       //off+=bs;
+       //}
+       //return buf;
+       //}
+    }
+    
   /**
    * Reads some data from the object into an existing array
    *
index c7798d15a12608ca2c67aa3f2983da2c91020247..081b8b874a74b9cc50d9f1bc16691c1922581259 100644 (file)
@@ -102,7 +102,7 @@ public class LargeObjectManager
     //
     // This is an example of Fastpath.addFunctions();
     //
-    ResultSet res = (postgresql.ResultSet)conn.createStatement().executeQuery("select proname, oid from pg_proc" +
+    java.sql.ResultSet res = (java.sql.ResultSet)conn.createStatement().executeQuery("select proname, oid from pg_proc" +
                                      " where proname = 'lo_open'" +
                                      "    or proname = 'lo_close'" +
                                      "    or proname = 'lo_creat'" +