/* * MM JDBC Drivers for MySQL * * $Id: ResultSet.java,v 1.7 2002/05/15 03:15:00 mark_matthews Exp $ * * Copyright (C) 1998 Mark Matthews * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * See the COPYING file located in the top-level-directory of * the archive of this library for complete text of license. * * Some portions: * * Copyright (c) 1996 Bradley McLean / Jeffrey Medeiros * Modifications Copyright (c) 1996/1997 Martin Rode * Copyright (c) 1997 Peter T Mount */ /** * 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. * *

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. * *

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. * *

For maximum portability, ResultSet columns within each row should be read * in left-to-right order and each column should be read only once. * *

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. * *

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. * *

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. * *

The number, types and properties of a ResultSet's columns are provided by * the ResultSetMetaData object returned by the getMetaData method. * * @see ResultSetMetaData * @see java.sql.ResultSet * @author Mark Matthews * @version $Id: ResultSet.java,v 1.7 2002/05/15 03:15:00 mark_matthews Exp $ */ package com.mysql.jdbc; import java.io.*; import java.math.*; import java.text.*; import java.util.*; import java.sql.*; public abstract class ResultSet { protected Vector _rows; // The results protected Field[] _fields; // The fields protected int _currentRow = -1; // Cursor to current row; protected byte[][] _thisRow; // Values for current row protected com.mysql.jdbc.Connection _connection; // The connection that created us protected java.sql.SQLWarning _warnings = null; // The warning chain protected boolean _wasNullFlag = false; // for wasNull() protected boolean _reallyResult = false; // for executeUpdate vs. execute protected Hashtable _columnNameToIndex = null; protected Hashtable _fullColumnNameToIndex = null; protected int _resultSetType = 0; protected int _resultSetConcurrency = 0; protected com.mysql.jdbc.Statement _owningStatement; protected boolean _closed = false; // 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. // protected long _updateID = -1; // for AUTO_INCREMENT protected long _updateCount; // How many rows did we update? // For getTimestamp() private SimpleDateFormat _TSDF = null; /** * 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. * *

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 * * @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); } boolean b; if (!reallyResult()) throw new java.sql.SQLException("ResultSet is from UPDATE. No Data", "S1000"); if (_rows.size() == 0) { b = false; } else { if (_currentRow + 1 >= _rows.size()) { // force scroll past end _currentRow = _rows.size(); b = false; } else { clearWarnings(); _currentRow = _currentRow + 1; _thisRow = (byte[][]) _rows.elementAt(_currentRow); 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. * *

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 * * @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 { if (_currentRow - 1 >= 0) { _currentRow--; _thisRow = (byte[][]) _rows.elementAt(_currentRow); return true; } else { return false; } } /** * 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. * *

Note: 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. * * @exception java.sql.SQLException if a database access error occurs */ public void close() throws java.sql.SQLException { if (_rows != null) { _rows.removeAllElements(); } _closed = true; } /** * 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; } /** * 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 */ public String getString(int columnIndex) throws java.sql.SQLException { checkRowPos(); if (_fields == null) { throw new java.sql.SQLException( "Query generated no fields for ResultSet", "S1002"); } if (columnIndex < 1 || columnIndex > _fields.length) throw new java.sql.SQLException( "Column Index out of range ( " + columnIndex + " > " + _fields.length + ").", "S1002"); try { if (_thisRow[columnIndex - 1] == null) { _wasNullFlag = true; } else { _wasNullFlag = false; } } catch (NullPointerException E) { _wasNullFlag = true; } if (_wasNullFlag) return null; if (_connection != null && _connection.useUnicode()) { try { String Encoding = _connection.getEncoding(); if (Encoding == null) { return new String(_thisRow[columnIndex - 1]); } else { return new String(_thisRow[columnIndex - 1], _connection.getEncoding()); } } catch (java.io.UnsupportedEncodingException E) { throw new SQLException( "Unsupported character encoding '" + _connection.getEncoding() + "'.", "0S100"); } } else { return new String(_thisRow[columnIndex - 1]); } } /** * 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 S = getString(columnIndex); if (S != null && S.length() > 0) { int c = S.toLowerCase().charAt(0); return ((c == 't') || (c == 'y') || (c == '1') || S.equals("-1")); } return false; // SQL NULL } /** * 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 */ public byte getByte(int columnIndex) throws java.sql.SQLException { checkRowPos(); if (columnIndex < 1 || columnIndex > _fields.length) throw new java.sql.SQLException( "Column Index out of range ( " + columnIndex + " > " + _fields.length + ").", "S1002"); try { if (_thisRow[columnIndex - 1] == null) { _wasNullFlag = true; } else { _wasNullFlag = false; } } catch (NullPointerException E) { _wasNullFlag = true; } if (_wasNullFlag) { return 0; } Field F = _fields[columnIndex - 1]; switch (F.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 S = getString(columnIndex); // Strip off the decimals if (S.indexOf(".") != -1) { S = S.substring(0, S.indexOf(".")); } return Byte.parseByte(S); } catch (NumberFormatException NFE) { throw new SQLException( "Value '" + getString(columnIndex) + "' is out of range [-127,127]", "S1009"); } default : return _thisRow[columnIndex - 1][0]; } } /** * 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 */ public short getShort(int columnIndex) throws java.sql.SQLException { return (short) getLong(columnIndex); } /** * 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 */ public int getInt(int columnIndex) throws java.sql.SQLException { return (int) getLong(columnIndex); } /** * 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 */ public long getLong(int columnIndex) throws java.sql.SQLException { checkRowPos(); if (_fields == null) { throw new java.sql.SQLException( "Query generated no fields for ResultSet", "S1002"); } if (columnIndex < 1 || columnIndex > _fields.length) throw new java.sql.SQLException( "Column Index out of range ( " + columnIndex + " > " + _fields.length + ").", "S1002"); try { if (_thisRow[columnIndex - 1] == null) { _wasNullFlag = true; } else { _wasNullFlag = false; } } catch (NullPointerException E) { _wasNullFlag = true; } if (_wasNullFlag) { return 0; } try { return getLong(_thisRow[columnIndex - 1]); } catch (NumberFormatException E) { throw new java.sql.SQLException( "Bad format for number '" + new String(_thisRow[columnIndex - 1]) + "' in column " + columnIndex + "(" + _fields[columnIndex - 1] + ").", "S1009"); } } /** * 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 */ public float getFloat(int columnIndex) throws java.sql.SQLException { return (float) getDouble(columnIndex); } /** * 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 { checkRowPos(); if (_fields == null) { throw new java.sql.SQLException( "Query generated no fields for ResultSet", "S1002"); } if (columnIndex < 1 || columnIndex > _fields.length) throw new java.sql.SQLException( "Column Index out of range ( " + columnIndex + " > " + _fields.length + ").", "S1002"); try { if (_thisRow[columnIndex - 1] == null) { _wasNullFlag = true; } else { _wasNullFlag = false; } } catch (NullPointerException E) { _wasNullFlag = true; } if (_wasNullFlag) { return 0; } try { return getDouble(_thisRow[columnIndex - 1]); } catch (NumberFormatException E) { throw new java.sql.SQLException( "Bad format for number '" + new String(_thisRow[columnIndex - 1]) + "' in column " + columnIndex + "(" + _fields[columnIndex - 1] + ").", "S1009"); } } /** * Get the value of a column in the current row as a * java.lang.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 S = getString(columnIndex); BigDecimal Val; if (S != null) { if (S.length() == 0) { Val = new BigDecimal(0); return Val.setScale(scale); } try { Val = new BigDecimal(S); } catch (NumberFormatException E) { throw new java.sql.SQLException( "Bad format for BigDecimal '" + S + "' in column " + columnIndex + "(" + _fields[columnIndex - 1] + ").", "S1009"); } try { return Val.setScale(scale); } catch (ArithmeticException E) { throw new java.sql.SQLException( "Bad format for BigDecimal '" + S + "' in column " + columnIndex + "(" + _fields[columnIndex - 1] + ").", "S1009"); } } return null; // SQL NULL } /** * Get the value of a column in the current row as a Java byte array. * *

Be warned If the blob is huge, then you may run out * of memory. * * @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(); if (columnIndex < 1 || columnIndex > _fields.length) throw new java.sql.SQLException( "Column Index out of range ( " + columnIndex + " > " + _fields.length + ").", "S1002"); try { if (_thisRow[columnIndex - 1] == null) { _wasNullFlag = true; } else { _wasNullFlag = false; } } catch (NullPointerException E) { _wasNullFlag = true; } if (_wasNullFlag) { return null; } else { return _thisRow[columnIndex - 1]; } } /** * 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 { Integer Y = null, M = null, D = null; String S = ""; try { S = getString(columnIndex); if (S == null) { return null; } else if (S.equals("0000-00-00")) { _wasNullFlag = true; return null; } else if ( _fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { // Convert from TIMESTAMP switch (S.length()) { case 14 : case 8 : { Y = new Integer(S.substring(0, 4)); M = new Integer(S.substring(4, 6)); D = new Integer(S.substring(6, 8)); return new java.sql.Date(Y.intValue() - 1900, M.intValue() - 1, D.intValue()); } case 12 : case 10 : case 6 : { Y = new Integer(S.substring(0, 2)); if (Y.intValue() <= 69) { Y = new Integer(Y.intValue() + 100); } M = new Integer(S.substring(2, 4)); D = new Integer(S.substring(4, 6)); return new java.sql.Date(Y.intValue(), M.intValue() - 1, D.intValue()); } case 4 : { Y = new Integer(S.substring(0, 4)); if (Y.intValue() <= 69) { Y = new Integer(Y.intValue() + 100); } M = new Integer(S.substring(2, 4)); return new java.sql.Date(Y.intValue(), M.intValue() - 1, 1); } case 2 : { Y = new Integer(S.substring(0, 2)); if (Y.intValue() <= 69) { Y = new Integer(Y.intValue() + 100); } return new java.sql.Date(Y.intValue(), 0, 1); } default : throw new SQLException( "Bad format for Date '" + S + "' in column " + columnIndex + "(" + _fields[columnIndex - 1] + ").", "S1009"); } /* endswitch */ } else if (_fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { Y = new Integer(S.substring(0, 4)); return new java.sql.Date(Y.intValue() - 1900, 0, 1); } else { if (S.length() < 10) { throw new SQLException( "Bad format for Date '" + S + "' in column " + columnIndex + "(" + _fields[columnIndex - 1] + ").", "S1009"); } Y = new Integer(S.substring(0, 4)); M = new Integer(S.substring(5, 7)); D = new Integer(S.substring(8, 10)); } return new java.sql.Date(Y.intValue() - 1900, M.intValue() - 1, D.intValue()); } catch (Exception e) { throw new java.sql.SQLException( "Cannot convert value '" + S + "' from column " + columnIndex + "(" + S + " ) to DATE.", "S1009"); } } /** * 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 * @exception java.sql.SQLException if a database access error occurs */ public Time getTime(int columnIndex) throws java.sql.SQLException { int hr = 0, min = 0, sec = 0; try { String S = getString(columnIndex); if (S == null) { return null; } else if (S.equals("0000-00-00")) { _wasNullFlag = true; return null; } Field F = _fields[columnIndex - 1]; if (F.getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { // It's a timestamp int length = S.length(); switch (length) { case 14 : case 12 : { hr = Integer.parseInt(S.substring(length - 6, length - 4)); min = Integer.parseInt(S.substring(length - 4, length - 2)); sec = Integer.parseInt(S.substring(length - 2, length)); } break; case 10 : { hr = Integer.parseInt(S.substring(6, 8)); min = Integer.parseInt(S.substring(8, 10)); sec = 0; } break; default : throw new SQLException( "Timestamp too small to convert to Time value in column " + columnIndex + "(" + _fields[columnIndex - 1] + ").", "S1009"); } /* endswitch */ SQLWarning W = new SQLWarning( "Precision lost converting TIMESTAMP to Time with getTime() on column " + columnIndex + "(" + _fields[columnIndex - 1] + ")."); if (_warnings == null) { _warnings = W; } else { _warnings.setNextWarning(W); } } else if (F.getMysqlType() == MysqlDefs.FIELD_TYPE_DATETIME) { hr = Integer.parseInt(S.substring(11, 13)); min = Integer.parseInt(S.substring(14, 16)); sec = Integer.parseInt(S.substring(17, 19)); SQLWarning W = new SQLWarning( "Precision lost converting DATETIME to Time with getTime() on column " + columnIndex + "(" + _fields[columnIndex - 1] + ")."); if (_warnings == null) { _warnings = W; } else { _warnings.setNextWarning(W); } } else { // convert a String to a Time if (S.length() != 5 && S.length() != 8) { throw new SQLException( "Bad format for Time '" + S + "' in column " + columnIndex + "(" + _fields[columnIndex - 1] + ").", "S1009"); } hr = Integer.parseInt(S.substring(0, 2)); min = Integer.parseInt(S.substring(3, 5)); sec = (S.length() == 5) ? 0 : Integer.parseInt(S.substring(6)); } return new Time(hr, min, sec); } catch (Exception E) { throw new java.sql.SQLException(E.getClass().getName(), "S1009"); } } /** * 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 { String S = getString(columnIndex); try { if (S == null) { return null; } else if (S.equals("0000-00-00")) { _wasNullFlag = true; return null; } else if (_fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { return new java.sql.Timestamp(Integer.parseInt(S.substring(0, 4)) - 1900, 0, 1, 0, 0, 0, 0); } else { // Convert from TIMESTAMP or DATE switch (S.length()) { case 19 : { int year = Integer.parseInt(S.substring(0, 4)); int month = Integer.parseInt(S.substring(5, 7)); int day = Integer.parseInt(S.substring(8, 10)); int hour = Integer.parseInt(S.substring(11, 13)); int minutes = Integer.parseInt(S.substring(14, 16)); int seconds = Integer.parseInt(S.substring(17, 19)); return new java.sql.Timestamp( year - 1900, month - 1, day, hour, minutes, seconds, 0); } case 14 : { int year = Integer.parseInt(S.substring(0, 4)); int month = Integer.parseInt(S.substring(4, 6)); int day = Integer.parseInt(S.substring(6, 8)); int hour = Integer.parseInt(S.substring(8, 10)); int minutes = Integer.parseInt(S.substring(10, 12)); int seconds = Integer.parseInt(S.substring(12, 14)); return new java.sql.Timestamp( year - 1900, month - 1, day, hour, minutes, seconds, 0); } case 12 : { int year = Integer.parseInt(S.substring(0, 2)); if (year <= 69) { year = (year + 100); } int month = Integer.parseInt(S.substring(2, 4)); int day = Integer.parseInt(S.substring(4, 6)); int hour = Integer.parseInt(S.substring(6, 8)); int minutes = Integer.parseInt(S.substring(8, 10)); int seconds = Integer.parseInt(S.substring(10, 12)); return new java.sql.Timestamp(year, month - 1, day, hour, minutes, seconds, 0); } case 10 : { int year = Integer.parseInt(S.substring(0, 2)); if (year <= 69) { year = (year + 100); } int month = Integer.parseInt(S.substring(2, 4)); int day = Integer.parseInt(S.substring(4, 6)); int hour = Integer.parseInt(S.substring(6, 8)); int minutes = Integer.parseInt(S.substring(8, 10)); return new java.sql.Timestamp(year, month - 1, day, hour, minutes, 0, 0); } case 8 : { int year = Integer.parseInt(S.substring(0, 4)); int month = Integer.parseInt(S.substring(4, 6)); int day = Integer.parseInt(S.substring(6, 8)); return new java.sql.Timestamp(year - 1900, month - 1, day, 0, 0, 0, 0); } case 6 : { int year = Integer.parseInt(S.substring(0, 2)); if (year <= 69) { year = (year + 100); } int month = Integer.parseInt(S.substring(2, 4)); int day = Integer.parseInt(S.substring(4, 6)); return new java.sql.Timestamp(year, month - 1, day, 0, 0, 0, 0); } case 4 : { int year = Integer.parseInt(S.substring(0, 2)); if (year <= 69) { year = (year + 100); } int month = Integer.parseInt(S.substring(2, 4)); return new java.sql.Timestamp(year, month - 1, 1, 0, 0, 0, 0); } case 2 : { int year = Integer.parseInt(S.substring(0, 2)); if (year <= 69) { year = (year + 100); } return new java.sql.Timestamp(year, 0, 1, 0, 0, 0, 0); } default : throw new java.sql.SQLException( "Bad format for Timestamp '" + S + "' in column " + columnIndex + "(" + _fields[columnIndex - 1] + ").", "S1009"); } } } catch (Exception e) { throw new java.sql.SQLException( "Cannot convert value '" + S + "' from column " + columnIndex + "(" + S + " ) to TIMESTAMP.", "S1009"); } } /** * 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. * *

Note: 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. * * @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); } /** * 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); } /** * 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; // SQL NULL } /** * 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)); } public boolean getBoolean(String ColumnName) throws java.sql.SQLException { return getBoolean(findColumn(ColumnName)); } public byte getByte(String ColumnName) throws java.sql.SQLException { return getByte(findColumn(ColumnName)); } public short getShort(String ColumnName) throws java.sql.SQLException { return getShort(findColumn(ColumnName)); } public int getInt(String ColumnName) throws java.sql.SQLException { return getInt(findColumn(ColumnName)); } public long getLong(String ColumnName) throws java.sql.SQLException { return getLong(findColumn(ColumnName)); } public float getFloat(String ColumnName) throws java.sql.SQLException { return getFloat(findColumn(ColumnName)); } public double getDouble(String ColumnName) throws java.sql.SQLException { return getDouble(findColumn(ColumnName)); } public BigDecimal getBigDecimal(String ColumnName, int scale) throws java.sql.SQLException { return getBigDecimal(findColumn(ColumnName), scale); } public byte[] getBytes(String ColumnName) throws java.sql.SQLException { return getBytes(findColumn(ColumnName)); } public java.sql.Date getDate(String ColumnName) throws java.sql.SQLException { return getDate(findColumn(ColumnName)); } public Time getTime(String ColumnName) throws java.sql.SQLException { return getTime(findColumn(ColumnName)); } public Timestamp getTimestamp(String ColumnName) throws java.sql.SQLException { return getTimestamp(findColumn(ColumnName)); } public InputStream getAsciiStream(String ColumnName) throws java.sql.SQLException { return getAsciiStream(findColumn(ColumnName)); } public InputStream getUnicodeStream(String ColumnName) throws java.sql.SQLException { return getUnicodeStream(findColumn(ColumnName)); } public InputStream getBinaryStream(String ColumnName) throws java.sql.SQLException { return getBinaryStream(findColumn(ColumnName)); } /** * The first warning reported by calls on this ResultSet is * returned. Subsequent ResultSet warnings will be chained * to this java.sql.SQLWarning. * *

The warning chain is automatically cleared each time a new * row is read. * *

Note: 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. * * @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 _warnings; } /** * 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 { _warnings = null; } /** * Get the name of the SQL cursor used by this ResultSet * *

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. * *

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. * *

Note: If positioned update is not supported, a java.sql.SQLException * is thrown. * * @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"); } /** * 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 abstract java.sql.ResultSetMetaData getMetaData() throws java.sql.SQLException; /** * Get the value of a column in the current row as a Java object * *

This method will return the value of the given column as a * Java object. The type of the Java object will be the default * Java Object type corresponding to the column's SQL type, following * the mapping specified in the JDBC specification. * *

This method may also be used to read database specific abstract * data types. * * @param columnIndex the first column is 1, the second is 2... * @return a Object holding the column value * @exception java.sql.SQLException if a database access error occurs */ public Object getObject(int columnIndex) throws java.sql.SQLException { checkRowPos(); if (Driver.trace) { Object[] args = { new Integer(columnIndex)}; Debug.methodCall(this, "getObject", args); } Field F; if (columnIndex < 1 || columnIndex > _fields.length) { throw new java.sql.SQLException( "Column index out of range (" + columnIndex + " > " + _fields.length + ").", "S1002"); } F = _fields[columnIndex - 1]; if (_thisRow[columnIndex - 1] == null) { _wasNullFlag = true; return null; } _wasNullFlag = false; switch (F.getSQLType()) { case Types.BIT : return new Boolean(getBoolean(columnIndex)); case Types.TINYINT : if (F.isUnsigned()) { return new Integer(getInt(columnIndex)); } else { return new Byte(getByte(columnIndex)); } case Types.SMALLINT : if (F.isUnsigned()) { return new Integer(getInt(columnIndex)); } else { return new Short(getShort(columnIndex)); } case Types.INTEGER : if (F.isUnsigned()) { return new Long(getLong(columnIndex)); } else { return new Integer(getInt(columnIndex)); } case Types.BIGINT : return new Long(getLong(columnIndex)); case Types.DECIMAL : case Types.NUMERIC : String S = getString(columnIndex); BigDecimal Val; if (S != null) { if (S.length() == 0) { Val = new BigDecimal(0); return Val; } try { Val = new BigDecimal(S); } catch (NumberFormatException E) { throw new java.sql.SQLException( "Bad format for BigDecimal '" + S + "' in column " + columnIndex + "(" + _fields[columnIndex - 1] + ").", "S1009"); } return Val; } else { return null; } case Types.REAL : case Types.FLOAT : return new Float(getFloat(columnIndex)); case Types.DOUBLE : return new Double(getDouble(columnIndex)); case Types.CHAR : case Types.VARCHAR : case Types.LONGVARCHAR : if (F.isBinary()) { return getBytes(columnIndex); } else { return getString(columnIndex); } case Types.BINARY : case Types.VARBINARY : case Types.LONGVARBINARY : if (!F.isBlob()) { return getString(columnIndex); } else if (!F.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 BIn = new ByteArrayInputStream(Data); ObjectInputStream ObjIn = new ObjectInputStream(BIn); Obj = ObjIn.readObject(); ObjIn.close(); BIn.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 * *

This method will return the value of the given column as a * Java object. The type of the Java object will be the default * Java Object type corresponding to the column's SQL type, following * the mapping specified in the JDBC specification. * *

This method may also be used to read database specific abstract * data types. * * @param 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)); } /** * 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; 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.", "S0022"); } } // **************************************************************** // // END OF PUBLIC INTERFACE // // **************************************************************** /** * Create a new ResultSet - Note that we create ResultSets to * represent the results of everything. * * @param fields an array of Field objects (basically, the * ResultSet MetaData) * @param tuples Vector of the actual data * @param status the status string returned from the back end * @param updateCount the number of rows affected by the operation * @param cursor the positioned update/delete cursor name */ public ResultSet( Field[] Fields, Vector Tuples, com.mysql.jdbc.Connection Conn) { this(Fields, Tuples); setConnection(Conn); } public ResultSet(Field[] Fields, Vector Tuples) { _currentRow = -1; this._fields = Fields; _rows = Tuples; _updateCount = (long) _rows.size(); if (Driver.debug) System.out.println("Retrieved " + _updateCount + " rows"); _reallyResult = true; // Check for no results if (_rows.size() > 0) { _thisRow = (byte[][]) _rows.elementAt(0); if (_updateCount == 1) { if (_thisRow == null) { _currentRow = -1; _rows.removeAllElements(); // empty result set _updateCount = -1; } } } else { _thisRow = null; } buildIndexMapping(); } void setStatement(com.mysql.jdbc.Statement stmt) { _owningStatement = stmt; } /** * Builds a hash between column names and their indices for fast * retrieval. */ protected void buildIndexMapping() { int numFields = _fields.length; _columnNameToIndex = new Hashtable(); _fullColumnNameToIndex = new Hashtable(); for (int i = 0; i < numFields; 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); } } } /** * Create a result set for an executeUpdate statement. * * @param updateCount the number of rows affected by the update */ public ResultSet(long updateCount, long updateID) { this._updateCount = updateCount; this._updateID = updateID; _reallyResult = false; _fields = new Field[0]; } public void setConnection(com.mysql.jdbc.Connection Conn) { this._connection = Conn; } boolean reallyResult() { return _reallyResult; } long getUpdateCount() { return _updateCount; } long getUpdateID() { return _updateID; } protected void checkRowPos() throws SQLException { if (_closed) { throw new SQLException("Operation not allowed after ResultSet closed"); } if (_currentRow < 0) { throw new SQLException("Before start of result set"); } if (_currentRow == _rows.size()) { throw new SQLException("After end of result set"); } } /////////////////////////////////////////// // // 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. */ public static double getDouble(byte[] buf) throws SQLException { if (buf.length == 0) { return 0; } try { return Double.parseDouble(new String(buf)); } catch (NumberFormatException e) { throw new SQLException("Bad format for number '" + new String(buf) + "'"); } } // // Following adopted from Sun class libraries // /** * All possible chars for representing a number as a String */ private final static char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; /** * Array of chars to lookup the char for the digit in the tenth's * place for a two digit, base ten number. The char can be got by * using the number as the index. */ private final static char[] radixTenTenths = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', '9', '9', '9', '9', '9', '9', '9', '9', '9', '9' }; /** * Array of chars to lookup the char for the digit in the unit's * place for a two digit, base ten number. The char can be got by * using the number as the index. */ private final static char[] radixTenUnits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; /** * Converts a string representation of an long * into an long */ public long getLong(byte[] buf) throws NumberFormatException { long result = 0; boolean negative = false; int i = 0, max = buf.length; long limit; long multmin; int digit; int radix = 10; if (max > 0) { if ((char) buf[0] == '-') { negative = true; limit = Long.MIN_VALUE; i++; } else { limit = -Long.MAX_VALUE; } multmin = limit / radix; if (i < max) { digit = Character.digit((char) buf[i++], radix); if (digit < 0) { throw new NumberFormatException(new String(buf)); } else { result = -digit; } } while (i < max) { // Accumulating negatively avoids surprises near MAX_VALUE digit = Character.digit((char) buf[i++], radix); if (digit < 0) { throw new NumberFormatException(new String(buf)); } if (result < multmin) { throw new NumberFormatException(new String(buf)); } result *= radix; if (result < limit + digit) { throw new NumberFormatException(new String(buf)); } result -= digit; } } else { throw new NumberFormatException(new String(buf)); } if (negative) { if (i > 1) { return result; } else { /* Only got "-" */ throw new NumberFormatException(new String(buf)); } } else { return -result; } } /** * Sets the result set type for (JDBC2) */ protected void setResultSetType(int typeFlag) { _resultSetType = typeFlag; } /** * Sets the concurrency (JDBC2) */ protected void setResultSetConcurrency(int concurrencyFlag) { _resultSetConcurrency = concurrencyFlag; } }