summaryrefslogtreecommitdiff
path: root/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java')
-rw-r--r--src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java2065
1 files changed, 1333 insertions, 732 deletions
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java
index d2c5ee0760..1a6379bf59 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java
@@ -6,747 +6,1348 @@ import java.io.*;
import java.sql.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
-import java.util.Vector;
+import java.util.*;
+import org.postgresql.Driver;
import org.postgresql.Field;
import org.postgresql.core.Encoding;
import org.postgresql.largeobject.*;
import org.postgresql.util.PGbytea;
import org.postgresql.util.PSQLException;
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2ResultSet.java,v 1.2 2002/07/24 22:08:42 barry Exp $
+
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2ResultSet.java,v 1.3 2002/07/25 22:45:28 barry Exp $
* This class defines methods of the jdbc2 specification. This class extends
* org.postgresql.jdbc1.AbstractJdbc1ResultSet which provides the jdbc1
* methods. The real Statement class (for jdbc2) is org.postgresql.jdbc2.Jdbc2ResultSet
*/
-public class AbstractJdbc2ResultSet extends org.postgresql.jdbc1.AbstractJdbc1ResultSet
-{
- protected Statement statement;
-
- protected String sqlQuery=null;
-
- public AbstractJdbc2ResultSet(org.postgresql.PGConnection conn, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor)
- {
- super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
- }
-
- public java.net.URL getURL(int columnIndex) throws SQLException
- {
- return null;
- }
-
- public java.net.URL getURL(String columnName) throws SQLException
- {
- return null;
- }
-
- /*
- * Get the value of a column in the current row as a Java object
- *
- * <p>This method will return the value of the given column as a
- * Java object. The type of the Java object will be the default
- * Java Object type corresponding to the column's SQL type, following
- * the mapping specified in the JDBC specification.
- *
- * <p>This method may also be used to read database specific abstract
- * data types.
- *
- * @param columnIndex the first column is 1, the second is 2...
- * @return a Object holding the column value
- * @exception SQLException if a database access error occurs
- */
- public Object getObject(int columnIndex) throws SQLException
- {
- Field field;
-
- checkResultSet( columnIndex );
-
- wasNullFlag = (this_row[columnIndex - 1] == null);
- if (wasNullFlag)
- return null;
-
- field = fields[columnIndex - 1];
-
- // some fields can be null, mainly from those returned by MetaData methods
- if (field == null)
- {
- wasNullFlag = true;
- return null;
- }
-
- switch (field.getSQLType())
- {
- case Types.BIT:
- return getBoolean(columnIndex) ? Boolean.TRUE : Boolean.FALSE;
- case Types.SMALLINT:
- return new Short(getShort(columnIndex));
- case Types.INTEGER:
- return new Integer(getInt(columnIndex));
- case Types.BIGINT:
- return new Long(getLong(columnIndex));
- case Types.NUMERIC:
- return getBigDecimal
- (columnIndex, (field.getMod() == -1) ? -1 : ((field.getMod() - 4) & 0xffff));
- case Types.REAL:
- return new Float(getFloat(columnIndex));
- case Types.DOUBLE:
- return new Double(getDouble(columnIndex));
- case Types.CHAR:
- case Types.VARCHAR:
- return getString(columnIndex);
- case Types.DATE:
- return getDate(columnIndex);
- case Types.TIME:
- return getTime(columnIndex);
- case Types.TIMESTAMP:
- return getTimestamp(columnIndex);
- case Types.BINARY:
- case Types.VARBINARY:
- return getBytes(columnIndex);
- case Types.ARRAY:
- return getArray(columnIndex);
- default:
- String type = field.getPGType();
- // if the backend doesn't know the type then coerce to String
- if (type.equals("unknown"))
- {
- return getString(columnIndex);
- }
- else
- {
- return connection.getObject(field.getPGType(), getString(columnIndex));
- }
- }
- }
-
- public boolean absolute(int index) throws SQLException
- {
- // index is 1-based, but internally we use 0-based indices
- int internalIndex;
-
- if (index == 0)
- throw new SQLException("Cannot move to index of 0");
-
- final int rows_size = rows.size();
-
- //if index<0, count from the end of the result set, but check
- //to be sure that it is not beyond the first index
- if (index < 0)
- {
- if (index >= -rows_size)
- internalIndex = rows_size + index;
- else
- {
- beforeFirst();
- return false;
- }
- }
- else
- {
- //must be the case that index>0,
- //find the correct place, assuming that
- //the index is not too large
- if (index <= rows_size)
- internalIndex = index - 1;
- else
- {
- afterLast();
- return false;
- }
- }
-
- current_row = internalIndex;
- this_row = (byte [][])rows.elementAt(internalIndex);
- return true;
- }
-
- public void afterLast() throws SQLException
- {
- final int rows_size = rows.size();
- if (rows_size > 0)
- current_row = rows_size;
- }
-
- public void beforeFirst() throws SQLException
- {
- if (rows.size() > 0)
- current_row = -1;
- }
-
- public void cancelRowUpdates() throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void deleteRow() throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public boolean first() throws SQLException
- {
- if (rows.size() <= 0)
- return false;
-
- current_row = 0;
- this_row = (byte [][])rows.elementAt(current_row);
-
- rowBuffer=new byte[this_row.length][];
- System.arraycopy(this_row,0,rowBuffer,0,this_row.length);
-
- return true;
- }
-
- public java.sql.Array getArray(String colName) throws SQLException
- {
- return getArray(findColumn(colName));
- }
-
- public java.sql.Array getArray(int i) throws SQLException
- {
- wasNullFlag = (this_row[i - 1] == null);
- if (wasNullFlag)
- return null;
-
- if (i < 1 || i > fields.length)
- throw new PSQLException("postgresql.res.colrange");
- return (java.sql.Array) new org.postgresql.jdbc2.Array( connection, i, fields[i - 1], (java.sql.ResultSet)this );
- }
-
- public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException
- {
- return getBigDecimal(columnIndex, -1);
- }
-
- public java.math.BigDecimal getBigDecimal(String columnName) throws SQLException
- {
- return getBigDecimal(findColumn(columnName));
- }
-
- public Blob getBlob(String columnName) throws SQLException
- {
- return getBlob(findColumn(columnName));
- }
-
- public Blob getBlob(int i) throws SQLException
- {
- return new org.postgresql.largeobject.PGblob(connection, getInt(i));
- }
-
- public java.io.Reader getCharacterStream(String columnName) throws SQLException
- {
- return getCharacterStream(findColumn(columnName));
- }
-
- public java.io.Reader getCharacterStream(int i) throws SQLException
- {
- checkResultSet( i );
- wasNullFlag = (this_row[i - 1] == null);
- if (wasNullFlag)
- return null;
-
- if (((AbstractJdbc2Connection)connection).haveMinimumCompatibleVersion("7.2"))
- {
- //Version 7.2 supports AsciiStream for all the PG text types
- //As the spec/javadoc for this method indicate this is to be used for
- //large text values (i.e. LONGVARCHAR) PG doesn't have a separate
- //long string datatype, but with toast the text datatype is capable of
- //handling very large values. Thus the implementation ends up calling
- //getString() since there is no current way to stream the value from the server
- return new CharArrayReader(getString(i).toCharArray());
- }
- else
- {
- // In 7.1 Handle as BLOBS so return the LargeObject input stream
- Encoding encoding = connection.getEncoding();
- InputStream input = getBinaryStream(i);
- return encoding.getDecodingReader(input);
- }
- }
-
- public Clob getClob(String columnName) throws SQLException
- {
- return getClob(findColumn(columnName));
- }
-
- public Clob getClob(int i) throws SQLException
- {
- return new org.postgresql.largeobject.PGclob(connection, getInt(i));
- }
-
- public int getConcurrency() throws SQLException
- {
- // The standard ResultSet class will now return
- // CONCUR_READ_ONLY. A sub-class will overide this if the query was
- // updateable.
- return java.sql.ResultSet.CONCUR_READ_ONLY;
- }
-
- public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException
- {
- // If I read the specs, this should use cal only if we don't
- // store the timezone, and if we do, then act just like getDate()?
- // for now...
- return getDate(i);
- }
-
- public Time getTime(int i, java.util.Calendar cal) throws SQLException
- {
- // If I read the specs, this should use cal only if we don't
- // store the timezone, and if we do, then act just like getTime()?
- // for now...
- return getTime(i);
- }
-
- public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException
- {
- // If I read the specs, this should use cal only if we don't
- // store the timezone, and if we do, then act just like getDate()?
- // for now...
- return getTimestamp(i);
- }
-
- public java.sql.Date getDate(String c, java.util.Calendar cal) throws SQLException
- {
- return getDate(findColumn(c), cal);
- }
-
- public Time getTime(String c, java.util.Calendar cal) throws SQLException
- {
- return getTime(findColumn(c), cal);
- }
-
- public Timestamp getTimestamp(String c, java.util.Calendar cal) throws SQLException
- {
- return getTimestamp(findColumn(c), cal);
- }
-
- public int getFetchDirection() throws SQLException
- {
- //PostgreSQL normally sends rows first->last
- return java.sql.ResultSet.FETCH_FORWARD;
- }
-
- public int getFetchSize() throws SQLException
- {
- // In this implementation we return the entire result set, so
- // here return the number of rows we have. Sub-classes can return a proper
- // value
- return rows.size();
- }
-
- public Object getObject(String columnName, java.util.Map map) throws SQLException
- {
- return getObject(findColumn(columnName), map);
- }
-
- /*
- * This checks against map for the type of column i, and if found returns
- * an object based on that mapping. The class must implement the SQLData
- * interface.
- */
- public Object getObject(int i, java.util.Map map) throws SQLException
- {
- throw org.postgresql.Driver.notImplemented();
- }
-
- public Ref getRef(String columnName) throws SQLException
- {
- return getRef(findColumn(columnName));
- }
-
- public Ref getRef(int i) throws SQLException
- {
- //The backend doesn't yet have SQL3 REF types
- throw new PSQLException("postgresql.psqlnotimp");
- }
-
- public int getRow() throws SQLException
- {
- final int rows_size = rows.size();
-
- if (current_row < 0 || current_row >= rows_size)
- return 0;
-
- return current_row + 1;
- }
-
- // This one needs some thought, as not all ResultSets come from a statement
- public Statement getStatement() throws SQLException
- {
- return statement;
- }
-
- public int getType() throws SQLException
- {
- // This implementation allows scrolling but is not able to
- // see any changes. Sub-classes may overide this to return a more
- // meaningful result.
- return java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE;
- }
-
- public void insertRow() throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public boolean isAfterLast() throws SQLException
- {
- final int rows_size = rows.size();
- return (current_row >= rows_size && rows_size > 0);
- }
-
- public boolean isBeforeFirst() throws SQLException
- {
- return (current_row < 0 && rows.size() > 0);
- }
-
- public boolean isFirst() throws SQLException
- {
- return (current_row == 0 && rows.size() >= 0);
- }
-
- public boolean isLast() throws SQLException
- {
- final int rows_size = rows.size();
- return (current_row == rows_size - 1 && rows_size > 0);
- }
-
- public boolean last() throws SQLException
- {
- final int rows_size = rows.size();
- if (rows_size <= 0)
- return false;
-
- current_row = rows_size - 1;
- this_row = (byte [][])rows.elementAt(current_row);
-
- rowBuffer=new byte[this_row.length][];
- System.arraycopy(this_row,0,rowBuffer,0,this_row.length);
-
- return true;
- }
-
- public void moveToCurrentRow() throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void moveToInsertRow() throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public boolean previous() throws SQLException
- {
- if (--current_row < 0)
- return false;
- this_row = (byte [][])rows.elementAt(current_row);
- System.arraycopy(this_row,0,rowBuffer,0,this_row.length);
- return true;
- }
-
- public void refreshRow() throws SQLException
- {
- throw new PSQLException("postgresql.notsensitive");
- }
-
- public boolean relative(int rows) throws SQLException
- {
- //have to add 1 since absolute expects a 1-based index
- return absolute(current_row + 1 + rows);
- }
-
- public boolean rowDeleted() throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- return false; // javac complains about not returning a value!
- }
-
- public boolean rowInserted() throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- return false; // javac complains about not returning a value!
- }
-
- public boolean rowUpdated() throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- return false; // javac complains about not returning a value!
- }
-
- public void setFetchDirection(int direction) throws SQLException
- {
- throw new PSQLException("postgresql.psqlnotimp");
- }
-
- public void setFetchSize(int rows) throws SQLException
- {
- // Sub-classes should implement this as part of their cursor support
- throw org.postgresql.Driver.notImplemented();
- }
-
- public void updateAsciiStream(int columnIndex,
- java.io.InputStream x,
- int length
- ) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateAsciiStream(String columnName,
- java.io.InputStream x,
- int length
- ) throws SQLException
- {
- updateAsciiStream(findColumn(columnName), x, length);
- }
-
- public void updateBigDecimal(int columnIndex,
- java.math.BigDecimal x
- ) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateBigDecimal(String columnName,
- java.math.BigDecimal x
- ) throws SQLException
- {
- updateBigDecimal(findColumn(columnName), x);
- }
-
- public void updateBinaryStream(int columnIndex,
- java.io.InputStream x,
- int length
- ) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateBinaryStream(String columnName,
- java.io.InputStream x,
- int length
- ) throws SQLException
- {
- updateBinaryStream(findColumn(columnName), x, length);
- }
-
- public void updateBoolean(int columnIndex, boolean x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateBoolean(String columnName, boolean x) throws SQLException
- {
- updateBoolean(findColumn(columnName), x);
- }
-
- public void updateByte(int columnIndex, byte x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateByte(String columnName, byte x) throws SQLException
- {
- updateByte(findColumn(columnName), x);
- }
-
- public void updateBytes(String columnName, byte[] x) throws SQLException
- {
- updateBytes(findColumn(columnName), x);
- }
-
- public void updateBytes(int columnIndex, byte[] x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateCharacterStream(int columnIndex,
- java.io.Reader x,
- int length
- ) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateCharacterStream(String columnName,
- java.io.Reader x,
- int length
- ) throws SQLException
- {
- updateCharacterStream(findColumn(columnName), x, length);
- }
-
- public void updateDate(int columnIndex, java.sql.Date x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateDate(String columnName, java.sql.Date x) throws SQLException
- {
- updateDate(findColumn(columnName), x);
- }
-
- public void updateDouble(int columnIndex, double x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateDouble(String columnName, double x) throws SQLException
- {
- updateDouble(findColumn(columnName), x);
- }
-
- public void updateFloat(int columnIndex, float x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateFloat(String columnName, float x) throws SQLException
- {
- updateFloat(findColumn(columnName), x);
- }
-
- public void updateInt(int columnIndex, int x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateInt(String columnName, int x) throws SQLException
- {
- updateInt(findColumn(columnName), x);
- }
-
- public void updateLong(int columnIndex, long x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateLong(String columnName, long x) throws SQLException
- {
- updateLong(findColumn(columnName), x);
- }
-
- public void updateNull(int columnIndex) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateNull(String columnName) throws SQLException
- {
- updateNull(findColumn(columnName));
- }
-
- public void updateObject(int columnIndex, Object x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateObject(String columnName, Object x) throws SQLException
- {
- updateObject(findColumn(columnName), x);
- }
-
- public void updateObject(int columnIndex, Object x, int scale) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateObject(String columnName, Object x, int scale) throws SQLException
- {
- updateObject(findColumn(columnName), x, scale);
- }
-
- public void updateRow() throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateShort(int columnIndex, short x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateShort(String columnName, short x) throws SQLException
- {
- updateShort(findColumn(columnName), x);
- }
-
- public void updateString(int columnIndex, String x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateString(String columnName, String x) throws SQLException
- {
- updateString(findColumn(columnName), x);
- }
-
- public void updateTime(int columnIndex, Time x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateTime(String columnName, Time x) throws SQLException
- {
- updateTime(findColumn(columnName), x);
- }
-
- public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException
- {
- // only sub-classes implement CONCUR_UPDATEABLE
- notUpdateable();
- }
-
- public void updateTimestamp(String columnName, Timestamp x) throws SQLException
- {
- updateTimestamp(findColumn(columnName), x);
- }
-
- // helper method. Throws an SQLException when an update is not possible
- public void notUpdateable() throws SQLException
- {
- throw new PSQLException("postgresql.noupdate");
- }
-
- /*
- * It's used currently by getStatement() but may also with the new core
- * package.
- */
- public void setStatement(Statement statement)
- {
- this.statement = statement;
- }
-
- public void setSQLQuery(String sqlQuery) {
- this.sqlQuery=sqlQuery;
- }
+public class AbstractJdbc2ResultSet extends org.postgresql.jdbc1.AbstractJdbc1ResultSet {
+ protected String sqlQuery = null;
+
+ //needed for updateable result set support
+ protected boolean updateable = false;
+ protected boolean doingUpdates = false;
+ protected boolean onInsertRow = false;
+ protected Hashtable updateValues = new Hashtable();
+ private boolean usingOID = false; // are we using the OID for the primary key?
+ private Vector primaryKeys; // list of primary keys
+ private int numKeys = 0;
+ private boolean singleTable = false;
+ protected String tableName = null;
+ protected PreparedStatement updateStatement = null;
+ protected PreparedStatement insertStatement = null;
+ protected PreparedStatement deleteStatement = null;
+ private PreparedStatement selectStatement = null;
+
+
+
+ public AbstractJdbc2ResultSet(org.postgresql.PGConnection conn, Statement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) {
+ super (conn, statement, fields, tuples, status, updateCount, insertOID, binaryCursor);
+ }
+
+ public java.net.URL getURL(int columnIndex) throws SQLException {
+ return null;
+ }
+
+
+ public java.net.URL getURL(String columnName) throws SQLException {
+ return null;
+ }
+
+
+ /*
+ * Get the value of a column in the current row as a Java object
+ *
+ * <p>This method will return the value of the given column as a
+ * Java object. The type of the Java object will be the default
+ * Java Object type corresponding to the column's SQL type, following
+ * the mapping specified in the JDBC specification.
+ *
+ * <p>This method may also be used to read database specific abstract
+ * data types.
+ *
+ * @param columnIndex the first column is 1, the second is 2...
+ * @return a Object holding the column value
+ * @exception SQLException if a database access error occurs
+ */
+ public Object getObject(int columnIndex) throws SQLException {
+ Field field;
+
+ checkResultSet( columnIndex );
+
+ wasNullFlag = (this_row[columnIndex - 1] == null);
+ if (wasNullFlag)
+ return null;
+
+ field = fields[columnIndex - 1];
+
+ // some fields can be null, mainly from those returned by MetaData methods
+ if (field == null) {
+ wasNullFlag = true;
+ return null;
+ }
+
+ switch (field.getSQLType()) {
+ case Types.BIT:
+ return getBoolean(columnIndex) ? Boolean.TRUE : Boolean.FALSE;
+
+ case Types.SMALLINT:
+ return new Short(getShort(columnIndex));
+
+ case Types.INTEGER:
+ return new Integer(getInt(columnIndex));
+
+ case Types.BIGINT:
+ return new Long(getLong(columnIndex));
+
+ case Types.NUMERIC:
+ return getBigDecimal
+ (columnIndex, (field.getMod() == -1) ? -1 : ((field.getMod() - 4) & 0xffff));
+
+ case Types.REAL:
+ return new Float(getFloat(columnIndex));
+
+ case Types.DOUBLE:
+ return new Double(getDouble(columnIndex));
+
+ case Types.CHAR:
+ case Types.VARCHAR:
+ return getString(columnIndex);
+
+ case Types.DATE:
+ return getDate(columnIndex);
+
+ case Types.TIME:
+ return getTime(columnIndex);
+
+ case Types.TIMESTAMP:
+ return getTimestamp(columnIndex);
+
+ case Types.BINARY:
+ case Types.VARBINARY:
+ return getBytes(columnIndex);
+
+ case Types.ARRAY:
+ return getArray(columnIndex);
+
+ default:
+ String type = field.getPGType();
+ // if the backend doesn't know the type then coerce to String
+ if (type.equals("unknown")) {
+ return getString(columnIndex);
+ }
+ else {
+ return connection.getObject(field.getPGType(), getString(columnIndex));
+ }
+ }
+ }
+
+
+ public boolean absolute(int index) throws SQLException {
+ // index is 1-based, but internally we use 0-based indices
+ int internalIndex;
+
+ if (index == 0)
+ throw new SQLException("Cannot move to index of 0");
+
+ final int rows_size = rows.size();
+
+ //if index<0, count from the end of the result set, but check
+ //to be sure that it is not beyond the first index
+ if (index < 0) {
+ if (index >= -rows_size)
+ internalIndex = rows_size + index;
+ else {
+ beforeFirst();
+ return false;
+ }
+ }
+ else {
+ //must be the case that index>0,
+ //find the correct place, assuming that
+ //the index is not too large
+ if (index <= rows_size)
+ internalIndex = index - 1;
+ else {
+ afterLast();
+ return false;
+ }
+ }
+
+ current_row = internalIndex;
+ this_row = (byte[][]) rows.elementAt(internalIndex);
+ return true;
+ }
+
+
+ public void afterLast() throws SQLException {
+ final int rows_size = rows.size();
+ if (rows_size > 0)
+ current_row = rows_size;
+ }
+
+
+ public void beforeFirst() throws SQLException {
+ if (rows.size() > 0)
+ current_row = -1;
+ }
+
+
+ public boolean first() throws SQLException {
+ if (rows.size() <= 0)
+ return false;
+
+ current_row = 0;
+ this_row = (byte[][]) rows.elementAt(current_row);
+
+ rowBuffer = new byte[this_row.length][];
+ System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length);
+
+ return true;
+ }
+
+
+ public java.sql.Array getArray(String colName) throws SQLException {
+ return getArray(findColumn(colName));
+ }
+
+
+ public java.sql.Array getArray(int i) throws SQLException {
+ wasNullFlag = (this_row[i - 1] == null);
+ if (wasNullFlag)
+ return null;
+
+ if (i < 1 || i > fields.length)
+ throw new PSQLException("postgresql.res.colrange");
+ return (java.sql.Array) new org.postgresql.jdbc2.Array( connection, i, fields[i - 1], (java.sql.ResultSet) this );
+ }
+
+
+ public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException {
+ return getBigDecimal(columnIndex, -1);
+ }
+
+
+ public java.math.BigDecimal getBigDecimal(String columnName) throws SQLException {
+ return getBigDecimal(findColumn(columnName));
+ }
+
+
+ public Blob getBlob(String columnName) throws SQLException {
+ return getBlob(findColumn(columnName));
+ }
+
+
+ public Blob getBlob(int i) throws SQLException {
+ return new org.postgresql.largeobject.PGblob(connection, getInt(i));
+ }
+
+
+ public java.io.Reader getCharacterStream(String columnName) throws SQLException {
+ return getCharacterStream(findColumn(columnName));
+ }
+
+
+ public java.io.Reader getCharacterStream(int i) throws SQLException {
+ checkResultSet( i );
+ wasNullFlag = (this_row[i - 1] == null);
+ if (wasNullFlag)
+ return null;
+
+ if (((AbstractJdbc2Connection) connection).haveMinimumCompatibleVersion("7.2")) {
+ //Version 7.2 supports AsciiStream for all the PG text types
+ //As the spec/javadoc for this method indicate this is to be used for
+ //large text values (i.e. LONGVARCHAR) PG doesn't have a separate
+ //long string datatype, but with toast the text datatype is capable of
+ //handling very large values. Thus the implementation ends up calling
+ //getString() since there is no current way to stream the value from the server
+ return new CharArrayReader(getString(i).toCharArray());
+ }
+ else {
+ // In 7.1 Handle as BLOBS so return the LargeObject input stream
+ Encoding encoding = connection.getEncoding();
+ InputStream input = getBinaryStream(i);
+ return encoding.getDecodingReader(input);
+ }
+ }
+
+
+ public Clob getClob(String columnName) throws SQLException {
+ return getClob(findColumn(columnName));
+ }
+
+
+ public Clob getClob(int i) throws SQLException {
+ return new org.postgresql.largeobject.PGclob(connection, getInt(i));
+ }
+
+
+ public int getConcurrency() throws SQLException {
+ if (statement == null)
+ return java.sql.ResultSet.CONCUR_READ_ONLY;
+ return statement.getResultSetConcurrency();
+ }
+
+
+ public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException {
+ // If I read the specs, this should use cal only if we don't
+ // store the timezone, and if we do, then act just like getDate()?
+ // for now...
+ return getDate(i);
+ }
+
+
+ public Time getTime(int i, java.util.Calendar cal) throws SQLException {
+ // If I read the specs, this should use cal only if we don't
+ // store the timezone, and if we do, then act just like getTime()?
+ // for now...
+ return getTime(i);
+ }
+
+
+ public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException {
+ // If I read the specs, this should use cal only if we don't
+ // store the timezone, and if we do, then act just like getDate()?
+ // for now...
+ return getTimestamp(i);
+ }
+
+
+ public java.sql.Date getDate(String c, java.util.Calendar cal) throws SQLException {
+ return getDate(findColumn(c), cal);
+ }
+
+
+ public Time getTime(String c, java.util.Calendar cal) throws SQLException {
+ return getTime(findColumn(c), cal);
+ }
+
+
+ public Timestamp getTimestamp(String c, java.util.Calendar cal) throws SQLException {
+ return getTimestamp(findColumn(c), cal);
+ }
+
+
+ public int getFetchDirection() throws SQLException {
+ //PostgreSQL normally sends rows first->last
+ return java.sql.ResultSet.FETCH_FORWARD;
+ }
+
+
+ public int getFetchSize() throws SQLException {
+ // In this implementation we return the entire result set, so
+ // here return the number of rows we have. Sub-classes can return a proper
+ // value
+ return rows.size();
+ }
+
+
+ public Object getObject(String columnName, java.util.Map map) throws SQLException {
+ return getObject(findColumn(columnName), map);
+ }
+
+
+ /*
+ * This checks against map for the type of column i, and if found returns
+ * an object based on that mapping. The class must implement the SQLData
+ * interface.
+ */
+ public Object getObject(int i, java.util.Map map) throws SQLException {
+ throw org.postgresql.Driver.notImplemented();
+ }
+
+
+ public Ref getRef(String columnName) throws SQLException {
+ return getRef(findColumn(columnName));
+ }
+
+
+ public Ref getRef(int i) throws SQLException {
+ //The backend doesn't yet have SQL3 REF types
+ throw new PSQLException("postgresql.psqlnotimp");
+ }
+
+
+ public int getRow() throws SQLException {
+ final int rows_size = rows.size();
+
+ if (current_row < 0 || current_row >= rows_size)
+ return 0;
+
+ return current_row + 1;
+ }
+
+
+ // This one needs some thought, as not all ResultSets come from a statement
+ public Statement getStatement() throws SQLException {
+ return statement;
+ }
+
+
+ public int getType() throws SQLException {
+ // This implementation allows scrolling but is not able to
+ // see any changes. Sub-classes may overide this to return a more
+ // meaningful result.
+ return java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE;
+ }
+
+
+ public boolean isAfterLast() throws SQLException {
+ final int rows_size = rows.size();
+ return (current_row >= rows_size && rows_size > 0);
+ }
+
+
+ public boolean isBeforeFirst() throws SQLException {
+ return (current_row < 0 && rows.size() > 0);
+ }
+
+
+ public boolean isFirst() throws SQLException {
+ return (current_row == 0 && rows.size() >= 0);
+ }
+
+
+ public boolean isLast() throws SQLException {
+ final int rows_size = rows.size();
+ return (current_row == rows_size - 1 && rows_size > 0);
+ }
+
+
+ public boolean last() throws SQLException {
+ final int rows_size = rows.size();
+ if (rows_size <= 0)
+ return false;
+
+ current_row = rows_size - 1;
+ this_row = (byte[][]) rows.elementAt(current_row);
+
+ rowBuffer = new byte[this_row.length][];
+ System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length);
+
+ return true;
+ }
+
+
+ public boolean previous() throws SQLException {
+ if (--current_row < 0)
+ return false;
+ this_row = (byte[][]) rows.elementAt(current_row);
+ System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length);
+ return true;
+ }
+
+
+ public boolean relative(int rows) throws SQLException {
+ //have to add 1 since absolute expects a 1-based index
+ return absolute(current_row + 1 + rows);
+ }
+
+
+ public void setFetchDirection(int direction) throws SQLException {
+ throw new PSQLException("postgresql.psqlnotimp");
+ }
+
+
+ public void setFetchSize(int rows) throws SQLException {
+ // Sub-classes should implement this as part of their cursor support
+ throw org.postgresql.Driver.notImplemented();
+ }
+
+
+ public synchronized void cancelRowUpdates() throws SQLException {
+ if (doingUpdates) {
+ doingUpdates = false;
+
+ clearRowBuffer();
+ }
+ }
+
+
+ public synchronized void deleteRow() throws SQLException {
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ if (onInsertRow) {
+ throw new PSQLException( "postgresql.updateable.oninsertrow" );
+ }
+
+ if (rows.size() == 0) {
+ throw new PSQLException( "postgresql.updateable.emptydelete" );
+ }
+ if (isBeforeFirst()) {
+ throw new PSQLException( "postgresql.updateable.beforestartdelete" );
+ }
+ if (isAfterLast()) {
+ throw new PSQLException( "postgresql.updateable.afterlastdelete" );
+ }
+
+
+ int numKeys = primaryKeys.size();
+ if ( deleteStatement == null ) {
+
+
+ StringBuffer deleteSQL = new StringBuffer("DELETE FROM " ).append(tableName).append(" where " );
+
+ for ( int i = 0; i < numKeys; i++ ) {
+ deleteSQL.append( ((PrimaryKey) primaryKeys.get(i)).name ).append( " = ? " );
+ if ( i < numKeys - 1 ) {
+ deleteSQL.append( " and " );
+ }
+ }
+
+ deleteStatement = ((java.sql.Connection) connection).prepareStatement(deleteSQL.toString());
+ }
+ deleteStatement.clearParameters();
+
+ for ( int i = 0; i < numKeys; i++ ) {
+ deleteStatement.setObject(i + 1, ((PrimaryKey) primaryKeys.get(i)).getValue());
+ }
+
+
+ deleteStatement.executeUpdate();
+
+ rows.removeElementAt(current_row);
+ }
+
+
+ public synchronized void insertRow() throws SQLException {
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ if (!onInsertRow) {
+ throw new PSQLException( "postgresql.updateable.notoninsertrow" );
+ }
+ else {
+
+ // loop through the keys in the insertTable and create the sql statement
+ // we have to create the sql every time since the user could insert different
+ // columns each time
+
+ StringBuffer insertSQL = new StringBuffer("INSERT INTO ").append(tableName).append(" (");
+ StringBuffer paramSQL = new StringBuffer(") values (" );
+
+ Enumeration columnNames = updateValues.keys();
+ int numColumns = updateValues.size();
+
+ for ( int i = 0; columnNames.hasMoreElements(); i++ ) {
+ String columnName = (String) columnNames.nextElement();
+
+ insertSQL.append( columnName );
+ if ( i < numColumns - 1 ) {
+ insertSQL.append(", ");
+ paramSQL.append("?,");
+ }
+ else {
+ paramSQL.append("?)");
+ }
+
+ }
+
+ insertSQL.append(paramSQL.toString());
+ insertStatement = ((java.sql.Connection) connection).prepareStatement(insertSQL.toString());
+
+ Enumeration keys = updateValues.keys();
+
+ for ( int i = 1; keys.hasMoreElements(); i++) {
+ String key = (String) keys.nextElement();
+ insertStatement.setObject(i, updateValues.get( key ) );
+ }
+
+ insertStatement.executeUpdate();
+
+ if ( usingOID ) {
+ // we have to get the last inserted OID and put it in the resultset
+
+ long insertedOID = ((AbstractJdbc2Statement) insertStatement).getLastOID();
+
+ updateValues.put("oid", new Long(insertedOID) );
+
+ }
+
+ // update the underlying row to the new inserted data
+ updateRowBuffer();
+
+ rows.addElement(rowBuffer);
+
+ // we should now reflect the current data in this_row
+ // that way getXXX will get the newly inserted data
+ this_row = rowBuffer;
+
+ // need to clear this in case of another insert
+ clearRowBuffer();
+
+
+ }
+ }
+
+
+ public synchronized void moveToCurrentRow() throws SQLException {
+ if (!updateable) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ this_row = (byte[][]) rows.elementAt(current_row);
+
+ rowBuffer = new byte[this_row.length][];
+ System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length);
+
+ onInsertRow = false;
+ doingUpdates = false;
+ }
+
+
+ public synchronized void moveToInsertRow() throws SQLException {
+ if (!updateable) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ if (insertStatement != null) {
+ insertStatement = null;
+ }
+
+
+ // make sure the underlying data is null
+ clearRowBuffer();
+
+ onInsertRow = true;
+ doingUpdates = false;
+
+ }
+
+
+ private synchronized void clearRowBuffer() throws SQLException {
+ // rowBuffer is the temporary storage for the row
+ rowBuffer = new byte[fields.length][];
+
+ // clear the updateValues hashTable for the next set of updates
+ updateValues.clear();
+
+ }
+
+
+ public boolean rowDeleted() throws SQLException {
+ // only sub-classes implement CONCURuPDATEABLE
+ throw Driver.notImplemented();
+ }
+
+
+ public boolean rowInserted() throws SQLException {
+ // only sub-classes implement CONCURuPDATEABLE
+ throw Driver.notImplemented();
+ }
+
+
+ public boolean rowUpdated() throws SQLException {
+ // only sub-classes implement CONCURuPDATEABLE
+ throw Driver.notImplemented();
+ }
+
+
+ public synchronized void updateAsciiStream(int columnIndex,
+ java.io.InputStream x,
+ int length
+ ) throws SQLException {
+
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ byte[] theData = null;
+
+ try {
+ x.read(theData, 0, length);
+ }
+ catch (NullPointerException ex ) {
+ throw new PSQLException("postgresql.updateable.inputstream");
+ }
+ catch (IOException ie) {
+ throw new PSQLException("postgresql.updateable.ioerror" + ie);
+ }
+
+ doingUpdates = !onInsertRow;
+
+ updateValues.put( fields[columnIndex - 1].getName(), theData );
+
+ }
+
+
+ public synchronized void updateBigDecimal(int columnIndex,
+ java.math.BigDecimal x )
+ throws SQLException {
+
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), x );
+
+ }
+
+
+ public synchronized void updateBinaryStream(int columnIndex,
+ java.io.InputStream x,
+ int length
+ ) throws SQLException {
+
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ byte[] theData = null;
+
+ try {
+ x.read(theData, 0, length);
+
+ }
+ catch ( NullPointerException ex ) {
+ throw new PSQLException("postgresql.updateable.inputstream");
+ }
+ catch (IOException ie) {
+ throw new PSQLException("postgresql.updateable.ioerror" + ie);
+ }
+
+ doingUpdates = !onInsertRow;
+
+ updateValues.put( fields[columnIndex - 1].getName(), theData );
+
+ }
+
+
+ public synchronized void updateBoolean(int columnIndex, boolean x) throws SQLException {
+
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ if ( Driver.logDebug ) Driver.debug("updating boolean " + fields[columnIndex - 1].getName() + "=" + x);
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), new Boolean(x) );
+
+ }
+
+
+ public synchronized void updateByte(int columnIndex, byte x) throws SQLException {
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ doingUpdates = true;
+ updateValues.put( fields[columnIndex - 1].getName(), String.valueOf(x) );
+ }
+
+
+ public synchronized void updateBytes(int columnIndex, byte[] x) throws SQLException {
+
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), x );
+
+ }
+
+
+ public synchronized void updateCharacterStream(int columnIndex,
+ java.io.Reader x,
+ int length
+ ) throws SQLException {
+
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ char[] theData = null;
+
+ try {
+ x.read(theData, 0, length);
+
+ }
+ catch (NullPointerException ex) {
+ throw new PSQLException("postgresql.updateable.inputstream");
+ }
+ catch (IOException ie) {
+ throw new PSQLException("postgresql.updateable.ioerror" + ie);
+ }
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), theData);
+
+ }
+
+
+ public synchronized void updateDate(int columnIndex, java.sql.Date x) throws SQLException {
+
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), x );
+ }
+
+
+ public synchronized void updateDouble(int columnIndex, double x) throws SQLException {
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ if ( Driver.logDebug ) Driver.debug("updating double " + fields[columnIndex - 1].getName() + "=" + x);
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), new Double(x) );
+
+ }
+
+
+ public synchronized void updateFloat(int columnIndex, float x) throws SQLException {
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ if ( Driver.logDebug ) Driver.debug("updating float " + fields[columnIndex - 1].getName() + "=" + x);
+
+ doingUpdates = !onInsertRow;
+
+ updateValues.put( fields[columnIndex - 1].getName(), new Float(x) );
+
+ }
+
+
+ public synchronized void updateInt(int columnIndex, int x) throws SQLException {
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ if ( Driver.logDebug ) Driver.debug("updating int " + fields[columnIndex - 1].getName() + "=" + x);
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), new Integer(x) );
+
+ }
+
+
+ public synchronized void updateLong(int columnIndex, long x) throws SQLException {
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ if ( Driver.logDebug ) Driver.debug("updating long " + fields[columnIndex - 1].getName() + "=" + x);
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), new Long(x) );
+
+ }
+
+
+ public synchronized void updateNull(int columnIndex) throws SQLException {
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), null);
+
+
+ }
+
+
+ public synchronized void updateObject(int columnIndex, Object x) throws SQLException {
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ if ( Driver.logDebug ) Driver.debug("updating object " + fields[columnIndex - 1].getName() + " = " + x);
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), x );
+ }
+
+
+ public synchronized void updateObject(int columnIndex, Object x, int scale) throws SQLException {
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ this.updateObject(columnIndex, x);
+
+ }
+
+
+ public void refreshRow() throws SQLException {
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ try {
+ StringBuffer selectSQL = new StringBuffer( "select ");
+
+ final int numColumns = java.lang.reflect.Array.getLength(fields);
+
+ for (int i = 0; i < numColumns; i++ ) {
+
+ selectSQL.append( fields[i].getName() );
+
+ if ( i < numColumns - 1 ) {
+
+ selectSQL.append(", ");
+ }
+
+ }
+ selectSQL.append(" from " ).append(tableName).append(" where ");
+
+ int numKeys = primaryKeys.size();
+
+ for ( int i = 0; i < numKeys; i++ ) {
+
+ PrimaryKey primaryKey = ((PrimaryKey) primaryKeys.get(i));
+ selectSQL.append(primaryKey.name).append("= ?");
+
+ if ( i < numKeys - 1 ) {
+ selectSQL.append(" and ");
+ }
+ }
+ if ( Driver.logDebug ) Driver.debug("selecting " + selectSQL.toString());
+ selectStatement = ((java.sql.Connection) connection).prepareStatement(selectSQL.toString());
+
+
+ for ( int j = 0, i = 1; j < numKeys; j++, i++) {
+ selectStatement.setObject( i, ((PrimaryKey) primaryKeys.get(j)).getValue() );
+ }
+
+ Jdbc2ResultSet rs = (Jdbc2ResultSet) selectStatement.executeQuery();
+
+ if ( rs.first() ) {
+ rowBuffer = rs.rowBuffer;
+ }
+
+ rows.setElementAt( rowBuffer, current_row );
+ if ( Driver.logDebug ) Driver.debug("done updates");
+
+ rs.close();
+ selectStatement.close();
+ selectStatement = null;
+
+ }
+ catch (Exception e) {
+ if ( Driver.logDebug ) Driver.debug(e.getClass().getName() + e);
+ throw new SQLException( e.getMessage() );
+ }
+
+ }
+
+
+ public synchronized void updateRow() throws SQLException {
+ if ( !isUpdateable() ) {
+ throw new PSQLException( "postgresql.updateable.notupdateable" );
+ }
+
+ if (doingUpdates) {
+
+ try {
+
+ StringBuffer updateSQL = new StringBuffer("UPDATE " + tableName + " SET ");
+
+ int numColumns = updateValues.size();
+ Enumeration columns = updateValues.keys();
+
+ for (int i = 0; columns.hasMoreElements(); i++ ) {
+
+ String column = (String) columns.nextElement();
+ updateSQL.append( column + "= ?");
+
+ if ( i < numColumns - 1 ) {
+
+ updateSQL.append(", ");
+ }
+
+ }
+ updateSQL.append( " WHERE " );
+
+ int numKeys = primaryKeys.size();
+
+ for ( int i = 0; i < numKeys; i++ ) {
+
+ PrimaryKey primaryKey = ((PrimaryKey) primaryKeys.get(i));
+ updateSQL.append(primaryKey.name).append("= ?");
+
+ if ( i < numKeys - 1 ) {
+ updateSQL.append(" and ");
+ }
+ }
+ if ( Driver.logDebug ) Driver.debug("updating " + updateSQL.toString());
+ updateStatement = ((java.sql.Connection) connection).prepareStatement(updateSQL.toString());
+
+ int i = 0;
+ Iterator iterator = updateValues.values().iterator();
+ for (; iterator.hasNext(); i++) {
+ updateStatement.setObject( i + 1, iterator.next() );
+
+ }
+ for ( int j = 0; j < numKeys; j++, i++) {
+ updateStatement.setObject( i + 1, ((PrimaryKey) primaryKeys.get(j)).getValue() );
+ }
+
+ updateStatement.executeUpdate();
+ updateStatement.close();
+
+ updateStatement = null;
+ updateRowBuffer();
+
+
+ if ( Driver.logDebug ) Driver.debug("copying data");
+ System.arraycopy(rowBuffer, 0, this_row, 0, rowBuffer.length);
+
+ rows.setElementAt( rowBuffer, current_row );
+ if ( Driver.logDebug ) Driver.debug("done updates");
+
+ doingUpdates = false;
+ }
+ catch (Exception e) {
+ if ( Driver.logDebug ) Driver.debug(e.getClass().getName() + e);
+ throw new SQLException( e.getMessage() );
+ }
+
+ }
+
+ }
+
+
+ public synchronized void updateShort(int columnIndex, short x) throws SQLException {
+ if ( Driver.logDebug ) Driver.debug("in update Short " + fields[columnIndex - 1].getName() + " = " + x);
+
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), new Short(x) );
+
+ }
+
+
+ public synchronized void updateString(int columnIndex, String x) throws SQLException {
+ if ( Driver.logDebug ) Driver.debug("in update String " + fields[columnIndex - 1].getName() + " = " + x);
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), x );
+
+ }
+
+
+ public synchronized void updateTime(int columnIndex, Time x) throws SQLException {
+ if ( Driver.logDebug ) Driver.debug("in update Time " + fields[columnIndex - 1].getName() + " = " + x);
+
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), x );
+
+ }
+
+
+ public synchronized void updateTimestamp(int columnIndex, Timestamp x) throws SQLException {
+ if ( Driver.logDebug ) Driver.debug("updating Timestamp " + fields[columnIndex - 1].getName() + " = " + x);
+
+ doingUpdates = !onInsertRow;
+ updateValues.put( fields[columnIndex - 1].getName(), x );
+
+
+ }
+
+
+ public synchronized void updateNull(String columnName) throws SQLException {
+ updateNull(findColumn(columnName));
+ }
+
+
+ public synchronized void updateBoolean(String columnName, boolean x) throws SQLException {
+ updateBoolean(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateByte(String columnName, byte x) throws SQLException {
+ updateByte(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateShort(String columnName, short x) throws SQLException {
+ updateShort(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateInt(String columnName, int x) throws SQLException {
+ updateInt(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateLong(String columnName, long x) throws SQLException {
+ updateLong(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateFloat(String columnName, float x) throws SQLException {
+ updateFloat(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateDouble(String columnName, double x) throws SQLException {
+ updateDouble(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateBigDecimal(String columnName, BigDecimal x)
+ throws SQLException {
+ updateBigDecimal(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateString(String columnName, String x) throws SQLException {
+ updateString(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateBytes(String columnName, byte x[]) throws SQLException {
+ updateBytes(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateDate(String columnName, java.sql.Date x)
+ throws SQLException {
+ updateDate(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateTime(String columnName, java.sql.Time x)
+ throws SQLException {
+ updateTime(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateTimestamp(String columnName, java.sql.Timestamp x)
+ throws SQLException {
+ updateTimestamp(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateAsciiStream(
+ String columnName,
+ java.io.InputStream x,
+ int length)
+ throws SQLException {
+ updateAsciiStream(findColumn(columnName), x, length);
+ }
+
+
+ public synchronized void updateBinaryStream(
+ String columnName,
+ java.io.InputStream x,
+ int length)
+ throws SQLException {
+ updateBinaryStream(findColumn(columnName), x, length);
+ }
+
+
+ public synchronized void updateCharacterStream(
+ String columnName,
+ java.io.Reader reader,
+ int length)
+ throws SQLException {
+ updateCharacterStream(findColumn(columnName), reader, length);
+ }
+
+
+ public synchronized void updateObject(String columnName, Object x, int scale)
+ throws SQLException {
+ updateObject(findColumn(columnName), x);
+ }
+
+
+ public synchronized void updateObject(String columnName, Object x) throws SQLException {
+ updateObject(findColumn(columnName), x);
+ }
+
+
+ private int _findColumn( String columnName ) {
+ int i;
+
+ final int flen = fields.length;
+ for (i = 0; i < flen; ++i) {
+ if (fields[i].getName().equalsIgnoreCase(columnName)) {
+ return (i + 1);
+ }
+ }
+ return -1;
+ }
+
+
+ /**
+ * Is this ResultSet updateable?
+ */
+
+ boolean isUpdateable() throws SQLException {
+
+ if (updateable) return true;
+
+ if ( Driver.logDebug ) Driver.debug("checking if rs is updateable");
+
+ parseQuery();
+
+ if ( singleTable == false ) {
+ if ( Driver.logDebug ) Driver.debug("not a single table");
+ return false;
+ }
+
+ if ( Driver.logDebug ) Driver.debug("getting primary keys");
+
+ //
+ // Contains the primary key?
+ //
+
+ primaryKeys = new Vector();
+
+ // this is not stricty jdbc spec, but it will make things much faster if used
+ // the user has to select oid, * from table and then we will just use oid
+
+
+ usingOID = false;
+ int oidIndex = _findColumn( "oid" );
+ int i = 0;
+
+
+ // if we find the oid then just use it
+
+ if ( oidIndex > 0 ) {
+ i++;
+ primaryKeys.add( new PrimaryKey( oidIndex, "oid" ) );
+ usingOID = true;
+ }
+ else {
+ // otherwise go and get the primary keys and create a hashtable of keys
+ java.sql.ResultSet rs = ((java.sql.Connection) connection).getMetaData().getPrimaryKeys("", "", tableName);
+
+
+ for (; rs.next(); i++ ) {
+ String columnName = rs.getString(4); // get the columnName
+
+ int index = findColumn( columnName );
+
+ if ( index > 0 ) {
+ primaryKeys.add( new PrimaryKey(index, columnName ) ); // get the primary key information
+ }
+ }
+
+ rs.close();
+ }
+
+ numKeys = primaryKeys.size();
+
+ if ( Driver.logDebug ) Driver.debug( "no of keys=" + i );
+
+ if ( i < 1 ) {
+ throw new SQLException("No Primary Keys");
+ }
+
+ updateable = primaryKeys.size() > 0;
+
+ if ( Driver.logDebug ) Driver.debug( "checking primary key " + updateable );
+
+ return updateable;
+ }
+
+
+ public void parseQuery() {
+ StringTokenizer st = new StringTokenizer(sqlQuery, " \r\t");
+ boolean tableFound = false, tablesChecked = false;
+ String name = "";
+
+ singleTable = true;
+
+ while ( !tableFound && !tablesChecked && st.hasMoreTokens() ) {
+ name = st.nextToken();
+ if ( !tableFound ) {
+ if (name.toLowerCase().equals("from")) {
+ tableName = st.nextToken();
+ tableFound = true;
+ }
+ }
+ else {
+ tablesChecked = true;
+ // if the very next token is , then there are multiple tables
+ singleTable = !name.equalsIgnoreCase(",");
+ }
+ }
+ }
+
+
+ private void updateRowBuffer() throws SQLException {
+
+ Enumeration columns = updateValues.keys();
+
+ while ( columns.hasMoreElements() ) {
+ String columnName = (String) columns.nextElement();
+ int columnIndex = _findColumn( columnName ) - 1;
+
+ switch ( connection.getSQLType( fields[columnIndex].getPGType() ) ) {
+
+ case Types.DECIMAL:
+ case Types.BIGINT:
+ case Types.DOUBLE:
+ case Types.BIT:
+ case Types.VARCHAR:
+ case Types.DATE:
+ case Types.TIME:
+ case Types.TIMESTAMP:
+ case Types.SMALLINT:
+ case Types.FLOAT:
+ case Types.INTEGER:
+ case Types.CHAR:
+ case Types.NUMERIC:
+ case Types.REAL:
+ case Types.TINYINT:
+
+ try {
+ rowBuffer[columnIndex] = String.valueOf( updateValues.get( columnName ) ).getBytes(connection.getEncoding().name() );
+ }
+ catch ( UnsupportedEncodingException ex) {
+ throw new SQLException("Unsupported Encoding " + connection.getEncoding().name());
+ }
+
+ case Types.NULL:
+ continue;
+
+ default:
+ rowBuffer[columnIndex] = (byte[]) updateValues.get( columnName );
+ }
+
+ }
+ }
+
+
+ public void setStatement(Statement statement) {
+ this.statement = statement;
+ }
+
+
+ public void setSQLQuery(String sqlQuery) {
+ this.sqlQuery = sqlQuery;
+ }
+
+
+ private class PrimaryKey {
+ int index; // where in the result set is this primaryKey
+ String name; // what is the columnName of this primary Key
+
+ PrimaryKey( int index, String name) {
+ this.index = index;
+ this.name = name;
+ }
+ Object getValue() throws SQLException {
+ return getObject(index);
+ }
+ };
+
+
+
}