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

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

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

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

  You should have received a copy of the GNU General Public License along with this
  program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
  Floor, Boston, MA 02110-1301  USA

 */

package com.mysql.jdbc;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Array;
import java.sql.Date;
import java.sql.Ref;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.TreeMap;

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

/**
 * A ResultSet provides access to a table of data generated by executing a Statement. The table rows are retrieved in sequence. Within a row its column
 * values can be accessed in any order.
 * 
 * <P>
 * A ResultSet maintains a cursor pointing to its current row of data. Initially the cursor is positioned before the first row. The 'next' method moves the
 * cursor to the next row.
 * </p>
 * 
 * <P>
 * The getXXX methods retrieve column values for the current row. You can retrieve values either using the index number of the column, or by using the name of
 * the column. In general using the column index will be more efficient. Columns are numbered from 1.
 * </p>
 * 
 * <P>
 * For maximum portability, ResultSet columns within each row should be read in left-to-right order and each column should be read only once.
 * </p>
 * 
 * <P>
 * For the getXXX methods, the JDBC driver attempts to convert the underlying data to the specified Java type and returns a suitable Java value. See the JDBC
 * specification for allowable mappings from SQL types to Java types with the ResultSet getXXX methods.
 * </p>
 * 
 * <P>
 * Column names used as input to getXXX methods are case insenstive. When performing a getXXX using a column name, if several columns have the same name, then
 * the value of the first matching column will be returned. The column name option is designed to be used when column names are used in the SQL Query. For
 * columns that are NOT explicitly named in the query, it is best to use column numbers. If column names were used there is no way for the programmer to
 * guarentee that they actually refer to the intended columns.
 * </p>
 * 
 * <P>
 * A ResultSet is automatically closed by the Statement that generated it when that Statement is closed, re-executed, or is used to retrieve the next result
 * from a sequence of multiple results.
 * </p>
 * 
 * <P>
 * The number, types and properties of a ResultSet's columns are provided by the ResultSetMetaData object returned by the getMetaData method.
 * </p>
 */
public class ResultSetImpl implements ResultSetInternalMethods {

    private static final Constructor<?> JDBC_4_RS_4_ARG_CTOR;
    private static final Constructor<?> JDBC_4_RS_5_ARG_CTOR;;
    private static final Constructor<?> JDBC_4_UPD_RS_5_ARG_CTOR;

