/*
  Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.

  The MySQL Connector/J is licensed under the terms of the GPLv2
  <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
  There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
  this software, see the FLOSS License Exception
  <http://www.mysql.com/about/legal/licensing/foss-exception.html>.

  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; version 2
  of the License.

  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., 51 Franklin St, Fifth
  Floor, Boston, MA 02110-1301  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.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Array;
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.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.TreeMap;

import com.mysql.jdbc.log.LogUtils;
import com.mysql.jdbc.profiler.ProfilerEvent;
import com.mysql.jdbc.profiler.ProfilerEventHandler;

/**
 * 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$
 * 
 * @see ResultSetMetaData
 * @see java.sql.ResultSet
 */
public class ResultSetImpl implements ResultSetInternalMethods {

	private static final Constructor<?> JDBC_4_RS_4_ARG_CTOR;
	private static final Constructor<?> JDBC_4_RS_6_ARG_CTOR;;
	private static final Constructor<?> JDBC_4_UPD_RS_6_ARG_CTOR;
	
	static {
		if (Util.isJdbc4()) {
			try {
				JDBC_4_RS_4_ARG_CTOR = Class.forName(
						"com.mysql.jdbc.JDBC4ResultSet").getConstructor(
						new Class[] { Long.TYPE, Long.TYPE,
								MySQLConnection.class,
								com.mysql.jdbc.StatementImpl.class });
				JDBC_4_RS_6_ARG_CTOR = Class.forName(
						"com.mysql.jdbc.JDBC4ResultSet").getConstructor(
						new Class[] { String.class, Field[].class,
								RowData.class, 
								MySQLConnection.class,
								com.mysql.jdbc.StatementImpl.class });
				JDBC_4_UPD_RS_6_ARG_CTOR = Class.forName(
						"com.mysql.jdbc.JDBC4UpdatableResultSet")
						.getConstructor(
								new Class[] { String.class, Field[].class,
										RowData.class,
										MySQLConnection.class,
										com.mysql.jdbc.StatementImpl.class });
			} catch (SecurityException e) {
				throw new RuntimeException(e);
			} catch (NoSuchMethodException e) {
				throw new RuntimeException(e);
			} catch (ClassNotFoundException e) {
				throw new RuntimeException(e);
			}
		} else {
			JDBC_4_RS_4_ARG_CTOR = null;
			JDBC_4_RS_6_ARG_CTOR = null;
			JDBC_4_UPD_RS_6_ARG_CTOR = null;
		}
	}
	
	/**
	 * Epsillon between Float.MIN_VALUE and the double representation of said value.
	 */
    protected static final double MIN_DIFF_PREC = Float.parseFloat(Float.toString(Float.MIN_VALUE))
        - Double.parseDouble(Float.toString(Float.MIN_VALUE));
    
    /**
	 * Epsillon between Float.MAX_VALUE and the double representation of said value.
	 */
    protected static final double MAX_DIFF_PREC = Float.parseFloat(Float.toString(Float.MAX_VALUE))
        - Double.parseDouble(Float.toString(Float.MAX_VALUE));
    
	/** Counter used to generate IDs for profiling. */
	static int resultCounter = 1;

	/**
	 * Converts the given value as a java long, to an 'unsigned' long, using the
	 * java.math.BigInteger class.
	 */
	protected static BigInteger convertLongToUlong(long longVal) {
		byte[] asBytes = new byte[8];
		asBytes[7] = (byte) (longVal & 0xff);
		asBytes[6] = (byte) (longVal >>> 8);
		asBytes[5] = (byte) (longVal >>> 16);
		asBytes[4] = (byte) (longVal >>> 24);
		asBytes[3] = (byte) (longVal >>> 32);
		asBytes[2] = (byte) (longVal >>> 40);
		asBytes[1] = (byte) (longVal >>> 48);
		asBytes[0] = (byte) (longVal >>> 56);

		return new BigInteger(1, asBytes);
	}

	/** The catalog that was in use when we were created */
	protected String catalog = null;

	/** Map column names (and all of their permutations) to column indices */
	protected Map<String, Integer> columnLabelToIndex = null;

	/** The above map is a case-insensitive tree-map, it can be slow, this caches
	 *  lookups into that map, because the other alternative is to create new
	 *  object instances for every call to findColumn()....
	 */
	protected Map<String, Integer> columnToIndexCache = null;
	
	/** Keep track of columns accessed */
	protected boolean[] columnUsed = null;

	/** The Connection instance that created us */
	protected volatile MySQLConnection connection; // The connection that
				   					               // created us

	protected long connectionId = 0;
	
	/** The current row #, -1 == before start of result set */
	protected int currentRow = -1; // Cursor to current row;

	TimeZone defaultTimeZone;

	/** Are we in the middle of doing updates to the current row? */
	protected boolean doingUpdates = false;

	protected ProfilerEventHandler eventSink = null;

	Calendar fastDateCal = null;

	/** 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;

	/** The fields for this result set */
	protected Field[] fields; // The fields

	/**
	 * 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;

	/** Map of fully-specified column names to column indices */
	protected Map<String, Integer> fullColumnNameToIndex = null;

	protected Map<String, Integer> columnNameToIndex = null;
	
	protected boolean hasBuiltIndexMapping = false;

	/**
	 * Is the data stored as strings (default) or natively (which is the case
	 * with results from PrepStmts)
	 */
	protected boolean isBinaryEncoded = false;

	/** Has this result set been closed? */
	protected boolean isClosed = false;

	protected ResultSetInternalMethods nextResultSet = null;

	/** Are we on the insert row? */
	protected boolean onInsertRow = false;

	/** The statement that created us */
	protected com.mysql.jdbc.StatementImpl owningStatement;

	/**
	 * StackTrace generated where ResultSet was created... used when profiling
	 */
	protected String pointOfOrigin;

	/** Are we tracking items for profileSql? */
	protected boolean profileSql = false;

	/**
	 * Do we actually contain rows, or just information about
	 * UPDATE/INSERT/DELETE?
	 */
	protected boolean reallyResult = false;

	/** The id (used when profiling) to identify us */
	protected int resultId;

	/** Are we read-only or updatable? */
	protected int resultSetConcurrency = 0;

	/** Are we scroll-sensitive/insensitive? */
	protected int resultSetType = 0;

	/** The actual rows */
	protected RowData rowData; // The results

	/**
	 * 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;

	PreparedStatement statementUsedForFetchingRows;

	/** Pointer to current row data */
	protected ResultSetRow thisRow = null; // Values for current row

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

	/** How many rows were affected by UPDATE/INSERT/DELETE? */
	protected long updateCount;

	/** Value generated for AUTO_INCREMENT columns */
	protected long updateId = -1;

	private boolean useStrictFloatingPoint = false;

	protected boolean useUsageAdvisor = false;

	/** The warning chain */
	protected java.sql.SQLWarning warningChain = null;

	/** Did the previous value retrieval find a NULL? */
	protected boolean wasNullFlag = false;

	protected java.sql.Statement wrapperStatement;

	protected boolean retainOwningStatement;

	protected Calendar gmtCalendar = null;

	protected boolean useFastDateParsing = false;

	private boolean padCharsWithSpace = false;

	private boolean jdbcCompliantTruncationForReads;
	
	private boolean useFastIntParsing = true;
	private boolean useColumnNamesInFindColumn;
	
	private ExceptionInterceptor exceptionInterceptor;
	
	final static char[] EMPTY_SPACE = new char[255];
	
	static {
		for (int i = 0; i < EMPTY_SPACE.length; i++) {
			EMPTY_SPACE[i] = ' ';
		}
	}
	
	protected static ResultSetImpl getInstance(long updateCount, long updateID,
			MySQLConnection conn, StatementImpl creatorStmt) throws SQLException {
		if (!Util.isJdbc4()) {
			return new ResultSetImpl(updateCount, updateID, conn, creatorStmt);
		}

		return (ResultSetImpl) Util.handleNewInstance(JDBC_4_RS_4_ARG_CTOR,
				new Object[] { Long.valueOf(updateCount), Long.valueOf(updateID), conn,
						creatorStmt}, conn.getExceptionInterceptor());
	}

	/**
	 * Creates a result set instance that represents a query result -- We need
	 * to provide factory-style methods so we can support both JDBC3 (and older)
	 * and JDBC4 runtimes, otherwise the class verifier complains when it tries
	 * to load JDBC4-only interface classes that are present in JDBC4 method
	 * signatures.
	 */

	protected static ResultSetImpl getInstance(String catalog, Field[] fields,
			RowData tuples, MySQLConnection conn, StatementImpl creatorStmt,
			boolean isUpdatable) throws SQLException {
		if (!Util.isJdbc4()) {
			if (!isUpdatable) {
				return new ResultSetImpl(catalog, fields, tuples, conn, creatorStmt);
			}

			return new UpdatableResultSet(catalog, fields, tuples, conn,
					creatorStmt);
		}

		if (!isUpdatable) {
			return (ResultSetImpl) Util
					.handleNewInstance(JDBC_4_RS_6_ARG_CTOR, new Object[] {
							catalog, fields, tuples, conn, creatorStmt }, conn.getExceptionInterceptor());
		}

		return (ResultSetImpl) Util.handleNewInstance(JDBC_4_UPD_RS_6_ARG_CTOR,
				new Object[] { catalog, fields, tuples, conn, creatorStmt }, conn.getExceptionInterceptor());
	}

	/**
	 * 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)
	 * @param conn
	 *            DOCUMENT ME!
	 * @param creatorStmt
	 *            DOCUMENT ME!
	 */
	public ResultSetImpl(long updateCount, long updateID, MySQLConnection conn,
			StatementImpl creatorStmt) {
		this.updateCount = updateCount;
		this.updateId = updateID;
		this.reallyResult = false;
		this.fields = new Field[0];

		this.connection = conn;
		this.owningStatement = creatorStmt;

		this.retainOwningStatement = false;
		
		if (this.connection != null) {
			this.exceptionInterceptor = this.connection.getExceptionInterceptor();
			
			this.retainOwningStatement = 
				this.connection.getRetainStatementAfterResultSetClose();
			
			this.connectionId = this.connection.getId();
			this.serverTimeZoneTz = this.connection.getServerTimezoneTZ();
			this.padCharsWithSpace = this.connection.getPadCharsWithSpace();
			
			useLegacyDatetimeCode = this.connection.getUseLegacyDatetimeCode();
		}
	}

	/**
	 * Creates a new ResultSet object.
	 * 
	 * @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.
	 * @param creatorStmt
	 *            DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             if an error occurs
	 */
	public ResultSetImpl(String catalog, Field[] fields, RowData tuples,
			MySQLConnection conn, StatementImpl creatorStmt) throws SQLException {
		this.connection = conn;
		
		this.retainOwningStatement = false;
		
		if (this.connection != null) {
			this.exceptionInterceptor = this.connection.getExceptionInterceptor();
			this.useStrictFloatingPoint = this.connection
					.getStrictFloatingPoint();
			this.setDefaultTimeZone(this.connection.getDefaultTimeZone());
			this.connectionId = this.connection.getId();
			this.useFastDateParsing = this.connection.getUseFastDateParsing();
			this.profileSql = this.connection.getProfileSql();
			this.retainOwningStatement = 
				this.connection.getRetainStatementAfterResultSetClose();
			this.jdbcCompliantTruncationForReads = this.connection.getJdbcCompliantTruncationForReads();
			this.useFastIntParsing = this.connection.getUseFastIntParsing();
			this.serverTimeZoneTz = this.connection.getServerTimezoneTZ();
			this.padCharsWithSpace = this.connection.getPadCharsWithSpace();
		}

		this.owningStatement = creatorStmt;

		this.catalog = catalog;

		this.fields = fields;
		this.rowData = tuples;
		this.updateCount = this.rowData.size();

		if (Driver.DEBUG) {
			System.out.println(Messages.getString("ResultSet.Retrieved__1")
					+ this.updateCount + " rows"); //$NON-NLS-1$
		}

		this.reallyResult = true;

		// Check for no results
		if (this.rowData.size() > 0) {
			if (this.updateCount == 1) {
				if (this.thisRow == null) {
					this.rowData.close(); // empty result set
					this.updateCount = -1;
				}
			}
		} else {
			this.thisRow = null;
		}

		this.rowData.setOwner(this);

		if (this.fields != null) {
			initializeWithMetadata();
		} // else called by Connection.initializeResultsMetadataFromCache() when cached
		useLegacyDatetimeCode = this.connection.getUseLegacyDatetimeCode();
		
		this.useColumnNamesInFindColumn = this.connection.getUseColumnNamesInFindColumn();
		
		setRowPositionValidity();
	}

	public void initializeWithMetadata() throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			this.rowData.setMetadata(this.fields);
			
			this.columnToIndexCache = new HashMap<String, Integer>();
			
			if (this.profileSql || this.connection.getUseUsageAdvisor()) {
				this.columnUsed = new boolean[this.fields.length];
				this.pointOfOrigin = LogUtils.findCallingClassAndMethod(new Throwable());
				this.resultId = resultCounter++;
				this.useUsageAdvisor = this.connection.getUseUsageAdvisor();
				this.eventSink = ProfilerEventHandlerFactory.getInstance(this.connection);
			}
	
