/* Copyright (C) 2002 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.mysql.jdbc; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.StringReader; import java.math.BigDecimal; import java.net.MalformedURLException; import java.net.URL; import java.sql.Array; import java.sql.Clob; import java.sql.Date; import java.sql.Ref; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; import java.util.TimeZone; /** * A ResultSet provides access to a table of data generated by executing a * Statement. The table rows are retrieved in sequence. Within a row its * column values can be accessed in any order. * * <P> * A ResultSet maintains a cursor pointing to its current row of data. * Initially the cursor is positioned before the first row. The 'next' method * moves the cursor to the next row. * </p> * * <P> * The getXXX methods retrieve column values for the current row. You can * retrieve values either using the index number of the column, or by using * the name of the column. In general using the column index will be more * efficient. Columns are numbered from 1. * </p> * * <P> * For maximum portability, ResultSet columns within each row should be read in * left-to-right order and each column should be read only once. * </p> * * <P> * For the getXXX methods, the JDBC driver attempts to convert the underlying * data to the specified Java type and returns a suitable Java value. See the * JDBC specification for allowable mappings from SQL types to Java types with * the ResultSet getXXX methods. * </p> * * <P> * Column names used as input to getXXX methods are case insenstive. When * performing a getXXX using a column name, if several columns have the same * name, then the value of the first matching column will be returned. The * column name option is designed to be used when column names are used in the * SQL Query. For columns that are NOT explicitly named in the query, it is * best to use column numbers. If column names were used there is no way for * the programmer to guarentee that they actually refer to the intended * columns. * </p> * * <P> * A ResultSet is automatically closed by the Statement that generated it when * that Statement is closed, re-executed, or is used to retrieve the next * result from a sequence of multiple results. * </p> * * <P> * The number, types and properties of a ResultSet's columns are provided by * the ResultSetMetaData object returned by the getMetaData method. * </p> * * @author Mark Matthews * @version $Id: ResultSet.java,v 1.18.2.23 2003/12/24 05:16:25 mmatthew Exp $ * * @see ResultSetMetaData * @see java.sql.ResultSet */ public class ResultSet implements java.sql.ResultSet { /** * This method ends up being staticly synchronized, so just store our own * copy.... */ private final TimeZone DEFAULT_TIMEZONE = TimeZone.getDefault(); /** The Connection instance that created us */ protected com.mysql.jdbc.Connection connection; // The connection that created us /** Map column names (and all of their permutations) to column indices */ protected Map columnNameToIndex = null; /** Map of fully-specified column names to column indices */ protected Map fullColumnNameToIndex = null; /** The actual rows */ protected RowData rowData; // The results /** The warning chain */ protected java.sql.SQLWarning warningChain = null; /** The statement that created us */ protected com.mysql.jdbc.Statement owningStatement; /** The catalog that was in use when we were created */ protected String catalog = null; /** * Any info message from the server that was created while generating this * result set (if 'info parsing' is enabled for the connection). */ protected String serverInfo = null; /** The fields for this result set */ protected Field[] fields; // The fields /** Pointer to current row data */ protected byte[][] thisRow; // Values for current row /** Are we in the middle of doing updates to the current row? */ protected boolean doingUpdates = false; /** Has this result set been closed? */ protected boolean isClosed = false; /** Are we on the insert row? */ protected boolean onInsertRow = false; /** * Do we actually contain rows, or just information about * UPDATE/INSERT/DELETE? */ protected boolean reallyResult = false; /** Did the previous value retrieval find a NULL? */ protected boolean wasNullFlag = false; /** * First character of the query that created this result set...Used to * determine whether or not to parse server info messages in certain * circumstances. */ protected char firstCharOfQuery; /** The current row #, -1 == before start of result set */ protected int currentRow = -1; // Cursor to current row; /** The direction to fetch rows (always FETCH_FORWARD) */ protected int fetchDirection = FETCH_FORWARD; /** The number of rows to fetch in one go... */ protected int fetchSize = 0; /** Are we read-only or updatable? */ protected int resultSetConcurrency = 0; /** Are we scroll-sensitive/insensitive? */ protected int resultSetType = 0; /** How many rows were affected by UPDATE/INSERT/DELETE? */ protected long updateCount; // These are longs for // recent versions of the MySQL server. // // They get reduced to ints via the JDBC API, // but can be retrieved through a MySQLStatement // in their entirety. // /** Value generated for AUTO_INCREMENT columns */ protected long updateId = -1; private Calendar fastDateCal = null; private boolean hasBuiltIndexMapping = false; private boolean useStrictFloatingPoint = false; /** * Create a result set for an executeUpdate statement. * * @param updateCount the number of rows affected by the update * @param updateID the autoincrement value (if any) */ public ResultSet(long updateCount, long updateID) { this.updateCount = updateCount; this.updateId = updateID; reallyResult = false; fields = new Field[0]; } /** * Create a new ResultSet * * @param catalog the database in use when we were created * @param fields an array of Field objects (basically, the ResultSet * MetaData) * @param tuples actual row data * @param conn the Connection that created us. * * @throws SQLException if an error occurs */ public ResultSet(String catalog, Field[] fields, RowData tuples, com.mysql.jdbc.Connection conn) throws SQLException { this(fields, tuples); setConnection(conn); this.catalog = catalog; } /** * Creates a new ResultSet object. * * @param fields DOCUMENT ME! * @param tuples DOCUMENT ME! * * @throws SQLException DOCUMENT ME! */ public ResultSet(Field[] fields, RowData tuples) throws SQLException { //_currentRow = -1; this.fields = fields; this.rowData = tuples; this.updateCount = (long) rowData.size(); if (Driver.DEBUG) { System.out.println("Retrieved " + updateCount + " rows"); } this.reallyResult = true; // Check for no results if (this.rowData.size() > 0) { //_thisRow = _rows.next(); if (this.updateCount == 1) { if (this.thisRow == null) { //_currentRow = -1; this.rowData.close(); // empty result set this.updateCount = -1; } } } else { this.thisRow = null; } this.rowData.setOwner(this); } /** * JDBC 2.0 * * <p> * Determine if the cursor is after the last row in the result set. * </p> * * @return true if after the last row, false otherwise. Returns false when * the result set contains no rows. * * @exception SQLException if a database-access error occurs. */ public boolean isAfterLast() throws SQLException { if (Driver.TRACE) { Object[] args = { }; Debug.methodCall(this, "isAfterLast", args); } boolean b = rowData.isAfterLast(); if (Driver.TRACE) { Debug.returnValue(this, "isAfterLast", new Boolean(b)); } return b; } /** * JDBC 2.0 Get an array column. * * @param i the first column is 1, the second is 2, ... * * @return an object representing an SQL array * * @throws SQLException if a database error occurs * @throws NotImplemented DOCUMENT ME! */ public java.sql.Array getArray(int i) throws SQLException { throw new NotImplemented(); } /** * JDBC 2.0 Get an array column. * * @param colName the column name * * @return an object representing an SQL array * * @throws SQLException if a database error occurs * @throws NotImplemented DOCUMENT ME! */ public java.sql.Array getArray(String colName) throws SQLException { throw new NotImplemented(); } /** * A column value can be retrieved as a stream of ASCII characters and then * read in chunks from the stream. This method is particulary suitable * for retrieving large LONGVARCHAR values. The JDBC driver will do any * necessary conversion from the database format into ASCII. * * <p> * <B>Note:</B> All the data in the returned stream must be read prior to * getting the value of any other column. The next call to a get method * implicitly closes the stream. Also, a stream may return 0 for * available() whether there is data available or not. * </p> * * @param columnIndex the first column is 1, the second is 2, ... * * @return a Java InputStream that delivers the database column value as a * stream of one byte ASCII characters. If the value is SQL NULL * then the result is null * * @exception java.sql.SQLException if a database access error occurs * * @see getBinaryStream */ public InputStream getAsciiStream(int columnIndex) throws java.sql.SQLException { checkRowPos(); return getBinaryStream(columnIndex); } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public InputStream getAsciiStream(String columnName) throws java.sql.SQLException { return getAsciiStream(findColumn(columnName)); } //--------------------------------------------------------------------- // Traversal/Positioning //--------------------------------------------------------------------- /** * JDBC 2.0 * * <p> * Determine if the cursor is before the first row in the result set. * </p> * * @return true if before the first row, false otherwise. Returns false * when the result set contains no rows. * * @exception SQLException if a database-access error occurs. */ public boolean isBeforeFirst() throws SQLException { if (Driver.TRACE) { Object[] args = { }; Debug.methodCall(this, "isBeforeFirst", args); } boolean b = rowData.isBeforeFirst(); if (Driver.TRACE) { Debug.returnValue(this, "isBeforeFirst", new Boolean(b)); } return b; } /** * Get the value of a column in the current row as a java.math.BigDecimal * object * * @param columnIndex the first column is 1, the second is 2... * @param scale the number of digits to the right of the decimal * * @return the column value; if the value is SQL NULL, null * * @exception java.sql.SQLException if a database access error occurs */ public BigDecimal getBigDecimal(int columnIndex, int scale) throws java.sql.SQLException { String stringVal = getString(columnIndex); BigDecimal val; if (stringVal != null) { if (stringVal.length() == 0) { val = new BigDecimal(0); return val.setScale(scale); } try { val = new BigDecimal(stringVal); } catch (NumberFormatException ex) { throw new java.sql.SQLException("Bad format for BigDecimal '" + stringVal + "' in column " + columnIndex + "(" + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } try { return val.setScale(scale); } catch (ArithmeticException ex) { throw new java.sql.SQLException("Bad format for BigDecimal '" + stringVal + "' in column " + columnIndex + "(" + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } return null; } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * @param scale DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public BigDecimal getBigDecimal(String columnName, int scale) throws java.sql.SQLException { return getBigDecimal(findColumn(columnName), scale); } /** * JDBC 2.0 Get the value of a column in the current row as a * java.math.BigDecimal object. * * @param columnIndex the first column is 1, the second is 2, ... * * @return the column value (full precision); if the value is SQL NULL, the * result is null * * @exception SQLException if a database-access error occurs. * @throws java.sql.SQLException DOCUMENT ME! */ public BigDecimal getBigDecimal(int columnIndex) throws SQLException { String stringVal = getString(columnIndex); BigDecimal val; if (stringVal != null) { if (stringVal.length() == 0) { val = new BigDecimal(0); return val; } try { val = new BigDecimal(stringVal); return val; } catch (NumberFormatException ex) { throw new java.sql.SQLException("Bad format for BigDecimal '" + stringVal + "' in column " + columnIndex + "(" + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } return null; } /** * JDBC 2.0 Get the value of a column in the current row as a * java.math.BigDecimal object. * * @param columnName the name of the column to retrieve the value from * * @return the BigDecimal value in the column * * @throws SQLException if an error occurs */ public BigDecimal getBigDecimal(String columnName) throws SQLException { return getBigDecimal(findColumn(columnName)); } /** * A column value can also be retrieved as a binary strea. This method is * suitable for retrieving LONGVARBINARY values. * * @param columnIndex the first column is 1, the second is 2... * * @return a Java InputStream that delivers the database column value as a * stream of bytes. If the value is SQL NULL, then the result is * null * * @exception java.sql.SQLException if a database access error occurs * * @see getAsciiStream * @see getUnicodeStream */ public InputStream getBinaryStream(int columnIndex) throws java.sql.SQLException { checkRowPos(); byte[] b = getBytes(columnIndex); if (b != null) { return new ByteArrayInputStream(b); } return null; } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public InputStream getBinaryStream(String columnName) throws java.sql.SQLException { return getBinaryStream(findColumn(columnName)); } /** * JDBC 2.0 Get a BLOB column. * * @param columnIndex the first column is 1, the second is 2, ... * * @return an object representing a BLOB * * @throws SQLException if an error occurs. * @throws java.sql.SQLException DOCUMENT ME! */ public java.sql.Blob getBlob(int columnIndex) throws SQLException { checkRowPos(); if ((columnIndex < 1) || (columnIndex > fields.length)) { throw new java.sql.SQLException("Column Index out of range ( " + columnIndex + " > " + fields.length + ").", SQLError.SQL_STATE_INVALID_COLUMN_NUMBER); } try { if (thisRow[columnIndex - 1] == null) { wasNullFlag = true; } else { wasNullFlag = false; } } catch (NullPointerException ex) { wasNullFlag = true; } if (wasNullFlag) { return null; } return new Blob(thisRow[columnIndex - 1]); } /** * JDBC 2.0 Get a BLOB column. * * @param colName the column name * * @return an object representing a BLOB * * @throws SQLException if an error occurs. */ public java.sql.Blob getBlob(String colName) throws SQLException { return getBlob(findColumn(colName)); } /** * Get the value of a column in the current row as a Java boolean * * @param columnIndex the first column is 1, the second is 2... * * @return the column value, false for SQL NULL * * @exception java.sql.SQLException if a database access error occurs */ public boolean getBoolean(int columnIndex) throws java.sql.SQLException { String stringVal = getString(columnIndex); if ((stringVal != null) && (stringVal.length() > 0)) { int c = Character.toLowerCase(stringVal.charAt(0)); return ((c == 't') || (c == 'y') || (c == '1') || stringVal.equals("-1")); } return false; } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public boolean getBoolean(String columnName) throws java.sql.SQLException { return getBoolean(findColumn(columnName)); } /** * Get the value of a column in the current row as a Java byte. * * @param columnIndex the first column is 1, the second is 2,... * * @return the column value; 0 if SQL NULL * * @exception java.sql.SQLException if a database access error occurs * @throws SQLException DOCUMENT ME! */ public byte getByte(int columnIndex) throws java.sql.SQLException { checkRowPos(); try { if (thisRow[columnIndex - 1] == null) { wasNullFlag = true; } else { wasNullFlag = false; } } catch (NullPointerException E) { wasNullFlag = true; } if (wasNullFlag) { return 0; } Field field = fields[columnIndex - 1]; switch (field.getMysqlType()) { case MysqlDefs.FIELD_TYPE_DECIMAL: case MysqlDefs.FIELD_TYPE_TINY: case MysqlDefs.FIELD_TYPE_SHORT: case MysqlDefs.FIELD_TYPE_LONG: case MysqlDefs.FIELD_TYPE_FLOAT: case MysqlDefs.FIELD_TYPE_DOUBLE: case MysqlDefs.FIELD_TYPE_LONGLONG: case MysqlDefs.FIELD_TYPE_INT24: try { String stringVal = getString(columnIndex); int decimalIndex = stringVal.indexOf("."); // Strip off the decimals if (decimalIndex != -1) { stringVal = stringVal.substring(0, decimalIndex); } return Byte.parseByte(stringVal); } catch (NumberFormatException NFE) { throw new SQLException("Value '" + getString(columnIndex) + "' is out of range [-127,127]", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } default: try { String stringVal = getString(columnIndex); int decimalIndex = stringVal.indexOf("."); // Strip off the decimals if (decimalIndex != -1) { stringVal = stringVal.substring(0, decimalIndex); } return Byte.parseByte(stringVal); } catch (NumberFormatException NFE) { throw new SQLException("Value '" + getString(columnIndex) + "' is out of range [-127,127]", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } // FIXME: JDBC-Compliance test is broken, wants to convert string->byte(num) //return _thisRow[columnIndex - 1][0]; } } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public byte getByte(String columnName) throws java.sql.SQLException { return getByte(findColumn(columnName)); } /** * Get the value of a column in the current row as a Java byte array. * * <p> * <b>Be warned</b> If the blob is huge, then you may run out of memory. * </p> * * @param columnIndex the first column is 1, the second is 2, ... * * @return the column value; if the value is SQL NULL, the result is null * * @exception java.sql.SQLException if a database access error occurs */ public byte[] getBytes(int columnIndex) throws java.sql.SQLException { checkRowPos(); try { if (thisRow[columnIndex - 1] == null) { wasNullFlag = true; } else { wasNullFlag = false; } } catch (NullPointerException E) { wasNullFlag = true; } catch (ArrayIndexOutOfBoundsException aioobEx) { throw new java.sql.SQLException("Column Index out of range ( " + columnIndex + " > " + fields.length + ").", SQLError.SQL_STATE_INVALID_COLUMN_NUMBER); } if (wasNullFlag) { return null; } else { return thisRow[columnIndex - 1]; } } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public byte[] getBytes(String columnName) throws java.sql.SQLException { return getBytes(findColumn(columnName)); } //--------------------------JDBC 2.0----------------------------------- //--------------------------------------------------------------------- // Getter's and Setter's //--------------------------------------------------------------------- /** * JDBC 2.0 * * <p> * Get the value of a column in the current row as a java.io.Reader. * </p> * * @param columnIndex the column to get the value from * * @return the value in the column as a java.io.Reader. * * @throws SQLException if an error occurs */ public java.io.Reader getCharacterStream(int columnIndex) throws SQLException { String stringVal = getString(columnIndex); if (stringVal != null) { return new StringReader(stringVal); } else { return null; } } /** * JDBC 2.0 * * <p> * Get the value of a column in the current row as a java.io.Reader. * </p> * * @param columnName the column name to retrieve the value from * * @return the value as a java.io.Reader * * @throws SQLException if an error occurs */ public java.io.Reader getCharacterStream(String columnName) throws SQLException { return getCharacterStream(findColumn(columnName)); } /** * JDBC 2.0 Get a CLOB column. * * @param i the first column is 1, the second is 2, ... * * @return an object representing a CLOB * * @throws SQLException if an error occurs */ public java.sql.Clob getClob(int i) throws SQLException { return new com.mysql.jdbc.Clob(getString(i)); } /** * JDBC 2.0 Get a CLOB column. * * @param colName the column name * * @return an object representing a CLOB * * @throws SQLException if an error occurs */ public java.sql.Clob getClob(String colName) throws SQLException { return getClob(findColumn(colName)); } /** * JDBC 2.0 Return the concurrency of this result set. The concurrency * used is determined by the statement that created the result set. * * @return the concurrency type, CONCUR_READ_ONLY, etc. * * @throws SQLException if a database-access error occurs */ public int getConcurrency() throws SQLException { return (CONCUR_READ_ONLY); } /** * DOCUMENT ME! * * @param conn the connection that created this result set. */ public void setConnection(com.mysql.jdbc.Connection conn) { this.connection = conn; if (connection != null) { useStrictFloatingPoint = connection.useStrictFloatingPoint(); } } /** * Get the name of the SQL cursor used by this ResultSet * * <p> * In SQL, a result table is retrieved though a cursor that is named. The * current row of a result can be updated or deleted using a positioned * update/delete statement that references the cursor name. * </p> * * <p> * JDBC supports this SQL feature by providing the name of the SQL cursor * used by a ResultSet. The current row of a ResulSet is also the current * row of this SQL cursor. * </p> * * <p> * <B>Note:</B> If positioned update is not supported, a * java.sql.SQLException is thrown. * </p> * * @return the ResultSet's SQL cursor name. * * @exception java.sql.SQLException if a database access error occurs */ public String getCursorName() throws java.sql.SQLException { throw new java.sql.SQLException("Positioned Update not supported.", "S1C00"); } /** * Get the value of a column in the current row as a java.sql.Date object * * @param columnIndex the first column is 1, the second is 2... * * @return the column value; null if SQL NULL * * @exception java.sql.SQLException if a database access error occurs */ public java.sql.Date getDate(int columnIndex) throws java.sql.SQLException { return getDate(columnIndex, null); } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public java.sql.Date getDate(String columnName) throws java.sql.SQLException { return getDate(findColumn(columnName)); } /** * JDBC 2.0 Get the value of a column in the current row as a java.sql.Date * object. Use the calendar to construct an appropriate millisecond value * for the Date, if the underlying database doesn't store timezone * information. * * @param columnIndex the first column is 1, the second is 2, ... * @param cal the calendar to use in constructing the date * * @return the column value; if the value is SQL NULL, the result is null * * @exception SQLException if a database-access error occurs. * @throws java.sql.SQLException DOCUMENT ME! */ public java.sql.Date getDate(int columnIndex, Calendar cal) throws SQLException { Integer year = null; Integer month = null; Integer day = null; String stringVal = ""; try { stringVal = getString(columnIndex); if (stringVal == null) { return null; } else { int length = stringVal.length(); if ((length > 0) && (stringVal.charAt(0) == '0') && (stringVal.equals("0000-00-00") || stringVal.equals("0000-00-00 00:00:00") || stringVal.equals("00000000000000") || stringVal.equals("0"))) { wasNullFlag = true; return null; } else if (fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { // Convert from TIMESTAMP switch (length) { case 14: case 8: { year = new Integer(stringVal.substring(0, 4)); month = new Integer(stringVal.substring(4, 6)); day = new Integer(stringVal.substring(6, 8)); return fastDateCreate(cal, year.intValue() - 1900, month.intValue() - 1, day.intValue()); } case 12: case 10: case 6: { year = new Integer(stringVal.substring(0, 2)); if (year.intValue() <= 69) { year = new Integer(year.intValue() + 100); } month = new Integer(stringVal.substring(2, 4)); day = new Integer(stringVal.substring(4, 6)); return fastDateCreate(cal, year.intValue(), month.intValue() - 1, day.intValue()); } case 4: { year = new Integer(stringVal.substring(0, 4)); if (year.intValue() <= 69) { year = new Integer(year.intValue() + 100); } month = new Integer(stringVal.substring(2, 4)); return fastDateCreate(cal, year.intValue(), month.intValue() - 1, 1); } case 2: { year = new Integer(stringVal.substring(0, 2)); if (year.intValue() <= 69) { year = new Integer(year.intValue() + 100); } return fastDateCreate(cal, year.intValue(), 0, 1); } default: throw new SQLException("Bad format for Date '" + stringVal + "' in column " + columnIndex + "(" + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } /* endswitch */ } else if (fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { year = new Integer(stringVal.substring(0, 4)); return fastDateCreate(cal, year.intValue() - 1900, 0, 1); } else { if (length < 10) { throw new SQLException("Bad format for Date '" + stringVal + "' in column " + columnIndex + "(" + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } year = new Integer(stringVal.substring(0, 4)); month = new Integer(stringVal.substring(5, 7)); day = new Integer(stringVal.substring(8, 10)); } return fastDateCreate(cal, year.intValue() - 1900, month.intValue() - 1, day.intValue()); } } catch (Exception e) { throw new java.sql.SQLException("Cannot convert value '" + stringVal + "' from column " + columnIndex + "(" + stringVal + " ) to DATE.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } /** * Get the value of a column in the current row as a java.sql.Date object. * Use the calendar to construct an appropriate millisecond value for the * Date, if the underlying database doesn't store timezone information. * * @param columnName is the SQL name of the column * @param cal the calendar to use in constructing the date * * @return the column value; if the value is SQL NULL, the result is null * * @exception SQLException if a database-access error occurs. */ public java.sql.Date getDate(String columnName, Calendar cal) throws SQLException { return getDate(columnName); } /** * Get the value of a column in the current row as a Java double. * * @param columnIndex the first column is 1, the second is 2,... * * @return the column value; 0 if SQL NULL * * @exception java.sql.SQLException if a database access error occurs */ public double getDouble(int columnIndex) throws java.sql.SQLException { try { return getDoubleInternal(columnIndex); } catch (NumberFormatException E) { throw new java.sql.SQLException("Bad format for number '" + new String(thisRow[columnIndex - 1]) + "' in column " + columnIndex + "(" + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public double getDouble(String columnName) throws java.sql.SQLException { return getDouble(findColumn(columnName)); } /** * JDBC 2.0 Give a hint as to the direction in which the rows in this * result set will be processed. The initial value is determined by the * statement that produced the result set. The fetch direction may be * changed at any time. * * @param direction the direction to fetch rows in. * * @exception SQLException if a database-access error occurs, or the result * set type is TYPE_FORWARD_ONLY and direction is not * FETCH_FORWARD. MM.MySQL actually ignores this, because it * has the whole result set anyway, so the direction is * immaterial. */ public void setFetchDirection(int direction) throws SQLException { if ((direction != FETCH_FORWARD) && (direction != FETCH_REVERSE) && (direction != FETCH_UNKNOWN)) { throw new SQLException("Illegal value for fetch direction", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } else { fetchDirection = direction; } } /** * JDBC 2.0 Returns the fetch direction for this result set. * * @return the fetch direction for this result set. * * @exception SQLException if a database-access error occurs */ public int getFetchDirection() throws SQLException { return fetchDirection; } /** * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that * should be fetched from the database when more rows are needed for this * result set. If the fetch size specified is zero, then the JDBC driver * ignores the value, and is free to make its own best guess as to what * the fetch size should be. The default value is set by the statement * that creates the result set. The fetch size may be changed at any * time. * * @param rows the number of rows to fetch * * @exception SQLException if a database-access error occurs, or the * condition 0 <= rows <= this.getMaxRows() is not * satisfied. Currently ignored by this driver. */ public void setFetchSize(int rows) throws SQLException { if (rows < 0) { /* || rows > getMaxRows()*/ throw new SQLException("Value must be between 0 and getMaxRows()", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } fetchSize = rows; } /** * JDBC 2.0 Return the fetch size for this result set. * * @return the fetch size for this result set. * * @exception SQLException if a database-access error occurs */ public int getFetchSize() throws SQLException { return fetchSize; } /** * JDBC 2.0 * * <p> * Determine if the cursor is on the first row of the result set. * </p> * * @return true if on the first row, false otherwise. * * @exception SQLException if a database-access error occurs. */ public boolean isFirst() throws SQLException { if (Driver.TRACE) { Object[] args = { }; Debug.methodCall(this, "isFirst", args); } boolean b = rowData.isFirst(); if (Driver.TRACE) { Debug.returnValue(this, "isFirst", new Boolean(b)); } return b; } /** * Get the value of a column in the current row as a Java float. * * @param columnIndex the first column is 1, the second is 2,... * * @return the column value; 0 if SQL NULL * * @exception java.sql.SQLException if a database access error occurs * @throws SQLException DOCUMENT ME! */ public float getFloat(int columnIndex) throws java.sql.SQLException { checkRowPos(); String val = null; try { val = getString(columnIndex); if ((val != null) && (val.length() != 0)) { float f = Float.parseFloat(val); return f; } else { return 0; } } catch (NumberFormatException nfe) { try { // To do: warn on under/overflow? return (float) Double.parseDouble(val); } catch (NumberFormatException newNfe) { ; // ignore, it's not a number } throw new SQLException("Invalid value for getFloat() - '" + val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public float getFloat(String columnName) throws java.sql.SQLException { return getFloat(findColumn(columnName)); } /** * Get the value of a column in the current row as a Java int. * * @param columnIndex the first column is 1, the second is 2,... * * @return the column value; 0 if SQL NULL * * @exception java.sql.SQLException if a database access error occurs * @throws SQLException DOCUMENT ME! */ public int getInt(int columnIndex) throws java.sql.SQLException { String val = null; try { val = getString(columnIndex); if ((val != null) && (val.length() != 0)) { if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1) && (val.indexOf(".") == -1)) { return Integer.parseInt(val); } else { // Convert floating point return (int) (Double.parseDouble(val)); } } else { return 0; } } catch (NumberFormatException nfe) { try { // To do: warn on under/overflow? return (int) Double.parseDouble(val); } catch (NumberFormatException newNfe) { ; // ignore, it's not a number } throw new SQLException("Invalid value for getInt() - '" + val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public int getInt(String columnName) throws java.sql.SQLException { return getInt(findColumn(columnName)); } /** * JDBC 2.0 * * <p> * Determine if the cursor is on the last row of the result set. Note: * Calling isLast() may be expensive since the JDBC driver might need to * fetch ahead one row in order to determine whether the current row is * the last row in the result set. * </p> * * @return true if on the last row, false otherwise. * * @exception SQLException if a database-access error occurs. */ public boolean isLast() throws SQLException { if (Driver.TRACE) { Object[] args = { }; Debug.methodCall(this, "isLast", args); } boolean b = rowData.isLast(); if (Driver.TRACE) { Debug.returnValue(this, "relative", new Boolean(b)); } return b; } /** * Get the value of a column in the current row as a Java long. * * @param columnIndex the first column is 1, the second is 2,... * * @return the column value; 0 if SQL NULL * * @exception java.sql.SQLException if a database access error occurs * @throws SQLException DOCUMENT ME! */ public long getLong(int columnIndex) throws java.sql.SQLException { checkRowPos(); String val = null; try { val = getString(columnIndex); if ((val != null) && (val.length() != 0)) { if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)) { return Long.parseLong(val); } else { // Convert floating point return Double.doubleToLongBits(Double.parseDouble(val)); } } else { return 0; } } catch (NumberFormatException nfe) { try { // To do: warn on under/overflow? return (long) Double.parseDouble(val); } catch (NumberFormatException newNfe) { ; // ignore, it's not a number } throw new SQLException("Invalid value for getLong() - '" + val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public long getLong(String columnName) throws java.sql.SQLException { return getLong(findColumn(columnName)); } /** * The numbers, types and properties of a ResultSet's columns are provided * by the getMetaData method * * @return a description of the ResultSet's columns * * @exception java.sql.SQLException if a database access error occurs */ public java.sql.ResultSetMetaData getMetaData() throws java.sql.SQLException { return new com.mysql.jdbc.ResultSetMetaData(fields); } /** * 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> * * <p> * This method may also be used to read database specific abstract data * types. * </p> * * @param columnIndex the first column is 1, the second is 2... * * @return a Object holding the column value * * @exception java.sql.SQLException if a database access error occurs * @throws SQLException DOCUMENT ME! */ public Object getObject(int columnIndex) throws java.sql.SQLException { checkRowPos(); if (Driver.TRACE) { Object[] args = { new Integer(columnIndex) }; Debug.methodCall(this, "getObject", args); } try { if (thisRow[columnIndex - 1] == null) { wasNullFlag = true; return null; } } catch (ArrayIndexOutOfBoundsException aioobEx) { throw new java.sql.SQLException("Column Index out of range ( " + columnIndex + " > " + fields.length + ").", SQLError.SQL_STATE_INVALID_COLUMN_NUMBER); } wasNullFlag = false; Field field; field = fields[columnIndex - 1]; switch (field.getSQLType()) { case Types.BIT: return new Boolean(getBoolean(columnIndex)); case Types.TINYINT: return new Integer(getInt(columnIndex)); case Types.SMALLINT: return new Integer(getInt(columnIndex)); case Types.INTEGER: if (field.isUnsigned()) { return new Long(getLong(columnIndex)); } else { return new Integer(getInt(columnIndex)); } case Types.BIGINT: if (field.isUnsigned()) { return getBigDecimal(columnIndex); } else { return new Long(getLong(columnIndex)); } case Types.DECIMAL: case Types.NUMERIC: String stringVal = getString(columnIndex); BigDecimal val; if (stringVal != null) { if (stringVal.length() == 0) { val = new BigDecimal(0); return val; } try { val = new BigDecimal(stringVal); } catch (NumberFormatException ex) { throw new java.sql.SQLException( "Bad format for BigDecimal '" + stringVal + "' in column " + columnIndex + "(" + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } return val; } else { return null; } case Types.REAL: return new Float(getFloat(columnIndex)); case Types.FLOAT: case Types.DOUBLE: return new Double(getDouble(columnIndex)); case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: return getString(columnIndex); case Types.BINARY: case Types.VARBINARY: case Types.LONGVARBINARY: if (!field.isBlob()) { return getString(columnIndex); } else if (!field.isBinary()) { return getString(columnIndex); } else { byte[] data = getBytes(columnIndex); Object obj = data; if ((data != null) && (data.length >= 2)) { if ((data[0] == -84) && (data[1] == -19)) { // Serialized object? try { ByteArrayInputStream bytesIn = new ByteArrayInputStream(data); ObjectInputStream objIn = new ObjectInputStream(bytesIn); obj = objIn.readObject(); objIn.close(); bytesIn.close(); } catch (ClassNotFoundException cnfe) { throw new SQLException("Class not found: " + cnfe.toString() + " while reading serialized object"); } catch (IOException ex) { obj = data; // not serialized? } } } return obj; } case Types.DATE: return getDate(columnIndex); case Types.TIME: return getTime(columnIndex); case Types.TIMESTAMP: return getTimestamp(columnIndex); default: return getString(columnIndex); } } /** * 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> * * <p> * This method may also be used to read database specific abstract data * types. * </p> * * @param columnName is the SQL name of the column * * @return a Object holding the column value * * @exception java.sql.SQLException if a database access error occurs */ public Object getObject(String columnName) throws java.sql.SQLException { return getObject(findColumn(columnName)); } /** * JDBC 2.0 Returns the value of column i as a Java object. Use the map to * determine the class from which to construct data of SQL structured and * distinct types. * * @param i the first column is 1, the second is 2, ... * @param map the mapping from SQL type names to Java classes * * @return an object representing the SQL value * * @throws SQLException because this is not implemented */ public Object getObject(int i, java.util.Map map) throws SQLException { return getObject(i); } /** * JDBC 2.0 Returns the value of column i as a Java object. Use the map to * determine the class from which to construct data of SQL structured and * distinct types. * * @param colName the column name * @param map the mapping from SQL type names to Java classes * * @return an object representing the SQL value * * @throws SQLException as this is not implemented */ public Object getObject(String colName, java.util.Map map) throws SQLException { return getObject(findColumn(colName), map); } /** * JDBC 2.0 Get a REF(<structured-type>) column. * * @param i the first column is 1, the second is 2, ... * * @return an object representing data of an SQL REF type * * @throws SQLException as this is not implemented * @throws NotImplemented DOCUMENT ME! */ public java.sql.Ref getRef(int i) throws SQLException { throw new NotImplemented(); } /** * JDBC 2.0 Get a REF(<structured-type>) column. * * @param colName the column name * * @return an object representing data of an SQL REF type * * @throws SQLException as this method is not implemented. * @throws NotImplemented DOCUMENT ME! */ public java.sql.Ref getRef(String colName) throws SQLException { throw new NotImplemented(); } /** * JDBC 2.0 * * <p> * Determine the current row number. The first row is number 1, the second * number 2, etc. * </p> * * @return the current row number, else return 0 if there is no current row * * @exception SQLException if a database-access error occurs. */ public int getRow() throws SQLException { if (Driver.TRACE) { Object[] args = { }; Debug.methodCall(this, "getRow", args); } int currentRow = rowData.getCurrentRowNumber(); int row = 0; // Non-dynamic result sets can be interrogated // for this information if (!rowData.isDynamic()) { if ((currentRow < 0) || rowData.isAfterLast() || rowData.isEmpty()) { row = 0; } else { row = currentRow + 1; } } else { // dynamic (streaming) can not row = currentRow + 1; } if (Driver.TRACE) { Debug.returnValue(this, "getRow", new Integer(row)); } if (Driver.TRACE) { Debug.returnValue(this, "getRow", new Integer(row)); } return row; } /** * Get the value of a column in the current row as a Java short. * * @param columnIndex the first column is 1, the second is 2,... * * @return the column value; 0 if SQL NULL * * @exception java.sql.SQLException if a database access error occurs * @throws SQLException DOCUMENT ME! */ public short getShort(int columnIndex) throws java.sql.SQLException { checkRowPos(); String val = null; try { val = getString(columnIndex); if ((val != null) && (val.length() != 0)) { if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1) && (val.indexOf(".") == -1)) { return Short.parseShort(val); } else { // Convert floating point return (short) (Double.parseDouble(val)); } } else { return 0; } } catch (NumberFormatException nfe) { try { // To do: warn on under/overflow? return (short) Double.parseDouble(val); } catch (NumberFormatException newNfe) { ; // ignore, it's not a number } throw new SQLException("Invalid value for getShort() - '" + val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public short getShort(String columnName) throws java.sql.SQLException { return getShort(findColumn(columnName)); } /** * JDBC 2.0 Return the Statement that produced the ResultSet. * * @return the Statment that produced the result set, or null if the result * was produced some other way. * * @exception SQLException if a database-access error occurs */ public java.sql.Statement getStatement() throws SQLException { return (java.sql.Statement) owningStatement; } /** * Get the value of a column in the current row as a Java String * * @param columnIndex the first column is 1, the second is 2... * * @return the column value, null for SQL NULL * * @exception java.sql.SQLException if a database access error occurs * @throws SQLException DOCUMENT ME! */ public String getString(int columnIndex) throws java.sql.SQLException { checkRowPos(); if (fields == null) { throw new java.sql.SQLException("Query generated no fields for ResultSet", SQLError.SQL_STATE_INVALID_COLUMN_NUMBER); } try { if (thisRow[columnIndex - 1] == null) { wasNullFlag = true; return null; } else { wasNullFlag = false; } } catch (NullPointerException E) { wasNullFlag = true; return null; } catch (ArrayIndexOutOfBoundsException aioobEx) { throw new java.sql.SQLException("Column Index out of range ( " + columnIndex + " > " + fields.length + ").", SQLError.SQL_STATE_INVALID_COLUMN_NUMBER); } String stringVal = null; columnIndex--; // JDBC is 1-based, Java is not !? if ((connection != null) && connection.useUnicode()) { try { String encoding = this.fields[columnIndex].getCharacterSet(); if (encoding == null) { stringVal = new String(thisRow[columnIndex]); } else { SingleByteCharsetConverter converter = this.connection.getCharsetConverter(encoding); if (converter != null) { stringVal = converter.toString(thisRow[columnIndex]); } else { stringVal = new String(thisRow[columnIndex], encoding); } } } catch (java.io.UnsupportedEncodingException E) { throw new SQLException("Unsupported character encoding '" + connection.getEncoding() + "'.", SQLError.SQL_STATE_GENERAL_ERROR); } } else { stringVal = StringUtils.toAsciiString(thisRow[columnIndex]); } return stringVal; } /** * The following routines simply convert the columnName into a columnIndex * and then call the appropriate routine above. * * @param columnName is the SQL name of the column * * @return the column value * * @exception java.sql.SQLException if a database access error occurs */ public String getString(String columnName) throws java.sql.SQLException { return getString(findColumn(columnName)); } /** * Get the value of a column in the current row as a java.sql.Time object * * @param columnIndex the first column is 1, the second is 2... * * @return the column value; null if SQL NULL * * @throws java.sql.SQLException if a database access error occurs */ public Time getTime(int columnIndex) throws java.sql.SQLException { return getTimeInternal(columnIndex, DEFAULT_TIMEZONE); } /** * Get the value of a column in the current row as a java.sql.Time object. * * @param columnName is the SQL name of the column * * @return the column value; if the value is SQL NULL, the result is null * * @throws java.sql.SQLException if a database-access error occurs. */ public Time getTime(String columnName) throws java.sql.SQLException { return getTime(findColumn(columnName)); } /** * Get the value of a column in the current row as a java.sql.Time object. * Use the calendar to construct an appropriate millisecond value for the * Time, if the underlying database doesn't store timezone information. * * @param columnIndex the first column is 1, the second is 2, ... * @param cal the calendar to use in constructing the time * * @return the column value; if the value is SQL NULL, the result is null * * @exception SQLException if a database-access error occurs. */ public java.sql.Time getTime(int columnIndex, Calendar cal) throws SQLException { return getTimeInternal(columnIndex, cal.getTimeZone()); } /** * Get the value of a column in the current row as a java.sql.Time object. * Use the calendar to construct an appropriate millisecond value for the * Time, if the underlying database doesn't store timezone information. * * @param columnName is the SQL name of the column * @param cal the calendar to use in constructing the time * * @return the column value; if the value is SQL NULL, the result is null * * @exception SQLException if a database-access error occurs. */ public java.sql.Time getTime(String columnName, Calendar cal) throws SQLException { return getTime(findColumn(columnName), cal); } /** * Get the value of a column in the current row as a java.sql.Timestamp * object * * @param columnIndex the first column is 1, the second is 2... * * @return the column value; null if SQL NULL * * @exception java.sql.SQLException if a database access error occurs */ public Timestamp getTimestamp(int columnIndex) throws java.sql.SQLException { return getTimestampInternal(columnIndex, DEFAULT_TIMEZONE); } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public Timestamp getTimestamp(String columnName) throws java.sql.SQLException { return getTimestamp(findColumn(columnName)); } /** * Get the value of a column in the current row as a java.sql.Timestamp * object. Use the calendar to construct an appropriate millisecond value * for the Timestamp, if the underlying database doesn't store timezone * information. * * @param columnIndex the first column is 1, the second is 2, ... * @param cal the calendar to use in constructing the timestamp * * @return the column value; if the value is SQL NULL, the result is null * * @exception SQLException if a database-access error occurs. */ public java.sql.Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { return getTimestampInternal(columnIndex, cal.getTimeZone()); } /** * Get the value of a column in the current row as a java.sql.Timestamp * object. Use the calendar to construct an appropriate millisecond value * for the Timestamp, if the underlying database doesn't store timezone * information. * * @param columnName is the SQL name of the column * @param cal the calendar to use in constructing the timestamp * * @return the column value; if the value is SQL NULL, the result is null * * @exception SQLException if a database-access error occurs. */ public java.sql.Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException { return getTimestamp(findColumn(columnName), cal); } /** * JDBC 2.0 Return the type of this result set. The type is determined * based on the statement that created the result set. * * @return TYPE_FORWARD_ONLY, TYPE_SCROLL_INSENSITIVE, or * TYPE_SCROLL_SENSITIVE * * @exception SQLException if a database-access error occurs */ public int getType() throws SQLException { return resultSetType; } /** * @see ResultSet#getURL(int) */ public URL getURL(int colIndex) throws SQLException { String val = getString(colIndex); if (val == null) { return null; } else { try { return new URL(val); } catch (MalformedURLException mfe) { throw new SQLException("Malformed URL '" + val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } } /** * @see ResultSet#getURL(String) */ public URL getURL(String colName) throws SQLException { String val = getString(colName); if (val == null) { return null; } else { try { return new URL(val); } catch (MalformedURLException mfe) { throw new SQLException("Malformed URL '" + val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } } /** * A column value can also be retrieved as a stream of Unicode characters. * We implement this as a binary stream. * * @param columnIndex the first column is 1, the second is 2... * * @return a Java InputStream that delivers the database column value as a * stream of two byte Unicode characters. If the value is SQL * NULL, then the result is null * * @exception java.sql.SQLException if a database access error occurs * * @see getAsciiStream * @see getBinaryStream */ public InputStream getUnicodeStream(int columnIndex) throws java.sql.SQLException { checkRowPos(); return getBinaryStream(columnIndex); } /** * DOCUMENT ME! * * @param columnName DOCUMENT ME! * * @return DOCUMENT ME! * * @throws java.sql.SQLException DOCUMENT ME! */ public InputStream getUnicodeStream(String columnName) throws java.sql.SQLException { return getUnicodeStream(findColumn(columnName)); } /** * The first warning reported by calls on this ResultSet is returned. * Subsequent ResultSet warnings will be chained to this * java.sql.SQLWarning. * * <p> * The warning chain is automatically cleared each time a new row is read. * </p> * * <p> * <B>Note:</B> This warning chain only covers warnings caused by ResultSet * methods. Any warnings caused by statement methods (such as reading OUT * parameters) will be chained on the Statement object. * </p> * * @return the first java.sql.SQLWarning or null; * * @exception java.sql.SQLException if a database access error occurs. */ public java.sql.SQLWarning getWarnings() throws java.sql.SQLException { return warningChain; } /** * JDBC 2.0 * * <p> * Move to an absolute row number in the result set. * </p> * * <p> * If row is positive, moves to an absolute row with respect to the * beginning of the result set. The first row is row 1, the second is row * 2, etc. * </p> * * <p> * If row is negative, moves to an absolute row position with respect to * the end of result set. For example, calling absolute(-1) positions the * cursor on the last row, absolute(-2) indicates the next-to-last row, * etc. * </p> * * <p> * An attempt to position the cursor beyond the first/last row in the * result set, leaves the cursor before/after the first/last row, * respectively. * </p> * * <p> * Note: Calling absolute(1) is the same as calling first(). Calling * absolute(-1) is the same as calling last(). * </p> * * @param row the row number to move to * * @return true if on the result set, false if off. * * @exception SQLException if a database-access error occurs, or row is 0, * or result set type is TYPE_FORWARD_ONLY. */ public boolean absolute(int row) throws SQLException { if (Driver.TRACE) { Object[] args = { new Integer(row) }; Debug.methodCall(this, "absolute", args); } checkClosed(); boolean b; if (rowData.size() == 0) { b = false; } else { if (row == 0) { throw new SQLException("Cannot absolute position to row 0", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } if (onInsertRow) { onInsertRow = false; } if (doingUpdates) { doingUpdates = false; } if (row == 1) { b = first(); } else if (row == -1) { b = last(); } else if (row > rowData.size()) { afterLast(); b = false; } else { if (row < 0) { // adjust to reflect after end of result set int newRowPosition = rowData.size() + row + 1; if (newRowPosition <= 0) { beforeFirst(); b = false; } else { b = absolute(newRowPosition); } } else { row--; // adjust for index difference rowData.setCurrentRow(row); thisRow = (byte[][]) rowData.getAt(row); b = true; } } } if (Driver.TRACE) { Debug.returnValue(this, "absolute", new Boolean(b)); } return b; } /** * JDBC 2.0 * * <p> * Moves to the end of the result set, just after the last row. Has no * effect if the result set contains no rows. * </p> * * @exception SQLException if a database-access error occurs, or result set * type is TYPE_FORWARD_ONLY. */ public void afterLast() throws SQLException { if (Driver.TRACE) { Object[] args = { }; Debug.methodCall(this, "afterLast", args); } checkClosed(); if (onInsertRow) { onInsertRow = false; } if (doingUpdates) { doingUpdates = false; } if (rowData.size() != 0) { rowData.afterLast(); thisRow = null; } } /** * JDBC 2.0 * * <p> * Moves to the front of the result set, just before the first row. Has no * effect if the result set contains no rows. * </p> * * @exception SQLException if a database-access error occurs, or result set * type is TYPE_FORWARD_ONLY */ public void beforeFirst() throws SQLException { if (Driver.TRACE) { Object[] args = { }; Debug.methodCall(this, "beforeFirst", args); } checkClosed(); if (onInsertRow) { onInsertRow = false; } if (doingUpdates) { doingUpdates = false; } if (rowData.size() == 0) { return; } else { rowData.beforeFirst(); thisRow = null; } } /** * JDBC 2.0 The cancelRowUpdates() method may be called after calling an * updateXXX() method(s) and before calling updateRow() to rollback the * updates made to a row. If no updates have been made or updateRow() has * already been called, then this method has no effect. * * @exception SQLException if a database-access error occurs, or if called * when on the insert row. * @throws NotUpdatable DOCUMENT ME! */ public void cancelRowUpdates() throws SQLException { throw new NotUpdatable(); } /** * After this call, getWarnings returns null until a new warning is * reported for this ResultSet * * @exception java.sql.SQLException if a database access error occurs */ public void clearWarnings() throws java.sql.SQLException { warningChain = null; } /** * In some cases, it is desirable to immediately release a ResultSet * database and JDBC resources instead of waiting for this to happen when * it is automatically closed. The close method provides this immediate * release. * * <p> * <B>Note:</B> A ResultSet is automatically closed by the Statement the * Statement that generated it when that Statement is closed, re-executed, * or is used to retrieve the next result from a sequence of multiple * results. A ResultSet is also automatically closed when it is garbage * collected. * </p> * * @exception java.sql.SQLException if a database access error occurs */ public void close() throws java.sql.SQLException { realClose(true); } /** * JDBC 2.0 Delete the current row from the result set and the underlying * database. Cannot be called when on the insert row. * * @exception SQLException if a database-access error occurs, or if called * when on the insert row. * @throws NotUpdatable DOCUMENT ME! */ public void deleteRow() throws SQLException { throw new NotUpdatable(); } /** * Map a ResultSet column name to a ResultSet column index * * @param columnName the name of the column * * @return the column index * * @exception java.sql.SQLException if a database access error occurs */ public int findColumn(String columnName) throws java.sql.SQLException { Integer index; synchronized (this) { if (!hasBuiltIndexMapping) { buildIndexMapping(); } } index = (Integer) columnNameToIndex.get(columnName); if (index == null) { index = (Integer) fullColumnNameToIndex.get(columnName); } if (index != null) { return index.intValue() + 1; } else { // Try this inefficient way, now String columnNameUC = columnName.toUpperCase(); for (int i = 0; i < fields.length; i++) { if (fields[i].getName().toUpperCase().equals(columnNameUC)) { return i + 1; } else if (fields[i].getFullName().toUpperCase().equals(columnNameUC)) { return i + 1; } } throw new java.sql.SQLException("Column '" + columnName + "' not found.", SQLError.SQL_STATE_COLUMN_NOT_FOUND); } } /** * JDBC 2.0 * * <p> * Moves to the first row in the result set. * </p> * * @return true if on a valid row, false if no rows in the result set. * * @exception SQLException if a database-access error occurs, or result set * type is TYPE_FORWARD_ONLY. */ public boolean first() throws SQLException { if (Driver.TRACE) { Object[] args = { }; Debug.methodCall(this, "first", args); } checkClosed(); if (onInsertRow) { onInsertRow = false; } if (rowData.isEmpty()) { return false; } else { if (doingUpdates) { doingUpdates = false; } rowData.beforeFirst(); thisRow = rowData.next(); return true; } } /** * JDBC 2.0 Insert the contents of the insert row into the result set and * the database. Must be on the insert row when this method is called. * * @exception SQLException if a database-access error occurs, if called * when not on the insert row, or if all non-nullable columns * in the insert row have not been given a value * @throws NotUpdatable DOCUMENT ME! */ public void insertRow() throws SQLException { throw new NotUpdatable(); } /** * JDBC 2.0 * * <p> * Moves to the last row in the result set. * </p> * * @return true if on a valid row, false if no rows in the result set. * * @exception SQLException if a database-access error occurs, or result set * type is TYPE_FORWARD_ONLY. */ public boolean last() throws SQLException { if (Driver.TRACE) { Object[] args = { }; Debug.methodCall(this, "last", args); } checkClosed(); if (rowData.size() == 0) { return false; } else { if (onInsertRow) { onInsertRow = false; } if (doingUpdates) { doingUpdates = false; } rowData.beforeLast(); thisRow = rowData.next(); return true; } } /** * JDBC 2.0 Move the cursor to the remembered cursor position, usually the * current row. Has no effect unless the cursor is on the insert row. * * @exception SQLException if a database-access error occurs, or the result * set is not updatable * @throws NotUpdatable DOCUMENT ME! */ public void moveToCurrentRow() throws SQLException { throw new NotUpdatable(); } /** * JDBC 2.0 Move to the insert row. The current cursor position is * remembered while the cursor is positioned on the insert row. The insert * row is a special row associated with an updatable result set. It is * essentially a buffer where a new row may be constructed by calling the * updateXXX() methods prior to inserting the row into the result set. * Only the updateXXX(), getXXX(), and insertRow() methods may be called * when the cursor is on the insert row. All of the columns in a result * set must be given a value each time this method is called before * calling insertRow(). UpdateXXX()must be called before getXXX() on a * column. * * @exception SQLException if a database-access error occurs, or the result * set is not updatable * @throws NotUpdatable DOCUMENT ME! */ public void moveToInsertRow() throws SQLException { throw new NotUpdatable(); } /** * A ResultSet is initially positioned before its first row, the first call * to next makes the first row the current row; the second call makes the * second row the current row, etc. * * <p> * If an input stream from the previous row is open, it is implicitly * closed. The ResultSet's warning chain is cleared when a new row is * read * </p> * * @return true if the new current is valid; false if there are no more * rows * * @exception java.sql.SQLException if a database access error occurs */ public boolean next() throws java.sql.SQLException { if (Driver.TRACE) { Object[] args = { }; Debug.methodCall(this, "next", args); } checkClosed(); if (onInsertRow) { onInsertRow = false; } if (doingUpdates) { doingUpdates = false; } boolean b; if (!reallyResult()) { throw new java.sql.SQLException("ResultSet is from UPDATE. No Data", SQLError.SQL_STATE_GENERAL_ERROR); } if (rowData.size() == 0) { b = false; } else { if (!rowData.hasNext()) { // force scroll past end rowData.next(); b = false; } else { clearWarnings(); thisRow = rowData.next(); b = true; } } if (Driver.TRACE) { Debug.returnValue(this, "next", new Boolean(b)); } return b; } /** * The prev method is not part of JDBC, but because of the architecture of * this driver it is possible to move both forward and backward within the * result set. * * <p> * If an input stream from the previous row is open, it is implicitly * closed. The ResultSet's warning chain is cleared when a new row is * read * </p> * * @return true if the new current is valid; false if there are no more * rows * * @exception java.sql.SQLException if a database access error occurs */ public boolean prev() throws java.sql.SQLException { checkClosed(); int rowIndex = rowData.getCurrentRowNumber(); if ((rowIndex - 1) >= 0) { rowIndex--; rowData.setCurrentRow(rowIndex); thisRow = (byte[][]) rowData.getAt(rowIndex); return true; } else if ((rowIndex - 1) == -1) { rowIndex--; rowData.setCurrentRow(rowIndex); thisRow = null; return false; } else { return false; } } /** * JDBC 2.0 * * <p> * Moves to the previous row in the result set. * </p> * * <p> * Note: previous() is not the same as relative(-1) since it makes sense to * call previous() when there is no current row. * </p> * * @return true if on a valid row, false if off the result set. * * @exception SQLException if a database-access error occurs, or result set * type is TYPE_FORWAR_DONLY. */ public boolean previous() throws SQLException { if (Driver.TRACE) { Object[] args = { }; Debug.methodCall(this, "previous", args); } if (onInsertRow) { onInsertRow = false; } if (doingUpdates) { doingUpdates = false; } return prev(); } /** * JDBC 2.0 Refresh the value of the current row with its current value in * the database. Cannot be called when on the insert row. The * refreshRow() method provides a way for an application to explicitly * tell the JDBC driver to refetch a row(s) from the database. An * application may want to call refreshRow() when caching or prefetching * is being done by the JDBC driver to fetch the latest value of a row * from the database. The JDBC driver may actually refresh multiple rows * at once if the fetch size is greater than one. All values are refetched * subject to the transaction isolation level and cursor sensitivity. If * refreshRow() is called after calling updateXXX(), but before calling * updateRow() then the updates made to the row are lost. Calling * refreshRow() frequently will likely slow performance. * * @exception SQLException if a database-access error occurs, or if called * when on the insert row. * @throws NotUpdatable DOCUMENT ME! */ public void refreshRow() throws SQLException { throw new NotUpdatable(); } /** * JDBC 2.0 * * <p> * Moves a relative number of rows, either positive or negative. Attempting * to move beyond the first/last row in the result set positions the * cursor before/after the the first/last row. Calling relative(0) is * valid, but does not change the cursor position. * </p> * * <p> * Note: Calling relative(1) is different than calling next() since is * makes sense to call next() when there is no current row, for example, * when the cursor is positioned before the first row or after the last * row of the result set. * </p> * * @param rows the number of relative rows to move the cursor. * * @return true if on a row, false otherwise. * * @throws SQLException if a database-access error occurs, or there is no * current row, or result set type is TYPE_FORWARD_ONLY. */ public boolean relative(int rows) throws SQLException { if (Driver.TRACE) { Object[] args = { new Integer(rows) }; Debug.methodCall(this, "relative", args); } checkClosed(); if (rowData.size() == 0) { return false; } rowData.moveRowRelative(rows); thisRow = rowData.getAt(rowData.getCurrentRowNumber()); boolean b = (!rowData.isAfterLast() && !rowData.isBeforeFirst()); if (Driver.TRACE) { Debug.returnValue(this, "relative", new Boolean(b)); } return b; } /** * JDBC 2.0 Determine if this row has been deleted. A deleted row may * leave a visible "hole" in a result set. This method can be used to * detect holes in a result set. The value returned depends on whether or * not the result set can detect deletions. * * @return true if deleted and deletes are detected * * @exception SQLException if a database-access error occurs * @throws NotImplemented DOCUMENT ME! * * @see DatabaseMetaData#deletesAreDetected */ public boolean rowDeleted() throws SQLException { throw new NotImplemented(); } /** * JDBC 2.0 Determine if the current row has been inserted. The value * returned depends on whether or not the result set can detect visible * inserts. * * @return true if inserted and inserts are detected * * @exception SQLException if a database-access error occurs * @throws NotImplemented DOCUMENT ME! * * @see DatabaseMetaData#insertsAreDetected */ public boolean rowInserted() throws SQLException { throw new NotImplemented(); } //--------------------------------------------------------------------- // Updates //--------------------------------------------------------------------- /** * JDBC 2.0 Determine if the current row has been updated. The value * returned depends on whether or not the result set can detect updates. * * @return true if the row has been visibly updated by the owner or * another, and updates are detected * * @exception SQLException if a database-access error occurs * @throws NotImplemented DOCUMENT ME! * * @see DatabaseMetaData#updatesAreDetected */ public boolean rowUpdated() throws SQLException { throw new NotImplemented(); } /** * @see ResultSet#updateArray(int, Array) */ public void updateArray(int arg0, Array arg1) throws SQLException { throw new NotImplemented(); } /** * @see ResultSet#updateArray(String, Array) */ public void updateArray(String arg0, Array arg1) throws SQLException { throw new NotImplemented(); } /** * 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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * @param length the length of the stream * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateAsciiStream(int columnIndex, java.io.InputStream x, int length) throws SQLException { throw new NotUpdatable(); } /** * 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 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 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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { throw new NotUpdatable(); } /** * 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 void updateBigDecimal(String columnName, BigDecimal x) throws SQLException { updateBigDecimal(findColumn(columnName), x); } /** * 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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * @param length the length of the stream * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateBinaryStream(int columnIndex, java.io.InputStream x, int length) throws SQLException { throw new NotUpdatable(); } /** * 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 void updateBinaryStream(String columnName, java.io.InputStream x, int length) throws SQLException { updateBinaryStream(findColumn(columnName), x, length); } /** * @see ResultSet#updateBlob(int, Blob) */ public void updateBlob(int arg0, java.sql.Blob arg1) throws SQLException { throw new NotUpdatable(); } /** * @see ResultSet#updateBlob(String, Blob) */ public void updateBlob(String arg0, java.sql.Blob arg1) throws SQLException { throw new NotUpdatable(); } /** * 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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateBoolean(int columnIndex, boolean x) throws SQLException { throw new NotUpdatable(); } /** * 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 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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateByte(int columnIndex, byte x) throws SQLException { throw new NotUpdatable(); } /** * 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 void updateByte(String columnName, byte x) throws SQLException { updateByte(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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateBytes(int columnIndex, byte[] x) throws SQLException { throw new NotUpdatable(); } /** * 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 void updateBytes(String columnName, byte[] x) throws SQLException { updateBytes(findColumn(columnName), x); } /** * 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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * @param length the length of the stream * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateCharacterStream(int columnIndex, java.io.Reader x, int length) throws SQLException { throw new NotUpdatable(); } /** * 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 reader the stream to update the column with * @param length of the stream * * @throws SQLException if a database-access error occurs */ public void updateCharacterStream(String columnName, java.io.Reader reader, int length) throws SQLException { updateCharacterStream(findColumn(columnName), reader, length); } /** * @see ResultSet#updateClob(int, Clob) */ public void updateClob(int arg0, java.sql.Clob arg1) throws SQLException { throw new NotUpdatable(); } /** * @see ResultSet#updateClob(String, Clob) */ public void updateClob(String columnName, java.sql.Clob clob) throws SQLException { updateClob(findColumn(columnName), clob); } /** * 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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateDate(int columnIndex, java.sql.Date x) throws SQLException { throw new NotUpdatable(); } /** * 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 void updateDate(String columnName, java.sql.Date x) throws SQLException { updateDate(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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateDouble(int columnIndex, double x) throws SQLException { throw new NotUpdatable(); } /** * 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 void updateDouble(String columnName, double x) throws SQLException { updateDouble(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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateFloat(int columnIndex, float x) throws SQLException { throw new NotUpdatable(); } /** * 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 void updateFloat(String columnName, float x) throws SQLException { updateFloat(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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateInt(int columnIndex, int x) throws SQLException { throw new NotUpdatable(); } /** * 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 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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateLong(int columnIndex, long x) throws SQLException { throw new NotUpdatable(); } /** * 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 void updateLong(String columnName, long x) throws SQLException { updateLong(findColumn(columnName), x); } /** * JDBC 2.0 Give a nullable column a null 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 columnIndex the first column is 1, the second is 2, ... * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateNull(int columnIndex) throws SQLException { throw new NotUpdatable(); } /** * JDBC 2.0 Update a column with a null 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 * * @exception SQLException if a database-access error occurs */ public void updateNull(String columnName) throws SQLException { updateNull(findColumn(columnName)); } /** * 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 columnIndex the first column is 1, the second is 2, ... * @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 * @throws NotUpdatable DOCUMENT ME! */ public void updateObject(int columnIndex, Object x, int scale) throws SQLException { throw new NotUpdatable(); } /** * 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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateObject(int columnIndex, Object x) throws SQLException { throw new NotUpdatable(); } /** * 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 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 void updateObject(String columnName, Object x) throws SQLException { updateObject(findColumn(columnName), x); } /** * @see ResultSet#updateRef(int, Ref) */ public void updateRef(int arg0, Ref arg1) throws SQLException { throw new NotImplemented(); } /** * @see ResultSet#updateRef(String, Ref) */ public void updateRef(String arg0, Ref arg1) throws SQLException { throw new NotImplemented(); } /** * JDBC 2.0 Update the underlying database with the new contents of the * current row. Cannot be called when on the insert row. * * @exception SQLException if a database-access error occurs, or if called * when on the insert row * @throws NotUpdatable DOCUMENT ME! */ public void updateRow() throws SQLException { throw new NotUpdatable(); } /** * 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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateShort(int columnIndex, short x) throws SQLException { throw new NotUpdatable(); } /** * 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 void updateShort(String columnName, short x) throws SQLException { updateShort(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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateString(int columnIndex, String x) throws SQLException { throw new NotUpdatable(); } /** * 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 void updateString(String columnName, String x) throws SQLException { updateString(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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateTime(int columnIndex, java.sql.Time x) throws SQLException { throw new NotUpdatable(); } /** * 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 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 columnIndex the first column is 1, the second is 2, ... * @param x the new column value * * @exception SQLException if a database-access error occurs * @throws NotUpdatable DOCUMENT ME! */ public void updateTimestamp(int columnIndex, java.sql.Timestamp x) throws SQLException { throw new NotUpdatable(); } /** * 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 void updateTimestamp(String columnName, java.sql.Timestamp x) throws SQLException { updateTimestamp(findColumn(columnName), x); } /** * A column may have the value of SQL NULL; wasNull() reports whether the * last column read had this special value. Note that you must first call * getXXX on a column to try to read its value and then call wasNull() to * find if the value was SQL NULL * * @return true if the last column read was SQL NULL * * @exception java.sql.SQLException if a database access error occurred */ public boolean wasNull() throws java.sql.SQLException { return wasNullFlag; } /////////////////////////////////////////// // // These number conversion routines save // a ton of "new()s", especially for the heavily // used getInt() and getDouble() methods // /////////////////////////////////////////// /** * Converts a string representation of a number to a double. Need a faster * way to do this. * * @param colIndex the 1-based index of the column to retrieve a double * from. * * @return the double value represented by the string in buf * * @throws SQLException if an error occurs */ protected double getDoubleInternal(int colIndex) throws SQLException { String s = ""; try { s = getString(colIndex); if ((s == null) || (s.length() == 0)) { return 0; } double d = Double.parseDouble(s); if (this.useStrictFloatingPoint) { // Fix endpoint rounding precision loss in MySQL server if (d == 2.147483648E9) { // Fix Odd end-point rounding on MySQL d = 2.147483647E9; } else if (d == 1.0000000036275E-15) { // Fix odd end-point rounding on MySQL d = 1.0E-15; } else if (d == 9.999999869911E14) { d = 9.99999999999999E14; } else if (d == 1.4012984643248E-45) { d = 1.4E-45; } else if (d == 1.4013E-45) { d = 1.4E-45; } else if (d == 3.4028234663853E37) { d = 3.4028235E37; } else if (d == -2.14748E9) { d = -2.147483648E9; } else if (d == 3.40282E37) { d = 3.4028235E37; } } return d; } catch (NumberFormatException e) { throw new SQLException("Bad format for number '" + s + "'"); } } /** * Sets the first character of the query that this result set was created * from. * * @param c the first character of the query...uppercased */ protected void setFirstCharOfQuery(char c) { this.firstCharOfQuery = c; } /** * Returns the first character of the query that this result set was * created from. * * @return the first character of the query...uppercased */ protected char getFirstCharOfQuery() { return this.firstCharOfQuery; } /** * Sets the concurrency (JDBC2) * * @param concurrencyFlag CONCUR_UPDATABLE or CONCUR_READONLY */ protected void setResultSetConcurrency(int concurrencyFlag) { resultSetConcurrency = concurrencyFlag; } /** * Sets the result set type for (JDBC2) * * @param typeFlag SCROLL_SENSITIVE or SCROLL_INSENSITIVE (we only support * SCROLL_INSENSITIVE) */ protected void setResultSetType(int typeFlag) { resultSetType = typeFlag; } /** * Sets server info (if any) * * @param info the server info message */ protected void setServerInfo(String info) { this.serverInfo = info; } /** * Returns the server info (if any), or null if none. * * @return server info created for this ResultSet */ protected String getServerInfo() { return this.serverInfo; } /** * Builds a hash between column names and their indices for fast retrieval. */ protected void buildIndexMapping() { int numFields = fields.length; columnNameToIndex = new HashMap(); fullColumnNameToIndex = new HashMap(); // We do this in reverse order, so that the 'first' column // with a given name ends up as the final mapping in the // hashtable... // // Quoting the JDBC Spec: // // "Column names used as input to getter // methods are case insensitive. When a getter method is called with a column // name and several columns have the same name, the value of the first // matching column will be returned. " // for (int i = numFields-1; i>= 0 ; i--) { Integer index = new Integer(i); String columnName = fields[i].getName(); String fullColumnName = fields[i].getFullName(); if (columnName != null) { columnNameToIndex.put(columnName, index); columnNameToIndex.put(columnName.toUpperCase(), index); columnNameToIndex.put(columnName.toLowerCase(), index); } if (fullColumnName != null) { fullColumnNameToIndex.put(fullColumnName, index); fullColumnNameToIndex.put(fullColumnName.toUpperCase(), index); fullColumnNameToIndex.put(fullColumnName.toLowerCase(), index); } } // set the flag to prevent rebuilding... hasBuiltIndexMapping = true; } /** * Ensures that the result set is not closed * * @throws SQLException if the result set is closed */ protected void checkClosed() throws SQLException { if (isClosed) { throw new SQLException("Operation not allowed after ResultSet closed", SQLError.SQL_STATE_GENERAL_ERROR); } } /** * Ensures that the cursor is positioned on a valid row and that the result * set is not closed * * @throws SQLException if the result set is not in a valid state for * traversal */ protected void checkRowPos() throws SQLException { checkClosed(); if (!rowData.isDynamic() && (rowData.size() == 0)) { throw new SQLException("Illegal operation on empty result set", SQLError.SQL_STATE_GENERAL_ERROR); } if (rowData.isBeforeFirst()) { throw new SQLException("Before start of result set", SQLError.SQL_STATE_GENERAL_ERROR); } if (rowData.isAfterLast()) { throw new SQLException("After end of result set", SQLError.SQL_STATE_GENERAL_ERROR); } } protected void realClose(boolean closeRowData) throws SQLException { try { if (closeRowData && (this.rowData != null)) { rowData.close(); } } finally { rowData = null; isClosed = true; } } void setStatement(com.mysql.jdbc.Statement stmt) { owningStatement = stmt; } long getUpdateCount() { return updateCount; } long getUpdateID() { return updateId; } boolean reallyResult() { return reallyResult; } /** * Get the value of a column in the current row as a java.sql.Time object * in the given timezone * * @param columnIndex the first column is 1, the second is 2... * @param tz the Timezone to use * * @return the column value; null if SQL NULL * * @exception java.sql.SQLException if a database access error occurs */ private Time getTimeInternal(int columnIndex, TimeZone tz) throws java.sql.SQLException { int hr = 0; int min = 0; int sec = 0; try { String timeAsString = getString(columnIndex); if (timeAsString == null) { return null; } else { int length = timeAsString.length(); if ((length > 0) && (timeAsString.charAt(0) == '0') && (timeAsString.equals("0000-00-00") || timeAsString.equals("0000-00-00 00:00:00") || timeAsString.equals("00000000000000"))) { wasNullFlag = true; return null; } Field timeColField = fields[columnIndex - 1]; if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { // It's a timestamp switch (length) { case 14: case 12: { hr = Integer.parseInt(timeAsString.substring(length - 6, length - 4)); min = Integer.parseInt(timeAsString.substring(length - 4, length - 2)); sec = Integer.parseInt(timeAsString.substring(length - 2, length)); } break; case 10: { hr = Integer.parseInt(timeAsString.substring(6, 8)); min = Integer.parseInt(timeAsString.substring(8, 10)); sec = 0; } break; default: throw new SQLException( "Timestamp too small to convert to Time value in column " + columnIndex + "(" + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } /* endswitch */ SQLWarning precisionLost = new SQLWarning( "Precision lost converting TIMESTAMP to Time with getTime() on column " + columnIndex + "(" + fields[columnIndex - 1] + ")."); if (warningChain == null) { warningChain = precisionLost; } else { warningChain.setNextWarning(precisionLost); } } else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATETIME) { hr = Integer.parseInt(timeAsString.substring(11, 13)); min = Integer.parseInt(timeAsString.substring(14, 16)); sec = Integer.parseInt(timeAsString.substring(17, 19)); SQLWarning precisionLost = new SQLWarning( "Precision lost converting DATETIME to Time with getTime() on column " + columnIndex + "(" + fields[columnIndex - 1] + ")."); if (warningChain == null) { warningChain = precisionLost; } else { warningChain.setNextWarning(precisionLost); } } else { // convert a String to a Time if ((length != 5) && (length != 8)) { throw new SQLException("Bad format for Time '" + timeAsString + "' in column " + columnIndex + "(" + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } hr = Integer.parseInt(timeAsString.substring(0, 2)); min = Integer.parseInt(timeAsString.substring(3, 5)); sec = (length == 5) ? 0 : Integer.parseInt(timeAsString .substring(6)); } return TimeUtil.changeTimezone(this.connection, fastTimeCreate(null, hr, min, sec), connection.getServerTimezone(), tz); } } catch (Exception ex) { throw new java.sql.SQLException(ex.getClass().getName(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } /** * Get the value of a column in the current row as a java.sql.Timestamp * object in the given timezone * * @param columnIndex the first column is 1, the second is 2... * @param tz the timezone to use * * @return the column value; null if SQL NULL * * @exception java.sql.SQLException if a database access error occurs */ private Timestamp getTimestampInternal(int columnIndex, TimeZone tz) throws java.sql.SQLException { String timestampValue = getString(columnIndex); try { if (timestampValue == null) { return null; } else { int length = timestampValue.length(); if ((length > 0) && (timestampValue.charAt(0) == '0') && (timestampValue.equals("0000-00-00") || timestampValue.equals("0000-00-00 00:00:00") || timestampValue.equals("00000000000000") || timestampValue.equals("0"))) { wasNullFlag = true; return null; } else if (fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { return TimeUtil.changeTimezone(this.connection, fastTimestampCreate(null, Integer.parseInt(timestampValue.substring(0, 4)) - 1900, 0, 1, 0, 0, 0, 0), connection.getServerTimezone(), tz); } else { // Convert from TIMESTAMP or DATE switch (length) { case 19: { int year = Integer.parseInt(timestampValue.substring( 0, 4)); int month = Integer.parseInt(timestampValue.substring( 5, 7)); int day = Integer.parseInt(timestampValue.substring(8, 10)); int hour = Integer.parseInt(timestampValue.substring( 11, 13)); int minutes = Integer.parseInt(timestampValue.substring( 14, 16)); int seconds = Integer.parseInt(timestampValue.substring( 17, 19)); return TimeUtil.changeTimezone(this.connection, fastTimestampCreate(null, year - 1900, month - 1, day, hour, minutes, seconds, 0), connection.getServerTimezone(), tz); } case 14: { int year = Integer.parseInt(timestampValue.substring( 0, 4)); int month = Integer.parseInt(timestampValue.substring( 4, 6)); int day = Integer.parseInt(timestampValue.substring(6, 8)); int hour = Integer.parseInt(timestampValue.substring( 8, 10)); int minutes = Integer.parseInt(timestampValue.substring( 10, 12)); int seconds = Integer.parseInt(timestampValue.substring( 12, 14)); return TimeUtil.changeTimezone(this.connection, fastTimestampCreate(null, year - 1900, month - 1, day, hour, minutes, seconds, 0), connection.getServerTimezone(), tz); } case 12: { int year = Integer.parseInt(timestampValue.substring( 0, 2)); if (year <= 69) { year = (year + 100); } int month = Integer.parseInt(timestampValue.substring( 2, 4)); int day = Integer.parseInt(timestampValue.substring(4, 6)); int hour = Integer.parseInt(timestampValue.substring( 6, 8)); int minutes = Integer.parseInt(timestampValue.substring( 8, 10)); int seconds = Integer.parseInt(timestampValue.substring( 10, 12)); return TimeUtil.changeTimezone(this.connection, fastTimestampCreate(null, year, month - 1, day, hour, minutes, seconds, 0), connection.getServerTimezone(), tz); } case 10: { int year; int month; int day; int hour; int minutes; if ((this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) || (timestampValue.indexOf("-") != -1)) { year = Integer.parseInt(timestampValue.substring( 0, 4)) - 1900; month = Integer.parseInt(timestampValue.substring( 5, 7)); day = Integer.parseInt(timestampValue.substring(8, 10)); hour = 0; minutes = 0; } else { year = Integer.parseInt(timestampValue.substring( 0, 2)); if (year <= 69) { year = (year + 100); } month = Integer.parseInt(timestampValue.substring( 2, 4)); day = Integer.parseInt(timestampValue.substring(4, 6)); hour = Integer.parseInt(timestampValue.substring( 6, 8)); minutes = Integer.parseInt(timestampValue.substring( 8, 10)); } return TimeUtil.changeTimezone(this.connection, fastTimestampCreate(null, year, month - 1, day, hour, minutes, 0, 0), connection.getServerTimezone(), tz); } case 8: { int year = Integer.parseInt(timestampValue.substring( 0, 4)); int month = Integer.parseInt(timestampValue.substring( 4, 6)); int day = Integer.parseInt(timestampValue.substring(6, 8)); return TimeUtil.changeTimezone(this.connection, fastTimestampCreate(null, year - 1900, month - 1, day, 0, 0, 0, 0), connection.getServerTimezone(), tz); } case 6: { int year = Integer.parseInt(timestampValue.substring( 0, 2)); if (year <= 69) { year = (year + 100); } int month = Integer.parseInt(timestampValue.substring( 2, 4)); int day = Integer.parseInt(timestampValue.substring(4, 6)); return TimeUtil.changeTimezone(this.connection, fastTimestampCreate(null, year, month - 1, day, 0, 0, 0, 0), connection.getServerTimezone(), tz); } case 4: { int year = Integer.parseInt(timestampValue.substring( 0, 2)); if (year <= 69) { year = (year + 100); } int month = Integer.parseInt(timestampValue.substring( 2, 4)); return TimeUtil.changeTimezone(this.connection, fastTimestampCreate(null, year, month - 1, 1, 0, 0, 0, 0), connection.getServerTimezone(), tz); } case 2: { int year = Integer.parseInt(timestampValue.substring( 0, 2)); if (year <= 69) { year = (year + 100); } return TimeUtil.changeTimezone(this.connection, fastTimestampCreate(null, year, 0, 1, 0, 0, 0, 0), connection.getServerTimezone(), tz); } default: throw new java.sql.SQLException( "Bad format for Timestamp '" + timestampValue + "' in column " + columnIndex + "(" + fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } } } catch (Exception e) { throw new java.sql.SQLException("Cannot convert value '" + timestampValue + "' from column " + columnIndex + "(" + timestampValue + " ) to TIMESTAMP.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } } private synchronized Date fastDateCreate(Calendar cal, int year, int month, int day) { if (cal == null) { if (this.fastDateCal == null) { this.fastDateCal = new GregorianCalendar(); this.fastDateCal.setTimeZone(DEFAULT_TIMEZONE); } cal = this.fastDateCal; } cal.clear(); cal.set(year + 1900, month, day, 0, 0, 0); long dateAsMillis = 0; try { dateAsMillis = cal.getTimeInMillis(); } catch (IllegalAccessError iae) { // Must be on JDK-1.3.1 or older.... dateAsMillis = cal.getTime().getTime(); } return new Date(dateAsMillis); } private synchronized Time fastTimeCreate(Calendar cal, int hour, int minute, int second) { if (cal == null) { if (this.fastDateCal == null) { this.fastDateCal = new GregorianCalendar(); this.fastDateCal.setTimeZone(DEFAULT_TIMEZONE); } cal = this.fastDateCal; } cal.clear(); // Set 'date' to epoch of Jan 1, 1970 cal.set(1970, 0, 1, hour, minute, second); long timeAsMillis = 0; try { timeAsMillis = cal.getTimeInMillis(); } catch (IllegalAccessError iae) { // Must be on JDK-1.3.1 or older.... timeAsMillis = cal.getTime().getTime(); } return new Time(timeAsMillis); } private synchronized Timestamp fastTimestampCreate(Calendar cal, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) { if (cal == null) { if (this.fastDateCal == null) { this.fastDateCal = new GregorianCalendar(); this.fastDateCal.setTimeZone(DEFAULT_TIMEZONE); } cal = this.fastDateCal; } cal.clear(); cal.set(year + 1900, month, day, hour, minute, seconds); long tsAsMillis = 0; try { tsAsMillis = cal.getTimeInMillis(); } catch (IllegalAccessError iae) { // Must be on JDK-1.3.1 or older.... tsAsMillis = cal.getTime().getTime(); } Timestamp ts = new Timestamp(tsAsMillis); ts.setNanos(secondsPart); return ts; } }