]> granicus.if.org Git - postgresql/commitdiff
Implemented updateable result sets based on raghu nidagal implementation
authorDave Cramer <davec@fastcrypt.com>
Thu, 13 Jun 2002 13:52:16 +0000 (13:52 +0000)
committerDave Cramer <davec@fastcrypt.com>
Thu, 13 Jun 2002 13:52:16 +0000 (13:52 +0000)
src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java

index 108125a7d9d57351350f2c25bef51f97fbe7a5fc..a85f2b420cd6acddb627adb65fc67f61182517df 100644 (file)
@@ -1,6 +1,6 @@
 package org.postgresql.jdbc2;
 
-// IMPORTANT NOTE: This is the begining of supporting updatable ResultSets.
+// IMPORTANT NOTE: This is the begining of supporting updateable ResultSets.
 // It is not a working solution (yet)!
 //
 // You will notice here we really do throw org.postgresql.Driver.notImplemented()
@@ -20,6 +20,7 @@ import java.sql.*;
 import org.postgresql.Field;
 import org.postgresql.largeobject.*;
 import org.postgresql.util.*;
+import org.postgresql.Driver;
 
 /*
  * @see ResultSet
@@ -29,6 +30,80 @@ import org.postgresql.util.*;
 public class UpdateableResultSet extends org.postgresql.jdbc2.ResultSet
 {
 
+
+  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;
+
+
+  private java.sql.Statement currentStatement = 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.
@@ -45,210 +120,1197 @@ public class UpdateableResultSet extends org.postgresql.jdbc2.ResultSet
                super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
        }
 
-       /*
-        * 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(Connection conn, Field[] fields, Vector tuples, String status, int updateCount)
-       //      {
-       //         super(conn,fields,tuples,status,updateCount,0,false);
-       //}
+  /**
+   *
+   * @throws SQLException
+   */
+       public synchronized void cancelRowUpdates() throws SQLException
+  {
+               if (doingUpdates)
+    {
+                       doingUpdates = false;
 
-       public void cancelRowUpdates() throws SQLException
-       {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+                       clearRowBuffer();
+               }
        }
 
-       public void deleteRow() throws SQLException
-       {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
-       }
+  /**
+   *
+   * @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
-               // CONCUR_UPDATEABLE.
+               // CONCURuPDATEABLE.
                return CONCUR_UPDATABLE;
        }
 
-       public void insertRow() throws SQLException
-       {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+  /**
+   *
+   * @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 = ((org.postgresql.Statement)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 void moveToCurrentRow() throws SQLException
+
+  /**
+   *
+   * @throws SQLException
+   */
+
+       public synchronized void moveToCurrentRow() throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+               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 void moveToInsertRow() throws SQLException
+  /**
+   *
+   * @throws SQLException
+   */
+       public synchronized void  moveToInsertRow() throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+               // 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 CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
-               //return false; // javac complains about not returning a value!
+               // only sub-classes implement CONCURuPDATEABLE
+               throw Driver.notImplemented();
        }
 
+  /**
+   *
+   * @return
+   * @throws SQLException
+   */
        public boolean rowInserted() throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
-               //return false; // javac complains about not returning a value!
+               // only sub-classes implement CONCURuPDATEABLE
+               throw Driver.notImplemented();
        }
 
+  /**
+   *
+   * @return
+   * @throws SQLException
+   */
        public boolean rowUpdated() throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
-               //return false; // javac complains about not returning a value!
+               // only sub-classes implement CONCURuPDATEABLE
+               throw Driver.notImplemented();
        }
 
-       public void updateAsciiStream(int columnIndex,
+  /**
+   *
+   * @param columnIndex
+   * @param x
+   * @param length
+   * @throws SQLException
+   */
+       public synchronized void updateAsciiStream(int columnIndex,
                                                                  java.io.InputStream x,
                                                                  int length
                                                                 ) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+
+               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 void updateBigDecimal(int columnIndex,
-                                                                java.math.BigDecimal x
-                                                               ) throws SQLException
+  /**
+   *
+   * @param columnIndex
+   * @param x
+   * @throws SQLException
+   */
+       public synchronized void updateBigDecimal(int columnIndex,
+                                           java.math.BigDecimal x )
+                                                                throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex-1].getName(), x );
+
        }
 
-       public void updateBinaryStream(int columnIndex,
-                                                                  java.io.InputStream x,
-                                                                  int length
-                                                                 ) throws SQLException
+  /**
+   *
+   * @param columnIndex
+   * @param x
+   * @param length
+   * @throws SQLException
+   */
+       public synchronized void updateBinaryStream(int columnIndex,
+                                               java.io.InputStream x,
+                                               int length
+                                              ) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+
+
+               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 void updateBoolean(int columnIndex, boolean x) throws SQLException
+  /**
+   *
+   * @param columnIndex
+   * @param x
+   * @throws SQLException
+   */
+       public synchronized void updateBoolean(int columnIndex, boolean x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+
+               if ( Driver.logDebug ) Driver.debug("updating boolean "+fields[columnIndex-1].getName()+"="+x);
+
+    doingUpdates = !onInsertRow;
+         updateValues.put( fields[columnIndex-1].getName(), new Boolean(x) );
+
        }
 
