diff --git a/src/interfaces/jdbc/CHANGELOG b/src/interfaces/jdbc/CHANGELOG
index a11df412b94e5f71664891122181acdc1bc5d141..2f3dfe2eb204145301a3f9183d962ef5614b8a66 100644
--- a/src/interfaces/jdbc/CHANGELOG
+++ b/src/interfaces/jdbc/CHANGELOG
@@ -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
index 0000000000000000000000000000000000000000..05ceee2a3cf8faf1613cb7f1ad1c65e725b290b6
--- /dev/null
+++ b/src/interfaces/jdbc/Implementation
@@ -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
+
diff --git a/src/interfaces/jdbc/Makefile b/src/interfaces/jdbc/Makefile
index bda001e8b0ea03698628aef771db40aab93cf85b..611687dc9a9c52466ffb9ecfeb93fff32c471d29 100644
--- a/src/interfaces/jdbc/Makefile
+++ b/src/interfaces/jdbc/Makefile
@@ -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:
diff --git a/src/interfaces/jdbc/README b/src/interfaces/jdbc/README
index 035ad008bf3a830f694b47a3a2c935055c13580d..f1e3fb8c2a9bd01a3ce4b193c053ff4ddae50388 100644
--- a/src/interfaces/jdbc/README
+++ b/src/interfaces/jdbc/README
@@ -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
index 0000000000000000000000000000000000000000..3badfa9354c4968429676e8af7dfa37d9a40ea92
--- /dev/null
+++ b/src/interfaces/jdbc/makeVersion.java
@@ -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");
+	}
+    }
+}
diff --git a/src/interfaces/jdbc/postgresql/Connection.java b/src/interfaces/jdbc/postgresql/Connection.java
index be15b38abe0979fa2c1e717696662061755f8d3a..df354776f7fc51f7645e8230b82af331e0a2c336 100644
--- a/src/interfaces/jdbc/postgresql/Connection.java
+++ b/src/interfaces/jdbc/postgresql/Connection.java
@@ -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;
 }
-
-// ***********************************************************************
-
diff --git a/src/interfaces/jdbc/postgresql/Driver.java b/src/interfaces/jdbc/postgresql/Driver.java
index 90563d5460712084e9fc3f16800cd7632edac070..b3cc57f4f2f520f831d32683ca5f26a9043e99c7 100644
--- a/src/interfaces/jdbc/postgresql/Driver.java
+++ b/src/interfaces/jdbc/postgresql/Driver.java
@@ -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.");
+    }
 }
 
diff --git a/src/interfaces/jdbc/postgresql/Field.java b/src/interfaces/jdbc/postgresql/Field.java
index b39aab20c1574ec84a15c25adf15cecfe0d9fd9d..416ddaa7e27c14acf5ec086dc4ee877eb7cbf501 100644
--- a/src/interfaces/jdbc/postgresql/Field.java
+++ b/src/interfaces/jdbc/postgresql/Field.java
@@ -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.
diff --git a/src/interfaces/jdbc/postgresql/ResultSet.java b/src/interfaces/jdbc/postgresql/ResultSet.java
index 12dcc476af43d117544253087e7859fe468012f4..69a00e67984ed437bac5c6c78e89e308dac7bfed 100644
--- a/src/interfaces/jdbc/postgresql/ResultSet.java
+++ b/src/interfaces/jdbc/postgresql/ResultSet.java
@@ -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;
 }
 
diff --git a/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java b/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java
index ab702f457f2d7c0e91baa72ebbd401e418b203b5..232f8f0248581903f75f7b3e290d2aa227fd7e34 100644
--- a/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java
+++ b/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java
@@ -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
index 0000000000000000000000000000000000000000..2a90157d0121e2e78ed0da9c3f0d11152f468b4d
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc1/CallableStatement.java
@@ -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
index 0000000000000000000000000000000000000000..790dfa6ab0d2d151be8e81e88e46383346b57402
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc1/Connection.java
@@ -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
index 0000000000000000000000000000000000000000..b7e90e8768adff5cce7cc8b59f48ee79ea474740
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc1/DatabaseMetaData.java
@@ -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
index 0000000000000000000000000000000000000000..78eaf3db35be6264cf4a37eda9517a897287b81d
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc1/PreparedStatement.java
@@ -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
index 0000000000000000000000000000000000000000..5d5e0334ac695c5fb40df6761dc407da7eadf466
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc1/ResultSet.java
@@ -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
index 0000000000000000000000000000000000000000..95492e81b910c72df28671a2483d7cff8cd3c4e8
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc1/ResultSetMetaData.java
@@ -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
index 0000000000000000000000000000000000000000..4bc026331b9c8778bba26654ed1831d8a8b9e5d6
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc1/Statement.java
@@ -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
index 0000000000000000000000000000000000000000..6cbc2ef148e8a449b3bb5ed8680c1df8298035f3
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc2/CallableStatement.java
@@ -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
index 0000000000000000000000000000000000000000..53e1bd6529572124879d5c1c8729de5d6e145fb5
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc2/Connection.java
@@ -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
index 0000000000000000000000000000000000000000..9a73f22d81f82589652c72bca93f2f9a855a8603
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc2/DatabaseMetaData.java
@@ -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
index 0000000000000000000000000000000000000000..7a835a8193527153ba5282a84c687d49391d1306
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc2/PreparedStatement.java
@@ -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
index 0000000000000000000000000000000000000000..9790dac4aa876c9fbf09c5d529096f97430d3651
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc2/ResultSet.java
@@ -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
index 0000000000000000000000000000000000000000..8ac88b905f42a51ef468f54e356d59e994de24c0
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc2/ResultSetMetaData.java
@@ -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
index 0000000000000000000000000000000000000000..427efe14df413e3e4a05637bfc98a2d1302dd296
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/jdbc2/Statement.java
@@ -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();
+    }
+    
+    
+}
diff --git a/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java b/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java
index 0cab20f8e337f2eb317a690ac874aee845d8c737..e2d394bf0510797fa12bad6e46c74a4f817f7e33 100644
--- a/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java
+++ b/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java
@@ -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
    *
diff --git a/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java b/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java
index c7798d15a12608ca2c67aa3f2983da2c91020247..081b8b874a74b9cc50d9f1bc16691c1922581259 100644
--- a/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java
+++ b/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java
@@ -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'" +