    static {
        if (Util.isJdbc4()) {
            try {
                String jdbc4ClassName = Util.isJdbc42() ? "com.mysql.jdbc.JDBC42ResultSet" : "com.mysql.jdbc.JDBC4ResultSet";
                JDBC_4_RS_4_ARG_CTOR = Class.forName(jdbc4ClassName)
                        .getConstructor(new Class[] { Long.TYPE, Long.TYPE, MySQLConnection.class, com.mysql.jdbc.StatementImpl.class });
                JDBC_4_RS_5_ARG_CTOR = Class.forName(jdbc4ClassName)
                        .getConstructor(new Class[] { String.class, Field[].class, RowData.class, MySQLConnection.class, com.mysql.jdbc.StatementImpl.class });

                jdbc4ClassName = Util.isJdbc42() ? "com.mysql.jdbc.JDBC42UpdatableResultSet" : "com.mysql.jdbc.JDBC4UpdatableResultSet";
                JDBC_4_UPD_RS_5_ARG_CTOR = Class.forName(jdbc4ClassName)
                        .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_5_ARG_CTOR = null;
            JDBC_4_UPD_RS_5_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;

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

    protected ProfilerEventHandler eventSink = null;

    Calendar fastDefaultCal = null;
    Calendar fastClientCal = null;

    /** The direction to fetch rows (always FETCH_FORWARD) */
    protected int fetchDirection = FETCH_FORWARD;

    /** The number of rows to fetch in one go... */
    protected int fetchSize = 0;

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

    /**
     * First character of the query that created this result set...Used to determine whether or not to parse server info messages in certain
     * circumstances.
     */
    protected char firstCharOfQuery;

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

    protected Map<String, Integer> columnNameToIndex = null;

    protected boolean hasBuiltIndexMapping = false;

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

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

    protected ResultSetInternalMethods nextResultSet = null;

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

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

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

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

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

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

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

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

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

    /**
     * Any info message from the server that was created while generating this result set (if 'info parsing' is enabled for the connection).
     */
    protected String serverInfo = null;

    PreparedStatement statementUsedForFetchingRows;

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

    // These are longs for
    // recent versions of the MySQL server.
    //
    // They get reduced to ints via the JDBC API,
    // but can be retrieved through a MySQLStatement
    // in their entirety.
    //

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

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

    private boolean useStrictFloatingPoint = false;

    protected boolean useUsageAdvisor = false;

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

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

    protected java.sql.Statement wrapperStatement;

    protected boolean retainOwningStatement;

    protected Calendar gmtCalendar = null;

    protected boolean useFastDateParsing = false;

    private boolean padCharsWithSpace = false;

    private boolean jdbcCompliantTruncationForReads;

    private boolean useFastIntParsing = true;
    private boolean useColumnNamesInFindColumn;

    private ExceptionInterceptor exceptionInterceptor;

    final static char[] EMPTY_SPACE = new char[255];

    static {
        for (int i = 0; i < EMPTY_SPACE.length; i++) {
            EMPTY_SPACE[i] = ' ';
        }
    }

    protected static ResultSetImpl getInstance(long updateCount, long updateID, MySQLConnection conn, StatementImpl creatorStmt) throws SQLException {
        if (!Util.isJdbc4()) {
            return new ResultSetImpl(updateCount, updateID, conn, creatorStmt);
        }

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

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

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

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

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

        return (ResultSetImpl) Util.handleNewInstance(JDBC_4_UPD_RS_5_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
     * @param creatorStmt
     */
    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();

            this.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
     * 
     * @throws SQLException
     *             if an error occurs
     */
    public ResultSetImpl(String catalog, Field[] fields, RowData tuples, MySQLConnection conn, StatementImpl creatorStmt) throws SQLException {
        this.connection = conn;

        this.retainOwningStatement = false;

        if (this.connection != null) {
            this.exceptionInterceptor = this.connection.getExceptionInterceptor();
            this.useStrictFloatingPoint = this.connection.getStrictFloatingPoint();
            this.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 (NonRegisteringDriver.DEBUG) {
            System.out.println(Messages.getString("ResultSet.Retrieved__1") + this.updateCount + " rows");
        }

        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
        this.useLegacyDatetimeCode = this.connection.getUseLegacyDatetimeCode();

        this.useColumnNamesInFindColumn = this.connection.getUseColumnNamesInFindColumn();

        setRowPositionValidity();
    }

    public void initializeWithMetadata() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            this.rowData.setMetadata(this.fields);

            this.columnToIndexCache = new HashMap<String, Integer>();

            if (this.profileSql || this.connection.getUseUsageAdvisor()) {
                this.columnUsed = new boolean[this.fields.length];
                this.pointOfOrigin = LogUtils.findCallingClassAndMethod(new Throwable());
                this.resultId = resultCounter++;
                this.useUsageAdvisor = this.connection.getUseUsageAdvisor();
                this.eventSink = ProfilerEventHandlerFactory.getInstance(this.connection);
            }

            if (this.connection.getGatherPerformanceMetrics()) {
                this.connection.incrementNumberOfResultSetsCreated();

                Set<String> tableNamesSet = new HashSet<String>();

                for (int i = 0; i < this.fields.length; i++) {
                    Field f = this.fields[i];

                    String tableName = f.getOriginalTableName();

                    if (tableName == null) {
                        tableName = f.getTableName();
                    }

                    if (tableName != null) {
                        if (this.connection.lowerCaseTableNames()) {
                            tableName = tableName.toLowerCase(); // on windows, table
                            // names are not case-sens.
                        }

                        tableNamesSet.add(tableName);
                    }
                }

                this.connection.reportNumberOfTablesAccessed(tableNamesSet.size());
            }
        }
    }

    private synchronized Calendar getFastDefaultCalendar() {
        if (this.fastDefaultCal == null) {
            this.fastDefaultCal = new GregorianCalendar(Locale.US);
            this.fastDefaultCal.setTimeZone(this.getDefaultTimeZone());
        }
        return this.fastDefaultCal;
    }

    private synchronized Calendar getFastClientCalendar() {
        if (this.fastClientCal == null) {
            this.fastClientCal = new GregorianCalendar(Locale.US);
        }
        return this.fastClientCal;
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Move to an absolute row number in the result set.
     * </p>
     * 
     * <p>
     * If row is positive, moves to an absolute row with respect to the beginning of the result set. The first row is row 1, the second is row 2, etc.
     * </p>
     * 
     * <p>
     * If row is negative, moves to an absolute row position with respect to the end of result set. For example, calling absolute(-1) positions the cursor on
     * the last row, absolute(-2) indicates the next-to-last row, etc.
     * </p>
     * 
     * <p>
     * An attempt to position the cursor beyond the first/last row in the result set, leaves the cursor before/after the first/last row, respectively.
     * </p>
     * 
     * <p>
     * Note: Calling absolute(1) is the same as calling first(). Calling absolute(-1) is the same as calling last().
     * </p>
     * 
     * @param row
     *            the row number to move to
     * 
     * @return true if on the result set, false if off.
     * 
     * @exception SQLException
     *                if a database-access error occurs, or row is 0, or result
     *                set type is TYPE_FORWARD_ONLY.
     */
    public boolean absolute(int row) throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {

            boolean b;

            if (this.rowData.size() == 0) {
                b = false;
            } else {
                if (this.onInsertRow) {
                    this.onInsertRow = false;
                }

                if (this.doingUpdates) {
                    this.doingUpdates = false;
                }

                if (this.thisRow != null) {
                    this.thisRow.closeOpenStreams();
                }

                if (row == 0) {
                    beforeFirst();
                    b = false;
                } else if (row == 1) {
                    b = first();
                } else if (row == -1) {
                    b = last();
                } else if (row > this.rowData.size()) {
                    afterLast();
                    b = false;
                } else {
                    if (row < 0) {
                        // adjust to reflect after end of result set
                        int newRowPosition = this.rowData.size() + row + 1;

                        if (newRowPosition <= 0) {
                            beforeFirst();
                            b = false;
                        } else {
                            b = absolute(newRowPosition);
                        }
                    } else {
                        row--; // adjust for index difference
                        this.rowData.setCurrentRow(row);
                        this.thisRow = this.rowData.getAt(row);
                        b = true;
                    }
                }
            }

            setRowPositionValidity();

            return b;
        }
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Moves to the end of the result set, just after the last row. Has no effect if the result set contains no rows.
     * </p>
     * 
     * @exception SQLException
     *                if a database-access error occurs, or result set type is
     *                TYPE_FORWARD_ONLY.
     */
    public void afterLast() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {

            if (this.onInsertRow) {
                this.onInsertRow = false;
            }

            if (this.doingUpdates) {
                this.doingUpdates = false;
            }

            if (this.thisRow != null) {
                this.thisRow.closeOpenStreams();
            }

            if (this.rowData.size() != 0) {
                this.rowData.afterLast();
                this.thisRow = null;
            }

            setRowPositionValidity();
        }
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Moves to the front of the result set, just before the first row. Has no effect if the result set contains no rows.
     * </p>
     * 
     * @exception SQLException
     *                if a database-access error occurs, or result set type is
     *                TYPE_FORWARD_ONLY
     */
    public void beforeFirst() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {

            if (this.onInsertRow) {
                this.onInsertRow = false;
            }

            if (this.doingUpdates) {
                this.doingUpdates = false;
            }

            if (this.rowData.size() == 0) {
                return;
            }

            if (this.thisRow != null) {
                this.thisRow.closeOpenStreams();
            }

            this.rowData.beforeFirst();
            this.thisRow = null;

            setRowPositionValidity();
        }
    }

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

    /**
     * Builds a hash between column names and their indices for fast retrieval.
     */
    public void buildIndexMapping() throws SQLException {
        int numFields = this.fields.length;
        this.columnLabelToIndex = new TreeMap<String, Integer>(String.CASE_INSENSITIVE_ORDER);
        this.fullColumnNameToIndex = new TreeMap<String, Integer>(String.CASE_INSENSITIVE_ORDER);
        this.columnNameToIndex = new TreeMap<String, Integer>(String.CASE_INSENSITIVE_ORDER);

        // We do this in reverse order, so that the 'first' column with a given name ends up as the final mapping in the hashtable...
        //
        // Quoting the JDBC Spec:
        //
        // "Column names used as input to getter methods are case insensitive. When a getter method is called with a column name and several columns have the
        // same name, the value of the first matching column will be returned. "
        //
        for (int i = numFields - 1; i >= 0; i--) {
            Integer index = Integer.valueOf(i);
            String columnName = this.fields[i].getOriginalName();
            String columnLabel = this.fields[i].getName();
            String fullColumnName = this.fields[i].getFullName();

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

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

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

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

    /**
     * JDBC 2.0 The cancelRowUpdates() method may be called after calling an
     * updateXXX() method(s) and before calling updateRow() to rollback the
     * updates made to a row. If no updates have been made or updateRow() has
     * already been called, then this method has no effect.
     * 
     * @exception SQLException
     *                if a database-access error occurs, or if called when on
     *                the insert row.
     * @throws NotUpdatable
     */
    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"),
                    SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
        }

        return c;
    }

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

            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");
            this.onValidRow = false;
        } else if (this.rowData.isBeforeFirst()) {
            this.invalidRowReason = Messages.getString("ResultSet.Before_start_of_result_set_146");
            this.onValidRow = false;
        } else if (this.rowData.isAfterLast()) {
            this.invalidRowReason = Messages.getString("ResultSet.After_end_of_result_set_148");
            this.onValidRow = false;
        } else {
            this.onValidRow = true;
            this.invalidRowReason = null;
        }
    }

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

    /**
     * After this call, getWarnings returns null until a new warning is reported
     * for this ResultSet
     * 
     * @exception SQLException
     *                if a database access error occurs
     */
    public void clearWarnings() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            this.warningChain = null;
        }
    }

    /**
     * In some cases, it is desirable to immediately release a ResultSet
     * database and JDBC resources instead of waiting for this to happen when it
     * is automatically closed. The close method provides this immediate
     * release.
     * 
     * <p>
     * <B>Note:</B> A ResultSet is automatically closed by the Statement the Statement that generated it when that Statement is closed, re-executed, or is used
     * to retrieve the next result from a sequence of multiple results. A ResultSet is also automatically closed when it is garbage collected.
     * </p>
     * 
     * @exception SQLException
     *                if a database access error occurs
     */
    public void close() throws SQLException {
        realClose(true);
    }

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

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

