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 ); } } } }