-       public void updateByte(int columnIndex, byte x) throws SQLException
+  /**
+   *
+   * @param columnIndex
+   * @param x
+   * @throws SQLException
+   */
+       public synchronized void updateByte(int columnIndex, byte x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+
+    doingUpdates = true;
+    updateValues.put( fields[columnIndex-1].getName(), String.valueOf(x) );
        }
 
-       public void updateBytes(int columnIndex, byte[] x) throws SQLException
+  /**
+   *
+   * @param columnIndex
+   * @param x
+   * @throws SQLException
+   */
+       public synchronized void updateBytes(int columnIndex, byte[] x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+
+    doingUpdates = !onInsertRow;
+               updateValues.put( fields[columnIndex-1].getName(), x );
+
        }
 
-       public void updateCharacterStream(int columnIndex,
-                                                                         java.io.Reader x,
-                                                                         int length
-                                                                        ) throws SQLException
+  /**
+   *
+   * @param columnIndex
+   * @param x
+   * @param length
+   * @throws SQLException
+   */
+       public synchronized void updateCharacterStream(int columnIndex,
+                                                  java.io.Reader x,
+                                                  int length
+                                                 ) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+
+
+               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 void updateDate(int columnIndex, java.sql.Date x) throws SQLException
+       public synchronized void updateDate(int columnIndex, java.sql.Date x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+
+    doingUpdates = !onInsertRow;
+               updateValues.put( fields[columnIndex-1].getName(), x );
        }
 
-       public void updateDouble(int columnIndex, double x) throws SQLException
+       public synchronized void updateDouble(int columnIndex, double x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+               if ( Driver.logDebug ) Driver.debug("updating double "+fields[columnIndex-1].getName()+"="+x);
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex-1].getName(), new Double(x) );
+
        }
 
-       public void updateFloat(int columnIndex, float x) throws SQLException
+       public synchronized void updateFloat(int columnIndex, float x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+               if ( Driver.logDebug ) Driver.debug("updating float "+fields[columnIndex-1].getName()+"="+x);
+
+    doingUpdates = !onInsertRow;
+
+               updateValues.put( fields[columnIndex-1].getName(), new Float(x) );
+
        }
 
-       public void updateInt(int columnIndex, int x) throws SQLException
+       public synchronized void updateInt(int columnIndex, int x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+               if ( Driver.logDebug ) Driver.debug("updating int "+fields[columnIndex-1].getName()+"="+x);
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex-1].getName(), new Integer(x) );
+
        }
 
-       public void updateLong(int columnIndex, long x) throws SQLException
+       public synchronized void updateLong(int columnIndex, long x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+               if ( Driver.logDebug ) Driver.debug("updating long "+fields[columnIndex-1].getName()+"="+x);
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex-1].getName(), new Long(x) );
+
        }
 
-       public void updateNull(int columnIndex) throws SQLException
+       public synchronized void updateNull(int columnIndex) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex-1].getName(), null);
+
+
        }
 
-       public void updateObject(int columnIndex, Object x) throws SQLException
+       public synchronized void updateObject(int columnIndex, Object x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+
+
+               if ( Driver.logDebug ) Driver.debug("updating object " + fields[columnIndex-1].getName() + " = " + x);
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex-1].getName(), x );
        }
 
-       public void updateObject(int columnIndex, Object x, int scale) throws SQLException
+       public synchronized void updateObject(int columnIndex, Object x, int scale) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+
+    this.updateObject(columnIndex, x);
+
        }
 
-       public void updateRow() throws SQLException
-       {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+  /**
+   *
+   * @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 void updateShort(int columnIndex, short x) throws SQLException
+       public synchronized void updateShort(int columnIndex, short x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+               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 void updateString(int columnIndex, String x) throws SQLException
+       public synchronized void updateString(int columnIndex, String x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+               if ( Driver.logDebug ) Driver.debug("in update String "+fields[columnIndex-1].getName()+" = "+x);
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex-1].getName(), x );
+
        }
 
-       public void updateTime(int columnIndex, Time x) throws SQLException
+       public synchronized void updateTime(int columnIndex, Time x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+               if ( Driver.logDebug ) Driver.debug("in update Time "+fields[columnIndex-1].getName()+" = "+x);
+
+
+    doingUpdates = !onInsertRow;
+    updateValues.put( fields[columnIndex-1].getName(), x );
+
        }
 
-       public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException
+       public synchronized void updateTimestamp(int columnIndex, Timestamp x) throws SQLException
        {
-               // only sub-classes implement CONCUR_UPDATEABLE
-               throw org.postgresql.Driver.notImplemented();
+               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  = ((org.postgresql.jdbc2.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 );
+      }
+
+    }
+  }
+
 }