    private String convertToZeroLiteralStringWithEmptyCheck() throws SQLException {

        if (this.connection.getEmptyStringsConvertToZero()) {
            return "0";
        }

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

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

            return rs;
        }
    }

    public void redefineFieldsForDBMD(Field[] f) {
        this.fields = f;

        for (int i = 0; i < this.fields.length; i++) {
            this.fields[i].setUseOldNameMetadata(true);
            this.fields[i].setConnection(this.connection);
        }
    }

    public void populateCachedMetaData(CachedResultSetMetaData cachedMetaData) throws SQLException {
        cachedMetaData.fields = this.fields;
        cachedMetaData.columnNameToIndex = this.columnLabelToIndex;
        cachedMetaData.fullColumnNameToIndex = this.fullColumnNameToIndex;
        cachedMetaData.metadata = getMetaData();
    }

    public void initializeFromCachedMetaData(CachedResultSetMetaData cachedMetaData) {
        this.fields = cachedMetaData.fields;
        this.columnLabelToIndex = cachedMetaData.columnNameToIndex;
        this.fullColumnNameToIndex = cachedMetaData.fullColumnNameToIndex;
        this.hasBuiltIndexMapping = true;
    }

    /**
     * JDBC 2.0 Delete the current row from the result set and the underlying
     * database. Cannot be called when on the insert row.
     * 
     * @exception SQLException
     *                if a database-access error occurs, or if called when on
     *                the insert row.
     * @throws NotUpdatable
     */
    public void deleteRow() throws SQLException {
        throw new NotUpdatable();
    }

    /**
     * @param columnIndex
     * @param stringVal
     * @param mysqlType
     * @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].getEncoding();

        return this.thisRow.getString(columnIndex - 1, encoding, this.connection);
    }

    protected Date fastDateCreate(Calendar cal, int year, int month, int day) throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            Calendar targetCalendar = cal;

            if (cal == null) {
                if (this.connection.getNoTimezoneConversionForDateType()) {
                    targetCalendar = getFastClientCalendar();
                } else {
                    targetCalendar = getFastDefaultCalendar();
                }
            }

            if (!this.useLegacyDatetimeCode) {
                return TimeUtil.fastDateCreate(year, month, day, targetCalendar);
            }

            boolean useGmtMillis = cal == null && !this.connection.getNoTimezoneConversionForDateType() && this.connection.getUseGmtMillisForDatetimes();

            return TimeUtil.fastDateCreate(useGmtMillis, useGmtMillis ? getGmtCalendar() : targetCalendar, targetCalendar, year, month, day);
        }
    }

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

            if (cal == null) {
                cal = getFastDefaultCalendar();
            }

            return TimeUtil.fastTimeCreate(cal, hour, minute, second, getExceptionInterceptor());
        }
    }

    protected Timestamp fastTimestampCreate(Calendar cal, int year, int month, int day, int hour, int minute, int seconds, int secondsPart)
            throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            if (!this.useLegacyDatetimeCode) {
                return TimeUtil.fastTimestampCreate(cal.getTimeZone(), year, month, day, hour, minute, seconds, secondsPart);
            }

            if (cal == null) {
                cal = getFastDefaultCalendar();
            }

            boolean useGmtMillis = this.connection.getUseGmtMillisForDatetimes();

            return TimeUtil.fastTimestampCreate(useGmtMillis, useGmtMillis ? getGmtCalendar() : null, cal, year, month, day, hour, minute, seconds,
                    secondsPart);
        }
    }

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

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

    /*
     * [For JDBC-3.0 and older - http://java.sun.com/j2se/1.5.0/docs/api/java/sql/ResultSet.html#findColumn(java.lang.String)]
     * Map a ResultSet column name to a ResultSet column index
     * 
     * @param columnName
     * the name of the column
     * 
     * @return the column index
     * 
     * @exception SQLException
     * if a database access error occurs
     * 
     * [For JDBC-4.0 and newer - http://java.sun.com/javase/6/docs/api/java/sql/ResultSet.html#findColumn(java.lang.String)]
     * 
     * Maps the given ResultSet column label to its ResultSet column index.
     * 
     * @param columnLabel
     * the label for the column specified with the SQL AS clause. If the
     * SQL AS clause was not specified, then the label is the name of the column
     * 
     * @return the column index of the given column name
     */
    public int findColumn(String columnName) throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            Integer index;

            if (!this.hasBuiltIndexMapping) {
                buildIndexMapping();
            }

            index = this.columnToIndexCache.get(columnName);

            if (index != null) {
                return index.intValue() + 1;
            }

            index = this.columnLabelToIndex.get(columnName);

            if (index == null && this.useColumnNamesInFindColumn) {
                index = this.columnNameToIndex.get(columnName);
            }

            if (index == null) {
                index = this.fullColumnNameToIndex.get(columnName);
            }

            if (index != null) {
                this.columnToIndexCache.put(columnName, index);

                return index.intValue() + 1;
            }

            // Try this inefficient way, now

            for (int i = 0; i < this.fields.length; i++) {
                if (this.fields[i].getName().equalsIgnoreCase(columnName)) {
                    return i + 1;
                } else if (this.fields[i].getFullName().equalsIgnoreCase(columnName)) {
                    return i + 1;
                }
            }

            throw SQLError.createSQLException(Messages.getString("ResultSet.Column____112") + columnName + Messages.getString("ResultSet.___not_found._113"),
                    SQLError.SQL_STATE_COLUMN_NOT_FOUND, getExceptionInterceptor());
        }
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Moves to the first row in the result set.
     * </p>
     * 
     * @return true if on a valid row, false if no rows in the result set.
     * 
     * @exception SQLException
     *                if a database-access error occurs, or result set type is
     *                TYPE_FORWARD_ONLY.
     */
    public boolean first() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {

            boolean b = true;

            if (this.rowData.isEmpty()) {
                b = false;
            } else {

                if (this.onInsertRow) {
                    this.onInsertRow = false;
                }

                if (this.doingUpdates) {
                    this.doingUpdates = false;
                }

                this.rowData.beforeFirst();
                this.thisRow = this.rowData.next();
            }

            setRowPositionValidity();

            return b;
        }
    }

    /**
     * JDBC 2.0 Get an array column.
     * 
     * @param i
     *            the first column is 1, the second is 2, ...
     * 
     * @return an object representing an SQL array
     * 
     * @throws SQLException
     *             if a database error occurs
     * @throws NotImplemented
     */
    public java.sql.Array getArray(int i) throws SQLException {
        checkColumnBounds(i);

        throw SQLError.createSQLFeatureNotSupportedException();
    }

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

    /**
     * @param columnName
     * 
     * @throws SQLException
     */
    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());
                }
            }

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

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

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

    /**
     * @param columnName
     * @param scale
     * 
     * @throws SQLException
     * 
     * @deprecated
     */
    @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);
                    }
                }
            }

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

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

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

    /**
     * @param columnName
     * 
     * @throws SQLException
     */
    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);
    }

    /**
     * @param columnName
     * 
     * @throws SQLException
     */
    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);
    }

    /**
     * @param columnName
     * 
     * @throws SQLException
     */
    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 + Messages.getString("ResultSet.___is_out_of_range_[-127,127]_174"),
                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
        }
    }

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

    /**
     * @param columnName
     * 
     * @throws SQLException
     */
    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.getServerCharset(), this.connection.parserKnowsUnicode(),
                    this.connection, getExceptionInterceptor());
        }

        return null;
    }

    public int getBytesSize() throws SQLException {
        RowData localRowData = this.rowData;

        checkClosed();

        if (localRowData instanceof RowDataStatic) {
            int bytesSize = 0;

            int numRows = localRowData.size();

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

            return bytesSize;
        }

        return -1;
    }

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

            // punt, no connection around
            return new GregorianCalendar();
        }
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Get the value of a column in the current row as a java.io.Reader.
     * </p>
     * 
     * @param columnIndex
     *            the column to get the value from
     * 
     * @return the value in the column as a java.io.Reader.
     * 
     * @throws SQLException
     *             if an error occurs
     */
    public java.io.Reader getCharacterStream(int columnIndex) throws SQLException {
        if (!this.isBinaryEncoded) {
            checkColumnBounds(columnIndex);

            int columnIndexMinusOne = columnIndex - 1;

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

                return null;
            }

            this.wasNullFlag = false;

            return this.thisRow.getReader(columnIndexMinusOne);
        }

        return getNativeCharacterStream(columnIndex);
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Get the value of a column in the current row as a java.io.Reader.
     * </p>
     * 
     * @param columnName
     *            the column name to retrieve the value from
     * 
     * @return the value as a java.io.Reader
     * 
     * @throws SQLException
     *             if an error occurs
     */
    public java.io.Reader getCharacterStream(String columnName) throws SQLException {
        return getCharacterStream(findColumn(columnName));
    }

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

        return null;
    }

    /**
     * JDBC 2.0 Get a CLOB column.
     * 
     * @param i
     *            the first column is 1, the second is 2, ...
     * 
     * @return an object representing a CLOB
     * 
     * @throws SQLException
     *             if an error occurs
     */
    public java.sql.Clob getClob(int i) throws SQLException {
        if (!this.isBinaryEncoded) {
            String asString = getStringForClob(i);

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

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

        return getNativeClob(i);
    }

    /**
     * JDBC 2.0 Get a CLOB column.
     * 
     * @param colName
     *            the column name
     * 
     * @return an object representing a CLOB
     * 
     * @throws SQLException
     *             if an error occurs
     */
    public java.sql.Clob getClob(String colName) throws SQLException {
        return getClob(findColumn(colName));
    }

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

    /**
     * JDBC 2.0 Return the concurrency of this result set. The concurrency used
     * is determined by the statement that created the result set.
     * 
     * @return the concurrency type, CONCUR_READ_ONLY, etc.
     * 
     * @throws SQLException
     *             if a database-access error occurs
     */
    public int getConcurrency() throws SQLException {
        return (CONCUR_READ_ONLY);
    }

    /**
     * Get the name of the SQL cursor used by this ResultSet
     * 
     * <p>
     * In SQL, a result table is retrieved though a cursor that is named. The current row of a result can be updated or deleted using a positioned update/delete
     * statement that references the cursor name.
     * </p>
     * 
     * <p>
     * JDBC supports this SQL feature by providing the name of the SQL cursor used by a ResultSet. The current row of a ResulSet is also the current row of this
     * SQL cursor.
     * </p>
     * 
     * <p>
     * <B>Note:</B> If positioned update is not supported, a SQLException is thrown.
     * </p>
     * 
     * @return the ResultSet's SQL cursor name.
     * 
     * @exception SQLException
     *                if a database access error occurs
     */
    public String getCursorName() throws SQLException {
        throw SQLError.createSQLException(Messages.getString("ResultSet.Positioned_Update_not_supported"), SQLError.SQL_STATE_DRIVER_NOT_CAPABLE,
                getExceptionInterceptor());
    }

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

    /**
     * @param columnName
     * 
     * @throws java.sql.SQLException
     */
    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();

            // truncate fractional part
            int dec = stringVal.indexOf(".");
            if (dec > -1) {
                stringVal = stringVal.substring(0, dec);
            }

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

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

            sqlEx.initCause(e);

            throw sqlEx;
        }
    }

    private TimeZone getDefaultTimeZone() {
        return this.useLegacyDatetimeCode ? this.connection.getDefaultTimeZone() : this.serverTimeZoneTz;
    }

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

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

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

    /**
     * Converts a string representation of a number to a double. Need a faster
     * way to do this.
     * 
     * @param colIndex
     *            the 1-based index of the column to retrieve a double from.
     * 
     * @return the double value represented by the string in buf
     * 
     * @throws SQLException
     *             if an error occurs
     */
    protected double getDoubleInternal(int colIndex) throws SQLException {
        return getDoubleInternal(getString(colIndex), colIndex);
    }

    /**
     * Converts a string representation of a number to a double. Need a faster
     * way to do this.
     * 
     * @param stringVal
     *            the double as a String
     * @param colIndex
     *            the 1-based index of the column to retrieve a double from.
     * 
     * @return the double value represented by the string in buf
     * 
     * @throws SQLException
     *             if an error occurs
     */
    protected double getDoubleInternal(String stringVal, int colIndex) throws SQLException {
        try {
            if ((stringVal == null)) {
                return 0;
            }

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

            double d = Double.parseDouble(stringVal);

            if (this.useStrictFloatingPoint) {
                // Fix endpoint rounding precision loss in MySQL server
                if (d == 2.147483648E9) {
                    // Fix Odd end-point rounding on MySQL
                    d = 2.147483647E9;
                } else if (d == 1.0000000036275E-15) {
                    // Fix odd end-point rounding on MySQL
                    d = 1.0E-15;
                } else if (d == 9.999999869911E14) {
                    d = 9.99999999999999E14;
                } else if (d == 1.4012984643248E-45) {
                    d = 1.4E-45;
                } else if (d == 1.4013E-45) {
                    d = 1.4E-45;
                } else if (d == 3.4028234663853E37) {
                    d = 3.4028235E37;
                } else if (d == -2.14748E9) {
                    d = -2.147483648E9;
                } else if (d == 3.40282E37) {
                    d = 3.4028235E37;
                }
            }

            return d;
        } catch (NumberFormatException e) {
            if (this.fields[colIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
                long valueAsLong = getNumericRepresentationOfSQLBitType(colIndex);

                return valueAsLong;
            }

            throw SQLError.createSQLException(Messages.getString("ResultSet.Bad_format_for_number", new Object[] { stringVal, Integer.valueOf(colIndex) }),
                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
        }
    }

    /**
     * JDBC 2.0 Returns the fetch direction for this result set.
     * 
     * @return the fetch direction for this result set.
     * 
     * @exception SQLException
     *                if a database-access error occurs
     */
    public int getFetchDirection() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            return this.fetchDirection;
        }
    }

    /**
     * JDBC 2.0 Return the fetch size for this result set.
     * 
     * @return the fetch size for this result set.
     * 
     * @exception SQLException
     *                if a database-access error occurs
     */
    public int getFetchSize() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            return this.fetchSize;
        }
    }

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

    /**
     * Get the value of a column in the current row as a Java float.
     * 
     * @param columnIndex
     *            the first column is 1, the second is 2,...
     * 
     * @return the column value; 0 if SQL NULL
     * 
     * @exception SQLException
     *                if a database access error occurs
     */
    public float getFloat(int columnIndex) throws SQLException {
        if (!this.isBinaryEncoded) {
            String val = null;

            val = getString(columnIndex);

            return getFloatFromString(val, columnIndex);
        }

        return getNativeFloat(columnIndex);
    }

    /**
     * @param columnName
     * 
     * @throws SQLException
     */
    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
                    + Messages.getString("ResultSet.___in_column__201") + columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
        }
    }

    /**
     * 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].getEncoding(), 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].getEncoding(), this.connection) + "'",
                                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 + "'",
                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
            }
        }

        return getNativeInt(columnIndex);
    }

    /**
     * @param columnName
     * 
     * @throws SQLException
     */
    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 + Messages.getString("ResultSet.___in_column__207") + columnIndex,
                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
        }
    }

    /**
     * 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].getEncoding(), 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].getEncoding(), this.connection) + "'",
                                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 + "'",
                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
            }
        }

        return getNativeLong(columnIndex, overflowCheck, true);
    }

    /**
     * @param columnName
     * 
     * @throws SQLException
     */
    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
                    + Messages.getString("ResultSet.___in_column__212") + (columnIndexZeroBased + 1), SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
                    getExceptionInterceptor());
        }
    }

    /**
     * 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(), this.connection.getYearIsDateType(),
                getExceptionInterceptor());
    }

    /**
     * JDBC 2.0 Get an array column.
     * 
     * @param i
     *            the first column is 1, the second is 2, ...
     * 
     * @return an object representing an SQL array
     * 
     * @throws SQLException
     *             if a database error occurs
     * @throws NotImplemented
     */
    protected java.sql.Array getNativeArray(int i) throws SQLException {
        throw SQLError.createSQLFeatureNotSupportedException();
    }

    /**
     * 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;
                }
                break;
            default:
                break;
        }
        int sqlType = field.getSQLType();

        if (sqlType == Types.VARBINARY || sqlType == Types.BINARY) {
            return (byte[]) value;
        }

        return getBytesFromString(getNativeString(columnIndex));
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Get the value of a column in the current row as a java.io.Reader.
     * </p>
     * 
     * @param columnIndex
     *            the column to get the value from
     * 
     * @return the value in the column as a java.io.Reader.
     * 
     * @throws SQLException
     *             if an error occurs
     */
    protected java.io.Reader getNativeCharacterStream(int columnIndex) throws SQLException {
        int columnIndexMinusOne = columnIndex - 1;

        switch (this.fields[columnIndexMinusOne].getSQLType()) {
            case Types.CHAR:
            case Types.VARCHAR:
            case Types.LONGVARCHAR:
            case Types.CLOB:
                if (this.thisRow.isNull(columnIndexMinusOne)) {
                    this.wasNullFlag = true;

                    return null;
                }

                this.wasNullFlag = false;

                return this.thisRow.getReader(columnIndexMinusOne);
        }

        String asString = getStringForClob(columnIndex);

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

        return getCharacterStreamFromString(asString);
    }

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

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

        return getClobFromString(stringVal);
    }

    private String getNativeConvertToString(int columnIndex, Field field) throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {

            int sqlType = field.getSQLType();
            int mysqlType = field.getMysqlType();

            switch (sqlType) {
                case Types.BIT:
                    return String.valueOf(getNumericRepresentationOfSQLBitType(columnIndex));
                case Types.BOOLEAN:
                    boolean booleanVal = getBoolean(columnIndex);

                    if (this.wasNullFlag) {
                        return null;
                    }

                    return String.valueOf(booleanVal);

                case Types.TINYINT:
                    byte tinyintVal = getNativeByte(columnIndex, false);

                    if (this.wasNullFlag) {
                        return null;
                    }

                    if (!field.isUnsigned() || tinyintVal >= 0) {
                        return String.valueOf(tinyintVal);
                    }

                    short unsignedTinyVal = (short) (tinyintVal & 0xff);

                    return String.valueOf(unsignedTinyVal);

                case Types.SMALLINT:

                    int intVal = getNativeInt(columnIndex, false);

                    if (this.wasNullFlag) {
                        return null;
                    }

                    if (!field.isUnsigned() || intVal >= 0) {
                        return String.valueOf(intVal);
                    }

                    intVal = intVal & 0xffff;

                    return String.valueOf(intVal);

                case Types.INTEGER:
                    intVal = getNativeInt(columnIndex, false);

                    if (this.wasNullFlag) {
                        return null;
                    }

                    if (!field.isUnsigned() || intVal >= 0 || field.getMysqlType() == MysqlDefs.FIELD_TYPE_INT24) {

                        return String.valueOf(intVal);
                    }

                    long longVal = intVal & 0xffffffffL;

                    return String.valueOf(longVal);

                case Types.BIGINT:

                    if (!field.isUnsigned()) {
                        longVal = getNativeLong(columnIndex, false, true);

                        if (this.wasNullFlag) {
                            return null;
                        }

                        return String.valueOf(longVal);
                    }

                    longVal = getNativeLong(columnIndex, false, false);

                    if (this.wasNullFlag) {
                        return null;
                    }

                    return String.valueOf(convertLongToUlong(longVal));
                case Types.REAL:
                    float floatVal = getNativeFloat(columnIndex);

                    if (this.wasNullFlag) {
                        return null;
                    }

                    return String.valueOf(floatVal);

                case Types.FLOAT:
                case Types.DOUBLE:
                    double doubleVal = getNativeDouble(columnIndex);

                    if (this.wasNullFlag) {
                        return null;
                    }

                    return String.valueOf(doubleVal);

                case Types.DECIMAL:
                case Types.NUMERIC:
                    String stringVal = StringUtils.toAsciiString(this.thisRow.getColumnValue(columnIndex - 1));

                    BigDecimal val;

                    if (stringVal != null) {
                        this.wasNullFlag = false;

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

                            return val.toString();
                        }

                        try {
                            val = new BigDecimal(stringVal);
                        } catch (NumberFormatException ex) {
                            throw SQLError.createSQLException(
                                    Messages.getString("ResultSet.Bad_format_for_BigDecimal", new Object[] { stringVal, Integer.valueOf(columnIndex) }),
                                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
                        }

                        return val.toString();
                    }

                    this.wasNullFlag = true;

                    return null;

                case Types.CHAR:
                case Types.VARCHAR:
                case Types.LONGVARCHAR:

                    return extractStringFromNativeColumn(columnIndex, mysqlType);
                case Types.BINARY:
                case Types.VARBINARY:
                case Types.LONGVARBINARY:

                    if (!field.isBlob()) {
                        return extractStringFromNativeColumn(columnIndex, mysqlType);
                    } else if (!field.isBinary()) {
                        return extractStringFromNativeColumn(columnIndex, mysqlType);
                    } else {
                        byte[] data = getBytes(columnIndex);
                        Object obj = data;

                        if ((data != null) && (data.length >= 2)) {
                            if ((data[0] == -84) && (data[1] == -19)) {
                                // Serialized object?
                                try {
                                    ByteArrayInputStream bytesIn = new ByteArrayInputStream(data);
                                    ObjectInputStream objIn = new ObjectInputStream(bytesIn);
                                    obj = objIn.readObject();
                                    objIn.close();
                                    bytesIn.close();
                                } catch (ClassNotFoundException cnfe) {
                                    throw SQLError.createSQLException(Messages.getString("ResultSet.Class_not_found___91") + cnfe.toString()
                                            + Messages.getString("ResultSet._while_reading_serialized_object_92"), getExceptionInterceptor());
                                } 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.connection.getDefaultTimeZone(), 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.connection.getDefaultTimeZone(), 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);
                    }
                    return extractStringFromNativeColumn(columnIndex, mysqlType);

                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
     */
    protected java.sql.Ref getNativeRef(int i) throws SQLException {
        throw SQLError.createSQLFeatureNotSupportedException();
    }

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

            StringBuilder zeroFillBuf = new StringBuilder(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());
        }
    }

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

            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", 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") + cnfe.toString()
                                            + Messages.getString("ResultSet._while_reading_serialized_object_92"), getExceptionInterceptor());
                                } 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 (this.connection.getAutoDeserialize()) {
                try {
                    return type.cast(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", 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
     */
    public java.sql.Ref getRef(int i) throws SQLException {
        checkColumnBounds(i);
        throw SQLError.createSQLFeatureNotSupportedException();
    }

    /**
     * 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
     */
    public java.sql.Ref getRef(String colName) throws SQLException {
        return getRef(findColumn(colName));
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Determine the current row number. The first row is number 1, the second number 2, etc.
     * </p>
     * 
     * @return the current row number, else return 0 if there is no current row
     * 
     * @exception SQLException
     *                if a database-access error occurs.
     */
    public int getRow() throws SQLException {
        checkClosed();

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

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

        return row;
    }

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

    private long getNumericRepresentationOfSQLBitType(int columnIndex) throws SQLException {

        Object value = this.thisRow.getColumnValue(columnIndex - 1);

        if (this.fields[columnIndex - 1].isSingleBit() || ((byte[]) value).length == 1) {
            return ((byte[]) value)[0];
        }

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

        int shift = 0;

        long[] steps = new long[asBytes.length];

        for (int i = asBytes.length - 1; i >= 0; i--) {
            steps[i] = (long) (asBytes[i] & 0xff) << shift;
            shift += 8;
        }

        long valueAsLong = 0;

        for (int i = 0; i < asBytes.length; i++) {
            valueAsLong |= steps[i];
        }

        return valueAsLong;
    }

    /**
     * Get the value of a column in the current row as a Java short.
     * 
     * @param columnIndex
     *            the first column is 1, the second is 2,...
     * 
     * @return the column value; 0 if SQL NULL
     * 
     * @exception SQLException
     *                if a database access error occurs
     */
    public short getShort(int columnIndex) throws SQLException {
        if (!this.isBinaryEncoded) {
            checkRowPos();

            if (this.useFastIntParsing) {

                checkColumnBounds(columnIndex);

                Object value = this.thisRow.getColumnValue(columnIndex - 1);

                if (value == null) {
                    this.wasNullFlag = true;
                } else {
                    this.wasNullFlag = false;
                }

                if (this.wasNullFlag) {
                    return 0;
                }

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

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

                boolean needsFullParse = false;

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

                        break;
                    }
                }

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

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

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

                            return (short) valueAsLong;
                        }

                        throw SQLError.createSQLException(
                                Messages.getString("ResultSet.Invalid_value_for_getShort()_-____96") + StringUtils.toString(shortAsBytes) + "'",
                                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 + "'",
                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
            }
        }

        return getNativeShort(columnIndex);
    }

    /**
     * @param columnName
     * 
     * @throws SQLException
     */
    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
                    + Messages.getString("ResultSet.___in_column__218") + columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
        }
    }

    /**
     * JDBC 2.0 Return the Statement that produced the ResultSet.
     * 
     * @return the Statment that produced the result set, or null if the result
     *         was produced some other way.
     * 
     * @exception SQLException
     *                if a database-access error occurs
     */
    public java.sql.Statement getStatement() throws SQLException {
        try {
            synchronized (checkClosed().getConnectionMutex()) {
                if (this.wrapperStatement != null) {
                    return this.wrapperStatement;
                }

                return this.owningStatement;
            }

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

            if (this.wrapperStatement != null) {
                return this.wrapperStatement;
            }

            return this.owningStatement;
        }

    }

    /**
     * Get the value of a column in the current row as a Java String
     * 
     * @param columnIndex
     *            the first column is 1, the second is 2...
     * 
     * @return the column value, null for SQL NULL
     * 
     * @exception SQLException
     *                if a database access error occurs
     */
    public String getString(int columnIndex) throws SQLException {
        String stringVal = getStringInternal(columnIndex, true);

        if (this.padCharsWithSpace && stringVal != null) {
            Field f = this.fields[columnIndex - 1];

            if (f.getMysqlType() == MysqlDefs.FIELD_TYPE_STRING) {
                int fieldLength = (int) f.getLength() /* safe, bytes in a CHAR <= 1024 */ / f.getMaxBytesPerCharacter(); /* safe, this will never be 0 */

                int currentLength = stringVal.length();

                if (currentLength < fieldLength) {
                    StringBuilder paddedBuf = new StringBuilder(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"),
                        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.getEncoding();

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

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

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

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

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

                    return null;
                }

                this.wasNullFlag = false;

                return dt.toString();
            }

            // Handles timezone conversion and zero-date behavior

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

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

                            return null;
                        }

                        this.wasNullFlag = false;

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

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

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

                            return null;
                        }

                        this.wasNullFlag = false;

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

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

                            return null;
                        }

                        this.wasNullFlag = false;

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

            return stringVal;
        }

        return getNativeString(columnIndex);
    }

    /**
     * Get the value of a column in the current row as a java.sql.Time object
     * 
     * @param columnIndex
     *            the first column is 1, the second is 2...
     * 
     * @return the column value; null if SQL NULL
     * 
     * @throws java.sql.SQLException
     *             if a database access error occurs
     */
    public Time getTime(int columnIndex) throws java.sql.SQLException {
        return getTimeInternal(columnIndex, null, this.getDefaultTimeZone(), false);
    }

    /**
     * Get the value of a column in the current row as a java.sql.Time object.
     * Use the calendar to construct an appropriate millisecond value for the
     * Time, if the underlying database doesn't store timezone information.
     * 
     * @param columnIndex
     *            the first column is 1, the second is 2, ...
     * @param cal
     *            the calendar to use in constructing the time
     * 
     * @return the column value; if the value is SQL NULL, the result is null
     * 
     * @exception SQLException
     *                if a database-access error occurs.
     */
    public java.sql.Time getTime(int columnIndex, Calendar cal) throws SQLException {
        return getTimeInternal(columnIndex, cal, cal.getTimeZone(), true);
    }

    /**
     * Get the value of a column in the current row as a java.sql.Time object.
     * 
     * @param columnName
     *            is the SQL name of the column
     * 
     * @return the column value; if the value is SQL NULL, the result is null
     * 
     * @throws java.sql.SQLException
     *             if a database-access error occurs.
     */
    public Time getTime(String columnName) throws java.sql.SQLException {
        return getTime(findColumn(columnName));
    }

    /**
     * Get the value of a column in the current row as a java.sql.Time object.
     * Use the calendar to construct an appropriate millisecond value for the
     * Time, if the underlying database doesn't store timezone information.
     * 
     * @param columnName
     *            is the SQL name of the column
     * @param cal
     *            the calendar to use in constructing the time
     * 
     * @return the column value; if the value is SQL NULL, the result is null
     * 
     * @exception SQLException
     *                if a database-access error occurs.
     */
    public java.sql.Time getTime(String columnName, Calendar cal) throws SQLException {
        return getTime(findColumn(columnName), cal);
    }

    private Time getTimeFromString(String timeAsString, Calendar targetCalendar, int columnIndex, TimeZone tz, boolean rollForward) throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            int hr = 0;
            int min = 0;
            int sec = 0;

            try {

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

                    return null;
                }

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

                timeAsString = timeAsString.trim();

                // truncate fractional part
                int dec = timeAsString.indexOf(".");
                if (dec > -1) {
                    timeAsString = timeAsString.substring(0, dec);
                }

                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")
                                    + columnIndex + "(" + this.fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
                    }

                    SQLWarning precisionLost = new SQLWarning(
                            Messages.getString("ResultSet.Precision_lost_converting_TIMESTAMP_to_Time_with_getTime()_on_column__261") + 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") + 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") + 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();

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

    /**
     * @param columnName
     * 
     * @throws java.sql.SQLException
     */
    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();

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

                // check for the fractional part
                int decimalIndex = timestampValue.indexOf(".");

                if (decimalIndex == length - 1) {
                    // if the dot is in last position
                    length--;

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

                        length = decimalIndex;
                    } else {
                        throw new IllegalArgumentException(); // re-thrown further down with a much better error message
                    }
                }

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

                        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
     */
    @Deprecated
    public InputStream getUnicodeStream(int columnIndex) throws SQLException {
        if (!this.isBinaryEncoded) {
            checkRowPos();

            return getBinaryStream(columnIndex);
        }

        return getNativeBinaryStream(columnIndex);
    }

    /**
     * @param columnName
     * 
     * @throws SQLException
     * 
     * @deprecated
     */
    @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());
        }
    }

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

    /**
     * The first warning reported by calls on this ResultSet is returned.
     * Subsequent ResultSet warnings will be chained to this
     * java.sql.SQLWarning.
     * 
     * <p>
     * The warning chain is automatically cleared each time a new row is read.
     * </p>
     * 
     * <p>
     * <B>Note:</B> This warning chain only covers warnings caused by ResultSet methods. Any warnings caused by statement methods (such as reading OUT
     * parameters) will be chained on the Statement object.
     * </p>
     * 
     * @return the first java.sql.SQLWarning or null;
     * 
     * @exception SQLException
     *                if a database access error occurs.
     */
    public java.sql.SQLWarning getWarnings() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            return this.warningChain;
        }
    }

    /**
     * JDBC 2.0 Insert the contents of the insert row into the result set and
     * the database. Must be on the insert row when this method is called.
     * 
     * @exception SQLException
     *                if a database-access error occurs, if called when not on
     *                the insert row, or if all non-nullable columns in the
     *                insert row have not been given a value
     * @throws NotUpdatable
     */
    public void insertRow() throws SQLException {
        throw new NotUpdatable();
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Determine if the cursor is after the last row in the result set.
     * </p>
     * 
     * @return true if after the last row, false otherwise. Returns false when
     *         the result set contains no rows.
     * 
     * @exception SQLException
     *                if a database-access error occurs.
     */
    public boolean isAfterLast() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            boolean b = this.rowData.isAfterLast();

            return b;
        }
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Determine if the cursor is before the first row in the result set.
     * </p>
     * 
     * @return true if before the first row, false otherwise. Returns false when
     *         the result set contains no rows.
     * 
     * @exception SQLException
     *                if a database-access error occurs.
     */
    public boolean isBeforeFirst() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            return this.rowData.isBeforeFirst();
        }
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Determine if the cursor is on the first row of the result set.
     * </p>
     * 
     * @return true if on the first row, false otherwise.
     * 
     * @exception SQLException
     *                if a database-access error occurs.
     */
    public boolean isFirst() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            return this.rowData.isFirst();
        }
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Determine if the cursor is on the last row of the result set. Note: Calling isLast() may be expensive since the JDBC driver might need to fetch ahead one
     * row in order to determine whether the current row is the last row in the result set.
     * </p>
     * 
     * @return true if on the last row, false otherwise.
     * 
     * @exception SQLException
     *                if a database-access error occurs.
     */
    public boolean isLast() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            return this.rowData.isLast();
        }
    }

    /**
     * @param string
     * @param mysqlType
     * @param s
     */
    private void issueConversionViaParsingWarning(String methodName, int columnIndex, Object value, Field fieldInfo, int[] typesWithNoParseConversion)
            throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            StringBuilder originalQueryBuf = new StringBuilder();

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

            StringBuilder convertibleTypesBuf = new StringBuilder();

            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(), this.connection.getYearIsDateType()),
                    MysqlDefs.typeToName(fieldInfo.getMysqlType()), convertibleTypesBuf.toString() });

            this.eventSink
                    .consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", (this.owningStatement == null) ? "N/A" : this.owningStatement.currentCatalog,
                            this.connectionId, (this.owningStatement == null) ? (-1) : this.owningStatement.getId(), this.resultId, System.currentTimeMillis(),
                            0, Constants.MILLIS_I18N, null, this.pointOfOrigin, message));
        }
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Moves to the last row in the result set.
     * </p>
     * 
     * @return true if on a valid row, false if no rows in the result set.
     * 
     * @exception SQLException
     *                if a database-access error occurs, or result set type is
     *                TYPE_FORWARD_ONLY.
     */
    public boolean last() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {

            boolean b = true;

            if (this.rowData.size() == 0) {
                b = false;
            } else {

                if (this.onInsertRow) {
                    this.onInsertRow = false;
                }

                if (this.doingUpdates) {
                    this.doingUpdates = false;
                }

                if (this.thisRow != null) {
                    this.thisRow.closeOpenStreams();
                }

                this.rowData.beforeLast();
                this.thisRow = this.rowData.next();
            }

            setRowPositionValidity();

            return b;
        }
    }

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

    /**
     * JDBC 2.0 Move the cursor to the remembered cursor position, usually the
     * current row. Has no effect unless the cursor is on the insert row.
     * 
     * @exception SQLException
     *                if a database-access error occurs, or the result set is
     *                not updatable
     * @throws NotUpdatable
     */
    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
     */
    public void moveToInsertRow() throws SQLException {
        throw new NotUpdatable();
    }

    /**
     * A ResultSet is initially positioned before its first row, the first call
     * to next makes the first row the current row; the second call makes the
     * second row the current row, etc.
     * 
     * <p>
     * If an input stream from the previous row is open, it is implicitly closed. The ResultSet's warning chain is cleared when a new row is read
     * </p>
     * 
     * @return true if the new current is valid; false if there are no more rows
     * 
     * @exception SQLException
     *                if a database access error occurs
     */
    public boolean next() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {

            if (this.onInsertRow) {
                this.onInsertRow = false;
            }

            if (this.doingUpdates) {
                this.doingUpdates = false;
            }

            boolean b;

            if (!reallyResult()) {
                throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), SQLError.SQL_STATE_GENERAL_ERROR,
                        getExceptionInterceptor());
            }

            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].getEncoding(), 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].getEncoding(), this.connection);
            }

            double valueAsDouble = Double.parseDouble(valueAsString == null ? StringUtils.toString(valueAsBytes) : valueAsString);

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

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

        double valueAsDouble = Double.parseDouble(val);

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

        return (short) valueAsDouble;
    }

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

        short shortValue = 0;

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

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

            valueAsString = valueAsString.trim();

            shortValue = Short.parseShort(valueAsString);
        }

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

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

        return shortValue;
    }

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

    /**
     * The prev method is not part of JDBC, but because of the architecture of
     * this driver it is possible to move both forward and backward within the
     * result set.
     * 
     * <p>
     * If an input stream from the previous row is open, it is implicitly closed. The ResultSet's warning chain is cleared when a new row is read
     * </p>
     * 
     * @return true if the new current is valid; false if there are no more rows
     * 
     * @exception java.sql.SQLException
     *                if a database access error occurs
     */
    public boolean prev() throws java.sql.SQLException {
        synchronized (checkClosed().getConnectionMutex()) {

            int rowIndex = this.rowData.getCurrentRowNumber();

            if (this.thisRow != null) {
                this.thisRow.closeOpenStreams();
            }

            boolean b = true;

            if ((rowIndex - 1) >= 0) {
                rowIndex--;
                this.rowData.setCurrentRow(rowIndex);
                this.thisRow = this.rowData.getAt(rowIndex);

                b = true;
            } else if ((rowIndex - 1) == -1) {
                rowIndex--;
                this.rowData.setCurrentRow(rowIndex);
                this.thisRow = null;

                b = false;
            } else {
                b = false;
            }

            setRowPositionValidity();

            return b;
        }
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Moves to the previous row in the result set.
     * </p>
     * 
     * <p>
     * Note: previous() is not the same as relative(-1) since it makes sense to call previous() when there is no current row.
     * </p>
     * 
     * @return true if on a valid row, false if off the result set.
     * 
     * @exception SQLException
     *                if a database-access error occurs, or result set type is
     *                TYPE_FORWAR_DONLY.
     */
    public boolean previous() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            if (this.onInsertRow) {
                this.onInsertRow = false;
            }

            if (this.doingUpdates) {
                this.doingUpdates = false;
            }

            return prev();
        }
    }

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

        if (locallyScopedConn == null) {
            return; // already closed
        }

        synchronized (locallyScopedConn.getConnectionMutex()) {

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

            try {
                if (this.useUsageAdvisor) {

                    // Report on result set closed by driver instead of application

                    if (!calledExplicitly) {
                        this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN, "",
                                (this.owningStatement == null) ? "N/A" : this.owningStatement.currentCatalog, this.connectionId,
                                (this.owningStatement == null) ? (-1) : this.owningStatement.getId(), this.resultId, System.currentTimeMillis(), 0,
                                Constants.MILLIS_I18N, null, this.pointOfOrigin, Messages.getString("ResultSet.ResultSet_implicitly_closed_by_driver")));
                    }

                    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,
                                    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,
                                    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",
                                            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()) {
                        StringBuilder buf = new StringBuilder(Messages.getString("ResultSet.The_following_columns_were_never_referenced"));

                        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.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.fastDefaultCal = null;
                this.fastClientCal = null;
                this.connection = null;

                this.isClosed = true;

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

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

    public boolean reallyResult() {
        if (this.rowData != null) {
            return true;
        }

        return this.reallyResult;
    }

    /**
     * JDBC 2.0 Refresh the value of the current row with its current value in
     * the database. Cannot be called when on the insert row. The refreshRow()
     * method provides a way for an application to explicitly tell the JDBC
     * driver to refetch a row(s) from the database. An application may want to
     * call refreshRow() when caching or prefetching is being done by the JDBC
     * driver to fetch the latest value of a row from the database. The JDBC
     * driver may actually refresh multiple rows at once if the fetch size is
     * greater than one. All values are refetched subject to the transaction
     * isolation level and cursor sensitivity. If refreshRow() is called after
     * calling updateXXX(), but before calling updateRow() then the updates made
     * to the row are lost. Calling refreshRow() frequently will likely slow
     * performance.
     * 
     * @exception SQLException
     *                if a database-access error occurs, or if called when on
     *                the insert row.
     * @throws NotUpdatable
     */
    public void refreshRow() throws SQLException {
        throw new NotUpdatable();
    }

    /**
     * JDBC 2.0
     * 
     * <p>
     * Moves a relative number of rows, either positive or negative. Attempting to move beyond the first/last row in the result set positions the cursor
     * before/after the the first/last row. Calling relative(0) is valid, but does not change the cursor position.
     * </p>
     * 
     * <p>
     * Note: Calling relative(1) is different than calling next() since is makes sense to call next() when there is no current row, for example, when the cursor
     * is positioned before the first row or after the last row of the result set.
     * </p>
     * 
     * @param rows
     *            the number of relative rows to move the cursor.
     * 
     * @return true if on a row, false otherwise.
     * 
     * @throws SQLException
     *             if a database-access error occurs, or there is no current
     *             row, or result set type is TYPE_FORWARD_ONLY.
     */
    public boolean relative(int rows) throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {

            if (this.rowData.size() == 0) {
                setRowPositionValidity();

                return false;
            }

            if (this.thisRow != null) {
                this.thisRow.closeOpenStreams();
            }

            this.rowData.moveRowRelative(rows);
            this.thisRow = this.rowData.getAt(this.rowData.getCurrentRowNumber());

            setRowPositionValidity();

            return (!this.rowData.isAfterLast() && !this.rowData.isBeforeFirst());
        }
    }

    /**
     * JDBC 2.0 Determine if this row has been deleted. A deleted row may leave
     * a visible "hole" in a result set. This method can be used to detect holes
     * in a result set. The value returned depends on whether or not the result
     * set can detect deletions.
     * 
     * @return true if deleted and deletes are detected
     * 
     * @exception SQLException
     *                if a database-access error occurs
     * @throws NotImplemented
     * 
     * @see DatabaseMetaData#deletesAreDetected
     */
    public boolean rowDeleted() throws SQLException {
        throw SQLError.createSQLFeatureNotSupportedException();
    }

    /**
     * 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
     * 
     * @see DatabaseMetaData#insertsAreDetected
     */
    public boolean rowInserted() throws SQLException {
        throw SQLError.createSQLFeatureNotSupportedException();
    }

    /**
     * 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
     * 
     * @see DatabaseMetaData#updatesAreDetected
     */
    public boolean rowUpdated() throws SQLException {
        throw SQLError.createSQLFeatureNotSupportedException();
    }

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

    /**
     * JDBC 2.0 Give a hint as to the direction in which the rows in this result
     * set will be processed. The initial value is determined by the statement
     * that produced the result set. The fetch direction may be changed at any
     * time.
     * 
     * @param direction
     *            the direction to fetch rows in.
     * 
     * @exception SQLException
     *                if a database-access error occurs, or the result set type
     *                is TYPE_FORWARD_ONLY and direction is not FETCH_FORWARD.
     *                MM.MySQL actually ignores this, because it has the whole
     *                result set anyway, so the direction is immaterial.
     */
    public void setFetchDirection(int direction) throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            if ((direction != FETCH_FORWARD) && (direction != FETCH_REVERSE) && (direction != FETCH_UNKNOWN)) {
                throw SQLError.createSQLException(Messages.getString("ResultSet.Illegal_value_for_fetch_direction_64"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
                        getExceptionInterceptor());
            }

            this.fetchDirection = direction;
        }
    }

    /**
     * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that should
     * be fetched from the database when more rows are needed for this result
     * set. If the fetch size specified is zero, then the JDBC driver ignores
     * the value, and is free to make its own best guess as to what the fetch
     * size should be. The default value is set by the statement that creates
     * the result set. The fetch size may be changed at any time.
     * 
     * @param rows
     *            the number of rows to fetch
     * 
     * @exception SQLException
     *                if a database-access error occurs, or the condition 0 lteq
     *                rows lteq this.getMaxRows() is not satisfied. Currently
     *                ignored by this driver.
     */
    public void setFetchSize(int rows) throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            if (rows < 0) { /* || rows > getMaxRows() */
                throw SQLError.createSQLException(Messages.getString("ResultSet.Value_must_be_between_0_and_getMaxRows()_66"),
                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
            }

            this.fetchSize = rows;
        }
    }

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

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

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

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

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

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

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

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

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

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

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

    @Override
    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.createSQLFeatureNotSupportedException();
    }

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

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

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

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

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