From 7873bed77c51bdc81442f31e479b879eb7cb7ede Mon Sep 17 00:00:00 2001 From: Dave Cramer Date: Thu, 13 Jun 2002 13:52:16 +0000 Subject: [PATCH] Implemented updateable result sets based on raghu nidagal implementation --- .../postgresql/jdbc2/UpdateableResultSet.java | 1290 +++++++++++++++-- 1 file changed, 1176 insertions(+), 114 deletions(-) diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java index 108125a7d9..a85f2b420c 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java @@ -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 ); + } + + } + } + } -- 2.40.0