/*------------------------------------------------------------------------- * * AbstractJdbc2ResultSet.java * This class defines methods of the jdbc2 specification. This class * extends org.postgresql.jdbc1.AbstractJdbc1ResultSet which provides the * jdbc1 methods. The real Statement class (for jdbc2) is * org.postgresql.jdbc2.Jdbc2ResultSet * * Copyright (c) 2003, PostgreSQL Global Development Group * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2ResultSet.java,v 1.21 2003/08/11 21:33:50 barry Exp $ * *------------------------------------------------------------------------- */ package org.postgresql.jdbc2; import java.io.CharArrayReader; import java.io.InputStream; import java.io.IOException; import java.math.BigDecimal; import java.sql.*; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.StringTokenizer; import java.util.Vector; import org.postgresql.Driver; import org.postgresql.core.BaseStatement; import org.postgresql.core.Field; import org.postgresql.core.Encoding; import org.postgresql.util.PSQLException; public abstract class AbstractJdbc2ResultSet extends org.postgresql.jdbc1.AbstractJdbc1ResultSet { //needed for updateable result set support protected boolean updateable = false; protected boolean doingUpdates = false; protected boolean onInsertRow = false; protected Hashtable updateValues = new Hashtable(); private boolean usingOID = false; // are we using the OID for the primary key? private Vector primaryKeys; // list of primary keys private int numKeys = 0; private boolean singleTable = false; protected String tableName = null; protected PreparedStatement updateStatement = null; protected PreparedStatement insertStatement = null; protected PreparedStatement deleteStatement = null; private PreparedStatement selectStatement = null; public AbstractJdbc2ResultSet(BaseStatement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) { super (statement, fields, tuples, status, updateCount, insertOID, binaryCursor); } public java.net.URL getURL(int columnIndex) throws SQLException { return null; } public java.net.URL getURL(String columnName) throws SQLException { return null; } /* * Get the value of a column in the current row as a Java object * *
This method will return the value of the given column as a * Java object. The type of the Java object will be the default * Java Object type corresponding to the column's SQL type, following * the mapping specified in the JDBC specification. * *
This method may also be used to read database specific abstract * data types. * * @param columnIndex the first column is 1, the second is 2... * @return a Object holding the column value * @exception SQLException if a database access error occurs */ public Object getObject(int columnIndex) throws SQLException { Field field; checkResultSet( columnIndex ); wasNullFlag = (this_row[columnIndex - 1] == null); if (wasNullFlag) return null; field = fields[columnIndex - 1]; // some fields can be null, mainly from those returned by MetaData methods if (field == null) { wasNullFlag = true; return null; } switch (field.getSQLType()) { case Types.BIT: return getBoolean(columnIndex) ? Boolean.TRUE : Boolean.FALSE; case Types.SMALLINT: return new Short(getShort(columnIndex)); case Types.INTEGER: return new Integer(getInt(columnIndex)); case Types.BIGINT: return new Long(getLong(columnIndex)); case Types.NUMERIC: return getBigDecimal (columnIndex, (field.getMod() == -1) ? -1 : ((field.getMod() - 4) & 0xffff)); case Types.REAL: return new Float(getFloat(columnIndex)); case Types.DOUBLE: return new Double(getDouble(columnIndex)); case Types.CHAR: case Types.VARCHAR: return getString(columnIndex); case Types.DATE: return getDate(columnIndex); case Types.TIME: return getTime(columnIndex); case Types.TIMESTAMP: return getTimestamp(columnIndex); case Types.BINARY: case Types.VARBINARY: return getBytes(columnIndex); case Types.ARRAY: return getArray(columnIndex); default: String type = field.getPGType(); // if the backend doesn't know the type then coerce to String if (type.equals("unknown")) { return getString(columnIndex); } // Specialized support for ref cursors is neater. else if (type.equals("refcursor")) { String cursorName = getString(columnIndex); return statement.createRefCursorResultSet(cursorName); } else { return connection.getObject(field.getPGType(), getString(columnIndex)); } } } public boolean absolute(int index) throws SQLException { // index is 1-based, but internally we use 0-based indices int internalIndex; if (index == 0) throw new SQLException("Cannot move to index of 0"); final int rows_size = rows.size(); //if index<0, count from the end of the result set, but check //to be sure that it is not beyond the first index if (index < 0) { if (index >= -rows_size) internalIndex = rows_size + index; else { beforeFirst(); return false; } } else { //must be the case that index>0, //find the correct place, assuming that //the index is not too large if (index <= rows_size) internalIndex = index - 1; else { afterLast(); return false; } } current_row = internalIndex; this_row = (byte[][]) rows.elementAt(internalIndex); rowBuffer = new byte[this_row.length][]; System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length); return true; } public void afterLast() throws SQLException { final int rows_size = rows.size(); if (rows_size > 0) current_row = rows_size; } public void beforeFirst() throws SQLException { if (rows.size() > 0) current_row = -1; } public boolean first() throws SQLException { if (rows.size() <= 0) return false; onInsertRow = false; current_row = 0; this_row = (byte[][]) rows.elementAt(current_row); rowBuffer = new byte[this_row.length][]; System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length); return true; } public java.sql.Array getArray(String colName) throws SQLException { return getArray(findColumn(colName)); } public java.sql.Array getArray(int i) throws SQLException { checkResultSet( i ); wasNullFlag = (this_row[i - 1] == null); if (wasNullFlag) return null; if (i < 1 || i > fields.length) throw new PSQLException("postgresql.res.colrange"); return (java.sql.Array) new org.postgresql.jdbc2.Array( connection, i, fields[i - 1], this ); } public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException { return getBigDecimal(columnIndex, -1); } public java.math.BigDecimal getBigDecimal(String columnName) throws SQLException { return getBigDecimal(findColumn(columnName)); } public Blob getBlob(String columnName) throws SQLException { return getBlob(findColumn(columnName)); } public abstract Blob getBlob(int i) throws SQLException; public java.io.Reader getCharacterStream(String columnName) throws SQLException { return getCharacterStream(findColumn(columnName)); } public java.io.Reader getCharacterStream(int i) throws SQLException { checkResultSet( i ); wasNullFlag = (this_row[i - 1] == null); if (wasNullFlag) return null; if (((AbstractJdbc2Connection) connection).haveMinimumCompatibleVersion("7.2")) { //Version 7.2 supports AsciiStream for all the PG text types //As the spec/javadoc for this method indicate this is to be used for //large text values (i.e. LONGVARCHAR) PG doesn't have a separate //long string datatype, but with toast the text datatype is capable of //handling very large values. Thus the implementation ends up calling //getString() since there is no current way to stream the value from the server return new CharArrayReader(getString(i).toCharArray()); } else { // In 7.1 Handle as BLOBS so return the LargeObject input stream Encoding encoding = connection.getEncoding(); InputStream input = getBinaryStream(i); return encoding.getDecodingReader(input); } } public Clob getClob(String columnName) throws SQLException { return getClob(findColumn(columnName)); } public abstract Clob getClob(int i) throws SQLException; public int getConcurrency() throws SQLException { if (statement == null) return java.sql.ResultSet.CONCUR_READ_ONLY; return statement.getResultSetConcurrency(); } public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException { // If I read the specs, this should use cal only if we don't // store the timezone, and if we do, then act just like getDate()? // for now... return getDate(i); } public Time getTime(int i, java.util.Calendar cal) throws SQLException { // If I read the specs, this should use cal only if we don't // store the timezone, and if we do, then act just like getTime()? // for now... return getTime(i); } public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException { // If I read the specs, this should use cal only if we don't // store the timezone, and if we do, then act just like getDate()? // for now... return getTimestamp(i); } public java.sql.Date getDate(String c, java.util.Calendar cal) throws SQLException { return getDate(findColumn(c), cal); } public Time getTime(String c, java.util.Calendar cal) throws SQLException { return getTime(findColumn(c), cal); } public Timestamp getTimestamp(String c, java.util.Calendar cal) throws SQLException { return getTimestamp(findColumn(c), cal); } public int getFetchDirection() throws SQLException { //PostgreSQL normally sends rows first->last return java.sql.ResultSet.FETCH_FORWARD; } public int getFetchSize() throws SQLException { // Returning the current batch size seems the right thing to do. return rows.size(); } public Object getObject(String columnName, java.util.Map map) throws SQLException { return getObject(findColumn(columnName), map); } /* * This checks against map for the type of column i, and if found returns * an object based on that mapping. The class must implement the SQLData * interface. */ public Object getObject(int i, java.util.Map map) throws SQLException { throw org.postgresql.Driver.notImplemented(); } public Ref getRef(String columnName) throws SQLException { return getRef(findColumn(columnName)); } public Ref getRef(int i) throws SQLException { //The backend doesn't yet have SQL3 REF types throw new PSQLException("postgresql.psqlnotimp"); } public int getRow() throws SQLException { final int rows_size = rows.size(); if (current_row < 0 || current_row >= rows_size) return 0; return current_row + 1; } // This one needs some thought, as not all ResultSets come from a statement public Statement getStatement() throws SQLException { return (Statement) statement; } public int getType() throws SQLException { // This implementation allows scrolling but is not able to // see any changes. Sub-classes may overide this to return a more // meaningful result. return java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; } public boolean isAfterLast() throws SQLException { final int rows_size = rows.size(); return (current_row >= rows_size && rows_size > 0); } public boolean isBeforeFirst() throws SQLException { return (current_row < 0 && rows.size() > 0); } public boolean isFirst() throws SQLException { return (current_row == 0 && rows.size() >= 0); } public boolean isLast() throws SQLException { final int rows_size = rows.size(); return (current_row == rows_size - 1 && rows_size > 0); } public boolean last() throws SQLException { final int rows_size = rows.size(); if (rows_size <= 0) return false; current_row = rows_size - 1; this_row = (byte[][]) rows.elementAt(current_row); rowBuffer = new byte[this_row.length][]; System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length); return true; } public boolean previous() throws SQLException { if (--current_row < 0) return false; this_row = (byte[][]) rows.elementAt(current_row); System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length); return true; } public boolean relative(int rows) throws SQLException { //have to add 1 since absolute expects a 1-based index return absolute(current_row + 1 + rows); } public void setFetchDirection(int direction) throws SQLException { throw new PSQLException("postgresql.psqlnotimp"); } public void setFetchSize(int rows) throws SQLException { // Sub-classes should implement this as part of their cursor support throw org.postgresql.Driver.notImplemented(); } public synchronized void cancelRowUpdates() throws SQLException { if (doingUpdates) { doingUpdates = false; clearRowBuffer(); } } public synchronized void deleteRow() throws SQLException { if ( !isUpdateable() ) { throw new PSQLException( "postgresql.updateable.notupdateable" ); } if (onInsertRow) { throw new PSQLException( "postgresql.updateable.oninsertrow" ); } if (rows.size() == 0) { throw new PSQLException( "postgresql.updateable.emptydelete" ); } if (isBeforeFirst()) { throw new PSQLException( "postgresql.updateable.beforestartdelete" ); } if (isAfterLast()) { throw new PSQLException( "postgresql.updateable.afterlastdelete" ); } int numKeys = primaryKeys.size(); if ( deleteStatement == null ) { StringBuffer deleteSQL = new StringBuffer("DELETE FROM " ).append(tableName).append(" where " ); for ( int i = 0; i < numKeys; i++ ) { deleteSQL.append( ((PrimaryKey) primaryKeys.get(i)).name ).append( " = ? " ); if ( i < numKeys - 1 ) { deleteSQL.append( " and " ); } } deleteStatement = ((java.sql.Connection) connection).prepareStatement(deleteSQL.toString()); } deleteStatement.clearParameters(); for ( int i = 0; i < numKeys; i++ ) { deleteStatement.setObject(i + 1, ((PrimaryKey) primaryKeys.get(i)).getValue()); } deleteStatement.executeUpdate(); rows.removeElementAt(current_row); } public synchronized void insertRow() throws SQLException { if ( !isUpdateable() ) { throw new PSQLException( "postgresql.updateable.notupdateable" ); } if (!onInsertRow) { throw new PSQLException( "postgresql.updateable.notoninsertrow" ); } else { // loop through the keys in the insertTable and create the sql statement // we have to create the sql every time since the user could insert different // columns each time StringBuffer insertSQL = new StringBuffer("INSERT INTO ").append(tableName).append(" ("); StringBuffer paramSQL = new StringBuffer(") values (" ); Enumeration columnNames = updateValues.keys(); int numColumns = updateValues.size(); for ( int i = 0; columnNames.hasMoreElements(); i++ ) { String columnName = (String) columnNames.nextElement(); insertSQL.append( columnName ); if ( i < numColumns - 1 ) { insertSQL.append(", "); paramSQL.append("?,"); } else { paramSQL.append("?)"); } } insertSQL.append(paramSQL.toString()); insertStatement = ((java.sql.Connection) connection).prepareStatement(insertSQL.toString()); Enumeration keys = updateValues.keys(); for ( int i = 1; keys.hasMoreElements(); i++) { String key = (String) keys.nextElement(); Object o = updateValues.get(key); if (o instanceof NullObject) insertStatement.setNull(i,java.sql.Types.NULL); else insertStatement.setObject(i, o); } insertStatement.executeUpdate(); if ( usingOID ) { // we have to get the last inserted OID and put it in the resultset long insertedOID = ((AbstractJdbc2Statement) insertStatement).getLastOID(); updateValues.put("oid", new Long(insertedOID) ); } // update the underlying row to the new inserted data updateRowBuffer(); rows.addElement(rowBuffer); // we should now reflect the current data in this_row // that way getXXX will get the newly inserted data this_row = rowBuffer; // need to clear this in case of another insert clearRowBuffer(); } } public synchronized void moveToCurrentRow() throws SQLException { if (!updateable) { throw new PSQLException( "postgresql.updateable.notupdateable" ); } if (current_row < 0) { this_row = null; rowBuffer = null; } else { this_row = (byte[][]) rows.elementAt(current_row); rowBuffer = new byte[this_row.length][]; System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length); } onInsertRow = false; doingUpdates = false; } public synchronized void moveToInsertRow() throws SQLException { if ( !isUpdateable() ) { throw new PSQLException( "postgresql.updateable.notupdateable" ); } if (insertStatement != null) { insertStatement = null; } // make sure the underlying data is null clearRowBuffer(); onInsertRow = true; doingUpdates = false; } private synchronized void clearRowBuffer() throws SQLException { // rowBuffer is the temporary storage for the row rowBuffer = new byte[fields.length][]; // clear the updateValues hashTable for the next set of updates updateValues.clear(); } public boolean rowDeleted() throws SQLException { // only sub-classes implement CONCURuPDATEABLE throw Driver.notImplemented(); } public boolean rowInserted() throws SQLException { // only sub-classes implement CONCURuPDATEABLE throw Driver.notImplemented(); } public boolean rowUpdated() throws SQLException { // only sub-classes implement CONCURuPDATEABLE throw Driver.notImplemented(); } public synchronized void updateAsciiStream(int columnIndex, java.io.InputStream x, int length ) throws SQLException { 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); } updateValue(columnIndex, theData); } public synchronized void updateBigDecimal(int columnIndex, java.math.BigDecimal x ) throws SQLException { updateValue(columnIndex, x); } 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); } updateValue(columnIndex, theData); } public synchronized void updateBoolean(int columnIndex, boolean x) throws SQLException { if ( Driver.logDebug ) Driver.debug("updating boolean " + fields[columnIndex - 1].getName() + "=" + x); updateValue(columnIndex, new Boolean(x)); } public synchronized void updateByte(int columnIndex, byte x) throws SQLException { updateValue(columnIndex, String.valueOf(x)); } public synchronized void updateBytes(int columnIndex, byte[] x) throws SQLException { updateValue(columnIndex, x); } 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); } updateValue(columnIndex, theData); } public synchronized void updateDate(int columnIndex, java.sql.Date x) throws SQLException { updateValue(columnIndex, x); } public synchronized void updateDouble(int columnIndex, double x) throws SQLException { if ( Driver.logDebug ) Driver.debug("updating double " + fields[columnIndex - 1].getName() + "=" + x); updateValue(columnIndex, 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); updateValue(columnIndex, 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); updateValue(columnIndex, 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); updateValue(columnIndex, new Long(x)); } public synchronized void updateNull(int columnIndex) throws SQLException { updateValue(columnIndex, new NullObject()); } public synchronized void updateObject(int columnIndex, Object x) throws SQLException { if ( Driver.logDebug ) Driver.debug("updating object " + fields[columnIndex - 1].getName() + " = " + x); updateValue(columnIndex, x); } public synchronized void updateObject(int columnIndex, Object x, int scale) throws SQLException { if ( !isUpdateable() ) { throw new PSQLException( "postgresql.updateable.notupdateable" ); } this.updateObject(columnIndex, x); } public void refreshRow() throws SQLException { if ( !isUpdateable() ) { throw new PSQLException( "postgresql.updateable.notupdateable" ); } try { StringBuffer selectSQL = new StringBuffer( "select "); final int numColumns = java.lang.reflect.Array.getLength(fields); for (int i = 0; i < numColumns; i++ ) { selectSQL.append( fields[i].getName() ); if ( i < numColumns - 1 ) { selectSQL.append(", "); } } selectSQL.append(" from " ).append(tableName).append(" where "); int numKeys = primaryKeys.size(); for ( int i = 0; i < numKeys; i++ ) { PrimaryKey primaryKey = ((PrimaryKey) primaryKeys.get(i)); selectSQL.append(primaryKey.name).append("= ?"); if ( i < numKeys - 1 ) { selectSQL.append(" and "); } } if ( Driver.logDebug ) Driver.debug("selecting " + selectSQL.toString()); selectStatement = ((java.sql.Connection) connection).prepareStatement(selectSQL.toString()); for ( int j = 0, i = 1; j < numKeys; j++, i++) { selectStatement.setObject( i, ((PrimaryKey) primaryKeys.get(j)).getValue() ); } AbstractJdbc2ResultSet rs = (AbstractJdbc2ResultSet) selectStatement.executeQuery(); if ( rs.first() ) { rowBuffer = rs.rowBuffer; } rows.setElementAt( rowBuffer, current_row ); this_row = rowBuffer; if ( Driver.logDebug ) Driver.debug("done updates"); rs.close(); selectStatement.close(); selectStatement = null; } catch (Exception e) { if ( Driver.logDebug ) Driver.debug(e.getClass().getName() + e); throw new SQLException( e.getMessage() ); } } public synchronized void updateRow() throws SQLException { if ( !isUpdateable() ) { throw new PSQLException( "postgresql.updateable.notupdateable" ); } if (doingUpdates) { try { StringBuffer updateSQL = new StringBuffer("UPDATE " + tableName + " SET "); int numColumns = updateValues.size(); Enumeration columns = updateValues.keys(); for (int i = 0; columns.hasMoreElements(); i++ ) { String column = (String) columns.nextElement(); updateSQL.append("\""); updateSQL.append( column ); updateSQL.append("\" = ?"); 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("\""); updateSQL.append(primaryKey.name); updateSQL.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++) { Object o = iterator.next(); if (o instanceof NullObject) updateStatement.setNull(i+1,java.sql.Types.NULL); else updateStatement.setObject( i + 1, o ); } 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"); updateValues.clear(); 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); updateValue(columnIndex, 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); updateValue(columnIndex, x); } public synchronized void updateTime(int columnIndex, Time x) throws SQLException { if ( Driver.logDebug ) Driver.debug("in update Time " + fields[columnIndex - 1].getName() + " = " + x); updateValue(columnIndex, x); } public synchronized void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { if ( Driver.logDebug ) Driver.debug("updating Timestamp " + fields[columnIndex - 1].getName() + " = " + x); updateValue(columnIndex, x); } public synchronized void updateNull(String columnName) throws SQLException { updateNull(findColumn(columnName)); } public synchronized void updateBoolean(String columnName, boolean x) throws SQLException { updateBoolean(findColumn(columnName), x); } public synchronized void updateByte(String columnName, byte x) throws SQLException { updateByte(findColumn(columnName), x); } public synchronized void updateShort(String columnName, short x) throws SQLException { updateShort(findColumn(columnName), x); } public synchronized void updateInt(String columnName, int x) throws SQLException { updateInt(findColumn(columnName), x); } public synchronized void updateLong(String columnName, long x) throws SQLException { updateLong(findColumn(columnName), x); } public synchronized void updateFloat(String columnName, float x) throws SQLException { updateFloat(findColumn(columnName), x); } public synchronized void updateDouble(String columnName, double x) throws SQLException { updateDouble(findColumn(columnName), x); } public synchronized void updateBigDecimal(String columnName, BigDecimal x) throws SQLException { updateBigDecimal(findColumn(columnName), x); } public synchronized void updateString(String columnName, String x) throws SQLException { updateString(findColumn(columnName), x); } public synchronized void updateBytes(String columnName, byte x[]) throws SQLException { updateBytes(findColumn(columnName), x); } public synchronized void updateDate(String columnName, java.sql.Date x) throws SQLException { updateDate(findColumn(columnName), x); } public synchronized void updateTime(String columnName, java.sql.Time x) throws SQLException { updateTime(findColumn(columnName), x); } public synchronized void updateTimestamp(String columnName, java.sql.Timestamp x) throws SQLException { updateTimestamp(findColumn(columnName), x); } public synchronized void updateAsciiStream( String columnName, java.io.InputStream x, int length) throws SQLException { updateAsciiStream(findColumn(columnName), x, length); } public synchronized void updateBinaryStream( String columnName, java.io.InputStream x, int length) throws SQLException { updateBinaryStream(findColumn(columnName), x, length); } public synchronized void updateCharacterStream( String columnName, java.io.Reader reader, int length) throws SQLException { updateCharacterStream(findColumn(columnName), reader, length); } public synchronized void updateObject(String columnName, Object x, int scale) throws SQLException { updateObject(findColumn(columnName), x); } public synchronized void updateObject(String columnName, Object x) throws SQLException { updateObject(findColumn(columnName), x); } /** * 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 = 0; try { oidIndex = findColumn( "oid" ); } catch (SQLException l_se) { //Ignore if column oid isn't selected } int i = 0; // if we find the oid then just use it //oidIndex will be >0 if the oid was in the select list 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 String[] s = quotelessTableName(tableName); String quotelessTableName = s[0]; String quotelessSchemaName = s[1]; java.sql.ResultSet rs = ((java.sql.Connection) connection).getMetaData().getPrimaryKeys("", quotelessSchemaName, quotelessTableName); 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; } /** Cracks out the table name and schema (if it exists) from a fully * qualified table name. * @param fullname string that we are trying to crack. Test cases:
* Table: table () * "Table": Table () * Schema.Table: table (schema) * "Schema"."Table": Table (Schema) * "Schema"."Dot.Table": Dot.Table (Schema) * Schema."Dot.Table": Dot.Table (schema) ** @return String array with element zero always being the tablename and * element 1 the schema name which may be a zero length string. */ public static String[] quotelessTableName(String fullname) { StringBuffer buf = new StringBuffer(fullname); String[] parts = new String[] {null, ""}; StringBuffer acc = new StringBuffer(); boolean betweenQuotes = false; for (int i = 0; i < buf.length(); i++) { char c = buf.charAt(i); switch (c) { case '"': if ((i < buf.length() - 1) && (buf.charAt(i+1) == '"')) { // two consecutive quotes - keep one i++; acc.append(c); // keep the quote } else { // Discard it betweenQuotes = !betweenQuotes; } break; case '.': if (betweenQuotes) { // Keep it acc.append(c); } else { // Have schema name parts[1] = acc.toString(); acc = new StringBuffer(); } break; default: acc.append((betweenQuotes) ? c : Character.toLowerCase(c)); break; } } // Always put table in slot 0 parts[0] = acc.toString(); return parts; } public void parseQuery() { String[] l_sqlFragments = ((AbstractJdbc2Statement)statement).getSqlFragments(); String l_sql = l_sqlFragments[0]; StringTokenizer st = new StringTokenizer(l_sql, " \r\t\n"); 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; Object valueObject = updateValues.get(columnName); if (valueObject instanceof NullObject) { rowBuffer[columnIndex] = null; } else { 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: rowBuffer[columnIndex] = connection.getEncoding().encode(String.valueOf( valueObject)); case Types.NULL: continue; default: rowBuffer[columnIndex] = (byte[]) valueObject; } } } } public void setStatement(BaseStatement statement) { this.statement = statement; } protected void updateValue(int columnIndex, Object value) throws SQLException { if ( !isUpdateable() ) { throw new PSQLException( "postgresql.updateable.notupdateable" ); } doingUpdates = !onInsertRow; if (value == null) updateNull(columnIndex); else updateValues.put(fields[columnIndex - 1].getName(), value); } private class PrimaryKey { int index; // where in the result set is this primaryKey String name; // what is the columnName of this primary Key PrimaryKey( int index, String name) { this.index = index; this.name = name; } Object getValue() throws SQLException { return getObject(index); } }; class NullObject { }; }