From 68c6eff9454caa57de1b807f58eade19997c9c14 Mon Sep 17 00:00:00 2001
From: Barry Lind <barry@xythos.com>
Date: Thu, 25 Jul 2002 22:45:28 +0000
Subject: [PATCH] Third phase of restructuring to add jdbc3 support.  Modified
 Files:  	jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java  
 jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java  
 jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java  
 jdbc/org/postgresql/jdbc1/DatabaseMetaData.java  
 jdbc/org/postgresql/jdbc1/Jdbc1Connection.java  
 jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java  
 jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java  
 jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java  
 jdbc/org/postgresql/jdbc2/Array.java  
 jdbc/org/postgresql/jdbc2/DatabaseMetaData.java  
 jdbc/org/postgresql/jdbc2/Jdbc2Connection.java  
 jdbc/org/postgresql/jdbc2/Jdbc2ResultSet.java  Added Files:  
 jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java  
 jdbc/org/postgresql/jdbc2/Jdbc2CallableStatement.java  Removed Files:  
 jdbc/org/postgresql/jdbc1/CallableStatement.java  
 jdbc/org/postgresql/jdbc2/CallableStatement.java  
 jdbc/org/postgresql/jdbc2/UpdateableResultSet.java

---
 .../jdbc1/AbstractJdbc1Connection.java        |    5 +-
 .../jdbc1/AbstractJdbc1ResultSet.java         |   13 +-
 .../jdbc1/AbstractJdbc1Statement.java         |  404 +++-
 .../postgresql/jdbc1/CallableStatement.java   |  322 ---
 .../postgresql/jdbc1/DatabaseMetaData.java    |   24 +-
 .../jdbc1/Jdbc1CallableStatement.java         |   14 +
 .../org/postgresql/jdbc1/Jdbc1Connection.java |   12 +-
 .../org/postgresql/jdbc1/Jdbc1ResultSet.java  |   11 +-
 .../jdbc2/AbstractJdbc2ResultSet.java         | 2065 +++++++++++------
 .../jdbc2/AbstractJdbc2Statement.java         |   61 +-
 .../jdbc/org/postgresql/jdbc2/Array.java      |    2 +-
 .../postgresql/jdbc2/CallableStatement.java   |  604 -----
 .../postgresql/jdbc2/DatabaseMetaData.java    |   26 +-
 .../jdbc2/Jdbc2CallableStatement.java         |   15 +
 .../org/postgresql/jdbc2/Jdbc2Connection.java |   18 +-
 .../org/postgresql/jdbc2/Jdbc2ResultSet.java  |   11 +-
 .../postgresql/jdbc2/UpdateableResultSet.java | 1389 -----------
 17 files changed, 1873 insertions(+), 3123 deletions(-)
 delete mode 100644 src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java
 create mode 100644 src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java
 delete mode 100644 src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java
 create mode 100644 src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2CallableStatement.java
 delete mode 100644 src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java

diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java
index 98aa33e5c91..f6d3807bb9a 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java
@@ -13,7 +13,7 @@ import org.postgresql.largeobject.LargeObjectManager;
 import org.postgresql.util.*;
 
 
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Connection.java,v 1.1 2002/07/23 03:59:55 barry Exp $
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Connection.java,v 1.2 2002/07/25 22:45:27 barry Exp $
  * This class defines methods of the jdbc1 specification.  This class is
  * extended by org.postgresql.jdbc2.AbstractJdbc2Connection which adds the jdbc2
  * methods.  The real Connection class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Connection
@@ -359,8 +359,7 @@ public abstract class AbstractJdbc1Connection implements org.postgresql.PGConnec
         // are common to all implementations (JDBC1 or 2), they are placed here.
         // This should make it easy to maintain the two specifications.
 
-//BJL TODO this method shouldn't need to take a Connection since this can be used.
-        public abstract java.sql.ResultSet getResultSet(java.sql.Statement stat, org.postgresql.Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException;
+        public abstract java.sql.ResultSet getResultSet(Statement statement, org.postgresql.Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException;
 
         /*
          * This adds a warning to the warning chain.
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java
index 5aa4f90298a..3bb278a8e9d 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java
@@ -13,15 +13,15 @@ import org.postgresql.largeobject.*;
 import org.postgresql.util.PGbytea;
 import org.postgresql.util.PSQLException;
 
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1ResultSet.java,v 1.1 2002/07/23 03:59:55 barry Exp $
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1ResultSet.java,v 1.2 2002/07/25 22:45:27 barry Exp $
  * This class defines methods of the jdbc1 specification.  This class is
  * extended by org.postgresql.jdbc2.AbstractJdbc2ResultSet which adds the jdbc2
  * methods.  The real ResultSet class (for jdbc1) is org.postgresql.jdbc1.Jdbc1ResultSet
  */
 public abstract class AbstractJdbc1ResultSet
 {
-
 	protected Vector rows;			// The results
+        protected Statement statement;
 	protected Field fields[];		// The field descriptions
 	protected String status;		// Status of the result
 	protected boolean binaryCursor = false; // is the data binary or Strings
@@ -33,7 +33,7 @@ public abstract class AbstractJdbc1ResultSet
 	protected SQLWarning warnings = null;	// The warning chain
 	protected boolean wasNullFlag = false;	// the flag for wasNull()
 
-	//	We can chain multiple resultSets together - this points to
+	// We can chain multiple resultSets together - this points to
 	// next resultSet in the chain.
 	protected ResultSet next = null;
 
@@ -41,9 +41,10 @@ public abstract class AbstractJdbc1ResultSet
 	public byte[][] rowBuffer=null;
 
 
-	public AbstractJdbc1ResultSet(org.postgresql.PGConnection conn, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor)
+	public AbstractJdbc1ResultSet(org.postgresql.PGConnection conn, Statement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor)
 	{
 		this.connection = conn;
+                this.statement = statement;
 		this.fields = fields;
 		this.rows = tuples;
 		this.status = status;
@@ -116,7 +117,7 @@ public abstract class AbstractJdbc1ResultSet
 				throw new PSQLException("postgresql.res.badbyte", s);
 			}
 		}
-		return 0;		// SQL NULL
+		return 0; // SQL NULL
 	}
 
 	public short getShort(int columnIndex) throws SQLException
@@ -134,7 +135,7 @@ public abstract class AbstractJdbc1ResultSet
 				throw new PSQLException("postgresql.res.badshort", s);
 			}
 		}
-		return 0;		// SQL NULL
+		return 0; // SQL NULL
 	}
 
 	public int getInt(int columnIndex) throws SQLException
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java
index 6b06cc873e0..96a50efa8fa 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java
@@ -8,7 +8,7 @@ import java.util.Vector;
 import org.postgresql.largeobject.*;
 import org.postgresql.util.*;
 
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.2 2002/07/24 22:08:39 barry Exp $
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.3 2002/07/25 22:45:27 barry Exp $
  * This class defines methods of the jdbc1 specification.  This class is
  * extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2
  * methods.  The real Statement class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Statement
@@ -47,6 +47,20 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
 	protected String[] templateStrings;
 	protected String[] inStrings;
 
+	//Used by the callablestatement style methods
+	private static final String JDBC_SYNTAX = "{[? =] call <some_function> ([? [,?]*]) }";
+	private static final String RESULT_COLUMN = "result";
+	private String originalSql = "";
+	private boolean isFunction;
+	// functionReturnType contains the user supplied value to check
+	// testReturn contains a modified version to make it easier to 
+	// check the getXXX methods..
+	private int functionReturnType;
+	private int testReturn;
+	// returnTypeSet is true when a proper call to registerOutParameter has been made
+	private boolean returnTypeSet;
+	protected Object callResult;
+
 
 
 	public AbstractJdbc1Statement (AbstractJdbc1Connection connection)
