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

 This program is free software; you can redistribute it and/or modify
 it under the terms of version 2 of the GNU General Public License as
 published by the Free Software Foundation.

 There are special exceptions to the terms and conditions of the GPL
 as it is applied to this software. View the full text of the
 exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
 software distribution.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA



 */
package com.mysql.jdbc;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.StringReader;
import java.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.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 Throwable 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.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()) {
			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 = 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()) {
	
			boolean b;
	
			if (this.rowData.size() == 0) {
				b = false;
			} else {
				if (row == 0) {
					throw SQLError.createSQLException(
							Messages
									.getString("ResultSet.Cannot_absolute_position_to_row_0_110"), //$NON-NLS-1$
							SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
				}
	
				if (this.onInsertRow) {
					this.onInsertRow = false;
				}
	
				if (this.doingUpdates) {
					this.doingUpdates = false;
				}
	
				if (this.thisRow != null) {
					this.thisRow.closeOpenStreams();
				}
				
				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()) {

			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()) {

			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()) {
			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()) {
			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()) {
			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()) {
			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()) {
			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()) {
			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()) {
			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()) {
	
			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()) {
			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()) {
			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()) {
			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()) {
				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(this.fields,
				this.connection.getUseOldAliasMetadataBehavior(), 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()) {
			
			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()) {
				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()) {
				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()) {
			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()) {
			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()) {
			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()) {
			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()) {
			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()) {
			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()) {
			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()),
					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()) {
	
			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()) {

			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()) {

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

	/**
	 * Closes this ResultSet and releases resources.
	 * 
	 * @param calledExplicitly
	 *            was this called by close()?
	 * 
	 * @throws SQLException
	 *             if an error occurs
	 */
	public void realClose(boolean calledExplicitly) throws SQLException {
		MySQLConnection locallyScopedConn;
		
		try {
			locallyScopedConn = checkClosed();
		} catch (SQLException sqlEx) {
			return; // already closed
		}
		
		synchronized (locallyScopedConn) {

			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;
				}
			}
		}
	}

	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()) {

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