			if (this.connection.getGatherPerformanceMetrics()) {
				this.connection.incrementNumberOfResultSetsCreated();
	
				Set<String> tableNamesSet = new HashSet<String>();
	
				for (int i = 0; i < this.fields.length; i++) {
					Field f = this.fields[i];
	
					String tableName = f.getOriginalTableName();
	
					if (tableName == null) {
						tableName = f.getTableName();
					}
	
					if (tableName != null) {
						if (this.connection.lowerCaseTableNames()) {
							tableName = tableName.toLowerCase(); // on windows, table
							// names are not case-sens.
						}
	
						tableNamesSet.add(tableName);
					}
				}
	
				this.connection.reportNumberOfTablesAccessed(tableNamesSet.size());
			}
		}
	}

	private synchronized void createCalendarIfNeeded() {
		if (this.fastDateCal == null) {
			this.fastDateCal = new GregorianCalendar(Locale.US);
			this.fastDateCal.setTimeZone(this.getDefaultTimeZone());
		}
	}

	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {
	
			boolean b;
	
			if (this.rowData.size() == 0) {
				b = false;
			} else {
				if (this.onInsertRow) {
					this.onInsertRow = false;
				}
	
				if (this.doingUpdates) {
					this.doingUpdates = false;
				}
	
				if (this.thisRow != null) {
					this.thisRow.closeOpenStreams();
				}
				
				if (row == 0) {
					beforeFirst();
					b = false;
				} else if (row == 1) {
					b = first();
				} else if (row == -1) {
					b = last();
				} else if (row > this.rowData.size()) {
					afterLast();
					b = false;
				} else {
					if (row < 0) {
						// adjust to reflect after end of result set
						int newRowPosition = this.rowData.size() + row + 1;
	
						if (newRowPosition <= 0) {
							beforeFirst();
							b = false;
						} else {
							b = absolute(newRowPosition);
						}
					} else {
						row--; // adjust for index difference
						this.rowData.setCurrentRow(row);
						this.thisRow = this.rowData.getAt(row);
						b = true;
					}
				}
			}
	
			setRowPositionValidity();
			
			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 {
		synchronized (checkClosed().getConnectionMutex()) {

			if (this.onInsertRow) {
				this.onInsertRow = false;
			}
	
			if (this.doingUpdates) {
				this.doingUpdates = false;
			}
	
			if (this.thisRow != null) {
				this.thisRow.closeOpenStreams();
			}
			
			if (this.rowData.size() != 0) {
				this.rowData.afterLast();
				this.thisRow = null;
			}
			
			setRowPositionValidity();
		}
	}

	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {

			if (this.onInsertRow) {
				this.onInsertRow = false;
			}
	
			if (this.doingUpdates) {
				this.doingUpdates = false;
			}
	
			if (this.rowData.size() == 0) {
				return;
			}
	
			if (this.thisRow != null) {
				this.thisRow.closeOpenStreams();
			}
			
			this.rowData.beforeFirst();
			this.thisRow = null;
			
			setRowPositionValidity();
		}
	}

	// ---------------------------------------------------------------------
	// Traversal/Positioning
	// ---------------------------------------------------------------------

	/**
	 * Builds a hash between column names and their indices for fast retrieval.
	 */
	public void buildIndexMapping() throws SQLException {
		int numFields = this.fields.length;
		this.columnLabelToIndex = new TreeMap<String, Integer>(String.CASE_INSENSITIVE_ORDER);
		this.fullColumnNameToIndex = new TreeMap<String, Integer>(String.CASE_INSENSITIVE_ORDER);
		this.columnNameToIndex = new TreeMap<String, Integer>(String.CASE_INSENSITIVE_ORDER);
		
		// 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 = Integer.valueOf(i);
			String columnName = this.fields[i].getOriginalName();
			String columnLabel = this.fields[i].getName();
			String fullColumnName = this.fields[i].getFullName();

			if (columnLabel != null) {			
				this.columnLabelToIndex.put(columnLabel, index);
			}

			if (fullColumnName != null) {
				this.fullColumnNameToIndex.put(fullColumnName, index);
			}
			
			if (columnName != null) {
				this.columnNameToIndex.put(columnName, index);
			}
		}

		// set the flag to prevent rebuilding...
		this.hasBuiltIndexMapping = true;
	}

	/**
	 * 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();
	}

	/**
	 * Ensures that the result set is not closed
	 * 
	 * @throws SQLException
	 *             if the result set is closed
	 */
	protected final MySQLConnection checkClosed() throws SQLException {
		MySQLConnection c = this.connection;
		
		if (c == null) {
			throw SQLError.createSQLException(
					Messages
							.getString("ResultSet.Operation_not_allowed_after_ResultSet_closed_144"), //$NON-NLS-1$
					SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
		}
		
		return c;
	}

	/**
	 * Checks if columnIndex is within the number of columns in this result set.
	 * 
	 * @param columnIndex
	 *            the index to check
	 * 
	 * @throws SQLException
	 *             if the index is out of bounds
	 */
	protected final void checkColumnBounds(int columnIndex) throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			if ((columnIndex < 1)) {
				throw SQLError.createSQLException(Messages.getString(
						"ResultSet.Column_Index_out_of_range_low", new Object[] {
								Integer.valueOf(columnIndex),
								Integer.valueOf(this.fields.length) }),
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
			} else if ((columnIndex > this.fields.length)) {
				throw SQLError.createSQLException(Messages.getString(
						"ResultSet.Column_Index_out_of_range_high", new Object[] {
								Integer.valueOf(columnIndex),
								Integer.valueOf(this.fields.length) }),
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
			}
	
			if (this.profileSql || this.useUsageAdvisor) {
				this.columnUsed[columnIndex - 1] = true;
			}
		}
	}

	/**
	 * 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 (!this.onValidRow) {
			throw SQLError.createSQLException(this.invalidRowReason, 
					SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
		}
	}
	
	private boolean onValidRow = false;
	private String invalidRowReason = null;
	protected boolean useLegacyDatetimeCode;
	private TimeZone serverTimeZoneTz;
	
	private void setRowPositionValidity() throws SQLException {
		if (!this.rowData.isDynamic() && (this.rowData.size() == 0)) {
			this.invalidRowReason = Messages
			.getString("ResultSet.Illegal_operation_on_empty_result_set");//$NON-NLS-1$
			this.onValidRow = false;
		} else if (this.rowData.isBeforeFirst()) {
			this.invalidRowReason = Messages
					.getString("ResultSet.Before_start_of_result_set_146"); //$NON-NLS-1$
			this.onValidRow = false;
		} else if (this.rowData.isAfterLast()) {
			this.invalidRowReason = Messages
					.getString("ResultSet.After_end_of_result_set_148"); //$NON-NLS-1$
			this.onValidRow = false;
		} else {
			this.onValidRow = true;
			this.invalidRowReason = null;
		}
	}

	/**
	 * We can't do this ourselves, otherwise the contract for
	 * Statement.getMoreResults() won't work correctly.
	 */
	public synchronized void clearNextResult() {
		this.nextResultSet = null;
	}

	/**
	 * After this call, getWarnings returns null until a new warning is reported
	 * for this ResultSet
	 * 
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	public void clearWarnings() throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			this.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 SQLException
	 *                if a database access error occurs
	 */
	public void close() throws SQLException {
		realClose(true);
	}

	/**
	 * @return
	 */
	private int convertToZeroWithEmptyCheck() throws SQLException {
		if (this.connection.getEmptyStringsConvertToZero()) {
			return 0;
		}

		throw SQLError.createSQLException("Can't convert empty string ('') to numeric",
				SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, getExceptionInterceptor());
	}
	
	private String convertToZeroLiteralStringWithEmptyCheck()
		throws SQLException {
		
		if (this.connection.getEmptyStringsConvertToZero()) {
			return "0";
		}

		throw SQLError.createSQLException("Can't convert empty string ('') to numeric",
				SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, getExceptionInterceptor());
	}

	//
	// Note, row data is linked between these two result sets
	//
	public ResultSetInternalMethods copy() throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			ResultSetInternalMethods rs = ResultSetImpl.getInstance(this.catalog, this.fields, this.rowData,
					this.connection, this.owningStatement, false); // note, doesn't work for updatable result sets
	
			return rs;
		}
	}

	public void redefineFieldsForDBMD(Field[] f) {
		this.fields = f;
		
		for (int i = 0; i < this.fields.length; i++) {
			this.fields[i].setUseOldNameMetadata(true);
			this.fields[i].setConnection(this.connection);
		}
	}
	
	public void populateCachedMetaData(CachedResultSetMetaData cachedMetaData)
		throws SQLException {
		cachedMetaData.fields = this.fields;
		cachedMetaData.columnNameToIndex = this.columnLabelToIndex;
		cachedMetaData.fullColumnNameToIndex = this.fullColumnNameToIndex;
		cachedMetaData.metadata = getMetaData();
	}
	
	public void initializeFromCachedMetaData(CachedResultSetMetaData cachedMetaData) {
		this.fields = cachedMetaData.fields;
		this.columnLabelToIndex = cachedMetaData.columnNameToIndex;
		this.fullColumnNameToIndex = cachedMetaData.fullColumnNameToIndex;
		this.hasBuiltIndexMapping = 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();
	}

	/**
	 * @param columnIndex
	 * @param stringVal
	 * @param mysqlType
	 * @return
	 * @throws SQLException
	 */
	private String extractStringFromNativeColumn(int columnIndex, int mysqlType)
			throws SQLException {
		int columnIndexMinusOne = columnIndex - 1;

		this.wasNullFlag = false;
		
		if (this.thisRow.isNull(columnIndexMinusOne)) {
			this.wasNullFlag = true;
			
			return null;
		}

		this.wasNullFlag = false;

		String encoding = this.fields[columnIndexMinusOne]
		      						.getCharacterSet();
		
		return this.thisRow.getString(columnIndex - 1, encoding, this.connection);
	}

	protected  Date fastDateCreate(Calendar cal, int year, int month,
			int day) throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			if (this.useLegacyDatetimeCode) {
				return TimeUtil.fastDateCreate(year, month, day, cal);
			}
			
			if (cal == null) {
				createCalendarIfNeeded();
				cal = this.fastDateCal;
			}
	
			boolean useGmtMillis = this.connection.getUseGmtMillisForDatetimes();
			
			return TimeUtil.fastDateCreate(useGmtMillis,
					useGmtMillis ? getGmtCalendar() : cal,
					cal, year, month, day);
		}
	}

	protected Time fastTimeCreate(Calendar cal, int hour,
			int minute, int second) throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			if (!this.useLegacyDatetimeCode) {
				return TimeUtil.fastTimeCreate(hour, minute, second, cal, getExceptionInterceptor());
			}
			
			if (cal == null) {
				createCalendarIfNeeded();
				cal = this.fastDateCal;
			}
	
			return TimeUtil.fastTimeCreate(cal, hour, minute, second, getExceptionInterceptor());
		}
	}

	protected Timestamp fastTimestampCreate(Calendar cal, int year,
			int month, int day, int hour, int minute, int seconds,
			int secondsPart) throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			if (!this.useLegacyDatetimeCode) {
				return TimeUtil.fastTimestampCreate(cal.getTimeZone(), year, month, day, hour,
						minute, seconds, secondsPart);
			}
			
			if (cal == null) {
				createCalendarIfNeeded();
				cal = this.fastDateCal;
			}
	
			boolean useGmtMillis = this.connection.getUseGmtMillisForDatetimes();
			
			return TimeUtil.fastTimestampCreate(useGmtMillis,
					useGmtMillis ? getGmtCalendar() : null,
					cal, year, month, day, hour,
					minute, seconds, secondsPart);
		}
	}

	/*
	/**
	 * Required by JDBC spec
	 */
	/*
	protected void finalize() throws Throwable {
		if (!this.isClosed) {
			realClose(false);
		}
	}
	*/

	// --------------------------JDBC 2.0-----------------------------------
	// ---------------------------------------------------------------------
	// Getter's and Setter's
	// ---------------------------------------------------------------------


	/*
	 * [For JDBC-3.0 and older - http://java.sun.com/j2se/1.5.0/docs/api/java/sql/ResultSet.html#findColumn(java.lang.String)]
	 * Map a ResultSet column name to a ResultSet column index
	 * 
	 * @param columnName
	 *            the name of the column
	 * 
	 * @return the column index
	 * 
	 * @exception SQLException
	 *                if a database access error occurs
	 *                
	 * [For JDBC-4.0 and newer - http://java.sun.com/javase/6/docs/api/java/sql/ResultSet.html#findColumn(java.lang.String)]
	 * 
	 * Maps the given ResultSet column label to its ResultSet column index.
	 * 
	 * @param columnLabel
	 *            the label for the column specified with the SQL AS clause. If the 
	 *            SQL AS clause was not specified, then the label is the name of the column
	 *            
	 * @return the column index of the given column name
	 */
	public int findColumn(String columnName) throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			Integer index;
	
			if (!this.hasBuiltIndexMapping) {
				buildIndexMapping();
			}
	
			index = this.columnToIndexCache.get(columnName);
	
			if (index != null) {
				return index.intValue() + 1;
			}
	
			index = this.columnLabelToIndex.get(columnName);
	
			if (index == null && this.useColumnNamesInFindColumn) {
				index = this.columnNameToIndex.get(columnName);
			}
			
			if (index == null) {
				index = this.fullColumnNameToIndex.get(columnName);
			}
			
			if (index != null) {
				this.columnToIndexCache.put(columnName, index);
				
				return index.intValue() + 1;
			}
	
			// Try this inefficient way, now
	
			for (int i = 0; i < this.fields.length; i++) {
				if (this.fields[i].getName().equalsIgnoreCase(columnName)) {
					return i + 1;
				} else if (this.fields[i].getFullName()
						.equalsIgnoreCase(columnName)) {
					return i + 1;
				}
			}
	
			throw SQLError.createSQLException(Messages.getString("ResultSet.Column____112")
					+ columnName
					+ Messages.getString("ResultSet.___not_found._113"), //$NON-NLS-1$ //$NON-NLS-2$
					SQLError.SQL_STATE_COLUMN_NOT_FOUND, getExceptionInterceptor());
		}
	}

	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {
	
			boolean b = true;
			
			if (this.rowData.isEmpty()) {
				b = false;
			} else {
		
				if (this.onInsertRow) {
					this.onInsertRow = false;
				}
		
				if (this.doingUpdates) {
					this.doingUpdates = false;
				}
		
				this.rowData.beforeFirst();
				this.thisRow = this.rowData.next();
			}
	
			setRowPositionValidity();
			
			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 {
		checkColumnBounds(i);
		
		throw SQLError.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 {
		return getArray(findColumn(colName));
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 * 
	 * @see getBinaryStream
	 */
	public InputStream getAsciiStream(int columnIndex) throws SQLException {
		checkRowPos();

		if (!this.isBinaryEncoded) {
			return getBinaryStream(columnIndex);
		}

		return getNativeBinaryStream(columnIndex);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param columnName
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public InputStream getAsciiStream(String columnName) throws SQLException {
		return getAsciiStream(findColumn(columnName));
	}

	/**
	 * 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.
	 */
	public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
		if (!this.isBinaryEncoded) {
			String stringVal = getString(columnIndex);
			BigDecimal val;

			if (stringVal != null) {
				if (stringVal.length() == 0) {
					
					val = new BigDecimal(
							convertToZeroLiteralStringWithEmptyCheck());

					return val;
				}

				try {
					val = new BigDecimal(stringVal);

					return val;
				} catch (NumberFormatException ex) {
					throw SQLError.createSQLException(Messages
							.getString("ResultSet.Bad_format_for_BigDecimal",
									new Object[] { stringVal,
											Integer.valueOf(columnIndex) }),
							SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
				}
			}

			return null;
		}

		return getNativeBigDecimal(columnIndex);
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 * 
	 * @deprecated
	 */
	public BigDecimal getBigDecimal(int columnIndex, int scale)
			throws SQLException {
		if (!this.isBinaryEncoded) {
			String stringVal = getString(columnIndex);
			BigDecimal val;

			if (stringVal != null) {
				if (stringVal.length() == 0) {
					val = new BigDecimal(
							convertToZeroLiteralStringWithEmptyCheck());

					try {
						return val.setScale(scale);
					} catch (ArithmeticException ex) {
						try {
							return val
									.setScale(scale, BigDecimal.ROUND_HALF_UP);
						} catch (ArithmeticException arEx) {
							throw SQLError.createSQLException(
									Messages
											.getString("ResultSet.Bad_format_for_BigDecimal", //$NON-NLS-1$
											 new Object[] {stringVal, Integer.valueOf(columnIndex)}),
									SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
						}
					}
				}

				try {
					val = new BigDecimal(stringVal);
				} catch (NumberFormatException ex) {
					if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
						long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);

						val = new BigDecimal(valueAsLong);
					} else {
						throw SQLError.createSQLException(Messages
							.getString("ResultSet.Bad_format_for_BigDecimal",
									new Object[] { Integer.valueOf(columnIndex),
											stringVal }),
							SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
						}
				}

				try {
					return val.setScale(scale);
				} catch (ArithmeticException ex) {
					try {
						return val.setScale(scale, BigDecimal.ROUND_HALF_UP);
					} catch (ArithmeticException arithEx) {
						throw SQLError.createSQLException(Messages.getString(
								"ResultSet.Bad_format_for_BigDecimal",
								new Object[] { Integer.valueOf(columnIndex),
										stringVal }),
								SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
					}
				}
			}

			return null;
		}

		return getNativeBigDecimal(columnIndex, scale);
	}

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

	/**
	 * DOCUMENT ME!
	 * 
	 * @param columnName
	 *            DOCUMENT ME!
	 * @param scale
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 * 
	 * @deprecated
	 */
	public BigDecimal getBigDecimal(String columnName, int scale)
			throws SQLException {
		return getBigDecimal(findColumn(columnName), scale);
	}

	private final BigDecimal getBigDecimalFromString(String stringVal,
			int columnIndex, int scale) throws SQLException {
		BigDecimal bdVal;

		if (stringVal != null) {
			if (stringVal.length() == 0) {
				bdVal = new BigDecimal(convertToZeroLiteralStringWithEmptyCheck());

				try {
					return bdVal.setScale(scale);
				} catch (ArithmeticException ex) {
					try {
						return bdVal.setScale(scale, BigDecimal.ROUND_HALF_UP);
					} catch (ArithmeticException arEx) {
						throw new SQLException(Messages
								.getString("ResultSet.Bad_format_for_BigDecimal",
										new Object[] { stringVal,
												Integer.valueOf(columnIndex) }),
								SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
					}
				}
			}

			try {
				try {
					return new BigDecimal(stringVal).setScale(scale);
				} catch (ArithmeticException ex) {
					try {
						return new BigDecimal(stringVal).setScale(scale,
								BigDecimal.ROUND_HALF_UP);
					} catch (ArithmeticException arEx) {
						throw new SQLException(Messages
								.getString("ResultSet.Bad_format_for_BigDecimal",
										new Object[] { stringVal,
												Integer.valueOf(columnIndex) }),
								SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
					}
				}
			} catch (NumberFormatException ex) {
				if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
					long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);

					try {
						return new BigDecimal(valueAsLong).setScale(scale);
					} catch (ArithmeticException arEx1) {
						try {
							return new BigDecimal(valueAsLong).setScale(scale,
									BigDecimal.ROUND_HALF_UP);
						} catch (ArithmeticException arEx2) {
							throw new SQLException(Messages
									.getString("ResultSet.Bad_format_for_BigDecimal",
											new Object[] { stringVal,
													Integer.valueOf(columnIndex) }),
									SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
						}
					}
				}
				
				if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TINY &&
						this.connection.getTinyInt1isBit() && this.fields[columnIndex - 1].getLength() == 1) {
					return new BigDecimal(stringVal.equalsIgnoreCase("true") ? 1 : 0).setScale(scale);
				}
				
				throw new SQLException(Messages
						.getString("ResultSet.Bad_format_for_BigDecimal",
								new Object[] { stringVal,
										Integer.valueOf(columnIndex) }),
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
			}
		}
		
		return null;
	}

	/**
	 * A column value can also be retrieved as a binary stream. 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 SQLException
	 *                if a database access error occurs
	 * 
	 * @see getAsciiStream
	 * @see getUnicodeStream
	 */
	public InputStream getBinaryStream(int columnIndex) throws SQLException {
		checkRowPos();

		if (!this.isBinaryEncoded) {
			checkColumnBounds(columnIndex);
			
			int columnIndexMinusOne = columnIndex - 1;
			
			if (this.thisRow.isNull(columnIndexMinusOne)) {
				this.wasNullFlag = true;
				
				return null;
			}
			
			this.wasNullFlag = false;
			
			return this.thisRow.getBinaryInputStream(columnIndexMinusOne);
		}

		return getNativeBinaryStream(columnIndex);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param columnName
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public InputStream getBinaryStream(String columnName) throws 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.
	 */
	public java.sql.Blob getBlob(int columnIndex) throws SQLException {
		if (!this.isBinaryEncoded) {
			checkRowPos();

			checkColumnBounds(columnIndex);

			int columnIndexMinusOne = columnIndex - 1;
			
			if (this.thisRow.isNull(columnIndexMinusOne)) {
				this.wasNullFlag = true;
			} else {
				this.wasNullFlag = false;
			}
			
			if (this.wasNullFlag) {
				return null;
			}

			if (!this.connection.getEmulateLocators()) {
				return new Blob(this.thisRow.getColumnValue(columnIndexMinusOne), getExceptionInterceptor());
			}

			return new BlobFromLocator(this, columnIndex, getExceptionInterceptor());
		}

		return getNativeBlob(columnIndex);
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	public boolean getBoolean(int columnIndex) throws SQLException {
		
		checkColumnBounds(columnIndex);

		//
		// MySQL 5.0 and newer have an actual BIT type,
		// so we need to check for that here...
		//

		int columnIndexMinusOne = columnIndex - 1;

		Field field = this.fields[columnIndexMinusOne];

		if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
			return byteArrayToBoolean(columnIndexMinusOne);
		}

		this.wasNullFlag = false;
		
		int sqlType = field.getSQLType();
		
		switch (sqlType) {
		case Types.BOOLEAN:
			if (field.getMysqlType() == -1) { // from dbmd
				String stringVal = getString(columnIndex);

				return getBooleanFromString(stringVal);
			}
				
			long boolVal = getLong(columnIndex, false);

			return (boolVal == -1 || boolVal > 0);
		case Types.BIT:
		case Types.TINYINT:
		case Types.SMALLINT:
		case Types.INTEGER:
		case Types.BIGINT:
		case Types.DECIMAL:
		case Types.NUMERIC:
		case Types.REAL:
		case Types.FLOAT:
		case Types.DOUBLE:
			boolVal = getLong(columnIndex, false);

			return (boolVal == -1 || boolVal > 0);
		default:
			if (this.connection.getPedantic()) {
				// Table B-6 from JDBC spec
				switch (sqlType) {
				case Types.BINARY:
				case Types.VARBINARY:
				case Types.LONGVARBINARY:
				case Types.DATE:
				case Types.TIME:
				case Types.TIMESTAMP:
				case Types.CLOB:
				case Types.BLOB:
				case Types.ARRAY:
				case Types.REF:
				case Types.DATALINK:
				case Types.STRUCT:
				case Types.JAVA_OBJECT:
					throw SQLError.createSQLException("Required type conversion not allowed",
							SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, getExceptionInterceptor());
				}
			}
		
			if (sqlType == Types.BINARY ||
				sqlType == Types.VARBINARY ||
				sqlType == Types.LONGVARBINARY ||
				sqlType == Types.BLOB) {
				return byteArrayToBoolean(columnIndexMinusOne);
			}
			
			if (this.useUsageAdvisor) {
				issueConversionViaParsingWarning("getBoolean()", columnIndex,
						this.thisRow.getColumnValue(columnIndexMinusOne), this.fields[columnIndex],
						new int[] {
								MysqlDefs.FIELD_TYPE_BIT,
								MysqlDefs.FIELD_TYPE_DOUBLE,
								MysqlDefs.FIELD_TYPE_TINY,
								MysqlDefs.FIELD_TYPE_SHORT,
								MysqlDefs.FIELD_TYPE_LONG,
								MysqlDefs.FIELD_TYPE_LONGLONG,
								MysqlDefs.FIELD_TYPE_FLOAT });
			}
		
			String stringVal = getString(columnIndex);

			return getBooleanFromString(stringVal);
		}
	}

	private boolean byteArrayToBoolean(int columnIndexMinusOne) throws SQLException {
		Object value = this.thisRow.getColumnValue(columnIndexMinusOne);
		
		if (value == null) {
			this.wasNullFlag = true;

			return false;
		}

		this.wasNullFlag = false;

		if (((byte[]) value).length == 0) {
			return false;
		}

		byte boolVal = ((byte[]) value)[0];

		if (boolVal == (byte)'1') {
			return true;
		} else if (boolVal == (byte)'0') {
			return false;
		}
		
		return (boolVal == -1 || boolVal > 0);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param columnName
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public boolean getBoolean(String columnName) throws SQLException {
		return getBoolean(findColumn(columnName));
	}

	private final boolean getBooleanFromString(String stringVal)
			throws SQLException {
		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;
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	public byte getByte(int columnIndex) throws SQLException {
		if (!this.isBinaryEncoded) {
			String stringVal = getString(columnIndex);

			if (this.wasNullFlag || (stringVal == null)) {
				return 0;
			}

			return getByteFromString(stringVal, columnIndex);
		}

		return getNativeByte(columnIndex);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param columnName
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public byte getByte(String columnName) throws SQLException {
		return getByte(findColumn(columnName));
	}

	private final byte getByteFromString(String stringVal, int columnIndex)
			throws SQLException {

		if (stringVal != null && stringVal.length() == 0) {
			return (byte) convertToZeroWithEmptyCheck();
		}

		//
		// JDK-6 doesn't like trailing whitespace
		//
		// Note this isn't a performance issue, other
		// than the iteration over the string, as String.trim()
		// will return a new string only if whitespace is present
		//
		
		if (stringVal == null) {
			return 0;
		}
		
		stringVal = stringVal.trim(); 
		
		try {
			int decimalIndex = stringVal.indexOf(".");

			
			if (decimalIndex != -1) {
				double valueAsDouble = Double.parseDouble(stringVal);

				if (this.jdbcCompliantTruncationForReads) {
					if (valueAsDouble < Byte.MIN_VALUE
							|| valueAsDouble > Byte.MAX_VALUE) {
						throwRangeException(stringVal, columnIndex,
								Types.TINYINT);
					}
				}

				return (byte) valueAsDouble;
			}

			long valueAsLong = Long.parseLong(stringVal);

			if (this.jdbcCompliantTruncationForReads) {
				if (valueAsLong < Byte.MIN_VALUE
						|| valueAsLong > Byte.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsLong),
							columnIndex, Types.TINYINT);
				}
			}

			return (byte) valueAsLong;
		} catch (NumberFormatException NFE) {
			throw SQLError.createSQLException(
					Messages.getString("ResultSet.Value____173")
							+ stringVal //$NON-NLS-1$
							+ Messages
									.getString("ResultSet.___is_out_of_range_[-127,127]_174"),
					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
		}
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	public byte[] getBytes(int columnIndex) throws SQLException {
		return getBytes(columnIndex, false);
	}

	protected byte[] getBytes(int columnIndex, boolean noConversion)
			throws SQLException {
		if (!this.isBinaryEncoded) {
			checkRowPos();
			
			checkColumnBounds(columnIndex);
			
			int columnIndexMinusOne = columnIndex - 1;

			if (this.thisRow.isNull(columnIndexMinusOne)) {
				this.wasNullFlag = true;
			} else {
				this.wasNullFlag = false;
			}

			if (this.wasNullFlag) {
				return null;
			}

			return this.thisRow.getColumnValue(columnIndexMinusOne);
		}

		return getNativeBytes(columnIndex, noConversion);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param columnName
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public byte[] getBytes(String columnName) throws SQLException {
		return getBytes(findColumn(columnName));
	}

	private final byte[] getBytesFromString(String stringVal)
			throws SQLException {
		if (stringVal != null) {
			return StringUtils.getBytes(stringVal, this.connection
					.getEncoding(), this.connection
					.getServerCharacterEncoding(), this.connection
					.parserKnowsUnicode(),
					this.connection, getExceptionInterceptor());
		}

		return null;
	}

	public int getBytesSize() throws SQLException {
		RowData localRowData = this.rowData;
		
		checkClosed();
		
		if (localRowData instanceof RowDataStatic) {
			int bytesSize = 0;
			
			int numRows = localRowData.size();

			for (int i = 0; i < numRows; i++) {
				bytesSize += localRowData.getAt(i).getBytesSize();
			}

			return bytesSize;
		}
		
		return -1;
	}
	
	/**
	 * Optimization to only use one calendar per-session, or calculate it for
	 * each call, depending on user configuration
	 */
	protected Calendar getCalendarInstanceForSessionOrNew() throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			if (this.connection != null) {
				return this.connection.getCalendarInstanceForSessionOrNew();
			}
			
			// punt, no connection around
			return new GregorianCalendar();
		}
	}

	/**
	 * 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 {
		if (!this.isBinaryEncoded) {
			checkColumnBounds(columnIndex);
			
			int columnIndexMinusOne = columnIndex - 1;
			
			if (this.thisRow.isNull(columnIndexMinusOne)) {
				this.wasNullFlag = true;
				
				return null;
			}
			
			this.wasNullFlag = false;
			
			return this.thisRow.getReader(columnIndexMinusOne);
		}

		return getNativeCharacterStream(columnIndex);
	}

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

	private final java.io.Reader getCharacterStreamFromString(String stringVal) 
			throws SQLException {
		if (stringVal != null) {
			return new StringReader(stringVal);
		}

		return null;
	}

	/**
	 * 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 {
		if (!this.isBinaryEncoded) {
			String asString = getStringForClob(i);
			
			if (asString == null) {
				return null;
			}

			return new com.mysql.jdbc.Clob(asString, getExceptionInterceptor());
		}

		return getNativeClob(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));
	}

	private final java.sql.Clob getClobFromString(String stringVal) throws SQLException {
		return new com.mysql.jdbc.Clob(stringVal, getExceptionInterceptor());
	}

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

	/**
	 * 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 SQLException is
	 * thrown.
	 * </p>
	 * 
	 * @return the ResultSet's SQL cursor name.
	 * 
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	public String getCursorName() throws SQLException {
		throw SQLError.createSQLException(Messages
				.getString("ResultSet.Positioned_Update_not_supported"),
				SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor()); //$NON-NLS-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 {
		return getDate(columnIndex, null);
	}

	/**
	 * 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.
	 */
	public java.sql.Date getDate(int columnIndex, Calendar cal)
			throws SQLException {
		if (this.isBinaryEncoded) {
			return getNativeDate(columnIndex, cal);
		}

		if (!this.useFastDateParsing) {
			String stringVal = getStringInternal(columnIndex, false);

			if (stringVal == null) {
				return null;
			}
			
			return getDateFromString(stringVal, columnIndex, cal);
		}
		
		checkColumnBounds(columnIndex);
		
		int columnIndexMinusOne = columnIndex - 1;
		Date tmpDate = this.thisRow.getDateFast(columnIndexMinusOne, this.connection, this, cal);
		if ((this.thisRow.isNull(columnIndexMinusOne)) 
				|| (tmpDate == null)) {
		
			this.wasNullFlag = true;
			
			return null;
		}
		
		this.wasNullFlag = false;
		
		return tmpDate;
	}

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

	/**
	 * 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(findColumn(columnName), cal);
	}

	private final java.sql.Date getDateFromString(String stringVal,
			int columnIndex, Calendar targetCalendar) throws SQLException {
		int year = 0;
		int month = 0;
		int day = 0;

		try {
			this.wasNullFlag = false;

			if (stringVal == null) {
				this.wasNullFlag = true;

				return null;
			}
			
			//
			// JDK-6 doesn't like trailing whitespace
			//
			// Note this isn't a performance issue, other
			// than the iteration over the string, as String.trim()
			// will return a new string only if whitespace is present
			//
			
			stringVal = stringVal.trim();

			if (stringVal.equals("0") || stringVal.equals("0000-00-00")
					|| stringVal.equals("0000-00-00 00:00:00")
					|| stringVal.equals("00000000000000")
					|| stringVal.equals("0")) {

				if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
						.equals(this.connection.getZeroDateTimeBehavior())) {
					this.wasNullFlag = true;

					return null;
				} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
						.equals(this.connection.getZeroDateTimeBehavior())) {
					throw SQLError.createSQLException("Value '" + stringVal
							+ "' can not be represented as java.sql.Date",
							SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
				}

				// We're left with the case of 'round' to a date Java _can_
				// represent, which is '0001-01-01'.
				return fastDateCreate(targetCalendar, 1, 1, 1);

			} else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) {
				// Convert from TIMESTAMP
				switch (stringVal.length()) {
				case 21:
				case 19: { // java.sql.Timestamp format
					year = Integer.parseInt(stringVal.substring(0, 4));
					month = Integer.parseInt(stringVal.substring(5, 7));
					day = Integer.parseInt(stringVal.substring(8, 10));

					return fastDateCreate(targetCalendar, year, month, day);
				}

				case 14:
				case 8: {
					year = Integer.parseInt(stringVal.substring(0, 4));
					month = Integer.parseInt(stringVal.substring(4, 6));
					day = Integer.parseInt(stringVal.substring(6, 8));

					return fastDateCreate(targetCalendar, year, month, day);
				}

				case 12:
				case 10:
				case 6: {
					year = Integer.parseInt(stringVal.substring(0, 2));

					if (year <= 69) {
						year = year + 100;
					}

					month = Integer.parseInt(stringVal.substring(2, 4));
					day = Integer.parseInt(stringVal.substring(4, 6));

					return fastDateCreate(targetCalendar, year + 1900, month, day);
				}

				case 4: {
					year = Integer.parseInt(stringVal.substring(0, 4));

					if (year <= 69) {
						year = year + 100;
					}

					month = Integer.parseInt(stringVal.substring(2, 4));

					return fastDateCreate(targetCalendar, year + 1900, month, 1);
				}

				case 2: {
					year = Integer.parseInt(stringVal.substring(0, 2));

					if (year <= 69) {
						year = year + 100;
					}

					return fastDateCreate(targetCalendar, year + 1900, 1, 1);
				}

				default:
					throw SQLError.createSQLException(Messages.getString(
							"ResultSet.Bad_format_for_Date", new Object[] {
									stringVal, Integer.valueOf(columnIndex) }),
							SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
				} /* endswitch */
			} else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) {

				if (stringVal.length() == 2 || stringVal.length() == 1) {
					year = Integer.parseInt(stringVal);

					if (year <= 69) {
						year = year + 100;
					}

					year += 1900;
				} else {
					year = Integer.parseInt(stringVal.substring(0, 4));
				}

				return fastDateCreate(targetCalendar, year, 1, 1);
			} else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIME) {
				return fastDateCreate(targetCalendar, 1970, 1, 1); // Return EPOCH
			} else {
				if (stringVal.length() < 10) {
					if (stringVal.length() == 8) {
						return fastDateCreate(targetCalendar, 1970, 1, 1); // Return EPOCH for TIME
					}
					
					throw SQLError.createSQLException(Messages.getString(
							"ResultSet.Bad_format_for_Date", new Object[] {
									stringVal, Integer.valueOf(columnIndex) }),
							SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
				}

				if (stringVal.length() != 18) {
					year = Integer.parseInt(stringVal.substring(0, 4));
					month = Integer.parseInt(stringVal.substring(5, 7));
					day = Integer.parseInt(stringVal.substring(8, 10));
				} else {
					// JDK-1.3 timestamp format, not real easy to parse positionally :p
					StringTokenizer st = new StringTokenizer(stringVal, "- ");
					
					year = Integer.parseInt(st.nextToken());
					month = Integer.parseInt(st.nextToken());
					day = Integer.parseInt(st.nextToken());
				}
			}

			return fastDateCreate(targetCalendar, year, month, day);
		} catch (SQLException sqlEx) {
			throw sqlEx; // don't re-wrap
		} catch (Exception e) {
			SQLException sqlEx = SQLError.createSQLException(Messages.getString(
					"ResultSet.Bad_format_for_Date", new Object[] { stringVal,
							Integer.valueOf(columnIndex) }),
					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
			
			sqlEx.initCause(e);
			
			throw sqlEx;
		}
	}
	
	private TimeZone getDefaultTimeZone() {
		if (!this.useLegacyDatetimeCode && this.connection != null) {
			return this.serverTimeZoneTz;
		}
		
		return this.connection.getDefaultTimeZone();
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	public double getDouble(int columnIndex) throws SQLException {
		if (!this.isBinaryEncoded) {
			return getDoubleInternal(columnIndex);
		}

		return getNativeDouble(columnIndex);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param columnName
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public double getDouble(String columnName) throws SQLException {
		return getDouble(findColumn(columnName));
	}

	private final double getDoubleFromString(String stringVal, int columnIndex)
			throws SQLException {
		return getDoubleInternal(stringVal, columnIndex);
	}

	/**
	 * 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 {
		return getDoubleInternal(getString(colIndex), colIndex);
	}

	/**
	 * Converts a string representation of a number to a double. Need a faster
	 * way to do this.
	 * 
	 * @param stringVal
	 *            the double as a String
	 * @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(String stringVal, int colIndex)
			throws SQLException {
		try {
			if ((stringVal == null)) {
				return 0;
			}

			if (stringVal.length() == 0) {
				return convertToZeroWithEmptyCheck();
			}

			double d = Double.parseDouble(stringVal);

			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) {
			if (this.fields[colIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
				long valueAsLong = getNumericRepresentationOfSQLBitType(colIndex);
				
				return valueAsLong;
			}
			
			throw SQLError.createSQLException(Messages.getString(
					"ResultSet.Bad_format_for_number", new Object[] {
							stringVal, Integer.valueOf(colIndex) }),
					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
		}
	}

	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {
			return this.fetchDirection;
		}
	}

	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {
			return this.fetchSize;
		}
	}

	/**
	 * Returns the first character of the query that this result set was created
	 * from.
	 * 
	 * @return the first character of the query...uppercased
	 */
	public char getFirstCharOfQuery() {
		try {
			synchronized (checkClosed().getConnectionMutex()) {
				return this.firstCharOfQuery;
			}
		} catch (SQLException e) {
			throw new RuntimeException(e); // FIXME: Need to evolve interface
		}
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	public float getFloat(int columnIndex) throws SQLException {
		if (!this.isBinaryEncoded) {
			String val = null;

			val = getString(columnIndex);

			return getFloatFromString(val, columnIndex);
		}

		return getNativeFloat(columnIndex);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param columnName
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public float getFloat(String columnName) throws SQLException {
		return getFloat(findColumn(columnName));
	}

	private final float getFloatFromString(String val, int columnIndex)
			throws SQLException {
		try {
			if ((val != null)) {
				if (val.length() == 0) {
					return convertToZeroWithEmptyCheck();
				}

				float f = Float.parseFloat(val);

				if (this.jdbcCompliantTruncationForReads) {
					if (f == Float.MIN_VALUE || f == Float.MAX_VALUE) {
						double valAsDouble = Double.parseDouble(val);

						// Straight comparison is not reliable when at
						// absolute endpoints of Float.MIN_VALUE or 
						// Float.MAX_VALUE, so use epsillons with DOUBLEs

			            if ((valAsDouble < Float.MIN_VALUE - MIN_DIFF_PREC)
			                || (valAsDouble > Float.MAX_VALUE - MAX_DIFF_PREC)) {
			              throwRangeException(String.valueOf(valAsDouble), columnIndex,
			                  Types.FLOAT);
			            }
					}
				}

				return f;
			}

			return 0; // for NULL
		} catch (NumberFormatException nfe) {
			try {
				Double valueAsDouble = new Double(val);
				float valueAsFloat = valueAsDouble.floatValue();
				
				if (this.jdbcCompliantTruncationForReads) {

					if (this.jdbcCompliantTruncationForReads && 
							valueAsFloat == Float.NEGATIVE_INFINITY ||
							valueAsFloat == Float.POSITIVE_INFINITY) {
						throwRangeException(valueAsDouble.toString(), 
								columnIndex, Types.FLOAT);
					}
				}

				return valueAsFloat;
			} catch (NumberFormatException newNfe) {
				; // ignore, it's not a number
			}

			throw SQLError.createSQLException(
					Messages
							.getString("ResultSet.Invalid_value_for_getFloat()_-____200")
							+ val //$NON-NLS-1$
							+ Messages.getString("ResultSet.___in_column__201")
							+ columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
		}
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	public int getInt(int columnIndex) throws SQLException {
		checkRowPos();

		if (!this.isBinaryEncoded) {
			int columnIndexMinusOne = columnIndex - 1;
			if (this.useFastIntParsing) {
				checkColumnBounds(columnIndex);

				if (this.thisRow.isNull(columnIndexMinusOne)) {
					this.wasNullFlag = true;
				} else {
					this.wasNullFlag = false;
				}

				if (this.wasNullFlag) {
					return 0;
				}

				if (this.thisRow.length(columnIndexMinusOne) == 0) {
					return convertToZeroWithEmptyCheck();
				}

				boolean needsFullParse = this.thisRow
						.isFloatingPointNumber(columnIndexMinusOne);

				if (!needsFullParse) {
					try {
						return getIntWithOverflowCheck(columnIndexMinusOne);
					} catch (NumberFormatException nfe) {
						try {

							return parseIntAsDouble(columnIndex, this.thisRow
									.getString(columnIndexMinusOne,
											this.fields[columnIndexMinusOne]
													.getCharacterSet(),
											this.connection));
						} catch (NumberFormatException newNfe) {
							// ignore, it's not a number
						}

						if (this.fields[columnIndexMinusOne].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
							long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);

							if (this.connection
									.getJdbcCompliantTruncationForReads()
									&& (valueAsLong < Integer.MIN_VALUE || valueAsLong > Integer.MAX_VALUE)) {
								throwRangeException(
										String.valueOf(valueAsLong),
										columnIndex, Types.INTEGER);
							}

							return (int) valueAsLong;
						}

						throw SQLError
								.createSQLException(
										Messages
												.getString("ResultSet.Invalid_value_for_getInt()_-____74")
												+ this.thisRow
														.getString(
																columnIndexMinusOne,
																this.fields[columnIndexMinusOne]
																		.getCharacterSet(),
																this.connection) //$NON-NLS-1$
												+ "'",
										SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
					}
				}
			}

			String val = null;

			try {
				val = getString(columnIndex);

				if ((val != null)) {
					if (val.length() == 0) {
						return convertToZeroWithEmptyCheck();
					}

					if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)
							&& (val.indexOf(".") == -1)) {
						int intVal = Integer.parseInt(val);
						
						checkForIntegerTruncation(columnIndexMinusOne, null, intVal);
						
						return intVal;
					}

					// Convert floating point
					int intVal =  parseIntAsDouble(columnIndex, val);
					
					checkForIntegerTruncation(columnIndex, null, intVal);
					
					return intVal;
				}

				return 0;
			} catch (NumberFormatException nfe) {
				try {
					return parseIntAsDouble(columnIndex, val);
				} catch (NumberFormatException newNfe) {
					; // ignore, it's not a number
				}

				if (this.fields[columnIndexMinusOne].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
					long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);

					if (this.jdbcCompliantTruncationForReads
							&& (valueAsLong < Integer.MIN_VALUE || valueAsLong > Integer.MAX_VALUE)) {
						throwRangeException(String.valueOf(valueAsLong),
								columnIndex, Types.INTEGER);
					}

					return (int) valueAsLong;
				}

				throw SQLError
						.createSQLException(
								Messages
										.getString("ResultSet.Invalid_value_for_getInt()_-____74")
										+ val //$NON-NLS-1$
										+ "'",
								SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
			}
		}

		return getNativeInt(columnIndex);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param columnName
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public int getInt(String columnName) throws SQLException {
		return getInt(findColumn(columnName));
	}

	private final int getIntFromString(String val, int columnIndex)
			throws SQLException {
		try {
			if ((val != null)) {

				if (val.length() == 0) {
					return convertToZeroWithEmptyCheck();
				}

				if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)
						&& (val.indexOf(".") == -1)) {
					//
					// JDK-6 doesn't like trailing whitespace
					//
					// Note this isn't a performance issue, other
					// than the iteration over the string, as String.trim()
					// will return a new string only if whitespace is present
					//
					
					val = val.trim(); 
					
					int valueAsInt = Integer.parseInt(val);

					if (this.jdbcCompliantTruncationForReads) {
						if (valueAsInt == Integer.MIN_VALUE
								|| valueAsInt == Integer.MAX_VALUE) {
							long valueAsLong = Long.parseLong(val);

							if (valueAsLong < Integer.MIN_VALUE
									|| valueAsLong > Integer.MAX_VALUE) {
								throwRangeException(
										String.valueOf(valueAsLong),
										columnIndex, Types.INTEGER);
							}
						}
					}

					return valueAsInt;
				}

				// Convert floating point

				double valueAsDouble = Double.parseDouble(val);

				if (this.jdbcCompliantTruncationForReads) {
					if (valueAsDouble < Integer.MIN_VALUE
							|| valueAsDouble > Integer.MAX_VALUE) {
						throwRangeException(String.valueOf(valueAsDouble),
								columnIndex, Types.INTEGER);
					}
				}

				return (int) valueAsDouble;
			}

			return 0; // for NULL
		} catch (NumberFormatException nfe) {
			try {
				double valueAsDouble = Double.parseDouble(val);

				if (this.jdbcCompliantTruncationForReads) {
					if (valueAsDouble < Integer.MIN_VALUE
							|| valueAsDouble > Integer.MAX_VALUE) {
						throwRangeException(String.valueOf(valueAsDouble),
								columnIndex, Types.INTEGER);
					}
				}

				return (int) valueAsDouble;
			} catch (NumberFormatException newNfe) {
				; // ignore, it's not a number
			}

			throw SQLError.createSQLException(Messages
					.getString("ResultSet.Invalid_value_for_getInt()_-____206")
					+ val //$NON-NLS-1$
					+ Messages.getString("ResultSet.___in_column__207")
					+ columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
		}
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	public long getLong(int columnIndex) throws SQLException {
		return getLong(columnIndex, true);
	}
	
	private long getLong(int columnIndex, boolean overflowCheck) throws SQLException {
		if (!this.isBinaryEncoded) {
			checkRowPos();
			
			int columnIndexMinusOne = columnIndex - 1;
			
			if (this.useFastIntParsing) {
			
				checkColumnBounds(columnIndex);

				if (this.thisRow.isNull(columnIndexMinusOne)) {
					this.wasNullFlag = true;
				} else {
					this.wasNullFlag = false;
				}
				
				if (this.wasNullFlag) {
					return 0;
				}

				if (this.thisRow.length(columnIndexMinusOne) == 0) {
					return convertToZeroWithEmptyCheck();
				}

				boolean needsFullParse = this.thisRow.isFloatingPointNumber(columnIndexMinusOne);

				if (!needsFullParse) {
					try {
						return getLongWithOverflowCheck(columnIndexMinusOne, overflowCheck);
					} catch (NumberFormatException nfe) {
						try {
							// To do: Warn of over/underflow???
							return parseLongAsDouble(columnIndexMinusOne, this.thisRow
									.getString(columnIndexMinusOne,
											this.fields[columnIndexMinusOne]
													.getCharacterSet(),
											this.connection));
						} catch (NumberFormatException newNfe) {
							// ; // ignore, it's not a number
						}

						if (this.fields[columnIndexMinusOne].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
							return getNumericRepresentationOfSQLBitType(columnIndex);
						}
						
						throw SQLError.createSQLException(
								Messages
										.getString("ResultSet.Invalid_value_for_getLong()_-____79")
										+ this.thisRow
										.getString(columnIndexMinusOne,
												this.fields[columnIndexMinusOne]
														.getCharacterSet(),
												this.connection) //$NON-NLS-1$
										+ "'",
								SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
					}
				}
			}

			String val = null;

			try {
				val = getString(columnIndex);

				if ((val != null)) {
					if (val.length() == 0) {
						return convertToZeroWithEmptyCheck();
					}

					if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)) {
						return parseLongWithOverflowCheck(columnIndexMinusOne, null,
								val, overflowCheck);
					}

					// Convert floating point
					return parseLongAsDouble(columnIndexMinusOne, val);
				}

				return 0; // for NULL
			} catch (NumberFormatException nfe) {
				try {
					return parseLongAsDouble(columnIndexMinusOne, val);
				} catch (NumberFormatException newNfe) {
					// ; // ignore, it's not a number
				}

				throw SQLError.createSQLException(
						Messages
								.getString("ResultSet.Invalid_value_for_getLong()_-____79")
								+ val //$NON-NLS-1$
								+ "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
			}
		}

		return getNativeLong(columnIndex, overflowCheck, true);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param columnName
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public long getLong(String columnName) throws SQLException {
		return getLong(findColumn(columnName));
	}

	private final long getLongFromString(String val, int columnIndexZeroBased)
			throws SQLException {
		try {
			if ((val != null)) {

				if (val.length() == 0) {
					return convertToZeroWithEmptyCheck();
				}

				if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)) {
					return parseLongWithOverflowCheck(columnIndexZeroBased, null, val, true);
				}

				// Convert floating point
				return parseLongAsDouble(columnIndexZeroBased, val);
			}

			return 0; // for NULL
		} catch (NumberFormatException nfe) {
			try {
				// To do: Warn of over/underflow???
				return parseLongAsDouble(columnIndexZeroBased, val);
			} catch (NumberFormatException newNfe) {
				; // ignore, it's not a number
			}

			throw SQLError.createSQLException(
					Messages
							.getString("ResultSet.Invalid_value_for_getLong()_-____211")
							+ val //$NON-NLS-1$
							+ Messages.getString("ResultSet.___in_column__212")
							+ (columnIndexZeroBased + 1), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
		}
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	public java.sql.ResultSetMetaData getMetaData() throws SQLException {
		checkClosed();

		return new com.mysql.jdbc.ResultSetMetaData(fields, connection.getUseOldAliasMetadataBehavior(),
				connection.getYearIsDateType(), getExceptionInterceptor());
	}

	/**
	 * 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!
	 */
	protected java.sql.Array getNativeArray(int i) throws SQLException {
		throw SQLError.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 SQLException
	 *                if a database access error occurs
	 * 
	 * @see getBinaryStream
	 */
	protected InputStream getNativeAsciiStream(int columnIndex)
			throws SQLException {
		checkRowPos();

		return getNativeBinaryStream(columnIndex);
	}

	/**
	 * 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.
	 */
	protected BigDecimal getNativeBigDecimal(int columnIndex)
			throws SQLException {

		checkColumnBounds(columnIndex);
		
		int scale = this.fields[columnIndex - 1].getDecimals();
		
		return getNativeBigDecimal(columnIndex, scale);
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	protected BigDecimal getNativeBigDecimal(int columnIndex, int scale)
			throws SQLException {
		checkColumnBounds(columnIndex);
		
		String stringVal = null;
		
		Field f = this.fields[columnIndex - 1];
		
		Object value = this.thisRow.getColumnValue(columnIndex - 1);
		
		if (value == null) {
			this.wasNullFlag = true;
			
			return null;
		}
		
		this.wasNullFlag = false;
		
		switch (f.getSQLType()) {
			case Types.DECIMAL:
			case Types.NUMERIC:
				stringVal = StringUtils
						.toAsciiString((byte[]) value);
				break;
			default:
				stringVal = getNativeString(columnIndex);
		}

		return getBigDecimalFromString(stringVal, columnIndex, scale);
	}

	/**
	 * A column value can also be retrieved as a binary stream. 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 SQLException
	 *                if a database access error occurs
	 * 
	 * @see getAsciiStream
	 * @see getUnicodeStream
	 */
	protected InputStream getNativeBinaryStream(int columnIndex)
			throws SQLException {
		checkRowPos();

		int columnIndexMinusOne = columnIndex - 1;
		
		if (this.thisRow.isNull(columnIndexMinusOne)) {
			this.wasNullFlag = true;
			
			return null;
		}
		
		this.wasNullFlag = false;
		
		switch (this.fields[columnIndexMinusOne].getSQLType()) {
		case Types.BIT:
		case Types.BINARY:
		case Types.VARBINARY:
		case Types.BLOB:
		case Types.LONGVARBINARY:
			return this.thisRow.getBinaryInputStream(columnIndexMinusOne);
		}
		
		byte[] b = getNativeBytes(columnIndex, false);

		if (b != null) {
			return new ByteArrayInputStream(b);
		}

		return null;
	}

	/**
	 * 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.
	 */
	protected java.sql.Blob getNativeBlob(int columnIndex) throws SQLException {
		checkRowPos();

		checkColumnBounds(columnIndex);

		Object value = this.thisRow.getColumnValue(columnIndex - 1);
		
		if (value == null) {
			this.wasNullFlag = true;
		} else {
			this.wasNullFlag = false;
		}

		if (this.wasNullFlag) {
			return null;
		}

		int mysqlType = this.fields[columnIndex - 1].getMysqlType();

		byte[] dataAsBytes = null;

		switch (mysqlType) {
		case MysqlDefs.FIELD_TYPE_TINY_BLOB:
		case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
		case MysqlDefs.FIELD_TYPE_LONG_BLOB:
		case MysqlDefs.FIELD_TYPE_BLOB:
			dataAsBytes = (byte[]) value;
			break;

		default:
			dataAsBytes = getNativeBytes(columnIndex, false);
		}

		if (!this.connection.getEmulateLocators()) {
			return new Blob(dataAsBytes, getExceptionInterceptor());
		}

		return new BlobFromLocator(this, columnIndex, getExceptionInterceptor());
	}

	public static boolean arraysEqual(byte[] left, byte[] right) {
		if (left == null) {
			return right == null;
		}
		if (right == null) {
			return false;
		}
		if (left.length != right.length) {
			return false;
		}
		for (int i = 0; i < left.length; i++) {
			if (left[i] != right[i]) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	protected byte getNativeByte(int columnIndex) throws SQLException {
		return getNativeByte(columnIndex, true);
	}
	
	protected byte getNativeByte(int columnIndex, boolean overflowCheck) throws SQLException {
		checkRowPos();

		checkColumnBounds(columnIndex);

		Object value = this.thisRow.getColumnValue(columnIndex - 1);
		
		if (value == null) {
			this.wasNullFlag = true;

			return 0;
		}
		
		this.wasNullFlag = false;

		columnIndex--;

		Field field = this.fields[columnIndex];

		switch (field.getMysqlType()) {
		case MysqlDefs.FIELD_TYPE_BIT:
			long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex + 1);
			
			if (overflowCheck && this.jdbcCompliantTruncationForReads &&
					(valueAsLong < Byte.MIN_VALUE
							|| valueAsLong > Byte.MAX_VALUE)) {
				throwRangeException(String.valueOf(valueAsLong), columnIndex + 1,
						Types.TINYINT);
			}
			
			return (byte)valueAsLong;
		case MysqlDefs.FIELD_TYPE_TINY:
			byte valueAsByte = ((byte[]) value)[0];
			
			if (!field.isUnsigned()) {
				return valueAsByte;
			}

			short valueAsShort = (valueAsByte >= 0) ? 
					valueAsByte : (short)(valueAsByte + (short)256);
			
			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsShort > Byte.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsShort),
							columnIndex + 1, Types.TINYINT);
				}
			}
			
			return (byte)valueAsShort;

		case MysqlDefs.FIELD_TYPE_SHORT:
		case MysqlDefs.FIELD_TYPE_YEAR:
			valueAsShort = getNativeShort(columnIndex + 1);

			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsShort < Byte.MIN_VALUE
						|| valueAsShort > Byte.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsShort),
							columnIndex + 1, Types.TINYINT);
				}
			}

			return (byte) valueAsShort;
		case MysqlDefs.FIELD_TYPE_INT24:
		case MysqlDefs.FIELD_TYPE_LONG:
			int valueAsInt = getNativeInt(columnIndex + 1, false);

			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsInt < Byte.MIN_VALUE || valueAsInt > Byte.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsInt),
							columnIndex + 1, Types.TINYINT);
				}
			}

			return (byte) valueAsInt;

		case MysqlDefs.FIELD_TYPE_FLOAT:
			float valueAsFloat = getNativeFloat(columnIndex + 1);

			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsFloat < Byte.MIN_VALUE
						|| valueAsFloat > Byte.MAX_VALUE) {

					throwRangeException(String.valueOf(valueAsFloat),
							columnIndex + 1, Types.TINYINT);
				}
			}

			return (byte) valueAsFloat;

		case MysqlDefs.FIELD_TYPE_DOUBLE:
			double valueAsDouble = getNativeDouble(columnIndex + 1);

			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsDouble < Byte.MIN_VALUE
						|| valueAsDouble > Byte.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsDouble),
							columnIndex + 1, Types.TINYINT);
				}
			}

			return (byte) valueAsDouble;

		case MysqlDefs.FIELD_TYPE_LONGLONG:
			valueAsLong = getNativeLong(columnIndex + 1, false, true);

			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsLong < Byte.MIN_VALUE
						|| valueAsLong > Byte.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsLong),
							columnIndex + 1, Types.TINYINT);
				}
			}

			return (byte) valueAsLong;

		default:
			if (this.useUsageAdvisor) {
				issueConversionViaParsingWarning("getByte()", columnIndex,
						this.thisRow.getColumnValue(columnIndex - 1), this.fields[columnIndex],
						new int[] { MysqlDefs.FIELD_TYPE_DOUBLE,
								MysqlDefs.FIELD_TYPE_TINY,
								MysqlDefs.FIELD_TYPE_SHORT,
								MysqlDefs.FIELD_TYPE_LONG,
								MysqlDefs.FIELD_TYPE_LONGLONG,
								MysqlDefs.FIELD_TYPE_FLOAT });
			}

			return getByteFromString(getNativeString(columnIndex + 1),
					columnIndex + 1);
		}
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	protected byte[] getNativeBytes(int columnIndex, boolean noConversion)
			throws SQLException {
		checkRowPos();

		checkColumnBounds(columnIndex);

		Object value = this.thisRow.getColumnValue(columnIndex - 1);
		
		if (value == null) {
			this.wasNullFlag = true;
		} else {
			this.wasNullFlag = false;
		}

		if (this.wasNullFlag) {
			return null;
		}

		Field field = this.fields[columnIndex - 1];
		
		int mysqlType = field.getMysqlType();

		// Workaround for emulated locators in servers > 4.1,
		// as server returns SUBSTRING(blob) as STRING type...
		if (noConversion) {
			mysqlType = MysqlDefs.FIELD_TYPE_BLOB;
		}

		switch (mysqlType) {
		case MysqlDefs.FIELD_TYPE_TINY_BLOB:
		case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
		case MysqlDefs.FIELD_TYPE_LONG_BLOB:
		case MysqlDefs.FIELD_TYPE_BLOB:
		case MysqlDefs.FIELD_TYPE_BIT:
			return (byte[]) value;

		case MysqlDefs.FIELD_TYPE_STRING:
		case MysqlDefs.FIELD_TYPE_VARCHAR:
		case MysqlDefs.FIELD_TYPE_VAR_STRING:
			if (value instanceof byte[]) {
				return (byte[]) value;
			}
			// fallthrough
		default:
			int sqlType = field.getSQLType();
		
			if (sqlType == Types.VARBINARY || sqlType == Types.BINARY) {
				return (byte[]) value;
			}
		
			return getBytesFromString(getNativeString(columnIndex));
		}
	}

	/**
	 * 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
	 */
	protected java.io.Reader getNativeCharacterStream(int columnIndex)
			throws SQLException {
		int columnIndexMinusOne = columnIndex - 1;
		
		switch (this.fields[columnIndexMinusOne].getSQLType()) {
		case Types.CHAR:
		case Types.VARCHAR:
		case Types.LONGVARCHAR:
		case Types.CLOB:		
			if (this.thisRow.isNull(columnIndexMinusOne)) {
				this.wasNullFlag = true;
				
				return null;
			}
			
			this.wasNullFlag = false;
			
			return this.thisRow.getReader(columnIndexMinusOne);
		}
		
		String asString = getStringForClob(columnIndex);

		if (asString == null) {
			return null;
		}
		
		return getCharacterStreamFromString(asString);
	}

	/**
	 * JDBC 2.0 Get a CLOB column.
	 * 
	 * @param columnIndex
	 *            the first column is 1, the second is 2, ...
	 * 
	 * @return an object representing a CLOB
	 * 
	 * @throws SQLException
	 *             if an error occurs
	 */
	protected java.sql.Clob getNativeClob(int columnIndex) throws SQLException {
		String stringVal = getStringForClob(columnIndex);

		if (stringVal == null) {
			return null;
		}

		return getClobFromString(stringVal);
	}

	private String getNativeConvertToString(int columnIndex, 
			Field field)
			throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			
			int sqlType = field.getSQLType();
			int mysqlType = field.getMysqlType();
	
			switch (sqlType) {
			case Types.BIT:
				return String.valueOf(getNumericRepresentationOfSQLBitType(columnIndex));
			case Types.BOOLEAN:
				boolean booleanVal = getBoolean(columnIndex);
	
				if (this.wasNullFlag) {
					return null;
				}
	
				return String.valueOf(booleanVal);
	
			case Types.TINYINT:
				byte tinyintVal = getNativeByte(columnIndex, false);
	
				if (this.wasNullFlag) {
					return null;
				}
	
				if (!field.isUnsigned() || tinyintVal >= 0) {
					return String.valueOf(tinyintVal);
				}
	
				short unsignedTinyVal = (short) (tinyintVal & 0xff);
	
				return String.valueOf(unsignedTinyVal);
	
			case Types.SMALLINT:
	
				int intVal = getNativeInt(columnIndex, false);
	
				if (this.wasNullFlag) {
					return null;
				}
	
				if (!field.isUnsigned() || intVal >= 0) {
					return String.valueOf(intVal);
				}
	
				intVal = intVal & 0xffff;
	
				return String.valueOf(intVal);
	
			case Types.INTEGER:
				intVal = getNativeInt(columnIndex, false);
	
				if (this.wasNullFlag) {
					return null;
				}
	
				if (!field.isUnsigned() || intVal >= 0
						|| field.getMysqlType() == MysqlDefs.FIELD_TYPE_INT24) {
	
					return String.valueOf(intVal);
				}
	
				long longVal = intVal & 0xffffffffL;
	
				return String.valueOf(longVal);
	
			case Types.BIGINT:
	
				if (!field.isUnsigned()) {
					longVal = getNativeLong(columnIndex, false, true);
	
					if (this.wasNullFlag) {
						return null;
					}
	
					return String.valueOf(longVal);
				}
	
				longVal = getNativeLong(columnIndex, false, false);
	
				if (this.wasNullFlag) {
					return null;
				}
	
				return String.valueOf(convertLongToUlong(longVal));
			case Types.REAL:
				float floatVal = getNativeFloat(columnIndex);
	
				if (this.wasNullFlag) {
					return null;
				}
	
				return String.valueOf(floatVal);
	
			case Types.FLOAT:
			case Types.DOUBLE:
				double doubleVal = getNativeDouble(columnIndex);
	
				if (this.wasNullFlag) {
					return null;
				}
	
				return String.valueOf(doubleVal);
	
			case Types.DECIMAL:
			case Types.NUMERIC:
				String stringVal = StringUtils
						.toAsciiString(this.thisRow.getColumnValue(columnIndex - 1));
	
				BigDecimal val;
	
				if (stringVal != null) {
					this.wasNullFlag = false;
					
					if (stringVal.length() == 0) {
						val = new BigDecimal(0);
	
						return val.toString();
					}
	
					try {
						val = new BigDecimal(stringVal);
					} catch (NumberFormatException ex) {
						throw SQLError.createSQLException(
								Messages
										.getString("ResultSet.Bad_format_for_BigDecimal", 
												new Object[] {stringVal, Integer.valueOf(columnIndex)}),
								SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
					}
	
					return val.toString();
				}
	
				this.wasNullFlag = true;
				
				return null;
	
			case Types.CHAR:
			case Types.VARCHAR:
			case Types.LONGVARCHAR:
	
				return extractStringFromNativeColumn(columnIndex, mysqlType);
			case Types.BINARY:
			case Types.VARBINARY:
			case Types.LONGVARBINARY:
	
				if (!field.isBlob()) {
					return extractStringFromNativeColumn(columnIndex, mysqlType);
				} else if (!field.isBinary()) {
					return extractStringFromNativeColumn(columnIndex, mysqlType);
				} 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 SQLError.createSQLException(
										Messages
												.getString("ResultSet.Class_not_found___91") //$NON-NLS-1$
												+ cnfe.toString()
												+ Messages
														.getString("ResultSet._while_reading_serialized_object_92"), getExceptionInterceptor()); //$NON-NLS-1$
							} catch (IOException ex) {
								obj = data; // not serialized?
							}
						}
	
						return obj.toString();
					}
	
					return extractStringFromNativeColumn(columnIndex, mysqlType);
				}
	
			case Types.DATE:
	
				// The YEAR datatype needs to be handled differently here.
				if (mysqlType == MysqlDefs.FIELD_TYPE_YEAR) {
					short shortVal = getNativeShort(columnIndex);
	
					if (!this.connection.getYearIsDateType()) {
	
						if (this.wasNullFlag) {
							return null;
						}
	
						return String.valueOf(shortVal);
					}
	
					if (field.getLength() == 2) {
	
						if (shortVal <= 69) {
							shortVal = (short) (shortVal + 100);
						}
	
						shortVal += 1900;
					}
	
					return fastDateCreate(null, shortVal, 1, 1).toString();
	
				}
	
				if (this.connection.getNoDatetimeStringSync()) {
					byte[] asBytes = getNativeBytes(columnIndex, true);
					
					if (asBytes == null) {
						return null;
					}
					
					if (asBytes.length == 0 /* newer versions of the server 
						seem to do this when they see all-zero datetime data */) {
						return "0000-00-00";
					}
					
					int year = (asBytes[0] & 0xff)
					| ((asBytes[1] & 0xff) << 8);
					int month = asBytes[2];
					int day = asBytes[3];
					
					if (year == 0 && month == 0 && day == 0) {
						return "0000-00-00";
					}
				}
				
				Date dt = getNativeDate(columnIndex);
	
				if (dt == null) {
					return null;
				}
	
				return String.valueOf(dt);
	
			case Types.TIME:
				Time tm = getNativeTime(columnIndex, null, this.defaultTimeZone, false);
	
				if (tm == null) {
					return null;
				}
	
				return String.valueOf(tm);
	
			case Types.TIMESTAMP:
				if (this.connection.getNoDatetimeStringSync()) {
					byte[] asBytes = getNativeBytes(columnIndex, true);
					
					if (asBytes == null) {
						return null;
					}
					
					if (asBytes.length == 0 /* newer versions of the server 
						seem to do this when they see all-zero datetime data */) {
						return "0000-00-00 00:00:00";
					}
					
					int year = (asBytes[0] & 0xff)
					| ((asBytes[1] & 0xff) << 8);
					int month = asBytes[2];
					int day = asBytes[3];
					
					if (year == 0 && month == 0 && day == 0) {
						return "0000-00-00 00:00:00";
					}
				}
				
				Timestamp tstamp = getNativeTimestamp(columnIndex,
						null, this.defaultTimeZone, false);
	
				if (tstamp == null) {
					return null;
				}
	
				String result = String.valueOf(tstamp);
	
				if (!this.connection.getNoDatetimeStringSync()) {
					return result;
				}
	
				if (result.endsWith(".0")) {
					return result.substring(0, result.length() - 2);
				}
	
			default:
				return extractStringFromNativeColumn(columnIndex, mysqlType);
			}
		}
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	protected java.sql.Date getNativeDate(int columnIndex) throws SQLException {
		return getNativeDate(columnIndex, null);
	}

	/**
	 * 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 tz
	 *            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.
	 */
	protected java.sql.Date getNativeDate(int columnIndex, Calendar cal)
			throws SQLException {
		checkRowPos();
		checkColumnBounds(columnIndex);

		int columnIndexMinusOne = columnIndex - 1;
		
		int mysqlType = this.fields[columnIndexMinusOne].getMysqlType();
		
		java.sql.Date dateToReturn = null;
		
		if (mysqlType == MysqlDefs.FIELD_TYPE_DATE) {

			dateToReturn = this.thisRow.getNativeDate(columnIndexMinusOne, 
					this.connection, this, cal);	
		} else {
			TimeZone tz = (cal != null) ? cal.getTimeZone()
					: this.getDefaultTimeZone();
			
			boolean rollForward = (tz != null && !tz.equals(this.getDefaultTimeZone()));
			
			dateToReturn = (Date) this.thisRow.getNativeDateTimeValue(columnIndexMinusOne,
					null, Types.DATE, mysqlType, tz, rollForward, this.connection,
					this);
		}
		
		//
		// normally, we allow ResultSetImpl methods to check for null first,
		// but with DATETIME values we have this wacky need to support
		// 0000-00-00 00:00:00 -> NULL, so we have to defer
		// to the RowHolder implementation, and check the return value.
		//

		if (dateToReturn == null) {

			this.wasNullFlag = true;

			return null;
		}

		this.wasNullFlag = false;

		return dateToReturn;
	}

	java.sql.Date getNativeDateViaParseConversion(int columnIndex) throws SQLException {
		if (this.useUsageAdvisor) {
			issueConversionViaParsingWarning("getDate()", columnIndex,
					this.thisRow.getColumnValue(columnIndex - 1), this.fields[columnIndex - 1],
					new int[] { MysqlDefs.FIELD_TYPE_DATE });
		}

		String stringVal = getNativeString(columnIndex);

		return getDateFromString(stringVal, columnIndex, null);
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	protected double getNativeDouble(int columnIndex) throws SQLException {
		checkRowPos();
		checkColumnBounds(columnIndex);

		columnIndex--; // / JDBC is 1-based

		if (this.thisRow.isNull(columnIndex)) {
			this.wasNullFlag = true;

			return 0;
		}

		this.wasNullFlag = false;

		Field f= this.fields[columnIndex];
		
		switch (f.getMysqlType()) {
		case MysqlDefs.FIELD_TYPE_DOUBLE:
			return this.thisRow.getNativeDouble(columnIndex);
		case MysqlDefs.FIELD_TYPE_TINY:
			if (!f.isUnsigned()) {
				return getNativeByte(columnIndex + 1);
			}
			
			return getNativeShort(columnIndex + 1);
		case MysqlDefs.FIELD_TYPE_SHORT:
		case MysqlDefs.FIELD_TYPE_YEAR:
			if (!f.isUnsigned()) {
				return getNativeShort(columnIndex + 1);
			}
			
			return getNativeInt(columnIndex + 1);
		case MysqlDefs.FIELD_TYPE_INT24:
		case MysqlDefs.FIELD_TYPE_LONG:
			if (!f.isUnsigned()) {
				return getNativeInt(columnIndex + 1);
			}
			
			return getNativeLong(columnIndex + 1);
		case MysqlDefs.FIELD_TYPE_LONGLONG:
			long valueAsLong = getNativeLong(columnIndex + 1);
			
			if (!f.isUnsigned()) {
				return valueAsLong;
			}
			
			BigInteger asBigInt = convertLongToUlong(valueAsLong);
			
			// TODO: Check for overflow
			
			return asBigInt.doubleValue();
		case MysqlDefs.FIELD_TYPE_FLOAT:
			return getNativeFloat(columnIndex + 1);
		case MysqlDefs.FIELD_TYPE_BIT:
			return getNumericRepresentationOfSQLBitType(columnIndex + 1);
		default:
			String stringVal = getNativeString(columnIndex + 1);

			if (this.useUsageAdvisor) {
				issueConversionViaParsingWarning("getDouble()", columnIndex,
						stringVal, this.fields[columnIndex],
						new int[] { MysqlDefs.FIELD_TYPE_DOUBLE,
								MysqlDefs.FIELD_TYPE_TINY,
								MysqlDefs.FIELD_TYPE_SHORT,
								MysqlDefs.FIELD_TYPE_LONG,
								MysqlDefs.FIELD_TYPE_LONGLONG,
								MysqlDefs.FIELD_TYPE_FLOAT });
			}

			return getDoubleFromString(stringVal, columnIndex + 1);
		}
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	protected float getNativeFloat(int columnIndex) throws SQLException {
		checkRowPos();
		checkColumnBounds(columnIndex);

		columnIndex--; // / JDBC is 1-based

		if (this.thisRow.isNull(columnIndex)) {
			this.wasNullFlag = true;

			return 0;
		}

		this.wasNullFlag = false;

		Field f = this.fields[columnIndex];
		
		switch (f.getMysqlType()) {
		case MysqlDefs.FIELD_TYPE_BIT:
			long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex + 1);

			return valueAsLong;
		case MysqlDefs.FIELD_TYPE_DOUBLE:
			
			// Only foolproof way to check for overflow
			// Not efficient, but if you don't want to be inefficient, use the
			// correct binding for the type!
			
			Double valueAsDouble = new Double(getNativeDouble(columnIndex + 1));
			
			float valueAsFloat = valueAsDouble.floatValue();
			
			if (this.jdbcCompliantTruncationForReads && 
					valueAsFloat == Float.NEGATIVE_INFINITY ||
					valueAsFloat == Float.POSITIVE_INFINITY) {
				throwRangeException(valueAsDouble.toString(), 
						columnIndex + 1, Types.FLOAT);
			}

			return (float) getNativeDouble(columnIndex + 1);
		case MysqlDefs.FIELD_TYPE_TINY:
			if (!f.isUnsigned()) {
				return getNativeByte(columnIndex + 1);
			}
			
			return getNativeShort(columnIndex + 1);
		case MysqlDefs.FIELD_TYPE_SHORT:
		case MysqlDefs.FIELD_TYPE_YEAR:
			if (!f.isUnsigned()) {
				return getNativeShort(columnIndex + 1);
			}
			
			return getNativeInt(columnIndex + 1);
		case MysqlDefs.FIELD_TYPE_INT24:
		case MysqlDefs.FIELD_TYPE_LONG:
			if (!f.isUnsigned()) {
				return getNativeInt(columnIndex + 1);
			}
			
			return getNativeLong(columnIndex + 1);
		case MysqlDefs.FIELD_TYPE_LONGLONG:
			valueAsLong = getNativeLong(columnIndex + 1);
			
			if (!f.isUnsigned()) {
				return valueAsLong;
			}
			
			BigInteger asBigInt = convertLongToUlong(valueAsLong);
			
			// TODO: Check for overflow
			
			return asBigInt.floatValue();
		case MysqlDefs.FIELD_TYPE_FLOAT:
			
			return this.thisRow.getNativeFloat(columnIndex);

		default:
			String stringVal = getNativeString(columnIndex + 1);
		
			if (this.useUsageAdvisor) {
				issueConversionViaParsingWarning("getFloat()", columnIndex,
						stringVal, this.fields[columnIndex],
						new int[] { MysqlDefs.FIELD_TYPE_DOUBLE,
								MysqlDefs.FIELD_TYPE_TINY,
								MysqlDefs.FIELD_TYPE_SHORT,
								MysqlDefs.FIELD_TYPE_LONG,
								MysqlDefs.FIELD_TYPE_LONGLONG,
								MysqlDefs.FIELD_TYPE_FLOAT });
			}

			return getFloatFromString(stringVal, columnIndex + 1);
		}
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	protected int getNativeInt(int columnIndex) throws SQLException {
		return getNativeInt(columnIndex, true);
	}
	
	protected int getNativeInt(int columnIndex, boolean overflowCheck) throws SQLException {
		checkRowPos();
		checkColumnBounds(columnIndex);

		columnIndex--; // / JDBC is 1-based

		if (this.thisRow.isNull(columnIndex)) {
			this.wasNullFlag = true;

			return 0;
		}

		this.wasNullFlag = false;

		Field f = this.fields[columnIndex];

		switch (f.getMysqlType()) {
		case MysqlDefs.FIELD_TYPE_BIT:
			long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex + 1);
			
			if (overflowCheck && this.jdbcCompliantTruncationForReads &&
					(valueAsLong < Integer.MIN_VALUE
							|| valueAsLong > Integer.MAX_VALUE)) {
				throwRangeException(String.valueOf(valueAsLong), columnIndex + 1,
						Types.INTEGER);
			}
			
			return (short)valueAsLong;
		case MysqlDefs.FIELD_TYPE_TINY:
			byte tinyintVal = getNativeByte(columnIndex + 1, false);
			
			if (!f.isUnsigned() || tinyintVal >= 0) {
				return tinyintVal;
			}

			return tinyintVal + 256;
		case MysqlDefs.FIELD_TYPE_SHORT:
		case MysqlDefs.FIELD_TYPE_YEAR:
			short asShort = getNativeShort(columnIndex + 1, false);
			
			if (!f.isUnsigned() || asShort >= 0) {
				return asShort;
			}

			return asShort + 65536;
		case MysqlDefs.FIELD_TYPE_INT24:
		case MysqlDefs.FIELD_TYPE_LONG:
			
			int valueAsInt = this.thisRow.getNativeInt(columnIndex);

			if (!f.isUnsigned()) {	
				return valueAsInt;
			}
			
			valueAsLong = (valueAsInt >= 0) ? 
					valueAsInt : valueAsInt + 4294967296L; 
			
			if (overflowCheck && this.jdbcCompliantTruncationForReads &&
					valueAsLong > Integer.MAX_VALUE) {
				throwRangeException(String.valueOf(valueAsLong),
						columnIndex + 1, Types.INTEGER);
			}
			
			return (int)valueAsLong;
		case MysqlDefs.FIELD_TYPE_LONGLONG:
			valueAsLong = getNativeLong(columnIndex + 1, false, true);

			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsLong < Integer.MIN_VALUE
						|| valueAsLong > Integer.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsLong),
							columnIndex + 1, Types.INTEGER);
				}
			}

			return (int) valueAsLong;
		case MysqlDefs.FIELD_TYPE_DOUBLE:
			double valueAsDouble = getNativeDouble(columnIndex + 1);

			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsDouble < Integer.MIN_VALUE
						|| valueAsDouble > Integer.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsDouble),
							columnIndex + 1, Types.INTEGER);
				}
			}

			return (int) valueAsDouble;
		case MysqlDefs.FIELD_TYPE_FLOAT:
			valueAsDouble = getNativeFloat(columnIndex + 1);

			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsDouble < Integer.MIN_VALUE
						|| valueAsDouble > Integer.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsDouble),
							columnIndex + 1, Types.INTEGER);
				}
			}

			return (int) valueAsDouble;

		default:
			String stringVal = getNativeString(columnIndex + 1);
		
			if (this.useUsageAdvisor) {
				issueConversionViaParsingWarning("getInt()", columnIndex,
						stringVal, this.fields[columnIndex],
						new int[] { MysqlDefs.FIELD_TYPE_DOUBLE,
								MysqlDefs.FIELD_TYPE_TINY,
								MysqlDefs.FIELD_TYPE_SHORT,
								MysqlDefs.FIELD_TYPE_LONG,
								MysqlDefs.FIELD_TYPE_LONGLONG,
								MysqlDefs.FIELD_TYPE_FLOAT });
			}

			return getIntFromString(stringVal, columnIndex + 1);
		}
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	protected long getNativeLong(int columnIndex) throws SQLException {
		return getNativeLong(columnIndex, true, true);
	}
	
	protected long getNativeLong(int columnIndex, boolean overflowCheck, 
			boolean expandUnsignedLong) throws SQLException {
		checkRowPos();
		checkColumnBounds(columnIndex);

		columnIndex--; // / JDBC is 1-based

		if (this.thisRow.isNull(columnIndex)) {
			this.wasNullFlag = true;

			return 0;
		}

		this.wasNullFlag = false;

		Field f = this.fields[columnIndex];

		switch (f.getMysqlType()) {
		case MysqlDefs.FIELD_TYPE_BIT:
			return getNumericRepresentationOfSQLBitType(columnIndex + 1);
		case MysqlDefs.FIELD_TYPE_TINY:
			if (!f.isUnsigned()) {
				return getNativeByte(columnIndex + 1);
			}

			return getNativeInt(columnIndex + 1);
		case MysqlDefs.FIELD_TYPE_SHORT:
			if (!f.isUnsigned()) {
				return getNativeShort(columnIndex + 1);
			}

			return getNativeInt(columnIndex + 1, false);
		case MysqlDefs.FIELD_TYPE_YEAR:

			return getNativeShort(columnIndex + 1);
		case MysqlDefs.FIELD_TYPE_INT24:
		case MysqlDefs.FIELD_TYPE_LONG:
			int asInt = getNativeInt(columnIndex + 1, false);
			
			if (!f.isUnsigned() || asInt >= 0) {
				return asInt;
			}

			return asInt + 4294967296L;
		case MysqlDefs.FIELD_TYPE_LONGLONG:
			long valueAsLong = this.thisRow.getNativeLong(columnIndex);

			if (!f.isUnsigned() || !expandUnsignedLong) {
				return valueAsLong;
			}
			
			BigInteger asBigInt = convertLongToUlong(valueAsLong);
			
			if (overflowCheck && this.jdbcCompliantTruncationForReads && 
					((asBigInt.compareTo(new BigInteger(String.valueOf(Long.MAX_VALUE))) > 0 ) ||
					 (asBigInt.compareTo(new BigInteger(String.valueOf(Long.MIN_VALUE))) < 0))) {
				throwRangeException(asBigInt.toString(),
						columnIndex + 1, Types.BIGINT);
			}
			
			return getLongFromString(asBigInt.toString(), columnIndex);

		case MysqlDefs.FIELD_TYPE_DOUBLE:
			double valueAsDouble = getNativeDouble(columnIndex + 1);

			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsDouble < Long.MIN_VALUE
						|| valueAsDouble > Long.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsDouble),
							columnIndex + 1, Types.BIGINT);
				}
			}

			return (long) valueAsDouble;
		case MysqlDefs.FIELD_TYPE_FLOAT:
			valueAsDouble = getNativeFloat(columnIndex + 1);

			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsDouble < Long.MIN_VALUE
						|| valueAsDouble > Long.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsDouble),
							columnIndex + 1, Types.BIGINT);
				}
			}

			return (long) valueAsDouble;
		default:
			String stringVal = getNativeString(columnIndex + 1);
		
			if (this.useUsageAdvisor) {
				issueConversionViaParsingWarning("getLong()", columnIndex,
						stringVal, this.fields[columnIndex],
						new int[] { MysqlDefs.FIELD_TYPE_DOUBLE,
								MysqlDefs.FIELD_TYPE_TINY,
								MysqlDefs.FIELD_TYPE_SHORT,
								MysqlDefs.FIELD_TYPE_LONG,
								MysqlDefs.FIELD_TYPE_LONGLONG,
								MysqlDefs.FIELD_TYPE_FLOAT });
			}

			return getLongFromString(stringVal, columnIndex + 1);
		}
	}

	/**
	 * JDBC 2.0 Get a REF(&lt;structured-type&gt;) 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!
	 */
	protected java.sql.Ref getNativeRef(int i) throws SQLException {
		throw SQLError.notImplemented();
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	protected short getNativeShort(int columnIndex) throws SQLException {
		return getNativeShort(columnIndex, true);
	}
	
	protected short getNativeShort(int columnIndex, boolean overflowCheck) throws SQLException {
		checkRowPos();
		checkColumnBounds(columnIndex);

		columnIndex--; // / JDBC is 1-based

		
		if (this.thisRow.isNull(columnIndex)) {
			this.wasNullFlag = true;

			return 0;
		}

		this.wasNullFlag = false;

		Field f = this.fields[columnIndex];

		switch (f.getMysqlType()) {

		case MysqlDefs.FIELD_TYPE_TINY:
			byte tinyintVal = getNativeByte(columnIndex + 1, false);
			
			if (!f.isUnsigned() || tinyintVal >= 0) {
             	return tinyintVal;
			}
			
			return (short)(tinyintVal + (short)256);
		case MysqlDefs.FIELD_TYPE_SHORT:
		case MysqlDefs.FIELD_TYPE_YEAR:
			
			short asShort = this.thisRow.getNativeShort(columnIndex);

			if (!f.isUnsigned()) {
				return asShort;
			}
			
			int valueAsInt = asShort & 0xffff;
			
			if (overflowCheck && this.jdbcCompliantTruncationForReads &&
					valueAsInt > Short.MAX_VALUE) {
				throwRangeException(String.valueOf(valueAsInt),
						columnIndex + 1, Types.SMALLINT);
			}
			
			return (short)valueAsInt;
		case MysqlDefs.FIELD_TYPE_INT24:
		case MysqlDefs.FIELD_TYPE_LONG:
			if (!f.isUnsigned()) {
				valueAsInt = getNativeInt(columnIndex + 1, false);
				
				if (overflowCheck && this.jdbcCompliantTruncationForReads &&
						valueAsInt > Short.MAX_VALUE ||
						valueAsInt < Short.MIN_VALUE) {
					throwRangeException(String.valueOf(valueAsInt),
							columnIndex + 1, Types.SMALLINT);
				}
				
				return (short)valueAsInt;
			}
			
			long valueAsLong = getNativeLong(columnIndex + 1, false, true);
			
			if (overflowCheck && this.jdbcCompliantTruncationForReads &&
					valueAsLong > Short.MAX_VALUE) {
				throwRangeException(String.valueOf(valueAsLong),
						columnIndex + 1, Types.SMALLINT);
			}
			
			return (short)valueAsLong;
			
		case MysqlDefs.FIELD_TYPE_LONGLONG:
			valueAsLong = getNativeLong(columnIndex + 1, false, false);
			
			if (!f.isUnsigned()) {
				if (overflowCheck && this.jdbcCompliantTruncationForReads) {
					if (valueAsLong < Short.MIN_VALUE
							|| valueAsLong > Short.MAX_VALUE) {
						throwRangeException(String.valueOf(valueAsLong),
								columnIndex + 1, Types.SMALLINT);
					}
				}
	
				return (short) valueAsLong;
			}
			
			BigInteger asBigInt = convertLongToUlong(valueAsLong);
			
			if (overflowCheck && this.jdbcCompliantTruncationForReads && 
					((asBigInt.compareTo(new BigInteger(String.valueOf(Short.MAX_VALUE))) > 0 ) ||
					 (asBigInt.compareTo(new BigInteger(String.valueOf(Short.MIN_VALUE))) < 0))) {
				throwRangeException(asBigInt.toString(),
						columnIndex + 1, Types.SMALLINT);
			}
			
			return (short)getIntFromString(asBigInt.toString(), columnIndex + 1);

		case MysqlDefs.FIELD_TYPE_DOUBLE:
			double valueAsDouble = getNativeDouble(columnIndex + 1);

			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsDouble < Short.MIN_VALUE
						|| valueAsDouble > Short.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsDouble),
							columnIndex + 1, Types.SMALLINT);
				}
			}

			return (short) valueAsDouble;
		case MysqlDefs.FIELD_TYPE_FLOAT:
			float valueAsFloat = getNativeFloat(columnIndex + 1);

			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
				if (valueAsFloat < Short.MIN_VALUE
						|| valueAsFloat > Short.MAX_VALUE) {
					throwRangeException(String.valueOf(valueAsFloat),
							columnIndex + 1, Types.SMALLINT);
				}
			}

			return (short) valueAsFloat;
		default:
			String stringVal = getNativeString(columnIndex + 1);
		
			if (this.useUsageAdvisor) {
				issueConversionViaParsingWarning("getShort()", columnIndex,
						stringVal, this.fields[columnIndex],
						new int[] { MysqlDefs.FIELD_TYPE_DOUBLE,
								MysqlDefs.FIELD_TYPE_TINY,
								MysqlDefs.FIELD_TYPE_SHORT,
								MysqlDefs.FIELD_TYPE_LONG,
								MysqlDefs.FIELD_TYPE_LONGLONG,
								MysqlDefs.FIELD_TYPE_FLOAT });
			}

			return getShortFromString(stringVal, columnIndex + 1);
		}
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	protected String getNativeString(int columnIndex) throws SQLException {
		checkRowPos();
		checkColumnBounds(columnIndex);

		if (this.fields == null) {
			throw SQLError.createSQLException(
					Messages
							.getString("ResultSet.Query_generated_no_fields_for_ResultSet_133"), //$NON-NLS-1$
					SQLError.SQL_STATE_INVALID_COLUMN_NUMBER, getExceptionInterceptor());
		}
		
		if (this.thisRow.isNull(columnIndex - 1)) {
			this.wasNullFlag = true;

			return null;
		}

		this.wasNullFlag = false;

		String stringVal = null;

		Field field = this.fields[columnIndex - 1];

		// TODO: Check Types Here.
		stringVal = getNativeConvertToString(columnIndex, field);
		int mysqlType = field.getMysqlType();
		
		if (mysqlType != MysqlDefs.FIELD_TYPE_TIMESTAMP && 
				mysqlType != MysqlDefs.FIELD_TYPE_DATE && 
				field.isZeroFill() && (stringVal != null)) {
			int origLength = stringVal.length();

			StringBuffer zeroFillBuf = new StringBuffer(origLength);

			long numZeros = field.getLength() - origLength;

			for (long i = 0; i < numZeros; i++) {
				zeroFillBuf.append('0');
			}

			zeroFillBuf.append(stringVal);

			stringVal = zeroFillBuf.toString();
		}

		return stringVal;
	}

	private Time getNativeTime(int columnIndex, Calendar targetCalendar,
			TimeZone tz, boolean rollForward)
			throws SQLException {
		checkRowPos();
		checkColumnBounds(columnIndex);

		int columnIndexMinusOne = columnIndex - 1;
		
		int mysqlType = this.fields[columnIndexMinusOne].getMysqlType();

		Time timeVal = null;
		
		if (mysqlType == MysqlDefs.FIELD_TYPE_TIME) {
			timeVal = this.thisRow.getNativeTime(columnIndexMinusOne, 
					targetCalendar, tz, rollForward, this.connection, this);
			
		} else {
			timeVal = (Time) this.thisRow.getNativeDateTimeValue(columnIndexMinusOne,
					null, Types.TIME, mysqlType, tz, rollForward, this.connection,
					this);
		}
		
		//
		// normally, we allow ResultSetImpl methods to check for null first,
		// but with DATETIME values we have this wacky need to support
		// 0000-00-00 00:00:00 -> NULL, so we have to defer
		// to the RowHolder implementation, and check the return value.
		//

		if (timeVal == null) {

			this.wasNullFlag = true;

			return null;
		}

		this.wasNullFlag = false;

		return timeVal;
	}
	
	Time getNativeTimeViaParseConversion(int columnIndex, Calendar targetCalendar,
			TimeZone tz, boolean rollForward) throws SQLException {
		if (this.useUsageAdvisor) {
			issueConversionViaParsingWarning("getTime()", columnIndex,
					this.thisRow.getColumnValue(columnIndex - 1), this.fields[columnIndex - 1],
					new int[] { MysqlDefs.FIELD_TYPE_TIME });
		}
	
		String strTime = getNativeString(columnIndex);
	
		return getTimeFromString(strTime, targetCalendar, columnIndex, tz, rollForward);
	}

	private Timestamp getNativeTimestamp(int columnIndex, 
			Calendar targetCalendar,
			TimeZone tz,
			boolean rollForward) throws SQLException {
		checkRowPos();
		checkColumnBounds(columnIndex);

		int columnIndexMinusOne = columnIndex - 1;
		
		Timestamp tsVal = null;

		int mysqlType = this.fields[columnIndexMinusOne].getMysqlType();

		switch (mysqlType) {
		case MysqlDefs.FIELD_TYPE_DATETIME:
		case MysqlDefs.FIELD_TYPE_TIMESTAMP:
			tsVal = this.thisRow.getNativeTimestamp(columnIndexMinusOne,
					targetCalendar, tz, rollForward, this.connection, this);
			break;

		default:
			

			tsVal = (Timestamp) this.thisRow.getNativeDateTimeValue(
					columnIndexMinusOne, null, Types.TIMESTAMP, mysqlType, tz,
					rollForward, this.connection, this);
		}

		//
		// normally, we allow ResultSetImpl methods to check for null first,
		// but with DATETIME values we have this wacky need to support
		// 0000-00-00 00:00:00 -> NULL, so we have to defer
		// to the RowHolder implementation, and check the return value.
		//
		
		if (tsVal == null) {

			this.wasNullFlag = true;

			return null;
		}

		this.wasNullFlag = false;

		return tsVal;
	}

	Timestamp getNativeTimestampViaParseConversion(int columnIndex, Calendar targetCalendar, 
			TimeZone tz, boolean rollForward) throws SQLException {
		if (this.useUsageAdvisor) {
			issueConversionViaParsingWarning("getTimestamp()", columnIndex,
					this.thisRow.getColumnValue(columnIndex - 1), this.fields[columnIndex - 1],
					new int[] { MysqlDefs.FIELD_TYPE_TIMESTAMP,
							MysqlDefs.FIELD_TYPE_DATETIME });
		}

		String strTimestamp = getNativeString(columnIndex);

		return getTimestampFromString(columnIndex, targetCalendar, strTimestamp, tz,
				rollForward);
	}

	// ---------------------------------------------------------------------
	// Updates
	// ---------------------------------------------------------------------

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 * 
	 * @see getAsciiStream
	 * @see getBinaryStream
	 */
	protected InputStream getNativeUnicodeStream(int columnIndex)
			throws SQLException {
		checkRowPos();

		return getBinaryStream(columnIndex);
	}

	/**
	 * @see ResultSetInternalMethods#getURL(int)
	 */
	protected URL getNativeURL(int colIndex) throws SQLException {
		String val = getString(colIndex);

		if (val == null) {
			return null;
		}

		try {
			return new URL(val);
		} catch (MalformedURLException mfe) {
			throw SQLError.createSQLException(Messages
					.getString("ResultSet.Malformed_URL____141")
					+ val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
		}
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @return Returns the nextResultSet, if any, null if none exists.
	 */
	public synchronized ResultSetInternalMethods getNextResultSet() {
		return this.nextResultSet;
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	public Object getObject(int columnIndex) throws SQLException {
		checkRowPos();
		checkColumnBounds(columnIndex);
		
		int columnIndexMinusOne = columnIndex - 1;
		
		if (this.thisRow.isNull(columnIndexMinusOne)) {
			this.wasNullFlag = true;

			return null;
		}
		
		this.wasNullFlag = false;

		Field field;
		field = this.fields[columnIndexMinusOne];

		switch (field.getSQLType()) {
		case Types.BIT:
		case Types.BOOLEAN:
			if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_BIT
					&& !field.isSingleBit()) {
				return getBytes(columnIndex);
			}

			// valueOf would be nicer here, but it isn't
			// present in JDK-1.3.1, which is what the CTS
			// uses.
			return Boolean.valueOf(getBoolean(columnIndex));

		case Types.TINYINT:
			if (!field.isUnsigned()) {
				return Integer.valueOf(getByte(columnIndex));
			}

			return Integer.valueOf(getInt(columnIndex));

		case Types.SMALLINT:

			return Integer.valueOf(getInt(columnIndex));

		case Types.INTEGER:

			if (!field.isUnsigned() || 
					field.getMysqlType() == MysqlDefs.FIELD_TYPE_INT24) {
				return Integer.valueOf(getInt(columnIndex));
			}

			return Long.valueOf(getLong(columnIndex));
			
		case Types.BIGINT:

			if (!field.isUnsigned()) {
				return Long.valueOf(getLong(columnIndex));
			}

			String stringVal = getString(columnIndex);

			if (stringVal == null) {
				return null;
			}

			try {
				return new BigInteger(stringVal);
			} catch (NumberFormatException nfe) {
				throw SQLError.createSQLException(Messages.getString(
						"ResultSet.Bad_format_for_BigInteger", new Object[] {
								Integer.valueOf(columnIndex), stringVal }),
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
			}

		case Types.DECIMAL:
		case Types.NUMERIC:
			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 SQLError.createSQLException(
							Messages
							.getString("ResultSet.Bad_format_for_BigDecimal", //$NON-NLS-1$
							 new Object[] {stringVal, Integer.valueOf(columnIndex)}),
					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
				}

				return val;
			}

			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:
			if (!field.isOpaqueBinary()) {
				return getString(columnIndex);
			}

			return getBytes(columnIndex);
		case Types.LONGVARCHAR:
			if (!field.isOpaqueBinary()) {
				return getStringForClob(columnIndex);
			}

			return getBytes(columnIndex);

		case Types.BINARY:
		case Types.VARBINARY:
		case Types.LONGVARBINARY:
			if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_GEOMETRY) {
				return getBytes(columnIndex);
			} else if (field.isBinary() || field.isBlob()) {
				byte[] data = getBytes(columnIndex);

				if (this.connection.getAutoDeserialize()) {
					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 SQLError.createSQLException(
										Messages
												.getString("ResultSet.Class_not_found___91") //$NON-NLS-1$
												+ cnfe.toString()
												+ Messages
														.getString("ResultSet._while_reading_serialized_object_92"), getExceptionInterceptor()); //$NON-NLS-1$
							} catch (IOException ex) {
								obj = data; // not serialized?
							}
						} else {
							return getString(columnIndex);
						}
					}

					return obj;
				}

				return data;
			}
			
			return getBytes(columnIndex); 

		case Types.DATE:
			if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR
					&& !this.connection.getYearIsDateType()) {
				return Short.valueOf(getShort(columnIndex));
			}

			return getDate(columnIndex);

		case Types.TIME:
			return getTime(columnIndex);

		case Types.TIMESTAMP:
			return getTimestamp(columnIndex);

		default:
			return getString(columnIndex);
		}
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
		if (type == null) {
			throw SQLError.createSQLException("Type parameter can not be null", 
					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
		}
		
		if (type.equals(String.class)) {
			return (T) getString(columnIndex);
		} else if (type.equals(BigDecimal.class)) {
			return (T) getBigDecimal(columnIndex);
		} else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE)) {
			return (T) Boolean.valueOf(getBoolean(columnIndex));
		} else if (type.equals(Integer.class) || type.equals(Integer.TYPE)) {
			return (T) Integer.valueOf(getInt(columnIndex));
		} else if (type.equals(Long.class) || type.equals(Long.TYPE)) {
			return (T) Long.valueOf(getLong(columnIndex));
		} else if (type.equals(Float.class) || type.equals(Float.TYPE)) {
			return (T) Float.valueOf(getFloat(columnIndex));
		} else if (type.equals(Double.class) || type.equals(Double.TYPE)) {
			return (T) Double.valueOf(getDouble(columnIndex));
		} else if (type.equals(byte[].class)) {
			return (T) getBytes(columnIndex);
		} else if (type.equals(java.sql.Date.class)) {
			return (T) getDate(columnIndex);
		} else if (type.equals(Time.class)) {
			return (T) getTime(columnIndex);
		} else if (type.equals(Timestamp.class)) {
			return (T) getTimestamp(columnIndex);
		} else if (type.equals(Clob.class)) {
			return (T) getClob(columnIndex);
		} else if (type.equals(Blob.class)) {
			return (T) getBlob(columnIndex);
		} else if (type.equals(Array.class)) {
			return (T) getArray(columnIndex);
		} else if (type.equals(Ref.class)) {
			return (T) getRef(columnIndex);
		} else if (type.equals(URL.class)) {
			return (T) getURL(columnIndex);
//		} else if (type.equals(Struct.class)) {
//				
//			} 
//		} else if (type.equals(RowId.class)) {
//			
//		} else if (type.equals(NClob.class)) {
//			
//		} else if (type.equals(SQLXML.class)) {
			
		} else {
			if (this.connection.getAutoDeserialize()) {
				try {
					return (T) getObject(columnIndex);
				} catch (ClassCastException cce) {
					SQLException sqlEx = SQLError.createSQLException("Conversion not supported for type " + type.getName(), 
							SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
					sqlEx.initCause(cce);
					
					throw sqlEx;
				}
			}
			
			throw SQLError.createSQLException("Conversion not supported for type " + type.getName(), 
					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
		}
	}
	
	// JDBC-4.1
	public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {
		return getObject(findColumn(columnLabel), type);
	}

	/**
	 * 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<String,Class<?>> map) throws SQLException {
		return getObject(i);
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	public Object getObject(String columnName) throws 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 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<String,Class<?>> map)
			throws SQLException {
		return getObject(findColumn(colName), map);
	}

	public Object getObjectStoredProc(int columnIndex, int desiredSqlType)
			throws SQLException {
		checkRowPos();
		checkColumnBounds(columnIndex);

		Object value = this.thisRow.getColumnValue(columnIndex - 1);
		
		if (value == null) {
			this.wasNullFlag = true;

			return null;
		}
		
		this.wasNullFlag = false;

		Field field;
		field = this.fields[columnIndex - 1];

		switch (desiredSqlType) {
		case Types.BIT:
		case Types.BOOLEAN:
			// valueOf would be nicer here, but it isn't
			// present in JDK-1.3.1, which is what the CTS
			// uses.
			return Boolean.valueOf(getBoolean(columnIndex));

		case Types.TINYINT:
			return Integer.valueOf(getInt(columnIndex));

		case Types.SMALLINT:
			return Integer.valueOf(getInt(columnIndex));

		case Types.INTEGER:

			if (!field.isUnsigned() || 
					field.getMysqlType() == MysqlDefs.FIELD_TYPE_INT24) {
				return Integer.valueOf(getInt(columnIndex));
			}

			return Long.valueOf(getLong(columnIndex));

		case Types.BIGINT:

			if (field.isUnsigned()) {
				return getBigDecimal(columnIndex);
			}

			return Long.valueOf(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 SQLError.createSQLException(
							Messages
							.getString("ResultSet.Bad_format_for_BigDecimal", //$NON-NLS-1$
							 new Object[] {stringVal, Integer.valueOf(columnIndex)}),
					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
				}

				return val;
			}

			return null;

		case Types.REAL:
			return new Float(getFloat(columnIndex));

		case Types.FLOAT:

			if (!this.connection.getRunningCTS13()) {
				return new Double(getFloat(columnIndex));
			}
			return new Float(getFloat(columnIndex));	// NB - bug in JDBC
														// compliance test,
														// according
			// to JDBC spec, FLOAT type should return DOUBLE
			// but causes ClassCastException in CTS :(

		case Types.DOUBLE:
			return new Double(getDouble(columnIndex));

		case Types.CHAR:
		case Types.VARCHAR:
			return getString(columnIndex);
		case Types.LONGVARCHAR:
			return getStringForClob(columnIndex);
		case Types.BINARY:
		case Types.VARBINARY:
		case Types.LONGVARBINARY:
			return getBytes(columnIndex);

		case Types.DATE:
			if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR
					&& !this.connection.getYearIsDateType()) {
				return Short.valueOf(getShort(columnIndex));
			}

			return getDate(columnIndex);

		case Types.TIME:
			return getTime(columnIndex);

		case Types.TIMESTAMP:
			return getTimestamp(columnIndex);

		default:
			return getString(columnIndex);
		}
	}

	public Object getObjectStoredProc(int i, java.util.Map<Object, Object> map,
			int desiredSqlType) throws SQLException {
		return getObjectStoredProc(i, desiredSqlType);
	}

	public Object getObjectStoredProc(String columnName, int desiredSqlType)
			throws SQLException {
		return getObjectStoredProc(findColumn(columnName), desiredSqlType);
	}

	public Object getObjectStoredProc(String colName, java.util.Map<Object, Object> map,
			int desiredSqlType) throws SQLException {
		return getObjectStoredProc(findColumn(colName), map, desiredSqlType);
	}

	/**
	 * JDBC 2.0 Get a REF(&lt;structured-type&gt;) 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 {
		checkColumnBounds(i);
		throw SQLError.notImplemented();
	}

	/**
	 * JDBC 2.0 Get a REF(&lt;structured-type&gt;) 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 {
		return getRef(findColumn(colName));
	}

	/**
	 * 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 {
		checkClosed();

		int currentRowNumber = this.rowData.getCurrentRowNumber();
		int row = 0;

		// Non-dynamic result sets can be interrogated
		// for this information
		if (!this.rowData.isDynamic()) {
			if ((currentRowNumber < 0) || this.rowData.isAfterLast()
					|| this.rowData.isEmpty()) {
				row = 0;
			} else {
				row = currentRowNumber + 1;
			}
		} else {
			// dynamic (streaming) can not
			row = currentRowNumber + 1;
		}

		return row;
	}

	/**
	 * Returns the server info (if any), or null if none.
	 * 
	 * @return server info created for this ResultSet
	 */
	public String getServerInfo() {
		try {
			synchronized (checkClosed().getConnectionMutex()) {
				return this.serverInfo;
			}
		} catch (SQLException e) {
			throw new RuntimeException(e); // FIXME: Need to evolve public interface
		}
	}

	private long getNumericRepresentationOfSQLBitType(int columnIndex) throws SQLException {
		
		Object value = this.thisRow.getColumnValue(columnIndex - 1);
		
		if (this.fields[columnIndex - 1].isSingleBit() || 
				((byte[])value).length == 1) {
			return ((byte[])value)[0];
		}
		
		
		byte[] asBytes = (byte[])value;
		
		
		int shift = 0;
		
		long[] steps = new long[asBytes.length];
		
		for (int i = asBytes.length - 1; i >= 0; i--) {
			steps[i] = (long)(asBytes[i] & 0xff) << shift;
			shift += 8;
		}
		
		long valueAsLong = 0;
		
		for (int i = 0; i < asBytes.length; i++) {
			valueAsLong |= steps[i];
		}
		
		return valueAsLong;
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	public short getShort(int columnIndex) throws SQLException {
		if (!this.isBinaryEncoded) {
			checkRowPos();
			
			if (this.useFastIntParsing) {
				
				checkColumnBounds(columnIndex);

				Object value = this.thisRow.getColumnValue(columnIndex - 1);
				
				if (value == null) {
					this.wasNullFlag = true;
				} else {
					this.wasNullFlag = false;
				}
				
				if (this.wasNullFlag) {
					return 0;
				}

				byte[] shortAsBytes = (byte[]) value;

				if (shortAsBytes.length == 0) {
					return (short) convertToZeroWithEmptyCheck();
				}

				boolean needsFullParse = false;

				for (int i = 0; i < shortAsBytes.length; i++) {
					if (((char) shortAsBytes[i] == 'e')
							|| ((char) shortAsBytes[i] == 'E')) {
						needsFullParse = true;

						break;
					}
				}

				if (!needsFullParse) {
					try {
						return parseShortWithOverflowCheck(columnIndex,
								shortAsBytes, null);
					} catch (NumberFormatException nfe) {
						try {
							// To do: Warn of over/underflow???
							return parseShortAsDouble(columnIndex, StringUtils.toString(
									shortAsBytes));
						} catch (NumberFormatException newNfe) {
							; // ignore, it's not a number
						}

						if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
							long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);
							
							if (this.jdbcCompliantTruncationForReads &&
									(valueAsLong < Short.MIN_VALUE
											|| valueAsLong > Short.MAX_VALUE)) {
								throwRangeException(String.valueOf(valueAsLong), columnIndex,
										Types.SMALLINT);
							}
							
							return (short)valueAsLong;
						}
						
						throw SQLError.createSQLException(
								Messages
										.getString("ResultSet.Invalid_value_for_getShort()_-____96")
										+ StringUtils.toString(shortAsBytes) //$NON-NLS-1$
										+ "'",
								SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
					}
				}
			}

			String val = null;

			try {
				val = getString(columnIndex);

				if ((val != null)) {

					if (val.length() == 0) {
						return (short) convertToZeroWithEmptyCheck();
					}

					if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)
							&& (val.indexOf(".") == -1)) {
						return parseShortWithOverflowCheck(columnIndex, null,
								val);
					}

					// Convert floating point
					return parseShortAsDouble(columnIndex, val);
				}

				return 0; // for NULL
			} catch (NumberFormatException nfe) {
				try {
					return parseShortAsDouble(columnIndex, val);
				} catch (NumberFormatException newNfe) {
					; // ignore, it's not a number
				}

				if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
					long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);
					
					if (this.jdbcCompliantTruncationForReads &&
							(valueAsLong < Short.MIN_VALUE
									|| valueAsLong > Short.MAX_VALUE)) {
						throwRangeException(String.valueOf(valueAsLong), columnIndex,
								Types.SMALLINT);
					}
					
					return (short)valueAsLong;
				}
				
				throw SQLError.createSQLException(
						Messages
								.getString("ResultSet.Invalid_value_for_getShort()_-____96")
								+ val //$NON-NLS-1$
								+ "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
			}
		}

		return getNativeShort(columnIndex);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param columnName
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public short getShort(String columnName) throws SQLException {
		return getShort(findColumn(columnName));
	}

	private final short getShortFromString(String val, int columnIndex)
			throws SQLException {
		try {
			if ((val != null)) {

				if (val.length() == 0) {
					return (short) convertToZeroWithEmptyCheck();
				}

				if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)
						&& (val.indexOf(".") == -1)) {
					return parseShortWithOverflowCheck(columnIndex, null, val);
				}

				// Convert floating point
				return parseShortAsDouble(columnIndex, val);
			}

			return 0; // for NULL
		} catch (NumberFormatException nfe) {
			try {
				return parseShortAsDouble(columnIndex, val);
			} catch (NumberFormatException newNfe) {
				; // ignore, it's not a number
			}

			throw SQLError.createSQLException(
					Messages
							.getString("ResultSet.Invalid_value_for_getShort()_-____217")
							+ val //$NON-NLS-1$
							+ Messages.getString("ResultSet.___in_column__218")
							+ columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
		}
	}

	/**
	 * 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 {
		try {
			synchronized (checkClosed().getConnectionMutex()) {
				if (this.wrapperStatement != null) {
					return this.wrapperStatement;
				}

				return this.owningStatement;
			}

		} catch (SQLException sqlEx) {
			if (!this.retainOwningStatement) {
				throw SQLError.createSQLException(
					"Operation not allowed on closed ResultSet. Statements "
							+ "can be retained over result set closure by setting the connection property "
							+ "\"retainStatementAfterResultSetClose\" to \"true\".",
					SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
			}
				
			if (this.wrapperStatement != null) {
				return this.wrapperStatement;
			}

			return this.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 SQLException
	 *                if a database access error occurs
	 */
	public String getString(int columnIndex) throws SQLException {
		String stringVal = getStringInternal(columnIndex, true);
		
		if (this.padCharsWithSpace && stringVal != null) {
			Field f = this.fields[columnIndex - 1];
			
			if (f.getMysqlType() == MysqlDefs.FIELD_TYPE_STRING ) {
				int fieldLength = (int)f.getLength() /* safe, bytes in a CHAR <= 1024 */ / 
					f.getMaxBytesPerCharacter(); /* safe, this will never be 0 */
				
				int currentLength = stringVal.length();
				
				if (currentLength < fieldLength) {
					StringBuffer paddedBuf = new StringBuffer(fieldLength);
					paddedBuf.append(stringVal);
					
					int difference = fieldLength - currentLength;
					
					paddedBuf.append(EMPTY_SPACE, 0, difference);
					
					stringVal = paddedBuf.toString();
				}
			}
		}
		
		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 SQLException
	 *                if a database access error occurs
	 */
	public String getString(String columnName) throws SQLException {
		return getString(findColumn(columnName));
	}

	private String getStringForClob(int columnIndex) throws SQLException {
		String asString = null;
		
		String forcedEncoding = 
			this.connection.getClobCharacterEncoding();
		
		if (forcedEncoding == null) {
			if (!this.isBinaryEncoded) {
				asString = getString(columnIndex);
			} else {
				asString = getNativeString(columnIndex);
			}
		} else {
			try {
				byte[] asBytes = null;
				
				if (!this.isBinaryEncoded) {
					asBytes = getBytes(columnIndex);
				} else {
					asBytes = getNativeBytes(columnIndex, true);
				}
				
				if (asBytes != null) {
					asString = StringUtils.toString(asBytes, forcedEncoding);
				}
			} catch (UnsupportedEncodingException uee) {
				throw SQLError.createSQLException("Unsupported character encoding " + 
						forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
			}
		}
		
		return asString;
	}

	protected String getStringInternal(int columnIndex, boolean checkDateTypes)
			throws SQLException {
		if (!this.isBinaryEncoded) {
			checkRowPos();
			checkColumnBounds(columnIndex);

			if (this.fields == null) {
				throw SQLError.createSQLException(
						Messages
								.getString("ResultSet.Query_generated_no_fields_for_ResultSet_99"), //$NON-NLS-1$
						SQLError.SQL_STATE_INVALID_COLUMN_NUMBER, getExceptionInterceptor());
			}

			// JDBC is 1-based, Java is not !?
			
			int internalColumnIndex = columnIndex - 1;
			
			if (this.thisRow.isNull(internalColumnIndex)) {
				this.wasNullFlag = true;

				return null;
			}

			this.wasNullFlag = false;

			
			Field metadata = this.fields[internalColumnIndex];
			
			String stringVal = null;
			
			if (metadata.getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
				if (metadata.isSingleBit()) {
					byte[] value = this.thisRow.getColumnValue(internalColumnIndex);
					
					if (value.length == 0) {
						return String.valueOf(convertToZeroWithEmptyCheck());
					}
					
					return String.valueOf(value[0]);
				}
				
				return String.valueOf(getNumericRepresentationOfSQLBitType(columnIndex));
			}
			
			String encoding = metadata.getCharacterSet();

			stringVal = this.thisRow.getString(internalColumnIndex, encoding, this.connection);

			//
			// Special handling for YEAR type from mysql, some people
			// want it as a DATE, others want to treat it as a SHORT
			//

			if (metadata.getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) {
				if (!this.connection.getYearIsDateType()) {
					return stringVal;
				}

				Date dt = getDateFromString(stringVal, columnIndex, null);

				if (dt == null) {
					this.wasNullFlag = true;

					return null;
				}

				this.wasNullFlag = false;

				return dt.toString();
			}

			// Handles timezone conversion and zero-date behavior

			if (checkDateTypes && !this.connection.getNoDatetimeStringSync()) {
				switch (metadata.getSQLType()) {
				case Types.TIME:
					Time tm = getTimeFromString(stringVal, null, columnIndex,
							this.getDefaultTimeZone(), false);

					if (tm == null) {
						this.wasNullFlag = true;

						return null;
					}

					this.wasNullFlag = false;

					return tm.toString();
				case Types.DATE:

					Date dt = getDateFromString(stringVal, columnIndex, null);

					if (dt == null) {
						this.wasNullFlag = true;

						return null;
					}

					this.wasNullFlag = false;

					return dt.toString();
				case Types.TIMESTAMP:
					Timestamp ts = getTimestampFromString(columnIndex,
							null, stringVal, this.getDefaultTimeZone(), false);

					if (ts == null) {
						this.wasNullFlag = true;

						return null;
					}

					this.wasNullFlag = false;

					return ts.toString();
				default:
					break;
				}
			}

			return stringVal;
		}

		return getNativeString(columnIndex);
	}

	/**
	 * 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, null, this.getDefaultTimeZone(), false);
	}

	/**
	 * 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, cal.getTimeZone(), true);
	}

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

	private Time getTimeFromString(String timeAsString, Calendar targetCalendar,
			int columnIndex,
			TimeZone tz, 
			boolean rollForward) throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			int hr = 0;
			int min = 0;
			int sec = 0;
	
			try {
				
				if (timeAsString == null) {
					this.wasNullFlag = true;
	
					return null;
				} 
				
				//
				// JDK-6 doesn't like trailing whitespace
				//
				// Note this isn't a performance issue, other
				// than the iteration over the string, as String.trim()
				// will return a new string only if whitespace is present
				//
				
				timeAsString = timeAsString.trim();
				
				if (timeAsString.equals("0")
						|| timeAsString.equals("0000-00-00")
						|| timeAsString.equals("0000-00-00 00:00:00")
						|| timeAsString.equals("00000000000000")) {
					if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
							.equals(this.connection.getZeroDateTimeBehavior())) {
						this.wasNullFlag = true;
	
						return null;
					} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
							.equals(this.connection.getZeroDateTimeBehavior())) {
						throw SQLError.createSQLException("Value '" + timeAsString
								+ "' can not be represented as java.sql.Time",
								SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
					}
	
					// We're left with the case of 'round' to a time Java _can_
					// represent, which is '00:00:00'
					return fastTimeCreate(targetCalendar, 0, 0, 0);
				}
	
				this.wasNullFlag = false;
	
				Field timeColField = this.fields[columnIndex - 1];
	
				if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) {
					// It's a timestamp
					int length = timeAsString.length();
	
					switch (length) {
					case 19: { // YYYY-MM-DD hh:mm:ss
					 
							hr = Integer.parseInt(timeAsString.substring(length - 8,
									length - 6));
							min = Integer.parseInt(timeAsString.substring(length - 5,
									length - 3));
							sec = Integer.parseInt(timeAsString.substring(length - 2,
									length));
					}
	
							break;
					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 SQLError.createSQLException(
								Messages
										.getString("ResultSet.Timestamp_too_small_to_convert_to_Time_value_in_column__257") //$NON-NLS-1$
										+ columnIndex
										+ "("
										+ this.fields[columnIndex - 1] + ").",
								SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
					} /* endswitch */
	
					SQLWarning precisionLost = new SQLWarning(
							Messages
									.getString("ResultSet.Precision_lost_converting_TIMESTAMP_to_Time_with_getTime()_on_column__261") //$NON-NLS-1$
									+ columnIndex
									+ "("
									+ this.fields[columnIndex - 1] + ").");
	
					if (this.warningChain == null) {
						this.warningChain = precisionLost;
					} else {
						this.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(
							Messages
									.getString("ResultSet.Precision_lost_converting_DATETIME_to_Time_with_getTime()_on_column__264") //$NON-NLS-1$
									+ columnIndex
									+ "("
									+ this.fields[columnIndex - 1] + ").");
	
					if (this.warningChain == null) {
						this.warningChain = precisionLost;
					} else {
						this.warningChain.setNextWarning(precisionLost);
					}
				} else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) {
					return fastTimeCreate(targetCalendar, 0, 0, 0); // midnight on the given
															// date
				} else {
					// convert a String to a Time
					if ((timeAsString.length() != 5)
							&& (timeAsString.length() != 8)) {
						throw SQLError.createSQLException(Messages
								.getString("ResultSet.Bad_format_for_Time____267") //$NON-NLS-1$
								+ timeAsString
								+ Messages.getString("ResultSet.___in_column__268")
								+ columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
					}
	
					hr = Integer.parseInt(timeAsString.substring(0, 2));
					min = Integer.parseInt(timeAsString.substring(3, 5));
					sec = (timeAsString.length() == 5) ? 0 : Integer
							.parseInt(timeAsString.substring(6));
				}
	
				Calendar sessionCalendar = this.getCalendarInstanceForSessionOrNew();
				
				synchronized (sessionCalendar) {
					return TimeUtil.changeTimezone(this.connection, 
							sessionCalendar,
							targetCalendar, 
							fastTimeCreate(
							sessionCalendar, hr, min, sec), 
							this.connection.getServerTimezoneTZ(),
							tz, rollForward);
				}
			} catch (RuntimeException ex) {
				SQLException sqlEx = SQLError.createSQLException(ex.toString(),
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
				sqlEx.initCause(ex);
				
				throw sqlEx;
			}
		}
	}
	
	/**
	 * 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, Calendar targetCalendar,
			TimeZone tz,
			boolean rollForward) throws java.sql.SQLException {
		checkRowPos();
		
		if (this.isBinaryEncoded) {
			return getNativeTime(columnIndex, targetCalendar, tz, rollForward);
		}

		if (!this.useFastDateParsing) {
			String timeAsString = getStringInternal(columnIndex, false);

			return getTimeFromString(timeAsString, targetCalendar,
				columnIndex, tz, rollForward);
		}
		
		checkColumnBounds(columnIndex);
		
		int columnIndexMinusOne = columnIndex - 1;
		
		if (this.thisRow.isNull(columnIndexMinusOne)) {
			this.wasNullFlag = true;
			
			return null;
		}
		
		this.wasNullFlag = false;
		
		return this.thisRow.getTimeFast(columnIndexMinusOne, 
				targetCalendar, tz, rollForward, this.connection, this);
	}

	/**
	 * 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, null, this.getDefaultTimeZone(),
				false);
	}

	/**
	 * 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, cal.getTimeZone(), true);
	}

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

	private Timestamp getTimestampFromString(int columnIndex,
			Calendar targetCalendar,
			String timestampValue, TimeZone tz, boolean rollForward)
	throws java.sql.SQLException {
		try {
			this.wasNullFlag = false;
			
			if (timestampValue == null) {
				this.wasNullFlag = true;
				
				return null;
			}
			
			//
			// JDK-6 doesn't like trailing whitespace
			//
			// Note this isn't a performance issue, other
			// than the iteration over the string, as String.trim()
			// will return a new string only if whitespace is present
			//
			
			timestampValue = timestampValue.trim();
			
			int length = timestampValue.length();
			
			Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ?
					this.connection.getUtcCalendar() : 
						getCalendarInstanceForSessionOrNew();
			
			synchronized (sessionCalendar) {
				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"))) {
					
					if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
							.equals(this.connection.getZeroDateTimeBehavior())) {
						this.wasNullFlag = true;
						
						return null;
					} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
							.equals(this.connection.getZeroDateTimeBehavior())) {
						throw SQLError.createSQLException("Value '" + timestampValue
								+ "' can not be represented as java.sql.Timestamp",
								SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
					}
					
					// We're left with the case of 'round' to a date Java _can_
					// represent, which is '0001-01-01'.
					return fastTimestampCreate(null, 1, 1, 1, 0, 0, 0, 0);
					
				} else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) {
					
					if (!this.useLegacyDatetimeCode) {
						return TimeUtil.fastTimestampCreate(tz, Integer
										.parseInt(timestampValue.substring(0, 4)), 1,
										1, 0, 0, 0, 0);
					}
					
					return TimeUtil.changeTimezone(this.connection,
							sessionCalendar, 
							targetCalendar,
							fastTimestampCreate(sessionCalendar, 
									Integer
									.parseInt(timestampValue.substring(0, 4)), 1,
									1, 0, 0, 0, 0), this.connection
									.getServerTimezoneTZ(), tz, rollForward);

				} else {
					if (timestampValue.endsWith(".")) {
						timestampValue = timestampValue.substring(0, timestampValue
								.length() - 1);
					}
					
					// Convert from TIMESTAMP or DATE
					
					int year = 0;
					int month = 0;
					int day = 0;
					int hour = 0;
					int minutes = 0;
					int seconds = 0;
					int nanos = 0;
					
					switch (length) {
					case 26:
					case 25:
					case 24:
					case 23:
					case 22:
					case 21:
					case 20:
					case 19: {
						year = Integer.parseInt(timestampValue.substring(0, 4));
						month = Integer
						.parseInt(timestampValue.substring(5, 7));
						day = Integer.parseInt(timestampValue.substring(8, 10));
						hour = Integer.parseInt(timestampValue
								.substring(11, 13));
						minutes = Integer.parseInt(timestampValue.substring(14,
								16));
						seconds = Integer.parseInt(timestampValue.substring(17,
								19));
						
						nanos = 0;
						
						if (length > 19) {
							int decimalIndex = timestampValue.lastIndexOf('.');
							
							if (decimalIndex != -1) {
								if ((decimalIndex + 2) <= length) {
									nanos = Integer.parseInt(timestampValue
											.substring(decimalIndex + 1));
									
									int numDigits = length - (decimalIndex + 1);
									
									if (numDigits < 9) {
										int factor = (int)(Math.pow(10, 9 - numDigits));
										nanos = nanos * factor;
									}
								} else {
									throw new IllegalArgumentException(); // re-thrown
									// further
									// down
									// with
									// a
									// much better error message
								}
							}
						}
						
						break;
					}
					
					case 14: {
						year = Integer.parseInt(timestampValue.substring(0, 4));
						month = Integer
						.parseInt(timestampValue.substring(4, 6));
						day = Integer.parseInt(timestampValue.substring(6, 8));
						hour = Integer
						.parseInt(timestampValue.substring(8, 10));
						minutes = Integer.parseInt(timestampValue.substring(10,
								12));
						seconds = Integer.parseInt(timestampValue.substring(12,
								14));
						
						break;
					}
					
					case 12: {
						year = Integer.parseInt(timestampValue.substring(0, 2));
						
						if (year <= 69) {
							year = (year + 100);
						}
						
						year += 1900;
						
						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));
						seconds = Integer.parseInt(timestampValue.substring(10,
								12));
						
						break;
					}
					
					case 10: {
						if ((this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_DATE)
								|| (timestampValue.indexOf("-") != -1)) {
							year = Integer.parseInt(timestampValue.substring(0, 4));
							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));
							
							year += 1900; // two-digit year
						}
						
						break;
					}
					
					case 8: {
						if (timestampValue.indexOf(":") != -1) {
							hour = Integer.parseInt(timestampValue.substring(0,
									2));
							minutes = Integer.parseInt(timestampValue
									.substring(3, 5));
							seconds = Integer.parseInt(timestampValue
									.substring(6, 8));
							year = 1970;
							month = 1;
							day = 1;
							break;
						}
						
						year = Integer.parseInt(timestampValue.substring(0, 4));
						month = Integer
						.parseInt(timestampValue.substring(4, 6));
						day = Integer.parseInt(timestampValue.substring(6, 8));
						
						year -= 1900;
						month--;
						
						break;
					}
					
					case 6: {
						year = Integer.parseInt(timestampValue.substring(0, 2));
						
						if (year <= 69) {
							year = (year + 100);
						}
						
						year += 1900;
						
						month = Integer
						.parseInt(timestampValue.substring(2, 4));
						day = Integer.parseInt(timestampValue.substring(4, 6));
						
						break;
					}
					
					case 4: {
						year = Integer.parseInt(timestampValue.substring(0, 2));
						
						if (year <= 69) {
							year = (year + 100);
						}
						
						year += 1900;
						
						month = Integer
						.parseInt(timestampValue.substring(2, 4));
						
						day = 1;
						
						break;
					}
					
					case 2: {
						year = Integer.parseInt(timestampValue.substring(0, 2));
						
						if (year <= 69) {
							year = (year + 100);
						}
						
						year += 1900;
						month = 1;
						day = 1;
						
						break;
					}
					
					default:
						throw new java.sql.SQLException(
								"Bad format for Timestamp '" + timestampValue
								+ "' in column " + columnIndex + ".",
								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
					}
					
					if (!this.useLegacyDatetimeCode) {
						return TimeUtil.fastTimestampCreate(tz, year, month, day, hour,
								minutes, seconds, nanos);
					}
						
					return TimeUtil.changeTimezone(this.connection,
							sessionCalendar,
							targetCalendar,
							fastTimestampCreate(sessionCalendar, year, month, day, hour,
									minutes, seconds, nanos), this.connection
									.getServerTimezoneTZ(), tz, rollForward);
				}
			}
		} catch (RuntimeException e) {
			SQLException sqlEx = SQLError.createSQLException("Cannot convert value '"
					+ timestampValue + "' from column " + columnIndex
					+ " to TIMESTAMP.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
			sqlEx.initCause(e);
			
			throw sqlEx;
		}
		
	}

	/**
	 * 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, Calendar targetCalendar,
			TimeZone tz,
			boolean rollForward) throws java.sql.SQLException {
		if (this.isBinaryEncoded) {
			return getNativeTimestamp(columnIndex, targetCalendar, tz, rollForward);
		}

		Timestamp tsVal = null;
		
		if (!this.useFastDateParsing) {
			String timestampValue = getStringInternal(columnIndex, false);

			tsVal = getTimestampFromString(columnIndex, targetCalendar, 
					timestampValue, tz,
					rollForward);
		} else {
			checkClosed();
			checkRowPos();
			checkColumnBounds(columnIndex);
			
			tsVal = this.thisRow.getTimestampFast(columnIndex - 1, 
					targetCalendar, tz, rollForward, this.connection, this);
		}
		
		if (tsVal == null) {
			this.wasNullFlag = true;
		} else {
			this.wasNullFlag = false;
		}
		
		return tsVal;
	}

	/**
	 * 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 this.resultSetType;
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 * 
	 * @see getAsciiStream
	 * @see getBinaryStream
	 * @deprecated
	 */
	public InputStream getUnicodeStream(int columnIndex) throws SQLException {
		if (!this.isBinaryEncoded) {
			checkRowPos();

			return getBinaryStream(columnIndex);
		}

		return getNativeBinaryStream(columnIndex);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param columnName
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 * 
	 * @deprecated
	 */
	public InputStream getUnicodeStream(String columnName) throws SQLException {
		return getUnicodeStream(findColumn(columnName));
	}

	public long getUpdateCount() {
		return this.updateCount;
	}

	public long getUpdateID() {
		return this.updateId;
	}

	/**
	 * @see ResultSetInternalMethods#getURL(int)
	 */
	public URL getURL(int colIndex) throws SQLException {
		String val = getString(colIndex);

		if (val == null) {
			return null;
		}

		try {
			return new URL(val);
		} catch (MalformedURLException mfe) {
			throw SQLError.createSQLException(Messages
					.getString("ResultSet.Malformed_URL____104")
					+ val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
		}
	}

	/**
	 * @see ResultSetInternalMethods#getURL(String)
	 */
	public URL getURL(String colName) throws SQLException {
		String val = getString(colName);

		if (val == null) {
			return null;
		}

		try {
			return new URL(val);
		} catch (MalformedURLException mfe) {
			throw SQLError.createSQLException(Messages
					.getString("ResultSet.Malformed_URL____107")
					+ val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
		}
	}

	/**
	 * 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 SQLException
	 *                if a database access error occurs.
	 */
	public java.sql.SQLWarning getWarnings() throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			return this.warningChain;
		}
	}

	/**
	 * 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>
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {
			boolean b = this.rowData.isAfterLast();
	
			return b;
		}
	}

	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {
			return this.rowData.isBeforeFirst();
		}
	}

	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {
			return this.rowData.isFirst();
		}
	}

	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {
			return this.rowData.isLast();
		}
	}

	/**
	 * @param string
	 * @param mysqlType
	 * @param s
	 */
	private void issueConversionViaParsingWarning(String methodName,
			int columnIndex, Object value, Field fieldInfo,
			int[] typesWithNoParseConversion) throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			StringBuffer originalQueryBuf = new StringBuffer();
			
			if (this.owningStatement != null
					&& this.owningStatement instanceof com.mysql.jdbc.PreparedStatement) {
				originalQueryBuf.append(Messages.getString("ResultSet.CostlyConversionCreatedFromQuery"));
				originalQueryBuf
						.append(((com.mysql.jdbc.PreparedStatement) this.owningStatement).originalSql);
				originalQueryBuf.append("\n\n");
			} else {
				originalQueryBuf.append(".");
			}
			
			StringBuffer convertibleTypesBuf = new StringBuffer();
			
			for (int i = 0; i < typesWithNoParseConversion.length; i++) {
				convertibleTypesBuf.append(MysqlDefs.typeToName(typesWithNoParseConversion[i]));
				convertibleTypesBuf.append("\n");
			}
			
			String message = Messages.getString("ResultSet.CostlyConversion", new Object[] {
					methodName,
					Integer.valueOf(columnIndex + 1),
					fieldInfo.getOriginalName(),
					fieldInfo.getOriginalTableName(),
					originalQueryBuf.toString(),
					value != null ? value.getClass().getName() : ResultSetMetaData.getClassNameForJavaType(
							fieldInfo.getSQLType(), 
							fieldInfo.isUnsigned(), 
							fieldInfo.getMysqlType(), 
							fieldInfo.isBinary() || fieldInfo.isBlob(),
							fieldInfo.isOpaqueBinary(),
							connection.getYearIsDateType()),
					MysqlDefs.typeToName(fieldInfo.getMysqlType()),
					convertibleTypesBuf.toString()});
					
			this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN,
					"", (this.owningStatement == null) ? "N/A"
							: this.owningStatement.currentCatalog,
					this.connectionId, (this.owningStatement == null) ? (-1)
							: this.owningStatement.getId(), this.resultId, System
							.currentTimeMillis(), 0, Constants.MILLIS_I18N, null,
					this.pointOfOrigin, message));
		}
	}
	
	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {
	
			boolean b = true;
			
			if (this.rowData.size() == 0) {
				b = false;
			} else {
	
				if (this.onInsertRow) {
					this.onInsertRow = false;
				}
		
				if (this.doingUpdates) {
					this.doingUpdates = false;
				}
		
				if (this.thisRow != null) {
					this.thisRow.closeOpenStreams();
				}
				
				this.rowData.beforeLast();
				this.thisRow = this.rowData.next();
			}
	
			setRowPositionValidity();
			
			return b;
		}
	}

	// /////////////////////////////////////////
	//
	// These number conversion routines save
	// a ton of "new()s", especially for the heavily
	// used getInt() and getDouble() methods
	//
	// /////////////////////////////////////////

	/**
	 * 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 SQLException
	 *                if a database access error occurs
	 */
	public boolean next() throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {

			if (this.onInsertRow) {
				this.onInsertRow = false;
			}
	
			if (this.doingUpdates) {
				this.doingUpdates = false;
			}
	
			boolean b;
	
			if (!reallyResult()) {
				throw SQLError.createSQLException(
						Messages
								.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"),
						SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); //$NON-NLS-1$
			}
	
			if (this.thisRow != null) {
				this.thisRow.closeOpenStreams();
			}
			
			if (this.rowData.size() == 0) {
				b = false;
			} else {
				this.thisRow = this.rowData.next();
				
				if (this.thisRow == null) {
					b = false;
				} else {
					clearWarnings();
					
					b = true;
					
				}
			}
	
			setRowPositionValidity();
			
			return b;
		}
	}

	private int parseIntAsDouble(int columnIndex, String val)
			throws NumberFormatException, SQLException {
		if (val == null) {
			return 0;
		}

		double valueAsDouble = Double.parseDouble(val);

		if (this.jdbcCompliantTruncationForReads) {
			if (valueAsDouble < Integer.MIN_VALUE
					|| valueAsDouble > Integer.MAX_VALUE) {
				throwRangeException(String.valueOf(valueAsDouble), columnIndex,
						Types.INTEGER);
			}
		}

		return (int) valueAsDouble;
	}

	private int getIntWithOverflowCheck(int columnIndex) throws SQLException {
		int intValue = this.thisRow.getInt(columnIndex);

		checkForIntegerTruncation(columnIndex, 
				null, intValue);

		return intValue;
	}
	
	private void checkForIntegerTruncation(int columnIndex,
			byte[] valueAsBytes, int intValue)
			throws SQLException {
		if (this.jdbcCompliantTruncationForReads) {
			if (intValue == Integer.MIN_VALUE || intValue == Integer.MAX_VALUE) {
				String valueAsString = null;
				
				if (valueAsBytes == null) {
					valueAsString = this.thisRow.getString(
							columnIndex, this.fields[columnIndex].getCharacterSet(),
							this.connection);
				}
				
				long valueAsLong = Long
						.parseLong(valueAsString == null ? StringUtils.toString(
								valueAsBytes) : valueAsString);

				if (valueAsLong < Integer.MIN_VALUE
						|| valueAsLong > Integer.MAX_VALUE) {
					throwRangeException(valueAsString == null ? StringUtils.toString(
							valueAsBytes) : valueAsString, columnIndex + 1,
							Types.INTEGER);
				}
			}
		}
	}

	private long parseLongAsDouble(int columnIndexZeroBased, String val)
			throws NumberFormatException, SQLException {
		if (val == null) {
			return 0;
		}

		double valueAsDouble = Double.parseDouble(val);

		if (this.jdbcCompliantTruncationForReads) {
			if (valueAsDouble < Long.MIN_VALUE
					|| valueAsDouble > Long.MAX_VALUE) {
				throwRangeException(val, columnIndexZeroBased + 1, Types.BIGINT);
			}
		}

		return (long) valueAsDouble;
	}

	private long getLongWithOverflowCheck(int columnIndexZeroBased, boolean doOverflowCheck) throws SQLException {
		long longValue = this.thisRow.getLong(columnIndexZeroBased);

		if (doOverflowCheck) {
			checkForLongTruncation(columnIndexZeroBased, null, longValue);
		}

		return longValue;
	}
	
	private long parseLongWithOverflowCheck(int columnIndexZeroBased,
			byte[] valueAsBytes, String valueAsString, boolean doCheck)
			throws NumberFormatException, SQLException {

		long longValue = 0;

		if (valueAsBytes == null && valueAsString == null) {
			return 0;
		}

		if (valueAsBytes != null) {
			longValue = StringUtils.getLong(valueAsBytes);
		} else {
			//
			// JDK-6 doesn't like trailing whitespace
			//
			// Note this isn't a performance issue, other
			// than the iteration over the string, as String.trim()
			// will return a new string only if whitespace is present
			//
			
			valueAsString = valueAsString.trim();
			
			longValue = Long.parseLong(valueAsString);
		}

		if (doCheck && this.jdbcCompliantTruncationForReads) {
			checkForLongTruncation(columnIndexZeroBased, valueAsBytes, longValue);
		}

		return longValue;
	}

	private void checkForLongTruncation(int columnIndexZeroBased, byte[] valueAsBytes, long longValue) throws SQLException {
		if (longValue == Long.MIN_VALUE
				|| longValue == Long.MAX_VALUE) {
			String valueAsString = null;
			
			if (valueAsBytes == null) {
				valueAsString = this.thisRow.getString(
						columnIndexZeroBased, this.fields[columnIndexZeroBased].getCharacterSet(),
						this.connection);
			}
			
			double valueAsDouble = Double
					.parseDouble(valueAsString == null ? StringUtils.toString(
							valueAsBytes) : valueAsString);

			if (valueAsDouble < Long.MIN_VALUE
					|| valueAsDouble > Long.MAX_VALUE) {
				throwRangeException(valueAsString == null ? StringUtils.toString(
						valueAsBytes) : valueAsString, columnIndexZeroBased + 1,
						Types.BIGINT);
			}
		}
	}

	private short parseShortAsDouble(int columnIndex, String val)
			throws NumberFormatException, SQLException {
		if (val == null) {
			return 0;
		}

		double valueAsDouble = Double.parseDouble(val);

		if (this.jdbcCompliantTruncationForReads) {
			if (valueAsDouble < Short.MIN_VALUE
					|| valueAsDouble > Short.MAX_VALUE) {
				throwRangeException(String.valueOf(valueAsDouble), columnIndex,
						Types.SMALLINT);
			}
		}

		return (short) valueAsDouble;
	}

	private short parseShortWithOverflowCheck(int columnIndex,
			byte[] valueAsBytes, String valueAsString)
			throws NumberFormatException, SQLException {

		short shortValue = 0;

		if (valueAsBytes == null && valueAsString == null) {
			return 0;
		}

		if (valueAsBytes != null) {
			shortValue = StringUtils.getShort(valueAsBytes);
		} else {
			//
			// JDK-6 doesn't like trailing whitespace
			//
			// Note this isn't a performance issue, other
			// than the iteration over the string, as String.trim()
			// will return a new string only if whitespace is present
			//
			
			valueAsString = valueAsString.trim();
		
			shortValue = Short.parseShort(valueAsString);
		}

		if (this.jdbcCompliantTruncationForReads) {
			if (shortValue == Short.MIN_VALUE || shortValue == Short.MAX_VALUE) {
				long valueAsLong = Long
						.parseLong(valueAsString == null ? StringUtils.toString(
								valueAsBytes) : valueAsString);

				if (valueAsLong < Short.MIN_VALUE
						|| valueAsLong > Short.MAX_VALUE) {
					throwRangeException(valueAsString == null ? StringUtils.toString(
							valueAsBytes) : valueAsString, columnIndex,
							Types.SMALLINT);
				}
			}
		}

		return shortValue;
	}

	// --------------------------JDBC 2.0-----------------------------------
	// ---------------------------------------------------------------------
	// Getter's and Setter's
	// ---------------------------------------------------------------------

	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {

			int rowIndex = this.rowData.getCurrentRowNumber();
	
			if (this.thisRow != null) {
				this.thisRow.closeOpenStreams();
			}
			
			boolean b = true;
			
			if ((rowIndex - 1) >= 0) {
				rowIndex--;
				this.rowData.setCurrentRow(rowIndex);
				this.thisRow = this.rowData.getAt(rowIndex);
	
				b = true;
			} else if ((rowIndex - 1) == -1) {
				rowIndex--;
				this.rowData.setCurrentRow(rowIndex);
				this.thisRow = null;
	
				b = false;
			} else {
				b = false;
			}
			
			setRowPositionValidity();
			
			return b;
		}
	}

	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {
			if (this.onInsertRow) {
				this.onInsertRow = false;
			}
	
			if (this.doingUpdates) {
				this.doingUpdates = false;
			}
	
			return prev();
		}
	}

	/**
	 * Closes this ResultSet and releases resources.
	 * 
	 * @param calledExplicitly
	 *            was realClose called by the standard ResultSet.close() method, or was it closed internally by the
	 *            driver?
	 * 
	 * @throws SQLException
	 *             if an error occurs
	 */
	public void realClose(boolean calledExplicitly) throws SQLException {
		MySQLConnection locallyScopedConn = this.connection;
		
		if (locallyScopedConn == null) return; // already closed
		
		synchronized (locallyScopedConn.getConnectionMutex()) {

			// additional check in case ResultSet was closed
			// while current thread was waiting for lock
			if (this.isClosed) return;

			try {
				if (this.useUsageAdvisor) {
					
					// Report on result set closed by driver instead of application
					
					if (!calledExplicitly) {		
						this.eventSink
								.consumeEvent(new ProfilerEvent(
										ProfilerEvent.TYPE_WARN,
										"",
										(this.owningStatement == null) ? "N/A"
												: this.owningStatement.currentCatalog,
										this.connectionId,
										(this.owningStatement == null) ? (-1)
												: this.owningStatement.getId(),
										this.resultId,
										System.currentTimeMillis(),
										0,
										Constants.MILLIS_I18N,
										null,
										this.pointOfOrigin,
										Messages
												.getString("ResultSet.ResultSet_implicitly_closed_by_driver"))); //$NON-NLS-1$
					}
	
					if (this.rowData instanceof RowDataStatic) {
						
						// Report on possibly too-large result sets
						
						if (this.rowData.size() > this.connection
								.getResultSetSizeThreshold()) {
							this.eventSink
									.consumeEvent(new ProfilerEvent(
											ProfilerEvent.TYPE_WARN,
											"",
											(this.owningStatement == null) ? Messages
													.getString("ResultSet.N/A_159")
													: this.owningStatement.currentCatalog, //$NON-NLS-1$
											this.connectionId,
											(this.owningStatement == null) ? (-1)
													: this.owningStatement.getId(),
											this.resultId,
											System.currentTimeMillis(),
											0,
											Constants.MILLIS_I18N,
											null,
											this.pointOfOrigin,
											Messages
													.getString(
															"ResultSet.Too_Large_Result_Set",
															new Object[] {
																	Integer.valueOf(
																			this.rowData
																					.size()),
																	Integer.valueOf(
																			this.connection
																					.getResultSetSizeThreshold()) })));
						}
						
						if (!isLast() && !isAfterLast() && (this.rowData.size() != 0)) {
	
							this.eventSink
									.consumeEvent(new ProfilerEvent(
											ProfilerEvent.TYPE_WARN,
											"",
											(this.owningStatement == null) ? Messages
													.getString("ResultSet.N/A_159")
													: this.owningStatement.currentCatalog, //$NON-NLS-1$
											this.connectionId,
											(this.owningStatement == null) ? (-1)
													: this.owningStatement.getId(),
											this.resultId,
											System.currentTimeMillis(),
											0,
											Constants.MILLIS_I18N,
											null,
											this.pointOfOrigin,
											Messages
													.getString(
															"ResultSet.Possible_incomplete_traversal_of_result_set", //$NON-NLS-1$
															new Object[] {
																	Integer.valueOf(
																			getRow()),
																	Integer.valueOf(
																			this.rowData
																					.size()) })));
						}
					}
	
					//
					// Report on any columns that were selected but
					// not referenced
					//
					
					if (this.columnUsed.length > 0 && !this.rowData.wasEmpty()) {
						StringBuffer buf = new StringBuffer(
								Messages
										.getString("ResultSet.The_following_columns_were_never_referenced")); //$NON-NLS-1$
	
						boolean issueWarn = false;
	
						for (int i = 0; i < this.columnUsed.length; i++) {
							if (!this.columnUsed[i]) {
								if (!issueWarn) {
									issueWarn = true;
								} else {
									buf.append(", ");
								}
	
								buf.append(this.fields[i].getFullName());
							}
						}
	
						if (issueWarn) {
							this.eventSink.consumeEvent(new ProfilerEvent(
									ProfilerEvent.TYPE_WARN, "",
									(this.owningStatement == null) ? "N/A"
											: this.owningStatement.currentCatalog,
									this.connectionId,
									(this.owningStatement == null) ? (-1)
											: this.owningStatement.getId(), 0,
									System.currentTimeMillis(), 0,
									Constants.MILLIS_I18N, null,
									this.pointOfOrigin, buf.toString()));
						}
					}
				}
			} finally {
				if (this.owningStatement != null && calledExplicitly) {
					this.owningStatement.removeOpenResultSet(this);
				}
				
				SQLException exceptionDuringClose = null;
	
				if (this.rowData != null) {
					try {
						this.rowData.close();
					} catch (SQLException sqlEx) {
						exceptionDuringClose = sqlEx;
					}
				}
	
				if (this.statementUsedForFetchingRows != null) {
					try {
						this.statementUsedForFetchingRows.realClose(true, false);
					} catch (SQLException sqlEx) {
						if (exceptionDuringClose != null) {
							exceptionDuringClose.setNextException(sqlEx);
						} else {
							exceptionDuringClose = sqlEx;
						}
					}
				}
				
				this.rowData = null;
				this.defaultTimeZone = null;
				this.fields = null;
				this.columnLabelToIndex = null;
				this.fullColumnNameToIndex = null;
				this.columnToIndexCache = null;
				this.eventSink = null;
				this.warningChain = null;
				
				if (!this.retainOwningStatement) {
					this.owningStatement = null;
				}
				
				this.catalog = null;
				this.serverInfo = null;
				this.thisRow = null;
				this.fastDateCal = null;
				this.connection = null;

				this.isClosed = true;

				if (exceptionDuringClose != null) {
					throw exceptionDuringClose;
				}
			}
		}
	}

	/**
	 * Returns true if this ResultSet is closed.
	 */
	public boolean isClosed() throws SQLException {
		return this.isClosed;
	}
	
	public boolean reallyResult() {
		if (this.rowData != null) {
			return true;
		}

		return this.reallyResult;
	}

	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {

			if (this.rowData.size() == 0) {
				setRowPositionValidity();
				
				return false;
			}
	
			if (this.thisRow != null) {
				this.thisRow.closeOpenStreams();
			}
			
			this.rowData.moveRowRelative(rows);
			this.thisRow = this.rowData.getAt(this.rowData.getCurrentRowNumber());
	
			setRowPositionValidity();
			
			return (!this.rowData.isAfterLast() && !this.rowData.isBeforeFirst());
		}
	}

	/**
	 * 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 SQLError.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 SQLError.notImplemented();
	}

	/**
	 * 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 SQLError.notImplemented();
	}

	/**
	 * Flag that this result set is 'binary' encoded (from a PreparedStatement),
	 * not stored as strings.
	 */
	protected void setBinaryEncoded() {
		this.isBinaryEncoded = true;
	}

	private void setDefaultTimeZone(TimeZone defaultTimeZone) throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			this.defaultTimeZone = defaultTimeZone;
		}
	}

	/**
	 * 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 {
		synchronized (checkClosed().getConnectionMutex()) {
			if ((direction != FETCH_FORWARD) && (direction != FETCH_REVERSE)
					&& (direction != FETCH_UNKNOWN)) {
				throw SQLError.createSQLException(
						Messages
								.getString("ResultSet.Illegal_value_for_fetch_direction_64"),
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
			}
	
			this.fetchDirection = direction;
		}
	}

	/**
	 * 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 lteq
	 *                rows lteq this.getMaxRows() is not satisfied. Currently
	 *                ignored by this driver.
	 */
	public void setFetchSize(int rows) throws SQLException {
		synchronized (checkClosed().getConnectionMutex()) {
			if (rows < 0) { /* || rows > getMaxRows() */
				throw SQLError.createSQLException(
						Messages
								.getString("ResultSet.Value_must_be_between_0_and_getMaxRows()_66"), //$NON-NLS-1$
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
			}
	
			this.fetchSize = rows;
		}
	}

	/**
	 * Sets the first character of the query that this result set was created
	 * from.
	 * 
	 * @param c
	 *            the first character of the query...uppercased
	 */
	public void setFirstCharOfQuery(char c) {
		try {
			synchronized (checkClosed().getConnectionMutex()) {
				this.firstCharOfQuery = c;
			}
		} catch (SQLException e) {
			throw new RuntimeException(e); // FIXME: Need to evolve public interface
		}
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param nextResultSet
	 *            Sets the next result set in the result set chain for multiple
	 *            result sets.
	 */
	protected synchronized void setNextResultSet(ResultSetInternalMethods nextResultSet) {
		this.nextResultSet = nextResultSet;
	}

	public void setOwningStatement(com.mysql.jdbc.StatementImpl owningStatement) {
		try {
			synchronized (checkClosed().getConnectionMutex()) {
				this.owningStatement = owningStatement;
			}
		} catch (SQLException e) {
			throw new RuntimeException(e); // FIXME: Need to evolve public interface
		}
	}

	/**
	 * Sets the concurrency (JDBC2)
	 * 
	 * @param concurrencyFlag
	 *            CONCUR_UPDATABLE or CONCUR_READONLY
	 */
	protected synchronized void setResultSetConcurrency(int concurrencyFlag) {
		try {
			synchronized (checkClosed().getConnectionMutex()) {
				this.resultSetConcurrency = concurrencyFlag;
			}
		} catch (SQLException e) {
			throw new RuntimeException(e); // FIXME: Need to evolve public interface
		}
	}

	/**
	 * Sets the result set type for (JDBC2)
	 * 
	 * @param typeFlag
	 *            SCROLL_SENSITIVE or SCROLL_INSENSITIVE (we only support
	 *            SCROLL_INSENSITIVE)
	 */
	protected synchronized void setResultSetType(int typeFlag) {
		try {
			synchronized (checkClosed().getConnectionMutex()) {
				this.resultSetType = typeFlag;
			}
		} catch (SQLException e) {
			throw new RuntimeException(e); // FIXME: Need to evolve public interface
		}
	}

	/**
	 * Sets server info (if any)
	 * 
	 * @param info
	 *            the server info message
	 */
	protected synchronized void setServerInfo(String info) {
		try {
			synchronized (checkClosed().getConnectionMutex()) {
				this.serverInfo = info;
			}
		} catch (SQLException e) {
			throw new RuntimeException(e); // FIXME: Need to evolve public interface
		}
	}

	public synchronized void setStatementUsedForFetchingRows(PreparedStatement stmt) {
		try {
			synchronized (checkClosed().getConnectionMutex()) {
				this.statementUsedForFetchingRows = stmt;
			}
		} catch (SQLException e) {
			throw new RuntimeException(e); // FIXME: Need to evolve public interface
		}
	}

	/**
	 * @param wrapperStatement
	 *            The wrapperStatement to set.
	 */
	public synchronized void setWrapperStatement(java.sql.Statement wrapperStatement) {
		try {
			synchronized (checkClosed().getConnectionMutex()) {
				this.wrapperStatement = wrapperStatement;
			}
		} catch (SQLException e) {
			throw new RuntimeException(e); // FIXME: Need to evolve public interface
		}
	}

	private void throwRangeException(String valueAsString, int columnIndex,
			int jdbcType) throws SQLException {
		String datatype = null;

		switch (jdbcType) {
		case Types.TINYINT:
			datatype = "TINYINT";
			break;
		case Types.SMALLINT:
			datatype = "SMALLINT";
			break;
		case Types.INTEGER:
			datatype = "INTEGER";
			break;
		case Types.BIGINT:
			datatype = "BIGINT";
			break;
		case Types.REAL:
			datatype = "REAL";
			break;
		case Types.FLOAT:
			datatype = "FLOAT";
			break;
		case Types.DOUBLE:
			datatype = "DOUBLE";
			break;
		case Types.DECIMAL:
			datatype = "DECIMAL";
			break;
		default:
			datatype = " (JDBC type '" + jdbcType + "')";
		}

		throw SQLError.createSQLException("'" + valueAsString + "' in column '"
				+ columnIndex + "' is outside valid range for the datatype "
				+ datatype + ".", SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE, getExceptionInterceptor());
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	public String toString() {
		if (this.reallyResult) {
			return super.toString();
		}

		return "Result set representing update count of " + this.updateCount;
	}

	/**
	 * @see ResultSetInternalMethods#updateArray(int, Array)
	 */
	public void updateArray(int arg0, Array arg1) throws SQLException {
		throw SQLError.notImplemented();
	}

	/**
	 * @see ResultSetInternalMethods#updateArray(String, Array)
	 */
	public void updateArray(String arg0, Array arg1) throws SQLException {
		throw SQLError.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 ResultSetInternalMethods#updateBlob(int, Blob)
	 */
	public void updateBlob(int arg0, java.sql.Blob arg1) throws SQLException {
		throw new NotUpdatable();
	}

	/**
	 * @see ResultSetInternalMethods#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 ResultSetInternalMethods#updateClob(int, Clob)
	 */
	public void updateClob(int arg0, java.sql.Clob arg1) throws SQLException {
		throw SQLError.notImplemented();
	}

	/**
	 * @see ResultSetInternalMethods#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
	 * 
	 * @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 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 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);
	}

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

	/**
	 * @see ResultSetInternalMethods#updateRef(int, Ref)
	 */
	public void updateRef(int arg0, Ref arg1) throws SQLException {
		throw SQLError.notImplemented();
	}

	/**
	 * @see ResultSetInternalMethods#updateRef(String, Ref)
	 */
	public void updateRef(String arg0, Ref arg1) throws SQLException {
		throw SQLError.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 SQLException
	 *                if a database access error occurred
	 */
	public boolean wasNull() throws SQLException {
		return this.wasNullFlag;
	}

	protected Calendar getGmtCalendar() {
		
		// Worst case we allocate this twice and the other gets GC'd,
		// however prevents deadlock
		if (this.gmtCalendar == null) {
			this.gmtCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
		}
		
		return this.gmtCalendar;
	}
	
	protected ExceptionInterceptor getExceptionInterceptor() {
		return this.exceptionInterceptor;
	}
}