@@ -62,6 +76,10 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
 	}
 
 	protected void parseSqlStmt () throws SQLException {
+	    if (this instanceof CallableStatement) {
+                modifyJdbcCall();
+	    }
+
 		Vector v = new Vector();
 		boolean inQuotes = false;
 		int lastParmEnd = 0, i;
@@ -179,7 +197,23 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
 		// New in 7.1, pass Statement so that ExecSQL can customise to it
 		result = ((AbstractJdbc1Connection)connection).ExecSQL(sql, (java.sql.Statement)this);
 
-		return (result != null && ((AbstractJdbc1ResultSet)result).reallyResultSet());
+                //If we are executing a callable statement function set the return data
+		if (isFunction) {
+  		        if (!((AbstractJdbc1ResultSet)result).reallyResultSet())
+			    throw new PSQLException("postgresql.call.noreturnval");
+			if (!result.next ())
+				throw new PSQLException ("postgresql.call.noreturnval");
+			callResult = result.getObject(1);
+			int columnType = result.getMetaData().getColumnType(1);
+			if (columnType != functionReturnType) 
+				throw new PSQLException ("postgresql.call.wrongrtntype",
+										 new Object[]{
+					"java.sql.Types=" + columnType, "java.sql.Types="+functionReturnType }); 
+		        result.close ();
+                        return true;
+		} else {
+		        return (result != null && ((AbstractJdbc1ResultSet)result).reallyResultSet());
+		}
 	}
 
 	/*
@@ -233,6 +267,8 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
 	{
 		if (result == null)
 			return -1;
+                if (isFunction)
+		        return 1;
 		if (((AbstractJdbc1ResultSet)result).reallyResultSet())
 			return -1;
 		return ((AbstractJdbc1ResultSet)result).getResultCount();
@@ -253,14 +289,6 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
 
 
 
-
-
-
-
-
-
-
-
 	/*
 	 * Returns the status message from the current Result.<p>
 	 * This is used internally by the driver.
@@ -1214,6 +1242,291 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
 			setSerialize(parameterIndex, connection.storeObject(x), x.getClass().getName() );
 	}
 
+	/*
+	 * 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.
+	 *
+	 * ONLY 1 RETURN PARAMETER if {?= call ..} syntax is used
+	 *
+	 * @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
+		{
+			if (parameterIndex != 1)
+				throw new PSQLException ("postgresql.call.noinout");
+			if (!isFunction)
+				throw new PSQLException ("postgresql.call.procasfunc", originalSql);
+			
+			// functionReturnType contains the user supplied value to check
+			// testReturn contains a modified version to make it easier to 
+			// check the getXXX methods..
+			functionReturnType = sqlType;
+			testReturn = sqlType;
+			if (functionReturnType == Types.CHAR || 
+				functionReturnType == Types.LONGVARCHAR)
+				testReturn = Types.VARCHAR;
+			else if (functionReturnType == Types.FLOAT)
+				testReturn = Types.REAL; // changes to streamline later error checking
+			returnTypeSet = true;
+		}
+
+	/*
+	 * 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
+		{
+			registerOutParameter (parameterIndex, sqlType); // ignore for now..
+		}
+
+	/*
+	 * 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 (callResult == 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
+	{
+		checkIndex (parameterIndex, Types.VARCHAR, "String");
+		return (String)callResult;
+	}
+
+
+	/*
+	 * 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
+	{
+		checkIndex (parameterIndex, Types.BIT, "Boolean");
+		if (callResult == null) return false;
+		return ((Boolean)callResult).booleanValue ();
+	}
+
+	/*
+	 * 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
+	{
+		checkIndex (parameterIndex, Types.TINYINT, "Byte");
+		if (callResult == null) return 0;
+		return (byte)((Integer)callResult).intValue ();
+	}
+
+	/*
+	 * 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
+	{
+		checkIndex (parameterIndex, Types.SMALLINT, "Short");
+		if (callResult == null) return 0;
+		return (short)((Integer)callResult).intValue ();
+	}
+		
+
+	/*
+	 * 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
+	{
+		checkIndex (parameterIndex, Types.INTEGER, "Int");
+		if (callResult == null) return 0;
+		return ((Integer)callResult).intValue ();
+	}
+
+	/*
+	 * 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
+	{
+		checkIndex (parameterIndex, Types.BIGINT, "Long");
+		if (callResult == null) return 0;
+		return ((Long)callResult).longValue ();
+	}
+
+	/*
+	 * 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
+	{
+		checkIndex (parameterIndex, Types.REAL, "Float");
+		if (callResult == null) return 0;
+		return ((Float)callResult).floatValue ();
+	}
+
+	/*
+	 * 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
+	{
+		checkIndex (parameterIndex, Types.DOUBLE, "Double");
+		if (callResult == null) return 0;
+		return ((Double)callResult).doubleValue ();
+	}
+
+	/*
+	 * 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.
+	 * @deprecated in Java2.0
+	 */
+	public BigDecimal getBigDecimal(int parameterIndex, int scale)
+	throws SQLException
+	{
+		checkIndex (parameterIndex, Types.NUMERIC, "BigDecimal");
+		return ((BigDecimal)callResult);
+	}
+
+	/*
+	 * 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
+	{
+		checkIndex (parameterIndex, Types.VARBINARY, "Bytes");
+		return ((byte [])callResult);
+	}
+
+
+	/*
+	 * 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
+	{
+		checkIndex (parameterIndex, Types.DATE, "Date");
+		return (java.sql.Date)callResult;
+	}
+
+	/*
+	 * 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
+	{
+		checkIndex (parameterIndex, Types.TIME, "Time");
+		return (java.sql.Time)callResult;
+	}
+
+	/*
+	 * 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
+	{
+		checkIndex (parameterIndex, Types.TIMESTAMP, "Timestamp");
+		return (java.sql.Timestamp)callResult;
+	}
+
+	// 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
+	{
+		checkIndex (parameterIndex);		
+		return callResult;
+	}
+
 	/*
 	 * Returns the SQL statement with the current template values
 	 * substituted.
@@ -1253,6 +1566,8 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
 	{
 		if (paramIndex < 1 || paramIndex > inStrings.length)
 			throw new PSQLException("postgresql.prep.range");
+		if (paramIndex == 1 && isFunction) // need to registerOut instead
+			throw new PSQLException ("postgresql.call.funcover");             
 		inStrings[paramIndex - 1] = s;
 	}
 
@@ -1267,6 +1582,13 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
 		sbuf.setLength(0);
 		int i;
 
+		if (isFunction && !returnTypeSet)
+			throw new PSQLException("postgresql.call.noreturntype");
+		if (isFunction) { // set entry 1 to dummy entry..
+			inStrings[0] = ""; // dummy entry which ensured that no one overrode
+			// and calls to setXXX (2,..) really went to first arg in a function call..
+		}
+
 		for (i = 0 ; i < inStrings.length ; ++i)
 		{
 			if (inStrings[i] == null)
@@ -1299,5 +1621,67 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme
 		set(parameterIndex, Long.toString(x) + "::" + tablename );
 	}
 
+	/** 
+	 * this method will turn a string of the form
+	 * {? = call <some_function> (?, [?,..]) }
+	 * into the PostgreSQL format which is 
+	 * select <some_function> (?, [?, ...]) as result
+	 * 
+	 */
+	private void modifyJdbcCall() throws SQLException {
+		// syntax checking is not complete only a few basics :(
+		originalSql = sql; // save for error msgs..
+		int index = sql.indexOf ("="); // is implied func or proc?
+		boolean isValid = true;
+		if (index != -1) {
+			isFunction = true;
+			isValid = sql.indexOf ("?") < index; // ? before =			
+		}
+		sql = sql.trim ();
+		if (sql.startsWith ("{") && sql.endsWith ("}")) {
+			sql = sql.substring (1, sql.length() -1);
+		} else isValid = false;
+		index = sql.indexOf ("call"); 
+		if (index == -1 || !isValid)
+			throw new PSQLException ("postgresql.call.malformed", 
+									 new Object[]{sql, JDBC_SYNTAX});
+		sql = sql.replace ('{', ' '); // replace these characters
+		sql = sql.replace ('}', ' ');
+		sql = sql.replace (';', ' ');
+		
+		// this removes the 'call' string and also puts a hidden '?'
+		// at the front of the line for functions, this will
+		// allow the registerOutParameter to work correctly
+                // because in the source sql there was one more ? for the return
+                // value that is not needed by the postgres syntax.  But to make 
+                // sure that the parameter numbers are the same as in the original
+                // sql we add a dummy parameter in this case
+		sql = (isFunction ? "?" : "") + sql.substring (index + 4);
+		
+		sql = "select " + sql + " as " + RESULT_COLUMN + ";";	  
+	}
+
+	/** helperfunction for the getXXX calls to check isFunction and index == 1
+	 */
+	protected void checkIndex (int parameterIndex, int type, String getName) 
+		throws SQLException {
+		checkIndex (parameterIndex);
+		if (type != this.testReturn) 
+			throw new PSQLException("postgresql.call.wrongget",
+									new Object[]{"java.sql.Types="+testReturn,
+													 getName,
+													 "java.sql.Types="+type});
+	}
+	/** helperfunction for the getXXX calls to check isFunction and index == 1
+	 * @param parameterIndex index of getXXX (index)
+	 * check to make sure is a function and index == 1
+	 */
+	private void checkIndex (int parameterIndex) throws SQLException {
+		if (!isFunction)
+			throw new PSQLException("postgresql.call.noreturntype");
+		if (parameterIndex != 1)
+			throw new PSQLException("postgresql.call.noinout");
+	}
+		
 
 }
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java
deleted file mode 100644
index dab157f97b6..00000000000
--- a/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java
+++ /dev/null
@@ -1,322 +0,0 @@
-package org.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
-// org.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 Jdbc1PreparedStatement implements java.sql.CallableStatement
-{
-	/*
-	 * @exception SQLException on failure
-	 */
-	CallableStatement(Jdbc1Connection 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/org/postgresql/jdbc1/DatabaseMetaData.java b/src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java
index 747ee1f1ea4..bf81f78874d 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java
@@ -13,7 +13,7 @@ import org.postgresql.util.PSQLException;
 /*
  * This class provides information about the database as a whole.
  *
- * $Id: DatabaseMetaData.java,v 1.48 2002/07/23 03:59:55 barry Exp $
+ * $Id: DatabaseMetaData.java,v 1.49 2002/07/25 22:45:28 barry Exp $
  *
  * <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
@@ -1549,7 +1549,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 
 			v.addElement(tuple);
 		}
-		return new Jdbc1ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -1627,7 +1627,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 
 		// add query loop here
 
-		return new Jdbc1ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -1762,7 +1762,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 			v.addElement(tuple);
 		}
 		r.close();
-		return new Jdbc1ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	// This array contains the valid values for the types argument
@@ -1809,7 +1809,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 		f[0] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32);
 		tuple[0] = "".getBytes();
 		v.addElement(tuple);
-		return new Jdbc1ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -1854,7 +1854,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 			tuple[0] = getTableTypes[i][0].getBytes();
 			v.addElement(tuple);
 		}
-		return new Jdbc1ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -2050,7 +2050,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 		}
 		r.close();
 
-		return new Jdbc1ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -2113,7 +2113,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 			//v.addElement(tuple);
 		}
 
-		return new Jdbc1ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -2203,7 +2203,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 		f[6] = new Field(connection, "DECIMAL_DIGITS", iInt2Oid, 2);
 		f[7] = new Field(connection, "PSEUDO_COLUMN", iInt2Oid, 2);
 
-		return new Jdbc1ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -2413,7 +2413,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 			while (hasMore);
 		}
 
-		return new Jdbc1ResultSet(connection, f, tuples, "OK", 1);
+		return connection.getResultSet(null, f, tuples, "OK", 1);
 	}
 
 	/*
@@ -2692,7 +2692,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 				v.addElement(tuple);
 			}
 			rs.close();
-			return new Jdbc1ResultSet(connection, f, v, "OK", 1);
+			return connection.getResultSet(null, f, v, "OK", 1);
 		}
 
 		throw new PSQLException("postgresql.metadata.unavailable");
@@ -2832,7 +2832,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 			}
 		}
 
-		return new Jdbc1ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 }
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java
new file mode 100644
index 00000000000..7cd69103bd5
--- /dev/null
+++ b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java
@@ -0,0 +1,14 @@
+package org.postgresql.jdbc1;
+
+
+import java.sql.*;
+
+public class Jdbc1CallableStatement extends AbstractJdbc1Statement implements java.sql.CallableStatement
+{
+
+	public Jdbc1CallableStatement(Jdbc1Connection connection, String sql) throws SQLException
+	{
+		super(connection, sql);
+	}
+}
+
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java
index 249a41049b6..b67b07fde93 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java
@@ -6,7 +6,7 @@ import java.sql.*;
 import org.postgresql.Field;
 import org.postgresql.util.PSQLException;
 
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/Jdbc1Connection.java,v 1.2 2002/07/24 22:08:40 barry Exp $
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/Jdbc1Connection.java,v 1.3 2002/07/25 22:45:28 barry Exp $
  * This class implements the java.sql.Connection interface for JDBC1.
  * However most of the implementation is really done in 
  * org.postgresql.jdbc1.AbstractJdbc1Connection
@@ -24,10 +24,9 @@ public class Jdbc1Connection extends org.postgresql.jdbc1.AbstractJdbc1Connectio
 		return new org.postgresql.jdbc1.Jdbc1PreparedStatement(this, sql);
 	}
 
-//BJL TODO - merge callable statement logic from jdbc2 to jdbc1
 	public java.sql.CallableStatement prepareCall(String sql) throws SQLException
 	{
-		throw new PSQLException("postgresql.con.call");
+		return new org.postgresql.jdbc1.Jdbc1CallableStatement(this, sql);
 	}
 
 	public java.sql.DatabaseMetaData getMetaData() throws SQLException
@@ -39,7 +38,12 @@ public class Jdbc1Connection extends org.postgresql.jdbc1.AbstractJdbc1Connectio
 
 	public java.sql.ResultSet getResultSet(java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException
 	{
-		return new Jdbc1ResultSet(this, fields, tuples, status, updateCount, insertOID, binaryCursor);
+		return new Jdbc1ResultSet(this, stat, fields, tuples, status, updateCount, insertOID, binaryCursor);
+	}
+
+	public java.sql.ResultSet getResultSet(java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount) throws SQLException
+	{
+		return new Jdbc1ResultSet(this, stat, fields, tuples, status, updateCount, 0, false);
 	}
 
 }
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java
index a959fef9d39..57850569ec8 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java
@@ -5,7 +5,7 @@ import java.sql.*;
 import java.util.Vector;
 import org.postgresql.Field;
 
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/Jdbc1ResultSet.java,v 1.1 2002/07/23 03:59:55 barry Exp $
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/Jdbc1ResultSet.java,v 1.2 2002/07/25 22:45:28 barry Exp $
  * This class implements the java.sql.ResultSet interface for JDBC1.
  * However most of the implementation is really done in 
  * org.postgresql.jdbc1.AbstractJdbc1ResultSet
@@ -13,14 +13,9 @@ import org.postgresql.Field;
 public class Jdbc1ResultSet extends org.postgresql.jdbc1.AbstractJdbc1ResultSet implements java.sql.ResultSet
 {
 
-	public Jdbc1ResultSet(Jdbc1Connection conn, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor)
+	public Jdbc1ResultSet(Jdbc1Connection conn, Statement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor)
 	{
-		super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
-	}
-
-	public Jdbc1ResultSet(Jdbc1Connection conn, Field[] fields, Vector tuples, String status, int updateCount)
-	{
-		super(conn, fields, tuples, status, updateCount, 0, false);
+		super(conn, statement, fields, tuples, status, updateCount, insertOID, binaryCursor);
 	}
 
 	public java.sql.ResultSetMetaData getMetaData() throws SQLException
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java
index d2c5ee07607..1a6379bf592 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java
@@ -6,747 +6,1348 @@ import java.io.*;
 import java.sql.*;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.util.Vector;
+import java.util.*;
+import org.postgresql.Driver;
 import org.postgresql.Field;
 import org.postgresql.core.Encoding;
 import org.postgresql.largeobject.*;
 import org.postgresql.util.PGbytea;
 import org.postgresql.util.PSQLException;
 
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2ResultSet.java,v 1.2 2002/07/24 22:08:42 barry Exp $
+
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2ResultSet.java,v 1.3 2002/07/25 22:45:28 barry Exp $
  * This class defines methods of the jdbc2 specification.  This class extends
  * org.postgresql.jdbc1.AbstractJdbc1ResultSet which provides the jdbc1
  * methods.  The real Statement class (for jdbc2) is org.postgresql.jdbc2.Jdbc2ResultSet
  */
-public class AbstractJdbc2ResultSet extends org.postgresql.jdbc1.AbstractJdbc1ResultSet
-{
-	protected Statement statement;
-
-	protected String sqlQuery=null;
-
-	public AbstractJdbc2ResultSet(org.postgresql.PGConnection conn, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor)
-	{
-		super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
-	}
-
-	public java.net.URL getURL(int columnIndex) throws SQLException
-	{
-		return null;
-	}
-
-	public java.net.URL getURL(String columnName) throws SQLException
-	{
-		return null;
-	}
-
-	/*
-	 * 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;
-
-		checkResultSet( columnIndex );
-
-		wasNullFlag = (this_row[columnIndex - 1] == null);
-		if (wasNullFlag)
-			return null;
-
-		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 getBoolean(columnIndex) ? Boolean.TRUE : Boolean.FALSE;
-			case Types.SMALLINT:
-				return new Short(getShort(columnIndex));
-			case Types.INTEGER:
-				return new Integer(getInt(columnIndex));
-			case Types.BIGINT:
-				return new Long(getLong(columnIndex));
-			case Types.NUMERIC:
-				return getBigDecimal
-					   (columnIndex, (field.getMod() == -1) ? -1 : ((field.getMod() - 4) & 0xffff));
-			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);
-			case Types.BINARY:
-			case Types.VARBINARY:
-				return getBytes(columnIndex);
-			case Types.ARRAY:
-				return getArray(columnIndex);
-			default:
-				String type = field.getPGType();
-				// if the backend doesn't know the type then coerce to String
-				if (type.equals("unknown"))
-				{
-					return getString(columnIndex);
-				}
-				else
-				{
-					return connection.getObject(field.getPGType(), getString(columnIndex));
-				}
-		}
-	}
-
-	public boolean absolute(int index) throws SQLException
-	{
-		// index is 1-based, but internally we use 0-based indices
-		int internalIndex;
-
-		if (index == 0)
-			throw new SQLException("Cannot move to index of 0");
-
-		final int rows_size = rows.size();
-
-		//if index<0, count from the end of the result set, but check
-		//to be sure that it is not beyond the first index
-		if (index < 0)
-		{
-			if (index >= -rows_size)
-				internalIndex = rows_size + index;
-			else
-			{
-				beforeFirst();
-				return false;
-			}
-		}
-		else
-		{
-			//must be the case that index>0,
-			//find the correct place, assuming that
-			//the index is not too large
-			if (index <= rows_size)
-				internalIndex = index - 1;
-			else
-			{
-				afterLast();
-				return false;
-			}
-		}
-
-		current_row = internalIndex;
-		this_row = (byte [][])rows.elementAt(internalIndex);
-		return true;
-	}
-
-	public void afterLast() throws SQLException
-	{
-		final int rows_size = rows.size();
-		if (rows_size > 0)
-			current_row = rows_size;
-	}
-
-	public void beforeFirst() throws SQLException
-	{
-		if (rows.size() > 0)
-			current_row = -1;
-	}
-
-	public void cancelRowUpdates() throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void deleteRow() throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public boolean first() throws SQLException
-	{
-		if (rows.size() <= 0)
-			return false;
-
-		current_row = 0;
-		this_row = (byte [][])rows.elementAt(current_row);
-
-		rowBuffer=new byte[this_row.length][];
-		System.arraycopy(this_row,0,rowBuffer,0,this_row.length);
-
-		return true;
-	}
-
-	public java.sql.Array getArray(String colName) throws SQLException
-	{
-		return getArray(findColumn(colName));
-	}
-
-	public java.sql.Array getArray(int i) throws SQLException
-	{
-		wasNullFlag = (this_row[i - 1] == null);
-		if (wasNullFlag)
-			return null;
-
-		if (i < 1 || i > fields.length)
-			throw new PSQLException("postgresql.res.colrange");
-		return (java.sql.Array) new org.postgresql.jdbc2.Array( connection, i, fields[i - 1], (java.sql.ResultSet)this );
-	}
-
-	public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException
-	{
-		return getBigDecimal(columnIndex, -1);
-	}
-
-	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
-	{
-		return new org.postgresql.largeobject.PGblob(connection, getInt(i));
-	}
-
-	public java.io.Reader getCharacterStream(String columnName) throws SQLException
-	{
-		return getCharacterStream(findColumn(columnName));
-	}
-
-	public java.io.Reader getCharacterStream(int i) throws SQLException
-	{
-		checkResultSet( i );
-		wasNullFlag = (this_row[i - 1] == null);
-		if (wasNullFlag)
-			return null;
-
-		if (((AbstractJdbc2Connection)connection).haveMinimumCompatibleVersion("7.2"))
-		{
-			//Version 7.2 supports AsciiStream for all the PG text types
-			//As the spec/javadoc for this method indicate this is to be used for
-			//large text values (i.e. LONGVARCHAR)	PG doesn't have a separate
-			//long string datatype, but with toast the text datatype is capable of
-			//handling very large values.  Thus the implementation ends up calling
-			//getString() since there is no current way to stream the value from the server
-			return new CharArrayReader(getString(i).toCharArray());
-		}
-		else
-		{
-			// In 7.1 Handle as BLOBS so return the LargeObject input stream
-			Encoding encoding = connection.getEncoding();
-			InputStream input = getBinaryStream(i);
-			return encoding.getDecodingReader(input);
-		}
-	}
-
-	public Clob getClob(String columnName) throws SQLException
-	{
-		return getClob(findColumn(columnName));
-	}
-
-	public Clob getClob(int i) throws SQLException
-	{
-		return new org.postgresql.largeobject.PGclob(connection, getInt(i));
-	}
-
-	public int getConcurrency() throws SQLException
-	{
-		// The standard ResultSet class will now return
-		// CONCUR_READ_ONLY. A sub-class will overide this if the query was
-		// updateable.
-		return java.sql.ResultSet.CONCUR_READ_ONLY;
-	}
-
-	public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException
-	{
-		// If I read the specs, this should use cal only if we don't
-		// store the timezone, and if we do, then act just like getDate()?
-		// for now...
-		return getDate(i);
-	}
-
-	public Time getTime(int i, java.util.Calendar cal) throws SQLException
-	{
-		// If I read the specs, this should use cal only if we don't
-		// store the timezone, and if we do, then act just like getTime()?
-		// for now...
-		return getTime(i);
-	}
-
-	public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException
-	{
-		// If I read the specs, this should use cal only if we don't
-		// store the timezone, and if we do, then act just like getDate()?
-		// for now...
-		return getTimestamp(i);
-	}
-
-	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
-	{
-		//PostgreSQL normally sends rows first->last
-		return java.sql.ResultSet.FETCH_FORWARD;
-	}
-
-	public int getFetchSize() throws SQLException
-	{
-		// In this implementation we return the entire result set, so
-		// here return the number of rows we have. Sub-classes can return a proper
-		// value
-		return rows.size();
-	}
-
-	public Object getObject(String columnName, java.util.Map map) throws SQLException
-	{
-		return getObject(findColumn(columnName), map);
-	}
-
-	/*
-	 * This checks against map for the type of column i, and if found returns
-	 * an object based on that mapping. The class must implement the SQLData
-	 * interface.
-	 */
-	public Object getObject(int i, java.util.Map map) throws SQLException
-	{
-		throw org.postgresql.Driver.notImplemented();
-	}
-
-	public Ref getRef(String columnName) throws SQLException
-	{
-		return getRef(findColumn(columnName));
-	}
-
-	public Ref getRef(int i) throws SQLException
-	{
-		//The backend doesn't yet have SQL3 REF types
-		throw new PSQLException("postgresql.psqlnotimp");
-	}
-
-	public int getRow() throws SQLException
-	{
-		final int rows_size = rows.size();
-
-		if (current_row < 0 || current_row >= rows_size)
-			return 0;
-
-		return current_row + 1;
-	}
-
-	// This one needs some thought, as not all ResultSets come from a statement
-	public Statement getStatement() throws SQLException
-	{
-		return statement;
-	}
-
-	public int getType() throws SQLException
-	{
-		// This implementation allows scrolling but is not able to
-		// see any changes. Sub-classes may overide this to return a more
-		// meaningful result.
-		return java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE;
-	}
-
-	public void insertRow() throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public boolean isAfterLast() throws SQLException
-	{
-		final int rows_size = rows.size();
-		return (current_row >= rows_size && rows_size > 0);
-	}
-
-	public boolean isBeforeFirst() throws SQLException
-	{
-		return (current_row < 0 && rows.size() > 0);
-	}
-
-	public boolean isFirst() throws SQLException
-	{
-		return (current_row == 0 && rows.size() >= 0);
-	}
-
-	public boolean isLast() throws SQLException
-	{
-		final int rows_size = rows.size();
-		return (current_row == rows_size - 1 && rows_size > 0);
-	}
-
-	public boolean last() throws SQLException
-	{
-		final int rows_size = rows.size();
-		if (rows_size <= 0)
-			return false;
-
-		current_row = rows_size - 1;
-		this_row = (byte [][])rows.elementAt(current_row);
-
-		rowBuffer=new byte[this_row.length][];
-		System.arraycopy(this_row,0,rowBuffer,0,this_row.length);
-
-		return true;
-	}
-
-	public void moveToCurrentRow() throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void moveToInsertRow() throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public boolean previous() throws SQLException
-	{
-		if (--current_row < 0)
-			return false;
-		this_row = (byte [][])rows.elementAt(current_row);
-		System.arraycopy(this_row,0,rowBuffer,0,this_row.length);
-		return true;
-	}
-
-	public void refreshRow() throws SQLException
-	{
-		throw new PSQLException("postgresql.notsensitive");
-	}
-
-	public boolean relative(int rows) throws SQLException
-	{
-		//have to add 1 since absolute expects a 1-based index
-		return absolute(current_row + 1 + rows);
-	}
-
-	public boolean rowDeleted() throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-		return false; // javac complains about not returning a value!
-	}
-
-	public boolean rowInserted() throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-		return false; // javac complains about not returning a value!
-	}
-
-	public boolean rowUpdated() throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-		return false; // javac complains about not returning a value!
-	}
-
-	public void setFetchDirection(int direction) throws SQLException
-	{
-		throw new PSQLException("postgresql.psqlnotimp");
-	}
-
-	public void setFetchSize(int rows) throws SQLException
-	{
-		// Sub-classes should implement this as part of their cursor support
-		throw org.postgresql.Driver.notImplemented();
-	}
-
-	public void updateAsciiStream(int columnIndex,
-								  java.io.InputStream x,
-								  int length
-								 ) throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	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
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	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
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	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
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateBoolean(String columnName, boolean x) throws SQLException
-	{
-		updateBoolean(findColumn(columnName), x);
-	}
-
-	public void updateByte(int columnIndex, byte x) throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	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
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateCharacterStream(int columnIndex,
-									  java.io.Reader x,
-									  int length
-									 ) throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	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
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateDate(String columnName, java.sql.Date x) throws SQLException
-	{
-		updateDate(findColumn(columnName), x);
-	}
-
-	public void updateDouble(int columnIndex, double x) throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateDouble(String columnName, double x) throws SQLException
-	{
-		updateDouble(findColumn(columnName), x);
-	}
-
-	public void updateFloat(int columnIndex, float x) throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateFloat(String columnName, float x) throws SQLException
-	{
-		updateFloat(findColumn(columnName), x);
-	}
-
-	public void updateInt(int columnIndex, int x) throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateInt(String columnName, int x) throws SQLException
-	{
-		updateInt(findColumn(columnName), x);
-	}
-
-    	public void updateLong(int columnIndex, long x) throws SQLException
-    	{
-    		// only sub-classes implement CONCUR_UPDATEABLE
-    		notUpdateable();
-    	}
-
-    	public void updateLong(String columnName, long x) throws SQLException
-    	{
-    		updateLong(findColumn(columnName), x);
-    	}
-
-    	public void updateNull(int columnIndex) throws SQLException
-    	{
-    		// only sub-classes implement CONCUR_UPDATEABLE
-    		notUpdateable();
-    	}
-
-	public void updateNull(String columnName) throws SQLException
-	{
-		updateNull(findColumn(columnName));
-	}
-
-	public void updateObject(int columnIndex, Object x) throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateObject(String columnName, Object x) throws SQLException
-	{
-		updateObject(findColumn(columnName), x);
-	}
-
-	public void updateObject(int columnIndex, Object x, int scale) throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateObject(String columnName, Object x, int scale) throws SQLException
-	{
-		updateObject(findColumn(columnName), x, scale);
-	}
-
-	public void updateRow() throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateShort(int columnIndex, short x) throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateShort(String columnName, short x) throws SQLException
-	{
-		updateShort(findColumn(columnName), x);
-	}
-
-	public void updateString(int columnIndex, String x) throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateString(String columnName, String x) throws SQLException
-	{
-		updateString(findColumn(columnName), x);
-	}
-
-	public void updateTime(int columnIndex, Time x) throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateTime(String columnName, Time x) throws SQLException
-	{
-		updateTime(findColumn(columnName), x);
-	}
-
-	public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException
-	{
-		// only sub-classes implement CONCUR_UPDATEABLE
-		notUpdateable();
-	}
-
-	public void updateTimestamp(String columnName, Timestamp x) throws SQLException
-	{
-		updateTimestamp(findColumn(columnName), x);
-	}
-
-	// helper method. Throws an SQLException when an update is not possible
-	public void notUpdateable() throws SQLException
-	{
-		throw new PSQLException("postgresql.noupdate");
-	}
-
-	/*
-	 * It's used currently by getStatement() but may also with the new core
-	 * package.
-	 */
-	public void setStatement(Statement statement)
-	{
-		this.statement = statement;
-	}
-
-	public void setSQLQuery(String sqlQuery) {
-		this.sqlQuery=sqlQuery;
-	}
+public class AbstractJdbc2ResultSet extends org.postgresql.jdbc1.AbstractJdbc1ResultSet {
+  protected String sqlQuery = null;
+
+  //needed for updateable result set support
+  protected boolean updateable = false;
+  protected boolean doingUpdates = false;
+  protected boolean onInsertRow = false;
+  protected Hashtable updateValues = new Hashtable();
+  private boolean usingOID = false;   // are we using the OID for the primary key?
+  private Vector primaryKeys;    // list of primary keys
+  private int numKeys = 0;
+  private boolean singleTable = false;
+  protected String tableName = null;
+  protected PreparedStatement updateStatement = null;
+  protected PreparedStatement insertStatement = null;
+  protected PreparedStatement deleteStatement = null;
+  private PreparedStatement selectStatement = null;
+
+
+
+  public AbstractJdbc2ResultSet(org.postgresql.PGConnection conn, Statement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) {
+    super (conn, statement, fields, tuples, status, updateCount, insertOID, binaryCursor);
+  }
+
+  public java.net.URL getURL(int columnIndex) throws SQLException {
+    return null;
+  }
+
+
+  public java.net.URL getURL(String columnName) throws SQLException {
+    return null;
+  }
+
+
+  /*
+   * 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;
+
+    checkResultSet( columnIndex );
+
+    wasNullFlag = (this_row[columnIndex - 1] == null);
+    if (wasNullFlag)
+      return null;
+
+    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 getBoolean(columnIndex) ? Boolean.TRUE : Boolean.FALSE;
+
+    case Types.SMALLINT:
+      return new Short(getShort(columnIndex));
+
+    case Types.INTEGER:
+      return new Integer(getInt(columnIndex));
+
+    case Types.BIGINT:
+      return new Long(getLong(columnIndex));
+
+    case Types.NUMERIC:
+      return getBigDecimal
+        (columnIndex, (field.getMod() == -1) ? -1 : ((field.getMod() - 4) & 0xffff));
+
+    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);
+
+    case Types.BINARY:
+    case Types.VARBINARY:
+      return getBytes(columnIndex);
+
+    case Types.ARRAY:
+      return getArray(columnIndex);
+
+    default:
+      String type = field.getPGType();
+      // if the backend doesn't know the type then coerce to String
+      if (type.equals("unknown")) {
+        return getString(columnIndex);
+      }
+      else {
+        return connection.getObject(field.getPGType(), getString(columnIndex));
+      }
+    }
+  }
+
+
+  public boolean absolute(int index) throws SQLException {
+    // index is 1-based, but internally we use 0-based indices
+    int internalIndex;
+
+    if (index == 0)
+      throw new SQLException("Cannot move to index of 0");
+
+    final int rows_size = rows.size();
+
+    //if index<0, count from the end of the result set, but check
+    //to be sure that it is not beyond the first index
+    if (index < 0) {
+      if (index >= -rows_size)
+        internalIndex = rows_size + index;
+      else {
+        beforeFirst();
+        return false;
+      }
+    }
+    else {
+      //must be the case that index>0,
+      //find the correct place, assuming that
+      //the index is not too large
+      if (index <= rows_size)
+        internalIndex = index - 1;
+      else {
+        afterLast();
+        return false;
+      }
+    }
+
+    current_row = internalIndex;
+    this_row = (byte[][]) rows.elementAt(internalIndex);
+    return true;
+  }
+
+
+  public void afterLast() throws SQLException {
+    final int rows_size = rows.size();
+    if (rows_size > 0)
+      current_row = rows_size;
+  }
+
+
+  public void beforeFirst() throws SQLException {
+    if (rows.size() > 0)
+      current_row = -1;
+  }
+
+
+  public boolean first() throws SQLException {
+    if (rows.size() <= 0)
+      return false;
+
+    current_row = 0;
+    this_row = (byte[][]) rows.elementAt(current_row);
+
+    rowBuffer = new byte[this_row.length][];
+    System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length);
+
+    return true;
+  }
+
+
+  public java.sql.Array getArray(String colName) throws SQLException {
+    return getArray(findColumn(colName));
+  }
+
+
+  public java.sql.Array getArray(int i) throws SQLException {
+    wasNullFlag = (this_row[i - 1] == null);
+    if (wasNullFlag)
+      return null;
+
+    if (i < 1 || i > fields.length)
+      throw new PSQLException("postgresql.res.colrange");
+    return (java.sql.Array) new org.postgresql.jdbc2.Array( connection, i, fields[i - 1], (java.sql.ResultSet) this );
+  }
+
+
+  public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException {
+    return getBigDecimal(columnIndex, -1);
+  }
+
+
+  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 {
+    return new org.postgresql.largeobject.PGblob(connection, getInt(i));
+  }
+
+
+  public java.io.Reader getCharacterStream(String columnName) throws SQLException {
+    return getCharacterStream(findColumn(columnName));
+  }
+
+
+  public java.io.Reader getCharacterStream(int i) throws SQLException {
+    checkResultSet( i );
+    wasNullFlag = (this_row[i - 1] == null);
+    if (wasNullFlag)
+      return null;
+
+    if (((AbstractJdbc2Connection) connection).haveMinimumCompatibleVersion("7.2")) {
+      //Version 7.2 supports AsciiStream for all the PG text types
+      //As the spec/javadoc for this method indicate this is to be used for
+      //large text values (i.e. LONGVARCHAR)	PG doesn't have a separate
+      //long string datatype, but with toast the text datatype is capable of
+      //handling very large values.  Thus the implementation ends up calling
+      //getString() since there is no current way to stream the value from the server
+      return new CharArrayReader(getString(i).toCharArray());
+    }
+    else {
+      // In 7.1 Handle as BLOBS so return the LargeObject input stream
+      Encoding encoding = connection.getEncoding();
+      InputStream input = getBinaryStream(i);
+      return encoding.getDecodingReader(input);
+    }
+  }
+
+
+  public Clob getClob(String columnName) throws SQLException {
+    return getClob(findColumn(columnName));
+  }
+
+
+  public Clob getClob(int i) throws SQLException {
+    return new org.postgresql.largeobject.PGclob(connection, getInt(i));
+  }
+
+
+  public int getConcurrency() throws SQLException {
+    if (statement == null)
+      return java.sql.ResultSet.CONCUR_READ_ONLY;
+    return statement.getResultSetConcurrency();
+  }
+
+
+  public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException {
+    // If I read the specs, this should use cal only if we don't
+    // store the timezone, and if we do, then act just like getDate()?
+    // for now...
+    return getDate(i);
+  }
+
+
+  public Time getTime(int i, java.util.Calendar cal) throws SQLException {
+    // If I read the specs, this should use cal only if we don't
+    // store the timezone, and if we do, then act just like getTime()?
+    // for now...
+    return getTime(i);
+  }
+
+
+  public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException {
+    // If I read the specs, this should use cal only if we don't
+    // store the timezone, and if we do, then act just like getDate()?
+    // for now...
+    return getTimestamp(i);
+  }
+
+
+  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 {
+    //PostgreSQL normally sends rows first->last
+    return java.sql.ResultSet.FETCH_FORWARD;
+  }
+
+
+  public int getFetchSize() throws SQLException {
+    // In this implementation we return the entire result set, so
+    // here return the number of rows we have. Sub-classes can return a proper
+    // value
+    return rows.size();
+  }
+
+
+  public Object getObject(String columnName, java.util.Map map) throws SQLException {
+    return getObject(findColumn(columnName), map);
+  }
+
+
+  /*
+   * This checks against map for the type of column i, and if found returns
+   * an object based on that mapping. The class must implement the SQLData
+   * interface.
+   */
+  public Object getObject(int i, java.util.Map map) throws SQLException {
+    throw org.postgresql.Driver.notImplemented();
+  }
+
+
+  public Ref getRef(String columnName) throws SQLException {
+    return getRef(findColumn(columnName));
+  }
+
+
+  public Ref getRef(int i) throws SQLException {
+    //The backend doesn't yet have SQL3 REF types
+    throw new PSQLException("postgresql.psqlnotimp");
+  }
+
+
+  public int getRow() throws SQLException {
+    final int rows_size = rows.size();
+
+    if (current_row < 0 || current_row >= rows_size)
+      return 0;
+
+    return current_row + 1;
+  }
+
+
+  // This one needs some thought, as not all ResultSets come from a statement
+  public Statement getStatement() throws SQLException {
+    return statement;
+  }
+
+
+  public int getType() throws SQLException {
+    // This implementation allows scrolling but is not able to
+    // see any changes. Sub-classes may overide this to return a more
+    // meaningful result.
+    return java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE;
+  }
+
+
+  public boolean isAfterLast() throws SQLException {
+    final int rows_size = rows.size();
+    return (current_row >= rows_size && rows_size > 0);
+  }
+
+
+  public boolean isBeforeFirst() throws SQLException {
+    return (current_row < 0 && rows.size() > 0);
+  }
+
+
+  public boolean isFirst() throws SQLException {
+    return (current_row == 0 && rows.size() >= 0);
+  }
+
+
+  public boolean isLast() throws SQLException {
+    final int rows_size = rows.size();
+    return (current_row == rows_size - 1 && rows_size > 0);
+  }
+
+
+  public boolean last() throws SQLException {
+    final int rows_size = rows.size();
+    if (rows_size <= 0)
+      return false;
+
+    current_row = rows_size - 1;
+    this_row = (byte[][]) rows.elementAt(current_row);
+
+    rowBuffer = new byte[this_row.length][];
+    System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length);
+
+    return true;
+  }
+
+
+  public boolean previous() throws SQLException {
+    if (--current_row < 0)
+      return false;
+    this_row = (byte[][]) rows.elementAt(current_row);
+    System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length);
+    return true;
+  }
+
+
+  public boolean relative(int rows) throws SQLException {
+    //have to add 1 since absolute expects a 1-based index
+    return absolute(current_row + 1 + rows);
+  }
+
+
+  public void setFetchDirection(int direction) throws SQLException {
+    throw new PSQLException("postgresql.psqlnotimp");
+  }
+
+
+  public void setFetchSize(int rows) throws SQLException {
+    // Sub-classes should implement this as part of their cursor support
+    throw org.postgresql.Driver.notImplemented();
+  }
+
+
+  public synchronized void cancelRowUpdates() throws SQLException {
+    if (doingUpdates) {
+      doingUpdates = false;
+
+      clearRowBuffer();
+    }
+  }
+
+
+  public synchronized void deleteRow() throws SQLException {
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    if (onInsertRow) {
+      throw new PSQLException( "postgresql.updateable.oninsertrow" );
+    }
+
+    if (rows.size() == 0) {
+      throw new PSQLException( "postgresql.updateable.emptydelete" );
+    }
+    if (isBeforeFirst()) {
+      throw new PSQLException( "postgresql.updateable.beforestartdelete" );
+    }
+    if (isAfterLast()) {
+      throw new PSQLException( "postgresql.updateable.afterlastdelete" );
+    }
+
+
+    int numKeys = primaryKeys.size();
+    if ( deleteStatement == null ) {
+
+
+      StringBuffer deleteSQL = new StringBuffer("DELETE FROM " ).append(tableName).append(" where " );
+
+      for ( int i = 0; i < numKeys; i++ ) {
+        deleteSQL.append( ((PrimaryKey) primaryKeys.get(i)).name ).append( " = ? " );
+        if ( i < numKeys - 1 ) {
+          deleteSQL.append( " and " );
+        }
+      }
+
+      deleteStatement = ((java.sql.Connection) connection).prepareStatement(deleteSQL.toString());
+    }
+    deleteStatement.clearParameters();
+
+    for ( int i = 0; i < numKeys; i++ ) {
+      deleteStatement.setObject(i + 1, ((PrimaryKey) primaryKeys.get(i)).getValue());
+    }
+
+
+    deleteStatement.executeUpdate();
+
+    rows.removeElementAt(current_row);
+  }
+
+
+  public synchronized void insertRow() throws SQLException {
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    if (!onInsertRow) {
+      throw new PSQLException( "postgresql.updateable.notoninsertrow" );
+    }
+    else {
+
+      // loop through the keys in the insertTable and create the sql statement
+      // we have to create the sql every time since the user could insert different
+      // columns each time
+
+      StringBuffer insertSQL = new StringBuffer("INSERT INTO ").append(tableName).append(" (");
+      StringBuffer paramSQL = new StringBuffer(") values (" );
+
+      Enumeration columnNames = updateValues.keys();
+      int numColumns = updateValues.size();
+
+      for ( int i = 0; columnNames.hasMoreElements(); i++ ) {
+        String columnName = (String) columnNames.nextElement();
+
+        insertSQL.append( columnName );
+        if ( i < numColumns - 1 ) {
+          insertSQL.append(", ");
+          paramSQL.append("?,");
+        }
+        else {
+          paramSQL.append("?)");
+        }
+
+      }
+
+      insertSQL.append(paramSQL.toString());
+      insertStatement = ((java.sql.Connection) connection).prepareStatement(insertSQL.toString());
+
+      Enumeration keys = updateValues.keys();
+
+      for ( int i = 1; keys.hasMoreElements(); i++) {
+        String key = (String) keys.nextElement();
+        insertStatement.setObject(i, updateValues.get( key ) );
+      }
+
+      insertStatement.executeUpdate();
+
+      if ( usingOID ) {
+        // we have to get the last inserted OID and put it in the resultset
+
+        long insertedOID = ((AbstractJdbc2Statement) insertStatement).getLastOID();
+
+        updateValues.put("oid", new Long(insertedOID) );
+
+      }
+
+      // update the underlying row to the new inserted data
+      updateRowBuffer();
+
+      rows.addElement(rowBuffer);
+
+      // we should now reflect the current data in this_row
+      // that way getXXX will get the newly inserted data
+      this_row = rowBuffer;
+
+      // need to clear this in case of another insert
+      clearRowBuffer();
+
+
+    }
+  }
+
+
+  public synchronized void moveToCurrentRow() throws SQLException {
+    if (!updateable) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    this_row = (byte[][]) rows.elementAt(current_row);
+
+    rowBuffer = new byte[this_row.length][];
+    System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length);
+
+    onInsertRow = false;
+    doingUpdates = false;
+  }
+
+
+  public synchronized void  moveToInsertRow() throws SQLException {
+    if (!updateable) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    if (insertStatement != null) {
+      insertStatement = null;
+    }
+
+
+    // make sure the underlying data is null
+    clearRowBuffer();
+
+    onInsertRow = true;
+    doingUpdates = false;
+
+  }
+
+
+  private synchronized void clearRowBuffer() throws SQLException {
+    // rowBuffer is the temporary storage for the row
+    rowBuffer = new byte[fields.length][];
+
+    // clear the updateValues hashTable for the next set of updates
+    updateValues.clear();
+
+  }
+
+
+  public boolean rowDeleted() throws SQLException {
+    // only sub-classes implement CONCURuPDATEABLE
+    throw Driver.notImplemented();
+  }
+
+
+  public boolean rowInserted() throws SQLException {
+    // only sub-classes implement CONCURuPDATEABLE
+    throw Driver.notImplemented();
+  }
+
+
+  public boolean rowUpdated() throws SQLException {
+    // only sub-classes implement CONCURuPDATEABLE
+    throw Driver.notImplemented();
+  }
+
+
+  public synchronized void updateAsciiStream(int columnIndex,
+    java.io.InputStream x,
+    int length
+  ) throws SQLException {
+
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    byte[] theData = null;
+
+    try {
+      x.read(theData, 0, length);
+    }
+    catch (NullPointerException ex ) {
+      throw new PSQLException("postgresql.updateable.inputstream");
+    }
+    catch (IOException ie) {
+      throw new PSQLException("postgresql.updateable.ioerror" + ie);
+    }
+
+    doingUpdates = !onInsertRow;
+
+    updateValues.put( fields[columnIndex - 1].getName(), theData );
+
+  }
+
+
+  public synchronized void updateBigDecimal(int columnIndex,
+    java.math.BigDecimal x )
+  throws SQLException {
+
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), x );
+
+  }
+
+
+  public synchronized void updateBinaryStream(int columnIndex,
+    java.io.InputStream x,
+    int length
+  ) throws SQLException {
+
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    byte[] theData = null;
+
+    try {
+      x.read(theData, 0, length);
+
+    }
+    catch ( NullPointerException ex ) {
+      throw new PSQLException("postgresql.updateable.inputstream");
+    }
+    catch (IOException ie) {
+      throw new PSQLException("postgresql.updateable.ioerror" + ie);
+    }
+
+    doingUpdates = !onInsertRow;
+
+    updateValues.put( fields[columnIndex - 1].getName(), theData );
+
+  }
+
+
+  public synchronized void updateBoolean(int columnIndex, boolean x) throws SQLException {
+
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    if ( Driver.logDebug ) Driver.debug("updating boolean " + fields[columnIndex - 1].getName() + "=" + x);
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), new Boolean(x) );
+
+  }
+
+
+  public synchronized void updateByte(int columnIndex, byte x) throws SQLException {
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    doingUpdates = true;
+    updateValues.put( fields[columnIndex - 1].getName(), String.valueOf(x) );
+  }
+
+
+  public synchronized void updateBytes(int columnIndex, byte[] x) throws SQLException {
+
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), x );
+
+  }
+
+
+  public synchronized void updateCharacterStream(int columnIndex,
+    java.io.Reader x,
+    int length
+  ) throws SQLException {
+
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    char[] theData = null;
+
+    try {
+      x.read(theData, 0, length);
+
+    }
+    catch (NullPointerException ex) {
+      throw new PSQLException("postgresql.updateable.inputstream");
+    }
+    catch (IOException ie) {
+      throw new PSQLException("postgresql.updateable.ioerror" + ie);
+    }
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), theData);
+
+  }
+
+
+  public synchronized void updateDate(int columnIndex, java.sql.Date x) throws SQLException {
+
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), x );
+  }
+
+
+  public synchronized void updateDouble(int columnIndex, double x) throws SQLException {
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    if ( Driver.logDebug ) Driver.debug("updating double " + fields[columnIndex - 1].getName() + "=" + x);
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), new Double(x) );
+
+  }
+
+
+  public synchronized void updateFloat(int columnIndex, float x) throws SQLException {
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    if ( Driver.logDebug ) Driver.debug("updating float " + fields[columnIndex - 1].getName() + "=" + x);
+
+    doingUpdates = !onInsertRow;
+
+    updateValues.put( fields[columnIndex - 1].getName(), new Float(x) );
+
+  }
+
+
+  public synchronized void updateInt(int columnIndex, int x) throws SQLException {
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    if ( Driver.logDebug ) Driver.debug("updating int " + fields[columnIndex - 1].getName() + "=" + x);
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), new Integer(x) );
+
+  }
+
+
+  public synchronized void updateLong(int columnIndex, long x) throws SQLException {
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    if ( Driver.logDebug ) Driver.debug("updating long " + fields[columnIndex - 1].getName() + "=" + x);
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), new Long(x) );
+
+  }
+
+
+  public synchronized void updateNull(int columnIndex) throws SQLException {
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), null);
+
+
+  }
+
+
+  public synchronized void updateObject(int columnIndex, Object x) throws SQLException {
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    if ( Driver.logDebug ) Driver.debug("updating object " + fields[columnIndex - 1].getName() + " = " + x);
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), x );
+  }
+
+
+  public synchronized void updateObject(int columnIndex, Object x, int scale) throws SQLException {
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    this.updateObject(columnIndex, x);
+
+  }
+
+
+  public void refreshRow() throws SQLException {
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    try {
+      StringBuffer selectSQL = new StringBuffer( "select ");
+
+      final int numColumns = java.lang.reflect.Array.getLength(fields);
+
+      for (int i = 0; i < numColumns; i++ ) {
+
+        selectSQL.append( fields[i].getName() );
+
+        if ( i < numColumns - 1 ) {
+
+          selectSQL.append(", ");
+        }
+
+      }
+      selectSQL.append(" from " ).append(tableName).append(" where ");
+
+      int numKeys = primaryKeys.size();
+
+      for ( int i = 0; i < numKeys; i++ ) {
+
+        PrimaryKey primaryKey = ((PrimaryKey) primaryKeys.get(i));
+        selectSQL.append(primaryKey.name).append("= ?");
+
+        if ( i < numKeys - 1 ) {
+          selectSQL.append(" and ");
+        }
+      }
+      if ( Driver.logDebug ) Driver.debug("selecting " + selectSQL.toString());
+      selectStatement = ((java.sql.Connection) connection).prepareStatement(selectSQL.toString());
+
+
+      for ( int j = 0, i = 1; j < numKeys; j++, i++) {
+        selectStatement.setObject( i, ((PrimaryKey) primaryKeys.get(j)).getValue() );
+      }
+
+      Jdbc2ResultSet rs = (Jdbc2ResultSet) selectStatement.executeQuery();
+
+      if ( rs.first() ) {
+        rowBuffer = rs.rowBuffer;
+      }
+
+      rows.setElementAt( rowBuffer, current_row );
+      if ( Driver.logDebug ) Driver.debug("done updates");
+
+      rs.close();
+      selectStatement.close();
+      selectStatement = null;
+
+    }
+    catch (Exception e) {
+      if ( Driver.logDebug ) Driver.debug(e.getClass().getName() + e);
+      throw new SQLException( e.getMessage() );
+    }
+
+  }
+
+
+  public synchronized void updateRow() throws SQLException {
+    if ( !isUpdateable() ) {
+      throw new PSQLException( "postgresql.updateable.notupdateable" );
+    }
+
+    if (doingUpdates) {
+
+      try {
+
+        StringBuffer updateSQL = new StringBuffer("UPDATE " + tableName + " SET  ");
+
+        int numColumns = updateValues.size();
+        Enumeration columns = updateValues.keys();
+
+        for (int i = 0; columns.hasMoreElements(); i++ ) {
+
+          String column = (String) columns.nextElement();
+          updateSQL.append( column + "= ?");
+
+          if ( i < numColumns - 1 ) {
+
+            updateSQL.append(", ");
+          }
+
+        }
+        updateSQL.append( " WHERE " );
+
+        int numKeys = primaryKeys.size();
+
+        for ( int i = 0; i < numKeys; i++ ) {
+
+          PrimaryKey primaryKey = ((PrimaryKey) primaryKeys.get(i));
+          updateSQL.append(primaryKey.name).append("= ?");
+
+          if ( i < numKeys - 1 ) {
+            updateSQL.append(" and ");
+          }
+        }
+        if ( Driver.logDebug ) Driver.debug("updating " + updateSQL.toString());
+        updateStatement = ((java.sql.Connection) connection).prepareStatement(updateSQL.toString());
+
+        int i = 0;
+        Iterator iterator = updateValues.values().iterator();
+        for (; iterator.hasNext(); i++) {
+          updateStatement.setObject( i + 1, iterator.next() );
+
+        }
+        for ( int j = 0; j < numKeys; j++, i++) {
+          updateStatement.setObject( i + 1, ((PrimaryKey) primaryKeys.get(j)).getValue() );
+        }
+
+        updateStatement.executeUpdate();
+        updateStatement.close();
+
+        updateStatement = null;
+        updateRowBuffer();
+
+
+        if ( Driver.logDebug ) Driver.debug("copying data");
+        System.arraycopy(rowBuffer, 0, this_row, 0, rowBuffer.length);
+
+        rows.setElementAt( rowBuffer, current_row );
+        if ( Driver.logDebug ) Driver.debug("done updates");
+
+        doingUpdates = false;
+      }
+      catch (Exception e) {
+        if ( Driver.logDebug ) Driver.debug(e.getClass().getName() + e);
+        throw new SQLException( e.getMessage() );
+      }
+
+    }
+
+  }
+
+
+  public synchronized void updateShort(int columnIndex, short x) throws SQLException {
+    if ( Driver.logDebug ) Driver.debug("in update Short " + fields[columnIndex - 1].getName() + " = " + x);
+
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), new Short(x) );
+
+  }
+
+
+  public synchronized void updateString(int columnIndex, String x) throws SQLException {
+    if ( Driver.logDebug ) Driver.debug("in update String " + fields[columnIndex - 1].getName() + " = " + x);
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), x );
+
+  }
+
+
+  public synchronized void updateTime(int columnIndex, Time x) throws SQLException {
+    if ( Driver.logDebug ) Driver.debug("in update Time " + fields[columnIndex - 1].getName() + " = " + x);
+
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), x );
+
+  }
+
+
+  public synchronized void updateTimestamp(int columnIndex, Timestamp x) throws SQLException {
+    if ( Driver.logDebug ) Driver.debug("updating Timestamp " + fields[columnIndex - 1].getName() + " = " + x);
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex - 1].getName(), x );
+
+
+  }
+
+
+  public synchronized void updateNull(String columnName) throws SQLException {
+    updateNull(findColumn(columnName));
+  }
+
+
+  public synchronized void updateBoolean(String columnName, boolean x) throws SQLException {
+    updateBoolean(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateByte(String columnName, byte x) throws SQLException {
+    updateByte(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateShort(String columnName, short x) throws SQLException {
+    updateShort(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateInt(String columnName, int x) throws SQLException {
+    updateInt(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateLong(String columnName, long x) throws SQLException {
+    updateLong(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateFloat(String columnName, float x) throws SQLException {
+    updateFloat(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateDouble(String columnName, double x) throws SQLException {
+    updateDouble(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateBigDecimal(String columnName, BigDecimal x)
+  throws SQLException {
+    updateBigDecimal(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateString(String columnName, String x) throws SQLException {
+    updateString(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateBytes(String columnName, byte x[]) throws SQLException {
+    updateBytes(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateDate(String columnName, java.sql.Date x)
+  throws SQLException {
+    updateDate(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateTime(String columnName, java.sql.Time x)
+  throws SQLException {
+    updateTime(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateTimestamp(String columnName, java.sql.Timestamp x)
+  throws SQLException {
+    updateTimestamp(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateAsciiStream(
+    String columnName,
+    java.io.InputStream x,
+    int length)
+  throws SQLException {
+    updateAsciiStream(findColumn(columnName), x, length);
+  }
+
+
+  public synchronized void updateBinaryStream(
+    String columnName,
+    java.io.InputStream x,
+    int length)
+  throws SQLException {
+    updateBinaryStream(findColumn(columnName), x, length);
+  }
+
+
+  public synchronized void updateCharacterStream(
+    String columnName,
+    java.io.Reader reader,
+    int length)
+  throws SQLException {
+    updateCharacterStream(findColumn(columnName), reader, length);
+  }
+
+
+  public synchronized void updateObject(String columnName, Object x, int scale)
+  throws SQLException {
+    updateObject(findColumn(columnName), x);
+  }
+
+
+  public synchronized void updateObject(String columnName, Object x) throws SQLException {
+    updateObject(findColumn(columnName), x);
+  }
+
+
+  private int _findColumn( String columnName ) {
+    int i;
+
+    final int flen = fields.length;
+    for (i = 0; i < flen; ++i) {
+      if (fields[i].getName().equalsIgnoreCase(columnName)) {
+        return (i + 1);
+      }
+    }
+    return -1;
+  }
+
+
+  /**
+   * Is this ResultSet updateable?
+   */
+
+  boolean isUpdateable() throws SQLException {
+
+    if (updateable) return true;
+
+    if ( Driver.logDebug ) Driver.debug("checking if rs is updateable");
+
+    parseQuery();
+
+    if ( singleTable == false ) {
+      if ( Driver.logDebug ) Driver.debug("not a single table");
+      return false;
+    }
+
+    if ( Driver.logDebug ) Driver.debug("getting primary keys");
+
+    //
+    // Contains the primary key?
+    //
+
+    primaryKeys = new Vector();
+
+    // this is not stricty jdbc spec, but it will make things much faster if used
+    // the user has to select oid, * from table and then we will just use oid
+
+
+    usingOID = false;
+    int oidIndex = _findColumn( "oid" );
+    int i = 0;
+
+
+    // if we find the oid then just use it
+
+    if ( oidIndex > 0 ) {
+      i++;
+      primaryKeys.add( new PrimaryKey( oidIndex, "oid" ) );
+      usingOID = true;
+    }
+    else {
+      // otherwise go and get the primary keys and create a hashtable of keys
+      java.sql.ResultSet rs  = ((java.sql.Connection) connection).getMetaData().getPrimaryKeys("", "", tableName);
+
+
+      for (; rs.next(); i++ ) {
+        String columnName = rs.getString(4);    // get the columnName
+
+        int index = findColumn( columnName );
+
+        if ( index > 0 ) {
+          primaryKeys.add( new PrimaryKey(index, columnName ) ); // get the primary key information
+        }
+      }
+
+      rs.close();
+    }
+
+    numKeys = primaryKeys.size();
+
+    if ( Driver.logDebug ) Driver.debug( "no of keys=" + i );
+
+    if ( i < 1 ) {
+      throw new SQLException("No Primary Keys");
+    }
+
+    updateable = primaryKeys.size() > 0;
+
+    if ( Driver.logDebug ) Driver.debug( "checking primary key " + updateable );
+
+    return updateable;
+  }
+
+
+  public void parseQuery() {
+    StringTokenizer st = new StringTokenizer(sqlQuery, " \r\t");
+    boolean tableFound = false, tablesChecked = false;
+    String name = "";
+
+    singleTable = true;
+
+    while ( !tableFound && !tablesChecked && st.hasMoreTokens() ) {
+      name = st.nextToken();
+      if ( !tableFound ) {
+        if (name.toLowerCase().equals("from")) {
+          tableName = st.nextToken();
+          tableFound = true;
+        }
+      }
+      else {
+        tablesChecked = true;
+        // if the very next token is , then there are multiple tables
+        singleTable =  !name.equalsIgnoreCase(",");
+      }
+    }
+  }
+
+
+  private void updateRowBuffer() throws SQLException {
+
+    Enumeration columns = updateValues.keys();
+
+    while ( columns.hasMoreElements() ) {
+      String columnName = (String) columns.nextElement();
+      int columnIndex = _findColumn( columnName ) - 1;
+
+      switch ( connection.getSQLType( fields[columnIndex].getPGType() ) ) {
+
+      case Types.DECIMAL:
+      case Types.BIGINT:
+      case Types.DOUBLE:
+      case Types.BIT:
+      case Types.VARCHAR:
+      case Types.DATE:
+      case Types.TIME:
+      case Types.TIMESTAMP:
+      case Types.SMALLINT:
+      case Types.FLOAT:
+      case Types.INTEGER:
+      case Types.CHAR:
+      case Types.NUMERIC:
+      case Types.REAL:
+      case Types.TINYINT:
+
+        try {
+          rowBuffer[columnIndex] = String.valueOf( updateValues.get( columnName ) ).getBytes(connection.getEncoding().name() );
+        }
+        catch ( UnsupportedEncodingException ex) {
+          throw new SQLException("Unsupported Encoding " + connection.getEncoding().name());
+        }
+
+      case Types.NULL:
+        continue;
+
+      default:
+        rowBuffer[columnIndex] = (byte[]) updateValues.get( columnName );
+      }
+
+    }
+  }
+
+
+  public void setStatement(Statement statement) {
+    this.statement = statement;
+  }
+
+
+  public void setSQLQuery(String sqlQuery) {
+    this.sqlQuery = sqlQuery;
+  }
+
+
+  private class PrimaryKey {
+    int index;              // where in the result set is this primaryKey
+    String name;            // what is the columnName of this primary Key
+
+    PrimaryKey( int index, String name) {
+      this.index = index;
+      this.name = name;
+    }
+    Object getValue() throws SQLException {
+      return getObject(index);
+    }
+  };
+
+
+
 }
 
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java
index 47c2c779831..d8d16985ad1 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java
@@ -2,12 +2,13 @@ package org.postgresql.jdbc2;
 
 
 import java.io.*;
+import java.math.*;
 import java.sql.*;
 import java.util.Vector;
 import org.postgresql.largeobject.*;
 import org.postgresql.util.PSQLException;
 
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2Statement.java,v 1.2 2002/07/24 22:08:42 barry Exp $
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2Statement.java,v 1.3 2002/07/25 22:45:28 barry Exp $
  * This class defines methods of the jdbc2 specification.  This class extends
  * org.postgresql.jdbc1.AbstractJdbc1Statement which provides the jdbc1
  * methods.  The real Statement class (for jdbc2) is org.postgresql.jdbc2.Jdbc2Statement
@@ -46,7 +47,7 @@ public abstract class AbstractJdbc2Statement extends org.postgresql.jdbc1.Abstra
 	{
 	        boolean l_return = super.execute(sql);
                 //Now do the jdbc2 specific stuff
-		//required for ResultSet.getStatement() to work
+		//required for ResultSet.getStatement() to work and updateable resultsets
 		((AbstractJdbc2ResultSet)result).setStatement((Statement)this);
 
 		// Added this so that the Updateable resultset knows the query that gave this
@@ -331,4 +332,60 @@ public abstract class AbstractJdbc2Statement extends org.postgresql.jdbc1.Abstra
 			setTimestamp(i, new java.sql.Timestamp(cal.getTime().getTime()));
 		}
 	}
+
+	// ** JDBC 2 Extensions for CallableStatement**
+
+	public java.sql.Array getArray(int i) throws SQLException
+	{
+		throw org.postgresql.Driver.notImplemented();
+	}
+
+	public java.math.BigDecimal getBigDecimal(int parameterIndex) throws SQLException
+	{
+		checkIndex (parameterIndex, Types.NUMERIC, "BigDecimal");
+		return ((BigDecimal)callResult);
+	}
+
+	public Blob getBlob(int i) throws SQLException
+	{
+		throw org.postgresql.Driver.notImplemented();
+	}
+
+	public Clob getClob(int i) throws SQLException
+	{
+		throw org.postgresql.Driver.notImplemented();
+	}
+
+	public Object getObject(int i, java.util.Map map) throws SQLException
+	{
+		throw org.postgresql.Driver.notImplemented();
+	}
+
+	public Ref getRef(int i) throws SQLException
+	{
+		throw org.postgresql.Driver.notImplemented();
+	}
+
+	public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException
+	{
+		throw org.postgresql.Driver.notImplemented();
+	}
+
+	public Time getTime(int i, java.util.Calendar cal) throws SQLException
+	{
+		throw org.postgresql.Driver.notImplemented();
+	}
+
+	public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException
+	{
+		throw org.postgresql.Driver.notImplemented();
+	}
+
+	// no custom types allowed yet..
+	public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException
+	{
+		throw org.postgresql.Driver.notImplemented();
+	}
+
+
 }
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Array.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Array.java
index 2105802d66a..99cbf0ea8ce 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc2/Array.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Array.java
@@ -340,7 +340,7 @@ public class Array implements java.sql.Array
 			default:
 				throw org.postgresql.Driver.notImplemented();
 		}
-		return new Jdbc2ResultSet((org.postgresql.jdbc2.Jdbc2Connection)conn, fields, rows, "OK", 1 );
+		return ((Jdbc2Connection)conn).getResultSet(null, fields, rows, "OK", 1 );
 	}
 
 	public String toString()
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java
deleted file mode 100644
index 51dd9b2c7f0..00000000000
--- a/src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java
+++ /dev/null
@@ -1,604 +0,0 @@
-package org.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
-// org.postgresql.jdbc1 package.
-
-import java.sql.*;
-import java.math.*;
-import org.postgresql.util.*;
-/*
- * 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
- * @author Paul Bethe (implementer)
- */
-
-public class CallableStatement extends org.postgresql.jdbc2.Jdbc2PreparedStatement implements java.sql.CallableStatement
-{
-	/*
-	 * @exception SQLException on failure
-	 */
-	public CallableStatement(Jdbc2Connection c, String q) throws SQLException
-	{
-		super(c, q); // don't parse yet..
-	}
-
-
-	/**
-	 * allows this class to tweak the standard JDBC call !see Usage
-	 * -> and replace with the pgsql function syntax
-	 * ie. select <function ([params])> as RESULT;
-	 */
-
-	protected void parseSqlStmt () throws SQLException {
-		modifyJdbcCall ();
-		super.parseSqlStmt ();
-	}
-	/** 
-	 * this method will turn a string of the form
-	 * {? = call <some_function> (?, [?,..]) }
-	 * into the PostgreSQL format which is 
-	 * select <some_function> (?, [?, ...]) as result
-	 * 
-	 */
-	private void modifyJdbcCall () throws SQLException {
-		// syntax checking is not complete only a few basics :(
-		originalSql = sql; // save for error msgs..
-		int index = sql.indexOf ("="); // is implied func or proc?
-		boolean isValid = true;
-		if (index != -1) {
-			isFunction = true;
-			isValid = sql.indexOf ("?") < index; // ? before =			
-		}
-		sql = sql.trim ();
-		if (sql.startsWith ("{") && sql.endsWith ("}")) {
-			sql = sql.substring (1, sql.length() -1);
-		} else isValid = false;
-		index = sql.indexOf ("call"); 
-		if (index == -1 || !isValid)
-			throw new PSQLException ("postgresql.call.malformed", 
-									 new Object[]{sql, JDBC_SYNTAX});
-		sql = sql.replace ('{', ' '); // replace these characters
-		sql = sql.replace ('}', ' ');
-		sql = sql.replace (';', ' ');
-		
-		// this removes the 'call' string and also puts a hidden '?'
-		// at the front of the line for functions, this will
-		// allow the registerOutParameter to work correctly
-                // because in the source sql there was one more ? for the return
-                // value that is not needed by the postgres syntax.  But to make 
-                // sure that the parameter numbers are the same as in the original
-                // sql we add a dummy parameter in this case
-		sql = (isFunction ? "?" : "") + sql.substring (index + 4);
-		
-		sql = "select " + sql + " as " + RESULT_COLUMN + ";";	  
-	}
-
-	// internals 
-	static final String JDBC_SYNTAX = "{[? =] call <some_function> ([? [,?]*]) }";
-	static final String RESULT_COLUMN = "result";
-	String originalSql = "";
-	boolean isFunction;
-	// functionReturnType contains the user supplied value to check
-	// testReturn contains a modified version to make it easier to 
-	// check the getXXX methods..
-	int functionReturnType;
-	int testReturn;
-	// returnTypeSet is true when a proper call to registerOutParameter has been made
-	boolean returnTypeSet;
-	Object result;
-
-	/*
-	 * 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.
-	 *
-	 * ONLY 1 RETURN PARAMETER if {?= call ..} syntax is used
-	 *
-	 * @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
-		{
-			if (parameterIndex != 1)
-				throw new PSQLException ("postgresql.call.noinout");
-			if (!isFunction)
-				throw new PSQLException ("postgresql.call.procasfunc", originalSql);
-			
-			// functionReturnType contains the user supplied value to check
-			// testReturn contains a modified version to make it easier to 
-			// check the getXXX methods..
-			functionReturnType = sqlType;
-			testReturn = sqlType;
-			if (functionReturnType == Types.CHAR || 
-				functionReturnType == Types.LONGVARCHAR)
-				testReturn = Types.VARCHAR;
-			else if (functionReturnType == Types.FLOAT)
-				testReturn = Types.REAL; // changes to streamline later error checking
-			returnTypeSet = true;
-		}
-
-	/**
-	 * allow calls to execute update
-	 * @return 1 if succesful call otherwise 0
-	 */
-	public int executeUpdate() throws SQLException
-	{
-		java.sql.ResultSet rs = super.executeQuery (compileQuery());	
-		if (isFunction) {
-			if (!rs.next ())
-				throw new PSQLException ("postgresql.call.noreturnval");
-			result = rs.getObject(1);
-			int columnType = rs.getMetaData().getColumnType(1);
-			if (columnType != functionReturnType) 
-				throw new PSQLException ("postgresql.call.wrongrtntype",
-										 new Object[]{
-					getSqlTypeName (columnType), getSqlTypeName (functionReturnType) }); 
-		}
-		rs.close ();
-		return 1;
-	}
-
-
-	/**
-	 * allow calls to execute update
-	 * @return true if succesful
-	 */
-	public boolean execute() throws SQLException
-	{
-		return (executeUpdate() == 1);
-	}
-
-	/*
-	 * 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
-		{
-			registerOutParameter (parameterIndex, sqlType); // ignore for now..
-		}
-
-	/*
-	 * override this method to check for set @ 1 when declared function..
-	 *
-	 * @param paramIndex the index into the inString
-	 * @param s a string to be stored
-	 * @exception SQLException if something goes wrong
-	 */
-	protected void set(int paramIndex, String s) throws SQLException
-	{
-		if (paramIndex == 1 && isFunction) // need to registerOut instead
-			throw new PSQLException ("postgresql.call.funcover");
-		super.set (paramIndex, s); // else set as usual..
-	}
-
-		/*
-	 * Helper - this compiles the SQL query from the various parameters
-	 * This is identical to toString() except it throws an exception if a
-	 * parameter is unused.
-	 */
-	protected synchronized String compileQuery()
-	throws SQLException
-	{
-		if (isFunction && !returnTypeSet)
-			throw new PSQLException("postgresql.call.noreturntype");
-		if (isFunction) { // set entry 1 to dummy entry..
-			inStrings[0] = ""; // dummy entry which ensured that no one overrode
-			// and calls to setXXX (2,..) really went to first arg in a function call..
-		}
-		return super.compileQuery ();
-	}
-
-	/*
-	 * 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 (result == 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
-	{
-		checkIndex (parameterIndex, Types.VARCHAR, "String");
-		return (String)result;
-	}
-	//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
-	{
-		checkIndex (parameterIndex, Types.BIT, "Boolean");
-		if (result == null) return false;
-		return ((Boolean)result).booleanValue ();
-	}
-
-	/*
-	 * 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
-	{
-		checkIndex (parameterIndex, Types.TINYINT, "Byte");
-		if (result == null) return 0;
-		return (byte)((Integer)result).intValue ();
-	}
-
-	/*
-	 * 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
-	{
-		checkIndex (parameterIndex, Types.SMALLINT, "Short");
-		if (result == null) return 0;
-		return (short)((Integer)result).intValue ();
-	}
-		
-
-	/*
-	 * 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
-	{
-		checkIndex (parameterIndex, Types.INTEGER, "Int");
-		if (result == null) return 0;
-		return ((Integer)result).intValue ();
-	}
-
-	/*
-	 * 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
-	{
-		checkIndex (parameterIndex, Types.BIGINT, "Long");
-		if (result == null) return 0;
-		return ((Long)result).longValue ();
-	}
-
-	/*
-	 * 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
-	{
-		checkIndex (parameterIndex, Types.REAL, "Float");
-		if (result == null) return 0;
-		return ((Float)result).floatValue ();
-	}
-
-	/*
-	 * 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
-	{
-		checkIndex (parameterIndex, Types.DOUBLE, "Double");
-		if (result == null) return 0;
-		return ((Double)result).doubleValue ();
-	}
-
-	/*
-	 * 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.
-	 * @deprecated in Java2.0
-	 */
-	public BigDecimal getBigDecimal(int parameterIndex, int scale)
-	throws SQLException
-	{
-		checkIndex (parameterIndex, Types.NUMERIC, "BigDecimal");
-		return ((BigDecimal)result);
-	}
-
-	/*
-	 * 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
-	{
-		checkIndex (parameterIndex, Types.VARBINARY, "Bytes");
-		return ((byte [])result);
-	}
-
-	// 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
-	{
-		checkIndex (parameterIndex, Types.DATE, "Date");
-		return (java.sql.Date)result;
-	}
-
-	/*
-	 * 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
-	{
-		checkIndex (parameterIndex, Types.TIME, "Time");
-		return (java.sql.Time)result;
-	}
-
-	/*
-	 * 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
-	{
-		checkIndex (parameterIndex, Types.TIMESTAMP, "Timestamp");
-		return (java.sql.Timestamp)result;
-	}
-
-	//----------------------------------------------------------------------
-	// 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
-	{
-		checkIndex (parameterIndex);		
-		return result;
-	}
-
-	// ** JDBC 2 Extensions **
-
-	public java.sql.Array getArray(int i) throws SQLException
-	{
-		throw org.postgresql.Driver.notImplemented();
-	}
-
-	public java.math.BigDecimal getBigDecimal(int parameterIndex) throws SQLException
-	{
-		checkIndex (parameterIndex, Types.NUMERIC, "BigDecimal");
-		return ((BigDecimal)result);
-	}
-
-	public Blob getBlob(int i) throws SQLException
-	{
-		throw org.postgresql.Driver.notImplemented();
-	}
-
-	public Clob getClob(int i) throws SQLException
-	{
-		throw org.postgresql.Driver.notImplemented();
-	}
-
-	public Object getObject(int i, java.util.Map map) throws SQLException
-	{
-		throw org.postgresql.Driver.notImplemented();
-	}
-
-	public Ref getRef(int i) throws SQLException
-	{
-		throw org.postgresql.Driver.notImplemented();
-	}
-
-	public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException
-	{
-		throw org.postgresql.Driver.notImplemented();
-	}
-
-	public Time getTime(int i, java.util.Calendar cal) throws SQLException
-	{
-		throw org.postgresql.Driver.notImplemented();
-	}
-
-	public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException
-	{
-		throw org.postgresql.Driver.notImplemented();
-	}
-
-	// no custom types allowed yet..
-	public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException
-	{
-		throw org.postgresql.Driver.notImplemented();
-	}
-
-
-
-	/** helperfunction for the getXXX calls to check isFunction and index == 1
-	 */
-	private void checkIndex (int parameterIndex, int type, String getName) 
-		throws SQLException {
-		checkIndex (parameterIndex);
-		if (type != this.testReturn) 
-			throw new PSQLException("postgresql.call.wrongget",
-									new Object[]{getSqlTypeName (testReturn),
-													 getName,
-													 getSqlTypeName (type)});
-	}
-	/** helperfunction for the getXXX calls to check isFunction and index == 1
-	 * @param parameterIndex index of getXXX (index)
-	 * check to make sure is a function and index == 1
-	 */
-	private void checkIndex (int parameterIndex) throws SQLException {
-		if (!isFunction)
-			throw new PSQLException("postgresql.call.noreturntype");
-		if (parameterIndex != 1)
-			throw new PSQLException("postgresql.call.noinout");
-	}
-		
-	/** helper function for creating msg with type names
-	 * @param sqlType a java.sql.Types.XX constant
-	 * @return String which is the name of the constant..
-	 */
-	private static String getSqlTypeName (int sqlType) {
-		switch (sqlType)
-			{
-			case Types.BIT:
-				return "BIT";
-			case Types.SMALLINT:
-				return "SMALLINT";
-			case Types.INTEGER:
-				return "INTEGER";
-			case Types.BIGINT:
-				return "BIGINT";
-			case Types.NUMERIC:
-				return "NUMERIC";
-			case Types.REAL:
-				return "REAL";
-			case Types.DOUBLE:
-				return "DOUBLE";
-			case Types.FLOAT:
-				return "FLOAT";
-			case Types.CHAR:
-				return "CHAR";
-			case Types.VARCHAR:
-				return "VARCHAR";
-			case Types.DATE:
-				return "DATE";
-			case Types.TIME:
-				return "TIME";
-			case Types.TIMESTAMP:
-				return "TIMESTAMP";
-			case Types.BINARY:
-				return "BINARY";
-			case Types.VARBINARY:
-				return "VARBINARY";
-			default:
-				return "UNKNOWN";
-			}
-	}
-}
-
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java b/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java
index 77415d051d4..a1c8e22737b 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java
@@ -15,7 +15,7 @@ import org.postgresql.util.PSQLException;
 /*
  * This class provides information about the database as a whole.
  *
- * $Id: DatabaseMetaData.java,v 1.59 2002/07/23 03:59:55 barry Exp $
+ * $Id: DatabaseMetaData.java,v 1.60 2002/07/25 22:45:28 barry Exp $
  *
  * <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
@@ -1653,7 +1653,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 
 			v.addElement(tuple);
 		}
-		return new Jdbc2ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -1731,7 +1731,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 
 		// add query loop here
 
-		return new Jdbc2ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -1866,7 +1866,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 			v.addElement(tuple);
 		}
 		r.close();
-		return new Jdbc2ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	// This array contains the valid values for the types argument
@@ -1913,7 +1913,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 		f[0] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32);
 		tuple[0] = "".getBytes();
 		v.addElement(tuple);
-		return new Jdbc2ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -1958,7 +1958,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 			tuple[0] = getTableTypes[i][0].getBytes();
 			v.addElement(tuple);
 		}
-		return new Jdbc2ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -2154,7 +2154,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 		}
 		r.close();
 
-		return new Jdbc2ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -2218,7 +2218,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 			//v.addElement(tuple);
 		}
 
-		return new Jdbc2ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -2281,7 +2281,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 			//v.addElement(tuple);
 		}
 
-		return new Jdbc2ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -2337,7 +2337,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
 		f[6] = new Field(connection, "DECIMAL_DIGITS", iInt2Oid, 2);
 		f[7] = new Field(connection, "PSEUDO_COLUMN", iInt2Oid, 2);
 
-		return new Jdbc2ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 	/*
@@ -2680,7 +2680,7 @@ WHERE
       tuples.addElement(tuple);
     }
 
-    return new Jdbc2ResultSet(connection, f, tuples, "OK", 1);
+    return connection.getResultSet(null, f, tuples, "OK", 1);
 	}
 
 	/*
@@ -2959,7 +2959,7 @@ WHERE
 				v.addElement(tuple);
 			}
 			rs.close();
-			return new Jdbc2ResultSet(connection, f, v, "OK", 1);
+			return connection.getResultSet(null, f, v, "OK", 1);
 		}
 
 		throw new PSQLException("postgresql.metadata.unavailable");
@@ -3097,7 +3097,7 @@ WHERE
 			}
 		}
 
-		return new Jdbc2ResultSet(connection, f, v, "OK", 1);
+		return connection.getResultSet(null, f, v, "OK", 1);
 	}
 
 
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2CallableStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2CallableStatement.java
new file mode 100644
index 00000000000..c8bc1b79073
--- /dev/null
+++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2CallableStatement.java
@@ -0,0 +1,15 @@
+package org.postgresql.jdbc2;
+
+
+import java.sql.*;
+
+public class Jdbc2CallableStatement extends AbstractJdbc2Statement implements java.sql.CallableStatement
+{
+
+	public Jdbc2CallableStatement(Jdbc2Connection connection, String sql) throws SQLException
+	{
+		super(connection, sql);
+	}
+
+}
+
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Connection.java
index 787b14e62af..b512355260c 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Connection.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Connection.java
@@ -3,9 +3,10 @@ package org.postgresql.jdbc2;
 
 import java.sql.*;
 import java.util.Vector;
+import java.util.Hashtable;
 import org.postgresql.Field;
 
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/Jdbc2Connection.java,v 1.2 2002/07/24 22:08:42 barry Exp $
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/Jdbc2Connection.java,v 1.3 2002/07/25 22:45:28 barry Exp $
  * This class implements the java.sql.Connection interface for JDBC2.
  * However most of the implementation is really done in 
  * org.postgresql.jdbc2.AbstractJdbc2Connection or one of it's parents
@@ -32,7 +33,7 @@ public class Jdbc2Connection extends org.postgresql.jdbc2.AbstractJdbc2Connectio
 
         public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
         {
-		org.postgresql.jdbc2.CallableStatement s = new org.postgresql.jdbc2.CallableStatement(this,sql);
+		Jdbc2CallableStatement s = new org.postgresql.jdbc2.Jdbc2CallableStatement(this,sql);
 		s.setResultSetType(resultSetType);
 	      	s.setResultSetConcurrency(resultSetConcurrency);
 	       	return s;
@@ -45,15 +46,14 @@ public class Jdbc2Connection extends org.postgresql.jdbc2.AbstractJdbc2Connectio
                 return metadata;
         }
 
-        public java.sql.ResultSet getResultSet(java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException
+        public java.sql.ResultSet getResultSet(Statement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException
         {
-                if (stat != null)
-                {
-                        if (stat.getResultSetConcurrency() == java.sql.ResultSet.CONCUR_UPDATABLE)
-                          return new org.postgresql.jdbc2.UpdateableResultSet(this, fields, tuples, status, updateCount, insertOID, binaryCursor);
-                }
+                return new Jdbc2ResultSet(this, statement, fields, tuples, status, updateCount, insertOID, binaryCursor);
+        }
 
-                return new Jdbc2ResultSet(this, fields, tuples, status, updateCount, insertOID, binaryCursor);
+        public java.sql.ResultSet getResultSet(Statement statement, Field[] fields, Vector tuples, String status, int updateCount) throws SQLException
+        {
+                return new Jdbc2ResultSet(this, statement, fields, tuples, status, updateCount, 0, false);
         }
 
 
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2ResultSet.java
index 7200cf549ad..e2c6ad7eea2 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2ResultSet.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2ResultSet.java
@@ -5,7 +5,7 @@ import java.sql.*;
 import java.util.Vector;
 import org.postgresql.Field;
 
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/Jdbc2ResultSet.java,v 1.1 2002/07/23 03:59:55 barry Exp $
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/Jdbc2ResultSet.java,v 1.2 2002/07/25 22:45:28 barry Exp $
  * This class implements the java.sql.ResultSet interface for JDBC2.
  * However most of the implementation is really done in 
  * org.postgresql.jdbc2.AbstractJdbc2ResultSet or one of it's parents
@@ -13,14 +13,9 @@ import org.postgresql.Field;
 public class Jdbc2ResultSet extends org.postgresql.jdbc2.AbstractJdbc2ResultSet implements java.sql.ResultSet
 {
 
-	public Jdbc2ResultSet(Jdbc2Connection conn, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor)
+	public Jdbc2ResultSet(Jdbc2Connection conn, Statement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor)
 	{
-		super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
-	}
-
-	public Jdbc2ResultSet(Jdbc2Connection conn, Field[] fields, Vector tuples, String status, int updateCount)
-	{
-		super(conn, fields, tuples, status, updateCount, 0, false);
+		super(conn, statement, fields, tuples, status, updateCount, insertOID, binaryCursor);
 	}
 
 	public java.sql.ResultSetMetaData getMetaData() throws SQLException
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java
deleted file mode 100644
index 6f6f67d8394..00000000000
--- a/src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java
+++ /dev/null
@@ -1,1389 +0,0 @@
-package org.postgresql.jdbc2;
-
-// IMPORTANT NOTE: This is the begining of supporting updateable ResultSets.
-//
-// This is because here we should be updateable, so any unimplemented methods
-// must say so.
-//
-// Also you'll notice that the String columnName based calls are not present.
-// They are not required as they are in the super class.
-//
-
-import java.lang.*;
-import java.io.*;
-import java.math.*;
-import java.text.*;
-import java.util.*;
-import java.sql.*;
-import org.postgresql.Field;
-import org.postgresql.largeobject.*;
-import org.postgresql.util.*;
-import org.postgresql.Driver;
-
-/*
- * @see ResultSet
- * @see ResultSetMetaData
- * @see java.sql.ResultSet
- */
-public class UpdateableResultSet extends org.postgresql.jdbc2.Jdbc2ResultSet
-{
-
-
-  class PrimaryKey
-  {
-    int index;              // where in the result set is this primaryKey
-    String name;            // what is the columnName of this primary Key
-
-    PrimaryKey( int index, String name)
-    {
-      this.index = index;
-      this.name = name;
-    }
-    Object getValue() throws SQLException
-    {
-      return getObject(index);
-    }
-  };
-
-  private boolean usingOID = false;   // are we using the OID for the primary key?
-
-  private Vector primaryKeys;    // list of primary keys
-
-  private int numKeys = 0;
-
-  private boolean singleTable = false;
-
-	protected String tableName = null;
-
-	/**
-	 * PreparedStatement used to delete data
-	 */
-
-	protected java.sql.PreparedStatement updateStatement = null;
-
-	/**
-	 * PreparedStatement used to insert data
-	 */
-
-	protected java.sql.PreparedStatement insertStatement = null;
-
-	/**
-	 * PreparedStatement used to delete data
-	 */
-
-	protected java.sql.PreparedStatement deleteStatement = null;
-
-
-  /**
-   * PreparedStatement used to refresh data
-   */
-  private java.sql.PreparedStatement selectStatement = null;
-
-
-	/**
-	 * Is this result set updateable?
-	 */
-
-	protected boolean updateable = false;
-
-	/**
-	 * Are we in the middle of doing updates to the current row?
-	 */
-
-	protected boolean doingUpdates = false;
-
-
-	/**
-	 * Are we on the insert row?
-	 */
-
-	protected boolean onInsertRow = false;
-
-
-	protected Hashtable updateValues = new Hashtable();
-
-	// The Row Buffer will be used to cache updated rows..then we shall sync this with the rows vector
-
-
-	/*
-	 * 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 UpdateableResultSet(Jdbc2Connection conn, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor)
-	{
-		super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
-	}
-
-  /**
-   *
-   * @throws SQLException
-   */
-	public synchronized void cancelRowUpdates() throws SQLException
-  {
-		if (doingUpdates)
-    {
-			doingUpdates = false;
-
-			clearRowBuffer();
-		}
-	}
-
-  /**
-   *
-   * @throws SQLException
-   */
-	public synchronized void deleteRow() throws SQLException
-  {
-		if ( !isUpdateable() )
-    {
-			throw new PSQLException( "postgresql.updateable.notupdateable" );
-		}
-
-		if (onInsertRow)
-    {
-			throw new PSQLException( "postgresql.updateable.oninsertrow" );
-		}
-
-    if (rows.size() == 0)
-    {
-      throw new PSQLException( "postgresql.updateable.emptydelete" );
-    }
-    if (isBeforeFirst())
-    {
-      throw new PSQLException( "postgresql.updateable.beforestartdelete" );
-    }
-    if (isAfterLast())
-    {
-      throw new PSQLException( "postgresql.updateable.afterlastdelete" );
-    }
-
-
-    int numKeys = primaryKeys.size();
-    if ( deleteStatement == null )
-    {
-
-
-      StringBuffer deleteSQL= new StringBuffer("DELETE FROM " ).append(tableName).append(" where " );
-
-      for ( int i=0; i < numKeys ; i++ )
-      {
-        deleteSQL.append( ((PrimaryKey)primaryKeys.get(i)).name ).append( " = ? " );
-        if ( i < numKeys-1 )
-        {
-          deleteSQL.append( " and " );
-        }
-      }
-
-      deleteStatement = ((java.sql.Connection)connection).prepareStatement(deleteSQL.toString());
-    }
-    deleteStatement.clearParameters();
-
-    for ( int i =0; i < numKeys; i++ )
-    {
-      deleteStatement.setObject(i+1, ((PrimaryKey)primaryKeys.get(i)).getValue());
-    }
-
-
-    deleteStatement.executeUpdate();
-
-    rows.removeElementAt(current_row);
-  }
-
-
-  /**
-   *
-   * @return
-   * @throws SQLException
-   */
-	public int getConcurrency() throws SQLException
-	{
-		// New in 7.1 - The updateable ResultSet class will now return
-		// CONCURuPDATEABLE.
-		return CONCUR_UPDATABLE;
-	}
-
-  /**
-   *
-   * @throws SQLException
-   */
-
-	public synchronized void insertRow() throws SQLException
-  {
-		if ( !isUpdateable() )
-    {
-			throw new PSQLException( "postgresql.updateable.notupdateable" );
-		}
-
-		if (!onInsertRow)
-    {
-			throw new PSQLException( "postgresql.updateable.notoninsertrow" );
-		}
-		else
-    {
-
-      // loop through the keys in the insertTable and create the sql statement
-      // we have to create the sql every time since the user could insert different
-      // columns each time
-
-      StringBuffer insertSQL=new StringBuffer("INSERT INTO ").append(tableName).append(" (");
-      StringBuffer paramSQL = new StringBuffer(") values (" );
-
-      Enumeration columnNames = updateValues.keys();
-      int numColumns = updateValues.size();
-
-      for ( int i=0; columnNames.hasMoreElements() ; i++ )
-      {
-        String columnName = (String)columnNames.nextElement();
-
-        insertSQL.append( columnName );
-        if ( i < numColumns - 1 )
-        {
-          insertSQL.append(", ");
-          paramSQL.append("?,");
-        }
-        else
-        {
-          paramSQL.append("?)");
-        }
-
-      }
-
-      insertSQL.append(paramSQL.toString());
-      insertStatement = ((java.sql.Connection)connection).prepareStatement(insertSQL.toString());
-
-      Enumeration keys = updateValues.keys();
-
-      for( int i=1; keys.hasMoreElements() ; i++)
-      {
-        String key = (String)keys.nextElement();
-        insertStatement.setObject(i, updateValues.get( key ) );
-      }
-
-      insertStatement.executeUpdate();
-
-      if ( usingOID )
-      {
-        // we have to get the last inserted OID and put it in the resultset
-
-        long insertedOID = ((AbstractJdbc2Statement)insertStatement).getLastOID();
-
-        updateValues.put("oid", new Long(insertedOID) );
-
-      }
-
-      // update the underlying row to the new inserted data
-      updateRowBuffer();
-
-			rows.addElement(rowBuffer);
-
-		  // we should now reflect the current data in this_row
-      // that way getXXX will get the newly inserted data
-      this_row = rowBuffer;
-
-      // need to clear this in case of another insert
-      clearRowBuffer();
-
-
-		}
-	}
-
-
-  /**
-   *
-   * @throws SQLException
-   */
-
-	public synchronized void moveToCurrentRow() throws SQLException
-	{
-		this_row = (byte [][])rows.elementAt(current_row);
-
-		rowBuffer=new byte[this_row.length][];
-		System.arraycopy(this_row,0,rowBuffer,0,this_row.length);
-
-    onInsertRow = false;
-    doingUpdates = false;
-	}
-
-  /**
-   *
-   * @throws SQLException
-   */
-	public synchronized void  moveToInsertRow() throws SQLException
-	{
-		// only sub-classes implement CONCURuPDATEABLE
-		if (!updateable)
-    {
-			throw new PSQLException( "postgresql.updateable.notupdateable" );
-		}
-
-		if (insertStatement != null)
-    {
-      insertStatement = null;
-		}
-
-
-    // make sure the underlying data is null
-    clearRowBuffer();
-
-		onInsertRow = true;
-		doingUpdates = false;
-
-	}
-
-  /**
-   *
-   * @throws SQLException
-   */
-	private synchronized void clearRowBuffer() throws SQLException
-  {
-    // rowBuffer is the temporary storage for the row
-		rowBuffer=new byte[fields.length][];
-
-    // clear the updateValues hashTable for the next set of updates
-    updateValues.clear();
-
-	}
-
-
-  /**
-   *
-   * @return
-   * @throws SQLException
-   */
-	public boolean rowDeleted() throws SQLException
-	{
-		// only sub-classes implement CONCURuPDATEABLE
-		throw Driver.notImplemented();
-	}
-
-  /**
-   *
-   * @return
-   * @throws SQLException
-   */
-	public boolean rowInserted() throws SQLException
-	{
-		// only sub-classes implement CONCURuPDATEABLE
-		throw Driver.notImplemented();
-	}
-
-  /**
-   *
-   * @return
-   * @throws SQLException
-   */
-	public boolean rowUpdated() throws SQLException
-	{
-		// only sub-classes implement CONCURuPDATEABLE
-		throw Driver.notImplemented();
-	}
-
-  /**
-   *
-   * @param columnIndex
-   * @param x
-   * @param length
-   * @throws SQLException
-   */
-	public synchronized void updateAsciiStream(int columnIndex,
-								  java.io.InputStream x,
-								  int length
-								 ) throws SQLException
-	{
-
-		byte[] theData=null;
-
-		try
-    {
-			x.read(theData,0,length);
-		}
-    catch (NullPointerException ex )
-    {
-      throw new PSQLException("postgresql.updateable.inputstream");
-    }
-		catch (IOException ie)
-    {
-			throw new PSQLException("postgresql.updateable.ioerror" + ie);
-		}
-
-    doingUpdates = !onInsertRow;
-
-    updateValues.put( fields[columnIndex-1].getName(), theData );
-
-	}
-
-  /**
-   *
-   * @param columnIndex
-   * @param x
-   * @throws SQLException
-   */
-	public synchronized void updateBigDecimal(int columnIndex,
-                                           java.math.BigDecimal x )
-                                                                throws SQLException
-	{
-
-    doingUpdates = !onInsertRow;
-    updateValues.put( fields[columnIndex-1].getName(), x );
-
-	}
-
-  /**
-   *
-   * @param columnIndex
-   * @param x
-   * @param length
-   * @throws SQLException
-   */
-	public synchronized void updateBinaryStream(int columnIndex,
-                                               java.io.InputStream x,
-                                               int length
-                                              ) throws SQLException
-	{
-
-
-		byte[] theData=null;
-
-		try {
-			x.read(theData,0,length);
-
-		}
-    catch( NullPointerException ex )
-    {
-      throw new PSQLException("postgresql.updateable.inputstream");
-    }
-		catch (IOException ie)
-    {
-			throw new PSQLException("postgresql.updateable.ioerror" + ie);
-		}
-
-    doingUpdates = !onInsertRow;
-
-    updateValues.put( fields[columnIndex-1].getName(), theData );
-
-	}
-
-  /**
-   *
-   * @param columnIndex
-   * @param x
-   * @throws SQLException
-   */
-	public synchronized void updateBoolean(int columnIndex, boolean x) throws SQLException
-	{
-
-		if ( Driver.logDebug ) Driver.debug("updating boolean "+fields[columnIndex-1].getName()+"="+x);
-
-    doingUpdates = !onInsertRow;
-	  updateValues.put( fields[columnIndex-1].getName(), new Boolean(x) );
-
-	}
-
-  /**
-   *
-   * @param columnIndex
-   * @param x
-   * @throws SQLException
-   */
-	public synchronized void updateByte(int columnIndex, byte x) throws SQLException
-	{
-
-    doingUpdates = true;
-    updateValues.put( fields[columnIndex-1].getName(), String.valueOf(x) );
-	}
-
-  /**
-   *
-   * @param columnIndex
-   * @param x
-   * @throws SQLException
-   */
-	public synchronized void updateBytes(int columnIndex, byte[] x) throws SQLException
-	{
-
-    doingUpdates = !onInsertRow;
-		updateValues.put( fields[columnIndex-1].getName(), x );
-
-	}
-
-  /**
-   *
-   * @param columnIndex
-   * @param x
-   * @param length
-   * @throws SQLException
-   */
-	public synchronized void updateCharacterStream(int columnIndex,
-                                                  java.io.Reader x,
-                                                  int length
-                                                 ) throws SQLException
-	{
-
-
-		char[] theData=null;
-
-		try
-    {
-			x.read(theData,0,length);
-
-		}
-    catch (NullPointerException ex)
-    {
-      throw new PSQLException("postgresql.updateable.inputstream");
-    }
-		catch (IOException ie)
-    {
-			throw new PSQLException("postgresql.updateable.ioerror" + ie);
-		}
-
-    doingUpdates = !onInsertRow;
-    updateValues.put( fields[columnIndex-1].getName(), theData);
-
-	}
-
-	public synchronized void updateDate(int columnIndex, java.sql.Date x) throws SQLException
-	{
-
-    doingUpdates = !onInsertRow;
-		updateValues.put( fields[columnIndex-1].getName(), x );
-	}
-
-	public synchronized void updateDouble(int columnIndex, double x) throws SQLException
-	{
-		if ( Driver.logDebug ) Driver.debug("updating double "+fields[columnIndex-1].getName()+"="+x);
-
-    doingUpdates = !onInsertRow;
-    updateValues.put( fields[columnIndex-1].getName(), new Double(x) );
-
-	}
-
-	public synchronized void updateFloat(int columnIndex, float x) throws SQLException
-	{
-		if ( Driver.logDebug ) Driver.debug("updating float "+fields[columnIndex-1].getName()+"="+x);
-
-    doingUpdates = !onInsertRow;
-
-		updateValues.put( fields[columnIndex-1].getName(), new Float(x) );
-
-	}
-
-	public synchronized void updateInt(int columnIndex, int x) throws SQLException
-	{
-		if ( Driver.logDebug ) Driver.debug("updating int "+fields[columnIndex-1].getName()+"="+x);
-
-    doingUpdates = !onInsertRow;
-    updateValues.put( fields[columnIndex-1].getName(), new Integer(x) );
-
-	}
-
-	public synchronized void updateLong(int columnIndex, long x) throws SQLException
-	{
-		if ( Driver.logDebug ) Driver.debug("updating long "+fields[columnIndex-1].getName()+"="+x);
-
-    doingUpdates = !onInsertRow;
-    updateValues.put( fields[columnIndex-1].getName(), new Long(x) );
-
-	}
-
-	public synchronized void updateNull(int columnIndex) throws SQLException
-	{
-
-    doingUpdates = !onInsertRow;
-    updateValues.put( fields[columnIndex-1].getName(), null);
-
-
-	}
-
-	public synchronized void updateObject(int columnIndex, Object x) throws SQLException
-	{
-
-
-		if ( Driver.logDebug ) Driver.debug("updating object " + fields[columnIndex-1].getName() + " = " + x);
-
-    doingUpdates = !onInsertRow;
-    updateValues.put( fields[columnIndex-1].getName(), x );
-	}
-
-	public synchronized void updateObject(int columnIndex, Object x, int scale) throws SQLException
-	{
-
-    this.updateObject(columnIndex, x);
-
-	}
-
-
-  public void refreshRow() throws SQLException
-  {
-		if ( !isUpdateable() )
-    {
-			throw new PSQLException( "postgresql.updateable.notupdateable" );
-		}
-
-    try
-    {
-      StringBuffer selectSQL = new StringBuffer( "select ");
-
-      final int numColumns = java.lang.reflect.Array.getLength(fields);
-
-      for (int i=0; i < numColumns ; i++ )
-      {
-
-        selectSQL.append( fields[i].getName() );
-
-        if ( i < numColumns - 1 )
-        {
-
-          selectSQL.append(", ");
-        }
-
-      }
-      selectSQL.append(" from " ).append(tableName).append(" where ");
-
-      int numKeys = primaryKeys.size();
-
-      for ( int i = 0; i < numKeys; i++ )
-      {
-
-        PrimaryKey primaryKey = ((PrimaryKey)primaryKeys.get(i));
-        selectSQL.append(primaryKey.name).append("= ?");
-
-        if ( i < numKeys -1 )
-        {
-          selectSQL.append(" and ");
-        }
-      }
-      if ( Driver.logDebug ) Driver.debug("selecting "+ selectSQL.toString());
-      selectStatement = ((java.sql.Connection)connection).prepareStatement(selectSQL.toString());
-
-
-      for( int j=0, i=1; j < numKeys; j++, i++)
-      {
-        selectStatement.setObject( i, ((PrimaryKey)primaryKeys.get(j)).getValue() );
-      }
-
-      Jdbc2ResultSet rs = (Jdbc2ResultSet) selectStatement.executeQuery();
-
-      if( rs.first() )
-      {
-        rowBuffer = rs.rowBuffer;
-      }
-
-      rows.setElementAt( rowBuffer, current_row );
-      if ( Driver.logDebug ) Driver.debug("done updates");
-
-      rs.close();
-      selectStatement.close();
-      selectStatement = null;
-
-    }
-    catch (Exception e)
-    {
-      if ( Driver.logDebug ) Driver.debug(e.getClass().getName()+e);
-      throw new SQLException( e.getMessage() );
-    }
-
-  }
-  /**
-   *
-   * @throws SQLException
-   */
-	public synchronized void updateRow() throws SQLException
-  {
-		if ( !isUpdateable() )
-    {
-			throw new PSQLException( "postgresql.updateable.notupdateable" );
-		}
-
-		if (doingUpdates)
-    {
-
-			try
-      {
-
-        StringBuffer updateSQL=new StringBuffer("UPDATE "+tableName+" SET  ");
-
-        int numColumns = updateValues.size();
-        Enumeration columns = updateValues.keys();
-
-        for (int i=0; columns.hasMoreElements() ; i++ )
-        {
-
-          String column = (String)columns.nextElement();
-          updateSQL.append( column + "= ?");
-
-          if ( i < numColumns - 1 )
-          {
-
-            updateSQL.append(", ");
-          }
-
-        }
-        updateSQL.append( " WHERE " );
-
-        int numKeys = primaryKeys.size();
-
-        for ( int i = 0; i < numKeys; i++ )
-        {
-
-          PrimaryKey primaryKey = ((PrimaryKey)primaryKeys.get(i));
-          updateSQL.append(primaryKey.name).append("= ?");
-
-          if ( i < numKeys -1 )
-          {
-            updateSQL.append(" and ");
-          }
-        }
-        if ( Driver.logDebug ) Driver.debug("updating "+updateSQL.toString());
-        updateStatement = ((java.sql.Connection)connection).prepareStatement(updateSQL.toString());
-
-        int i = 0;
-        Iterator iterator = updateValues.values().iterator();
-        for (; iterator.hasNext(); i++)
-        {
-          updateStatement.setObject( i+1, iterator.next() );
-
-        }
-        for( int j=0; j < numKeys; j++, i++)
-        {
-          updateStatement.setObject( i+1, ((PrimaryKey)primaryKeys.get(j)).getValue() );
-        }
-
-        updateStatement.executeUpdate();
-        updateStatement.close();
-
-        updateStatement = null;
-        updateRowBuffer();
-
-
-				if ( Driver.logDebug ) Driver.debug("copying data");
-				System.arraycopy(rowBuffer,0,this_row,0,rowBuffer.length);
-
-				rows.setElementAt( rowBuffer, current_row );
-        if ( Driver.logDebug ) Driver.debug("done updates");
-
-			  doingUpdates = false;
-			}
-			catch(Exception e)
-      {
-				if ( Driver.logDebug ) Driver.debug(e.getClass().getName()+e);
-        throw new SQLException( e.getMessage() );
-			}
-
-		}
-
-	}
-
-	public synchronized void updateShort(int columnIndex, short x) throws SQLException
-	{
-		if ( Driver.logDebug ) Driver.debug("in update Short "+fields[columnIndex-1].getName()+" = "+x);
-
-
-    doingUpdates = !onInsertRow;
-    updateValues.put( fields[columnIndex-1].getName(), new Short(x) );
-
-	}
-
-	public synchronized void updateString(int columnIndex, String x) throws SQLException
-	{
-		if ( Driver.logDebug ) Driver.debug("in update String "+fields[columnIndex-1].getName()+" = "+x);
-
-    doingUpdates = !onInsertRow;
-    updateValues.put( fields[columnIndex-1].getName(), x );
-
-	}
-
-	public synchronized void updateTime(int columnIndex, Time x) throws SQLException
-	{
-		if ( Driver.logDebug ) Driver.debug("in update Time "+fields[columnIndex-1].getName()+" = "+x);
-
-
-    doingUpdates = !onInsertRow;
-    updateValues.put( fields[columnIndex-1].getName(), x );
-
-	}
-
-	public synchronized void updateTimestamp(int columnIndex, Timestamp x) throws SQLException
-	{
-		if ( Driver.logDebug ) Driver.debug("updating Timestamp "+fields[columnIndex-1].getName()+" = "+x);
-
-    doingUpdates = !onInsertRow;
-    updateValues.put( fields[columnIndex-1].getName(), x );
-
-
-	}
-
-	public synchronized void updateNull(String columnName) throws SQLException
-  {
-		updateNull(findColumn(columnName));
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a boolean value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateBoolean(String columnName, boolean x) throws SQLException
-  {
-		updateBoolean(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a byte value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateByte(String columnName, byte x) throws SQLException
-  {
-		updateByte(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a short value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateShort(String columnName, short x) throws SQLException
-  {
-		updateShort(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with an integer value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateInt(String columnName, int x) throws SQLException
-  {
-		updateInt(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a long value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateLong(String columnName, long x) throws SQLException
-  {
-		updateLong(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a float value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateFloat(String columnName, float x) throws SQLException
-  {
-		updateFloat(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a double value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateDouble(String columnName, double x) throws SQLException
-  {
-		updateDouble(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a BigDecimal value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateBigDecimal(String columnName, BigDecimal x)
-		                                                           throws SQLException
-  {
-		updateBigDecimal(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a String value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateString(String columnName, String x) throws SQLException
-  {
-		updateString(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a byte array value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateBytes(String columnName, byte x[]) throws SQLException
-  {
-		updateBytes(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a Date value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateDate(String columnName, java.sql.Date x)
-		                                                                   throws SQLException
-  {
-		updateDate(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a Time value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateTime(String columnName, java.sql.Time x)
-		                                                                   throws SQLException
-  {
-		updateTime(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a Timestamp value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateTimestamp(String columnName, java.sql.Timestamp x)
-                                                                                 throws SQLException
-  {
-		updateTimestamp(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with an ascii stream value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @param length of the stream
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateAsciiStream(
-                                              String columnName,
-                                              java.io.InputStream x,
-                                              int length)
-                                              throws SQLException
-  {
-		updateAsciiStream(findColumn(columnName), x, length);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a binary stream value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @param length of the stream
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateBinaryStream(
-                                              String columnName,
-                                              java.io.InputStream x,
-                                              int length)
-                                              throws SQLException
-  {
-		updateBinaryStream(findColumn(columnName), x, length);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with a character stream value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @param length of the stream
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateCharacterStream(
-                                                  String columnName,
-                                                  java.io.Reader reader,
-                                                  int length)
-                                                  throws SQLException
-  {
-		updateCharacterStream(findColumn(columnName), reader,length);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with an Object value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @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 synchronized void updateObject(String columnName, Object x, int scale)
-		                                                              throws SQLException
-  {
-		updateObject(findColumn(columnName), x);
-	}
-
-	/**
-	 * JDBC 2.0
-	 *
-	 * Update a column with an Object value.
-	 *
-	 * The updateXXX() methods are used to update column values in the
-	 * current row, or the insert row.  The updateXXX() methods do not
-	 * update the underlying database, instead the updateRow() or insertRow()
-	 * methods are called to update the database.
-	 *
-	 * @param columnName the name of the column
-	 * @param x the new column value
-	 * @exception SQLException if a database-access error occurs
-	 */
-
-	public synchronized void updateObject(String columnName, Object x) throws SQLException
-  {
-		updateObject(findColumn(columnName), x);
-	}
-
-
-
-  private int _findColumn( String columnName )
-  {
-		int i;
-
-		final int flen = fields.length;
-		for (i = 0 ; i < flen; ++i)
-    {
-			if (fields[i].getName().equalsIgnoreCase(columnName))
-      {
-				return (i + 1);
-      }
-    }
-    return -1;
-	}
-
-
-	/**
-	 * Is this ResultSet updateable?
-	 */
-
-	boolean isUpdateable() throws SQLException
-  {
-
-    if (updateable) return true;
-
-    if ( Driver.logDebug ) Driver.debug("checking if rs is updateable");
-
-    parseQuery();
-
-    if ( singleTable == false )
-    {
-		  if ( Driver.logDebug ) Driver.debug("not a single table");
-      return false;
-    }
-
-		if ( Driver.logDebug ) Driver.debug("getting primary keys");
-
-		//
-		// Contains the primary key?
-		//
-
-		primaryKeys = new Vector();
-
-    // this is not stricty jdbc spec, but it will make things much faster if used
-    // the user has to select oid, * from table and then we will just use oid
-
-
-    usingOID = false;
-    int oidIndex = _findColumn( "oid" );
-    int i = 0;
-
-
-    // if we find the oid then just use it
-
-    if ( oidIndex > 0 )
-    {
-      i++;
-      primaryKeys.add( new PrimaryKey( oidIndex, "oid" ) );
-      usingOID = true;
-    }
-    else
-    {
-      // otherwise go and get the primary keys and create a hashtable of keys
-      java.sql.ResultSet rs  = ((java.sql.Connection)connection).getMetaData().getPrimaryKeys("","",tableName);
-
-
-      for( ; rs.next(); i++ )
-      {
-        String columnName = rs.getString(4);    // get the columnName
-
-        int index = findColumn( columnName );
-
-        if ( index > 0 )
-        {
-          primaryKeys.add( new PrimaryKey(index, columnName ) ); // get the primary key information
-        }
-      }
-
-      rs.close();
-    }
-
-    numKeys = primaryKeys.size();
-
-    if ( Driver.logDebug ) Driver.debug( "no of keys=" + i );
-
-    if ( i < 1 )
-    {
-			throw new SQLException("No Primary Keys");
-		}
-
-		updateable = primaryKeys.size() > 0;
-
-		if ( Driver.logDebug ) Driver.debug( "checking primary key " + updateable );
-
-		return updateable;
-	}
-
-
-  /**
-   *
-   */
-	public void parseQuery()
-  {
-		StringTokenizer st=new StringTokenizer(sqlQuery," \r\t");
-		boolean tableFound=false, tablesChecked = false;
-		String name="";
-
-    singleTable = true;
-
-    while ( !tableFound && !tablesChecked && st.hasMoreTokens() )
-    {
-			name=st.nextToken();
-      if ( !tableFound )
-      {
-        if (name.toLowerCase().equals("from"))
-        {
-          tableName=st.nextToken();
-          tableFound=true;
-        }
-      }
-      else
-      {
-        tablesChecked = true;
-        // if the very next token is , then there are multiple tables
-        singleTable =  !name.equalsIgnoreCase(",");
-      }
-		}
-	}
-
-
-  private void updateRowBuffer() throws SQLException
-  {
-
-    Enumeration columns = updateValues.keys();
-
-    while( columns.hasMoreElements() )
-    {
-      String columnName = (String)columns.nextElement();
-      int columnIndex = _findColumn( columnName ) - 1;
-
-      switch ( connection.getSQLType( fields[columnIndex].getPGType() ) )
-      {
-
-        case Types.DECIMAL:
-        case Types.BIGINT:
-        case Types.DOUBLE:
-        case Types.BIT:
-        case Types.VARCHAR:
-        case Types.DATE:
-        case Types.TIME:
-        case Types.TIMESTAMP:
-        case Types.SMALLINT:
-        case Types.FLOAT:
-        case Types.INTEGER:
-        case Types.CHAR:
-        case Types.NUMERIC:
-        case Types.REAL:
-        case Types.TINYINT:
-
-          try
-          {
-            rowBuffer[columnIndex] = String.valueOf( updateValues.get( columnName ) ).getBytes(connection.getEncoding().name() );
-          }
-          catch ( UnsupportedEncodingException ex)
-          {
-            throw new SQLException("Unsupported Encoding "+connection.getEncoding().name());
-          }
-        case Types.NULL:
-          continue;
-        default:
-          rowBuffer[columnIndex] = (byte [])updateValues.get( columnName );
-      }
-
-    }
-  }
-
-}
-
-- 
GitLab