Logo Search packages:      
Sourcecode: mysql-connector-java version File versions

Statement.java

/*
      Copyright (C) 2002-2004 MySQL AB

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

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

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

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



 */
package com.mysql.jdbc;

import com.mysql.jdbc.profiler.ProfileEventSink;
import com.mysql.jdbc.profiler.ProfilerEvent;
import com.mysql.jdbc.util.LRUCache;

import java.sql.DataTruncation;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Types;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;


/**
 * A Statement object is used for executing a static SQL statement and
 * obtaining the results produced by it.
 *
 * <p>
 * Only one ResultSet per Statement can be open at any point in time.
 * Therefore, if the reading of one ResultSet is interleaved with the reading
 * of another, each must have been generated by different Statements.  All
 * statement execute methods implicitly close a statement's current ResultSet
 * if an open one exists.
 * </p>
 *
 * @author Mark Matthews
 * @version $Id: Statement.java,v 1.20.4.47 2005/02/16 21:44:31 mmatthews Exp $
 *
 * @see java.sql.Statement
 * @see ResultSet
 */
00061 public class Statement implements java.sql.Statement {
    /** Used to generate IDs when profiling. */
00063     protected static int statementCounter = 1;
    public final static byte USES_VARIABLES_TRUE = 1;
    public final static byte USES_VARIABLES_FALSE = 0;
    public final static byte USES_VARIABLES_UNKNOWN = -1;

    /** The connection that created us */
00069     protected Connection connection = null;

    /** Cache of ResultSet metadata */
00072     protected LRUCache resultSetMetadataCache;

    /** Holds batched commands */
00075     protected List batchedArgs;

    /** List of currently-open ResultSets */
00078     protected List openResults = new ArrayList();

    /** If we're profiling, where should events go to? */
00081     protected ProfileEventSink eventSink = null;

    /** The current results */
00084     protected ResultSet results = null;

    /** The warnings chain. */
00087     protected SQLWarning warningChain = null;

    /** The character converter to use (if available) */
00090     protected SingleByteCharsetConverter charConverter = null;

    /** The character encoding to use (if available) */
00093     protected String charEncoding = null;

    /** The catalog in use */
00096     protected String currentCatalog = null;

    /**
     * Where this statement was created, only used if profileSql or
     * useUsageAdvisor set to true.
     */
00102     protected Throwable pointOfOrigin;

    /** Should we process escape codes? */
00105     protected boolean doEscapeProcessing = true;

    /** Has this statement been closed? */
00108     protected boolean isClosed = false;

    /** Has someone changed this for this statement? */
00111     protected boolean maxRowsChanged = false;

    /** Are we in pedantic mode? */
00114     protected boolean pedantic = false;

    /** Should we profile? */
00117     protected boolean profileSQL = false;

    /** Should we use the usage advisor? */
00120     protected boolean useUsageAdvisor = false;

    /** The max field size for this statement */
00123     protected int maxFieldSize = MysqlIO.getMaxBuf();

    /**
     * The maximum number of rows to return for this statement (-1 means _all_
     * rows)
     */
00129     protected int maxRows = -1;

    /** The concurrency for this result set (updatable or not) */
00132     protected int resultSetConcurrency = 0;

    /** The type of this result set (scroll sensitive or in-sensitive) */
00135     protected int resultSetType = 0;

    /** Used to identify this statement when profiling. */
00138     protected int statementId;

    /** The timeout for a query */
00141     protected int timeout = 0;

    /** The auto_increment value for the last insert */
00144     protected long lastInsertId = -1;

    /** The update count for this statement */
00147     protected long updateCount = -1;

    /** The number of rows to fetch at a time (currently ignored) */
00150     private int fetchSize = 0;

    /**
     * Constructor for a Statement.
     *
     * @param c the Connection instantation that creates us
     * @param catalog the database name in use when we were created
     *
     * @throws SQLException if an error occurs.
     */
00160     public Statement(Connection c, String catalog) throws SQLException {
        if ((c == null) || c.isClosed()) {
            throw new SQLException(Messages.getString("Statement.0"), //$NON-NLS-1$
                SQLError.SQL_STATE_CONNECTION_NOT_OPEN); //$NON-NLS-1$ //$NON-NLS-2$
        }

        this.connection = c;
        this.currentCatalog = catalog;
        this.pedantic = this.connection.getPedantic();
        
        if (!this.connection.getDontTrackOpenResources()) {
            this.connection.registerStatement(this);
        }

        //
        // Adjust, if we know it
        //
        if (this.connection != null) {
            this.maxFieldSize = this.connection.getMaxAllowedPacket();
        }

        if (this.connection.getUseUnicode()) {
            this.charEncoding = this.connection.getEncoding();

            this.charConverter = this.connection.getCharsetConverter(this.charEncoding);
        }

        if (this.connection.getProfileSql() ||
                this.connection.getUseUsageAdvisor()) {
            this.statementId = statementCounter++;
            this.pointOfOrigin = new Throwable();
            this.profileSQL = this.connection.getProfileSql();
            this.useUsageAdvisor = this.connection.getUseUsageAdvisor();
            this.eventSink = ProfileEventSink.getInstance(this.connection);
        }

        int maxRowsConn = this.connection.getMaxRows();

        if (maxRowsConn != -1) {
            setMaxRows(maxRowsConn);
        }
    }

    /**
     * JDBC 2.0 Return the Connection that produced the Statement.
     *
     * @return the Connection that produced the Statement
     *
     * @throws SQLException if an error occurs
     */
00210     public synchronized java.sql.Connection getConnection()
        throws SQLException {
        return this.connection;
    }

    /**
     * setCursorName defines the SQL cursor name that will be used by
     * subsequent execute methods.  This name can then be used in SQL
     * positioned update/delete statements to identify the current row in the
     * ResultSet generated by this statement.  If a database doesn't support
     * positioned update/delete, this method is a no-op.
     *
     * <p>
     * <b>Note:</b> This MySQL driver does not support cursors.
     * </p>
     *
     * @param name the new cursor name
     *
     * @exception SQLException if a database access error occurs
     */
00230     public void setCursorName(String name) throws SQLException {
        // No-op
    }

    /**
     * If escape scanning is on (the default), the driver will do escape
     * substitution before sending the SQL to the database.
     *
     * @param enable true to enable; false to disable
     *
     * @exception SQLException if a database access error occurs
     */
00242     public synchronized void setEscapeProcessing(boolean enable)
        throws SQLException {
        this.doEscapeProcessing = enable;
    }

    //--------------------------JDBC 2.0-----------------------------

    /**
     * JDBC 2.0 Give a hint as to the direction in which the rows in a result
     * set will be processed. The hint applies only to result sets created
     * using this Statement object.  The default value is
     * ResultSet.FETCH_FORWARD.
     *
     * @param direction the initial direction for processing rows
     *
     * @exception SQLException if a database-access error occurs or direction
     *            is not one of ResultSet.FETCH_FORWARD,
     *            ResultSet.FETCH_REVERSE, or ResultSet.FETCH_UNKNOWN
     */
00261     public void setFetchDirection(int direction) throws SQLException {
        switch (direction) {
        case java.sql.ResultSet.FETCH_FORWARD:
        case java.sql.ResultSet.FETCH_REVERSE:
        case java.sql.ResultSet.FETCH_UNKNOWN:
            break;

        default:
            throw new SQLException(Messages.getString("Statement.5"), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
        }
    }

    /**
     * JDBC 2.0 Determine the fetch direction.
     *
     * @return the default fetch direction
     *
     * @exception SQLException if a database-access error occurs
     */
00281     public int getFetchDirection() throws SQLException {
        return java.sql.ResultSet.FETCH_FORWARD;
    }

    /**
     * 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.  The
     * number of rows specified only affects result sets created using this
     * statement. If the value specified is zero, then the hint is ignored.
     * The default value is zero.
     *
     * @param rows the number of rows to fetch
     *
     * @exception SQLException if a database-access error occurs, or the
     *            condition 0 &lt;= rows &lt;= this.getMaxRows() is not
     *            satisfied.
     */
00298     public synchronized void setFetchSize(int rows) throws SQLException {
        if (((rows < 0) && (rows != Integer.MIN_VALUE)) ||
                ((this.maxRows != 0) && (this.maxRows != -1) &&
                (rows > this.getMaxRows()))) {
            throw new SQLException(Messages.getString("Statement.7"), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
        }

        this.fetchSize = rows;
    }

    /**
     * JDBC 2.0 Determine the default fetch size.
     *
     * @return the number of rows to fetch at a time
     *
     * @throws SQLException if an error occurs
     */
00316     public synchronized int getFetchSize() throws SQLException {
        return this.fetchSize;
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     *
     * @throws SQLException DOCUMENT ME!
     */
00327     public synchronized java.sql.ResultSet getGeneratedKeys()
        throws SQLException {
        return getGeneratedKeysInternal();
    }

    /*
     * Needed because there's no concept of super.super to get to this
     * implementation from ServerPreparedStatement when dealing with
     * batched updates.
     */
    protected synchronized java.sql.ResultSet getGeneratedKeysInternal()
        throws SQLException {
        Field[] fields = new Field[1];
        fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$

        ArrayList rowSet = new ArrayList();

        long beginAt = getLastInsertID();
        int numKeys = getUpdateCount();

        String serverInfo = this.results.getServerInfo();

        // 
        // Only parse server info messages for 'REPLACE'
        // queries
        //
        if ((numKeys > 0) && (this.results.getFirstCharOfQuery() == 'R') &&
                (serverInfo != null) && (serverInfo.length() > 0)) {
            numKeys = getRecordCountFromInfo(serverInfo);
        }

        if ((beginAt > 0) && (numKeys > 0)) {
            for (int i = 0; i < numKeys; i++) {
                byte[][] row = new byte[1][];
                row[0] = Long.toString(beginAt++).getBytes();
                rowSet.add(row);
            }
        }

        return new com.mysql.jdbc.ResultSet(this.currentCatalog, fields,
            new RowDataStatic(rowSet), this.connection, this);
    }

    /**
     * getLastInsertID returns the value of the auto_incremented key after an
     * executeQuery() or excute() call.
     *
     * <p>
     * This gets around the un-threadsafe behavior of "select LAST_INSERT_ID()"
     * which is tied to the Connection that created this Statement, and
     * therefore could have had many INSERTS performed before one gets a
     * chance to call "select LAST_INSERT_ID()".
     * </p>
     *
     * @return the last update ID.
     */
00383     public synchronized long getLastInsertID() {
        return this.lastInsertId;
    }

    /**
     * getLongUpdateCount returns the current result as an update count, if the
     * result is a ResultSet or there are no more results, -1 is returned.  It
     * should only be called once per result.
     *
     * <p>
     * This method returns longs as MySQL server versions newer than 3.22.4
     * return 64-bit values for update counts
     * </p>
     *
     * @return the current update count.
     */
00399     public synchronized long getLongUpdateCount() {
        if (this.results == null) {
            return -1;
        }

        if (this.results.reallyResult()) {
            return -1;
        }

        return this.updateCount;
    }

    /**
     * Sets the maxFieldSize
     *
     * @param max the new max column size limit; zero means unlimited
     *
     * @exception SQLException if size exceeds buffer size
     */
00418     public synchronized void setMaxFieldSize(int max) throws SQLException {
        if (max < 0) {
            throw new SQLException(Messages.getString("Statement.11"), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
        }

        int maxBuf = (this.connection != null)
            ? this.connection.getMaxAllowedPacket() : MysqlIO.getMaxBuf();

        if (max > maxBuf) {
            throw new SQLException(Messages.getString("Statement.13", //$NON-NLS-1$
                    new Object[] { new Long(maxBuf) }), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
        }

        this.maxFieldSize = max;
    }

    /**
     * The maxFieldSize limit (in bytes) is the maximum amount of data returned
     * for any column value; it only applies to BINARY, VARBINARY,
     * LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR columns.  If the limit is
     * exceeded, the excess data is silently discarded.
     *
     * @return the current max column size limit; zero means unlimited
     *
     * @exception SQLException if a database access error occurs
     */
00446     public synchronized int getMaxFieldSize() throws SQLException {
        return this.maxFieldSize;
    }

    /**
     * Set the maximum number of rows
     *
     * @param max the new max rows limit; zero means unlimited
     *
     * @exception SQLException if a database access error occurs
     *
     * @see getMaxRows
     */
00459     public synchronized void setMaxRows(int max) throws SQLException {
        if ((max > MysqlDefs.MAX_ROWS) || (max < 0)) {
            throw new SQLException(Messages.getString("Statement.15") +
                max //$NON-NLS-1$
                 +" > " //$NON-NLS-1$ //$NON-NLS-2$
                 +MysqlDefs.MAX_ROWS + ".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
        }

        if (max == 0) {
            max = -1;
        }

        this.maxRows = max;
        this.maxRowsChanged = true;

        if (this.maxRows == -1) {
            this.connection.unsetMaxRows(this);
            this.maxRowsChanged = false;
        } else {
            // Most people don't use setMaxRows()
            // so don't penalize them
            // with the extra query it takes
            // to do it efficiently unless we need
            // to.
            this.connection.maxRowsChanged(this);
        }
    }

    /**
     * The maxRows limit is set to limit the number of rows that any ResultSet
     * can contain.  If the limit is exceeded, the excess rows are silently
     * dropped.
     *
     * @return the current maximum row limit; zero means unlimited
     *
     * @exception SQLException if a database access error occurs
     */
00496     public synchronized int getMaxRows() throws SQLException {
        if (this.maxRows <= 0) {
            return 0;
        }

        return this.maxRows;
    }

    /**
     * getMoreResults moves to a Statement's next result.  If it returns true,
     * this result is a ResulSet.
     *
     * @return true if the next ResultSet is valid
     *
     * @exception SQLException if a database access error occurs
     */
00512     public boolean getMoreResults() throws SQLException {
        return getMoreResults(CLOSE_CURRENT_RESULT);
    }

    /**
     * @see Statement#getMoreResults(int)
     */
00519     public synchronized boolean getMoreResults(int current)
        throws SQLException {
      ResultSet nextResultSet = this.results.getNextResultSet();
      
        switch (current) {
        case java.sql.Statement.CLOSE_CURRENT_RESULT:

            if (this.results != null) {
                this.results.close();
            }

            break;

        case java.sql.Statement.CLOSE_ALL_RESULTS:

            if (this.results != null) {
                this.results.close();
            }

            closeAllOpenResults();

            break;

        case java.sql.Statement.KEEP_CURRENT_RESULT:
            if (!this.connection.getDontTrackOpenResources()) {
                  this.openResults.add(this.results);
            }

            break;

        default:
            throw new SQLException(Messages.getString("Statement.19"), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
        }

        this.results = nextResultSet;

        if (this.results == null) {
            this.updateCount = -1;
            this.lastInsertId = -1;
        } else if (this.results.reallyResult()) {
            this.updateCount = -1;
            this.lastInsertId = -1;
        } else {
            this.updateCount = this.results.getUpdateCount();
            this.lastInsertId = this.results.getUpdateID();
        }

        return ((this.results != null) && this.results.reallyResult()) ? true
                                                                       : false;
    }

    /**
     * Sets the queryTimeout limit
     *
     * @param seconds - the new query timeout limit in seconds
     *
     * @exception SQLException if a database access error occurs
     */
00578     public void setQueryTimeout(int seconds) throws SQLException {
        if (seconds < 0) {
            throw new SQLException(Messages.getString("Statement.21"), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
        }

        this.timeout = seconds;
    }

    /**
     * The queryTimeout limit is the number of seconds the driver will wait for
     * a Statement to execute.  If the limit is exceeded, a SQLException is
     * thrown.
     *
     * @return the current query timeout limit in seconds; 0 = unlimited
     *
     * @exception SQLException if a database access error occurs
     */
00596     public int getQueryTimeout() throws SQLException {
        return this.timeout;
    }

    /**
     * getResultSet returns the current result as a ResultSet.  It should only
     * be called once per result.
     *
     * @return the current result set; null if there are no more
     *
     * @exception SQLException if a database access error occurs (why?)
     */
00608     public synchronized java.sql.ResultSet getResultSet()
        throws SQLException {
        return ((this.results != null) && this.results.reallyResult())
        ? (java.sql.ResultSet) this.results : null;
    }

    /**
     * JDBC 2.0 Determine the result set concurrency.
     *
     * @return CONCUR_UPDATABLE or CONCUR_READONLY
     *
     * @throws SQLException if an error occurs
     */
00621     public synchronized int getResultSetConcurrency() throws SQLException {
        return this.resultSetConcurrency;
    }

    /**
     * @see Statement#getResultSetHoldability()
     */
00628     public int getResultSetHoldability() throws SQLException {
        return java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
    }

    /**
     * JDBC 2.0 Determine the result set type.
     *
     * @return the ResultSet type (SCROLL_SENSITIVE or SCROLL_INSENSITIVE)
     *
     * @throws SQLException if an error occurs.
     */
00639     public synchronized int getResultSetType() throws SQLException {
        return this.resultSetType;
    }

    /**
     * getUpdateCount returns the current result as an update count, if the
     * result is a ResultSet or there are no more results, -1 is returned.  It
     * should only be called once per result.
     *
     * @return the current result as an update count.
     *
     * @exception SQLException if a database access error occurs
     */
00652     public synchronized int getUpdateCount() throws SQLException {
        if (this.results == null) {
            return -1;
        }

        if (this.results.reallyResult()) {
            return -1;
        }

        int truncatedUpdateCount = 0;

        if (this.results.getUpdateCount() > Integer.MAX_VALUE) {
            truncatedUpdateCount = Integer.MAX_VALUE;
        } else {
            truncatedUpdateCount = (int) this.results.getUpdateCount();
        }

        return truncatedUpdateCount;
    }

    /**
     * The first warning reported by calls on this Statement is returned.  A
     * Statement's execute methods clear its java.sql.SQLWarning chain.
     * Subsequent Statement warnings will be chained to this
     * java.sql.SQLWarning.
     *
     * <p>
     * The Warning chain is automatically cleared each time a statement is
     * (re)executed.
     * </p>
     *
     * <p>
     * <B>Note:</B>  If you are processing a ResultSet then any warnings
     * associated with ResultSet reads will be chained on the ResultSet
     * object.
     * </p>
     *
     * @return the first java.sql.SQLWarning or null
     *
     * @exception SQLException if a database access error occurs
     */
00693     public synchronized java.sql.SQLWarning getWarnings()
        throws SQLException {
        if (this.connection.versionMeetsMinimum(4, 1, 0)) {
            SQLWarning pendingWarningsFromServer = SQLError.convertShowWarningsToSQLWarnings(this.connection);
            
            if (this.warningChain != null) {
                  this.warningChain.setNextWarning(pendingWarningsFromServer);
            } else {
                  this.warningChain = pendingWarningsFromServer;
            }

            return this.warningChain;
        }

        return this.warningChain;
    }

    /**
     * DOCUMENT ME!
     *
     * @param sql DOCUMENT ME!
     *
     * @throws SQLException DOCUMENT ME!
     */
00717     public synchronized void addBatch(String sql) throws SQLException {
        if (this.batchedArgs == null) {
            this.batchedArgs = new ArrayList();
        }

        if (sql != null) {
            this.batchedArgs.add(sql);
        }
    }

    /**
     * Cancel can be used by one thread to cancel a statement that is being
     * executed by another thread.  However this driver is synchronous, so
     * this really has no meaning - we define it as a no-op (i.e. you can't
     * cancel, but there is no error if you try.)
     *
     * @exception SQLException only because thats the spec.
     */
00735     public void cancel() throws SQLException {
        // No-op
    }

    /**
     * JDBC 2.0 Make the set of commands in the current batch empty. This
     * method is optional.
     *
     * @exception SQLException if a database-access error occurs, or the driver
     *            does not support batch statements
     */
00746     public synchronized void clearBatch() throws SQLException {
        if (this.batchedArgs != null) {
            this.batchedArgs.clear();
        }
    }

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

    /**
     * In many cases, it is desirable to immediately release a Statement's
     * 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 Statement is automatically closed when it is garbage
     * collected.  When a Statement is closed, its current ResultSet, if one
     * exists, is also closed.
     * </p>
     *
     * @exception SQLException if a database access error occurs
     */
00776     public void close() throws SQLException {
        realClose(true);
    }

    /**
     * Workaround for containers that 'check' for sane values
     * of Statement.setFetchSize().
     * 
     * @throws SQLException
     */
00786     public void enableStreamingResults() throws SQLException {
      setFetchSize(Integer.MIN_VALUE);
      setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
    }
    
    /**
     * Execute a SQL statement that may return multiple results. We don't have
     * to worry about this since we do not support multiple ResultSets.   You
     * can use getResultSet or getUpdateCount to retrieve the result.
     *
     * @param sql any SQL statement
     *
     * @return true if the next result is a ResulSet, false if it is an update
     *         count or there are no more results
     *
     * @exception SQLException if a database access error occurs
     */
00803     public synchronized boolean execute(String sql) throws SQLException {
        checkNullOrEmptyQuery(sql);

        checkClosed();

        char firstNonWsChar = StringUtils.firstNonWsCharUc(sql);

        boolean isSelect = true;

        if (firstNonWsChar != 'S') {
            isSelect = false;

            if (this.connection.isReadOnly()) {
                throw new SQLException(Messages.getString("Statement.27") //$NON-NLS-1$
                     +Messages.getString("Statement.28"), //$NON-NLS-1$
                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
            }
        }

        if (this.doEscapeProcessing) {
            Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, 
                        this.connection.serverSupportsConvertFn());
            
            if (escapedSqlResult instanceof String) {
                  sql = (String)escapedSqlResult;
            } else {
                  sql = ((EscapeProcessorResult)escapedSqlResult).escapedSql;
            }
        }

        if (this.results != null) {
            if (!this.connection.getHoldResultsOpenOverStatementClose()) {
                  this.results.realClose(false);
            }
        }

        CachedResultSetMetaData cachedMetaData = null;

        ResultSet rs = null;

        // If there isn't a limit clause in the SQL
        // then limit the number of rows to return in
        // an efficient manner. Only do this if
        // setMaxRows() hasn't been used on any Statements
        // generated from the current Connection (saves
        // a query, and network traffic).
        synchronized (this.connection.getMutex()) {
            String oldCatalog = null;

            if (!this.connection.getCatalog().equals(this.currentCatalog)) {
                oldCatalog = this.connection.getCatalog();
                this.connection.setCatalog(this.currentCatalog);
            }

            //
            // Check if we have cached metadata for this query...
            //
            if (this.connection.getCacheResultSetMetadata()) {
                cachedMetaData = getCachedMetaData(sql);
            }

            //
            // Only apply max_rows to selects
            //
            if (this.connection.useMaxRows()) {
                int rowLimit = -1;

                if (isSelect) {
                    if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
                        rowLimit = this.maxRows;
                    } else {
                        if (this.maxRows <= 0) {
                            this.connection.execSQL(this,
                                "SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, //$NON-NLS-1$
                                null, java.sql.ResultSet.TYPE_FORWARD_ONLY,
                                java.sql.ResultSet.CONCUR_READ_ONLY, false,
                                false, this.currentCatalog, true); //$NON-NLS-1$
                        } else {
                            this.connection.execSQL(this,
                                "SET OPTION SQL_SELECT_LIMIT=" + this.maxRows, //$NON-NLS-1$
                                -1, null, java.sql.ResultSet.TYPE_FORWARD_ONLY,
                                java.sql.ResultSet.CONCUR_READ_ONLY, false,
                                false, this.currentCatalog, true); //$NON-NLS-1$
                        }
                    }
                } else {
                    this.connection.execSQL(this,
                        "SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, //$NON-NLS-1$
                        java.sql.ResultSet.TYPE_FORWARD_ONLY,
                        java.sql.ResultSet.CONCUR_READ_ONLY, false, false,
                        this.currentCatalog, true); //$NON-NLS-1$
                }

                // Finally, execute the query
                rs = this.connection.execSQL(this, sql, rowLimit, null,
                        this.resultSetType, this.resultSetConcurrency,
                        createStreamingResultSet(), isSelect,
                        this.currentCatalog, (cachedMetaData == null));
            } else {
                rs = this.connection.execSQL(this, sql, -1, null,
                        this.resultSetType, this.resultSetConcurrency,
                        createStreamingResultSet(), isSelect,
                        this.currentCatalog, (cachedMetaData == null));
            }

            if (oldCatalog != null) {
                this.connection.setCatalog(oldCatalog);
            }
        }

        this.lastInsertId = rs.getUpdateID();

        if (rs != null) {
            this.results = rs;

            rs.setFirstCharOfQuery(firstNonWsChar);

            if (rs.reallyResult()) {
                if (cachedMetaData != null) {
                    initializeResultsMetadataFromCache(sql, cachedMetaData,
                        this.results);
                } else {
                    if (this.connection.getCacheResultSetMetadata()) {
                        initializeResultsMetadataFromCache(sql,
                            null /* will be created */, this.results);
                    }
                }
            }
        }

        return ((rs != null) && rs.reallyResult());
    }

    /**
     * @see Statement#execute(String, int)
     */
00939     public boolean execute(String sql, int returnGeneratedKeys)
        throws SQLException {
        if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
            checkClosed();

            synchronized (this.connection.getMutex()) {
                // If this is a 'REPLACE' query, we need to be able to parse
                // the 'info' message returned from the server to determine
                // the actual number of keys generated.
                boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
                this.connection.setReadInfoMsgEnabled(true);

                try {
                    return execute(sql);
                } finally {
                    this.connection.setReadInfoMsgEnabled(readInfoMsgState);
                }
            }
        }

        return execute(sql);
    }

    /**
     * @see Statement#execute(String, int[])
     */
00965     public boolean execute(String sql, int[] generatedKeyIndices)
        throws SQLException {
        if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
            checkClosed();

            synchronized (this.connection.getMutex()) {
                // If this is a 'REPLACE' query, we need to be able to parse
                // the 'info' message returned from the server to determine
                // the actual number of keys generated.
                boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
                this.connection.setReadInfoMsgEnabled(true);

                try {
                    return execute(sql);
                } finally {
                    this.connection.setReadInfoMsgEnabled(readInfoMsgState);
                }
            }
        }

        return execute(sql);
    }

    /**
     * @see Statement#execute(String, String[])
     */
00991     public boolean execute(String sql, String[] generatedKeyNames)
        throws SQLException {
        if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
            checkClosed();

            synchronized (this.connection.getMutex()) {
                // If this is a 'REPLACE' query, we need to be able to parse
                // the 'info' message returned from the server to determine
                // the actual number of keys generated.
                boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
                this.connection.setReadInfoMsgEnabled(true);

                try {
                    return execute(sql);
                } finally {
                    this.connection.setReadInfoMsgEnabled(readInfoMsgState);
                }
            }
        }

        return execute(sql);
    }

    /**
     * JDBC 2.0 Submit a batch of commands to the database for execution. This
     * method is optional.
     *
     * @return an array of update counts containing one element for each
     *         command in the batch.  The array is ordered according to the
     *         order in which commands were inserted into the batch
     *
     * @exception SQLException if a database-access error occurs, or the driver
     *            does not support batch statements
     * @throws java.sql.BatchUpdateException DOCUMENT ME!
     */
01026     public synchronized int[] executeBatch() throws SQLException {
        if (this.connection.isReadOnly()) {
            throw new SQLException(Messages.getString("Statement.34") //$NON-NLS-1$
                 +Messages.getString("Statement.35"), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
        }

        if (this.results != null) {
            if (!this.connection.getHoldResultsOpenOverStatementClose()) {
                  this.results.realClose(false);
            }
        }

        try {
            int[] updateCounts = null;

            if (this.batchedArgs != null) {
                int nbrCommands = this.batchedArgs.size();
                updateCounts = new int[nbrCommands];

                for (int i = 0; i < nbrCommands; i++) {
                    updateCounts[i] = -3;
                }

                SQLException sqlEx = null;

                int commandIndex = 0;

                for (commandIndex = 0; commandIndex < nbrCommands;
                        commandIndex++) {
                    try {
                        updateCounts[commandIndex] = executeUpdate((String) this.batchedArgs.get(
                                    commandIndex));
                    } catch (SQLException ex) {
                        updateCounts[commandIndex] = EXECUTE_FAILED;

                        if (this.connection.getContinueBatchOnError()) {
                            sqlEx = ex;
                        } else {
                            int[] newUpdateCounts = new int[commandIndex];
                            System.arraycopy(updateCounts, 0, newUpdateCounts,
                                0, commandIndex);

                            throw new java.sql.BatchUpdateException(ex.getMessage(),
                                ex.getSQLState(), ex.getErrorCode(),
                                newUpdateCounts);
                        }
                    }
                }

                if (sqlEx != null) {
                    throw new java.sql.BatchUpdateException(sqlEx.getMessage(),
                        sqlEx.getSQLState(), sqlEx.getErrorCode(), updateCounts);
                }
            }

            return (updateCounts != null) ? updateCounts : new int[0];
        } finally {
            clearBatch();
        }
    }

    /**
     * Execute a SQL statement that retruns a single ResultSet
     *
     * @param sql typically a static SQL SELECT statement
     *
     * @return a ResulSet that contains the data produced by the query
     *
     * @exception SQLException if a database access error occurs
     */
01097     public synchronized java.sql.ResultSet executeQuery(String sql)
        throws SQLException {
        checkNullOrEmptyQuery(sql);

        checkClosed();

        if (this.doEscapeProcessing) {
            Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
                        this.connection.serverSupportsConvertFn());
            
            if (escapedSqlResult instanceof String) {
                  sql = (String)escapedSqlResult;
            } else {
                  sql = ((EscapeProcessorResult)escapedSqlResult).escapedSql;
            }
        }

        char firstStatementChar = StringUtils.firstNonWsCharUc(sql);

        checkForDml(sql, firstStatementChar);

        if (this.results != null) {
            if (!this.connection.getHoldResultsOpenOverStatementClose()) {
                  this.results.realClose(false);
            }
        }

        CachedResultSetMetaData cachedMetaData = null;

        // If there isn't a limit clause in the SQL
        // then limit the number of rows to return in
        // an efficient manner. Only do this if
        // setMaxRows() hasn't been used on any Statements
        // generated from the current Connection (saves
        // a query, and network traffic).
        synchronized (this.connection.getMutex()) {
            String oldCatalog = null;

            if (!this.connection.getCatalog().equals(this.currentCatalog)) {
                oldCatalog = this.connection.getCatalog();
                this.connection.setCatalog(this.currentCatalog);
            }

            //
            // Check if we have cached metadata for this query...
            //
            if (this.connection.getCacheResultSetMetadata()) {
                cachedMetaData = getCachedMetaData(sql);
            }

            if (this.connection.useMaxRows()) {
                // We need to execute this all together
                // So synchronize on the Connection's mutex (because
                // even queries going through there synchronize
                // on the connection
                if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
                    this.results = this.connection.execSQL(this, sql,
                            this.maxRows, null, this.resultSetType,
                            this.resultSetConcurrency,
                            createStreamingResultSet(), true,
                            this.currentCatalog, (cachedMetaData == null));
                } else {
                    if (this.maxRows <= 0) {
                        this.connection.execSQL(this,
                            "SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, //$NON-NLS-1$
                            java.sql.ResultSet.TYPE_FORWARD_ONLY,
                            java.sql.ResultSet.CONCUR_READ_ONLY, false, false,
                            this.currentCatalog, true); //$NON-NLS-1$
                    } else {
                        this.connection.execSQL(this,
                            "SET OPTION SQL_SELECT_LIMIT=" + this.maxRows, -1, //$NON-NLS-1$
                            null, java.sql.ResultSet.TYPE_FORWARD_ONLY,
                            java.sql.ResultSet.CONCUR_READ_ONLY, false, false,
                            this.currentCatalog, true); //$NON-NLS-1$
                    }

                    this.results = this.connection.execSQL(this, sql, -1, null,
                            this.resultSetType, this.resultSetConcurrency,
                            createStreamingResultSet(), true,
                            this.currentCatalog, (cachedMetaData == null));

                    if (oldCatalog != null) {
                        this.connection.setCatalog(oldCatalog);
                    }
                }
            } else {
                this.results = this.connection.execSQL(this, sql, -1, null,
                        this.resultSetType, this.resultSetConcurrency,
                        createStreamingResultSet(), true, this.currentCatalog,
                        (cachedMetaData == null));
            }

            if (oldCatalog != null) {
                this.connection.setCatalog(oldCatalog);
            }
        }

        this.lastInsertId = this.results.getUpdateID();

        /*
        if (!this.results.reallyResult()) {
            if (!this.connection.getAutoCommit()) {
                this.connection.rollback();
            }

            throw new SQLException(Messages.getString("Statement.40"), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
        }*/
        if (cachedMetaData != null) {
            initializeResultsMetadataFromCache(sql, cachedMetaData, this.results);
        } else {
            if (this.connection.getCacheResultSetMetadata()) {
                initializeResultsMetadataFromCache(sql,
                    null /* will be created */, this.results);
            }
        }

        return this.results;
    }

    /**
     * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition SQL
     * statements that return nothing such as SQL DDL statements can be
     * executed Any IDs generated for AUTO_INCREMENT fields can be retrieved
     * by casting this Statement to org.gjt.mm.mysql.Statement and calling the
     * getLastInsertID() method.
     *
     * @param sql a SQL statement
     *
     * @return either a row count, or 0 for SQL commands
     *
     * @exception SQLException if a database access error occurs
     */
01230     public synchronized int executeUpdate(String sql) throws SQLException {
        checkNullOrEmptyQuery(sql);

        checkClosed();

        if (this.connection.isReadOnly()) {
            throw new SQLException(Messages.getString("Statement.42") //$NON-NLS-1$
                 +Messages.getString("Statement.43"), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
        }

        if (StringUtils.startsWithIgnoreCaseAndWs(sql, "select")) { //$NON-NLS-1$
            throw new SQLException(Messages.getString("Statement.46"), //$NON-NLS-1$
                "01S03"); //$NON-NLS-1$
        }

        char firstStatementChar = StringUtils.firstNonWsCharUc(sql);

        if (this.doEscapeProcessing) {
            Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
                        this.connection.serverSupportsConvertFn());
            
            if (escapedSqlResult instanceof String) {
                  sql = (String)escapedSqlResult;
            } else {
                  sql = ((EscapeProcessorResult)escapedSqlResult).escapedSql;
            }
        }

        if (this.results != null) {
            if (!this.connection.getHoldResultsOpenOverStatementClose()) {
                  this.results.realClose(false);
            }
        }

        // The checking and changing of catalogs
        // must happen in sequence, so synchronize
        // on the same mutex that _conn is using
        ResultSet rs = null;

        synchronized (this.connection.getMutex()) {
            String oldCatalog = null;

            if (!this.connection.getCatalog().equals(this.currentCatalog)) {
                oldCatalog = this.connection.getCatalog();
                this.connection.setCatalog(this.currentCatalog);
            }

            //
            // Only apply max_rows to selects
            //
            if (this.connection.useMaxRows()) {
                this.connection.execSQL(this,
                    "SET OPTION SQL_SELECT_LIMIT=DEFAULT", //$NON-NLS-1$
                    -1, null, java.sql.ResultSet.TYPE_FORWARD_ONLY,
                    java.sql.ResultSet.CONCUR_READ_ONLY, false, false,
                    this.currentCatalog, true);
            }

            rs = this.connection.execSQL(this, sql, -1, null,
                    java.sql.ResultSet.TYPE_FORWARD_ONLY,
                    java.sql.ResultSet.CONCUR_READ_ONLY, false, false,
                    this.currentCatalog,
                    true /* force read of field info on DML */);

            if (oldCatalog != null) {
                this.connection.setCatalog(oldCatalog);
            }
        }

        this.results = rs;

        rs.setFirstCharOfQuery(firstStatementChar);

        this.updateCount = rs.getUpdateCount();

        int truncatedUpdateCount = 0;

        if (this.updateCount > Integer.MAX_VALUE) {
            truncatedUpdateCount = Integer.MAX_VALUE;
        } else {
            truncatedUpdateCount = (int) this.updateCount;
        }

        this.lastInsertId = rs.getUpdateID();

        return truncatedUpdateCount;
    }

    /**
     * @see Statement#executeUpdate(String, int)
     */
01322     public int executeUpdate(String sql, int returnGeneratedKeys)
        throws SQLException {
        if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
            checkClosed();

            synchronized (this.connection.getMutex()) {
                // If this is a 'REPLACE' query, we need to be able to parse
                // the 'info' message returned from the server to determine
                // the actual number of keys generated.
                boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
                this.connection.setReadInfoMsgEnabled(true);

                try {
                    return executeUpdate(sql);
                } finally {
                    this.connection.setReadInfoMsgEnabled(readInfoMsgState);
                }
            }
        }

        return executeUpdate(sql);
    }

    /**
     * @see Statement#executeUpdate(String, int[])
     */
01348     public int executeUpdate(String sql, int[] generatedKeyIndices)
        throws SQLException {
        if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
            checkClosed();

            synchronized (this.connection.getMutex()) {
                // If this is a 'REPLACE' query, we need to be able to parse
                // the 'info' message returned from the server to determine
                // the actual number of keys generated.
                boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
                this.connection.setReadInfoMsgEnabled(true);

                try {
                    return executeUpdate(sql);
                } finally {
                    this.connection.setReadInfoMsgEnabled(readInfoMsgState);
                }
            }
        }

        return executeUpdate(sql);
    }

    /**
     * @see Statement#executeUpdate(String, String[])
     */
01374     public int executeUpdate(String sql, String[] generatedKeyNames)
        throws SQLException {
        if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
            checkClosed();

            synchronized (this.connection.getMutex()) {
                // If this is a 'REPLACE' query, we need to be able to parse
                // the 'info' message returned from the server to determine
                // the actual number of keys generated.
                boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
                this.connection.setReadInfoMsgEnabled(true);

                try {
                    return executeUpdate(sql);
                } finally {
                    this.connection.setReadInfoMsgEnabled(readInfoMsgState);
                }
            }
        }

        return executeUpdate(sql);
    }

    /**
     * Returns cached metadata (or null if not cached) for the  given query,
     * which must match _exactly_. Note this method is guarded against
     * concurrent access via the synchronized{} block in execute() and
     * executeQuery().
     *
     * @param sql the query that is the key to the cache
     *
     * @return DOCUMENT ME!
     */
01407     protected CachedResultSetMetaData getCachedMetaData(String sql) {
        if (this.resultSetMetadataCache != null) {
            return (CachedResultSetMetaData) this.resultSetMetadataCache.get(sql);
        }

        return null; // no cache exists (yet)
    }

    /**
     * Returns the id used when profiling
     *
     * @return the id used when profiling.
     */
01420     protected int getId() {
        return this.statementId;
    }

    /**
     * Checks if closed() has been called, and throws an exception if so
     *
     * @throws SQLException if this statement has been closed
     */
01429     protected void checkClosed() throws SQLException {
        if (this.isClosed) {
            throw new SQLException(Messages.getString("Statement.49"), //$NON-NLS-1$
                SQLError.SQL_STATE_CONNECTION_NOT_OPEN); //$NON-NLS-1$
        }
    }

    /**
     * Checks if the given SQL query with the given first non-ws char is a DML
     * statement. Throws an exception if it is.
     *
     * @param sql the SQL to check
     * @param firstStatementChar the UC first non-ws char of the statement
     *
     * @throws SQLException if the statement contains DML
     */
01445     protected void checkForDml(String sql, char firstStatementChar)
        throws SQLException {
        if ((firstStatementChar == 'I') || (firstStatementChar == 'U') ||
                (firstStatementChar == 'D') || (firstStatementChar == 'A') ||
                (firstStatementChar == 'C')) {
            if (StringUtils.startsWithIgnoreCaseAndWs(sql, "INSERT") //$NON-NLS-1$
                     ||StringUtils.startsWithIgnoreCaseAndWs(sql, "UPDATE") //$NON-NLS-1$
                     ||StringUtils.startsWithIgnoreCaseAndWs(sql, "DELETE") //$NON-NLS-1$
                     ||StringUtils.startsWithIgnoreCaseAndWs(sql, "DROP") //$NON-NLS-1$
                     ||StringUtils.startsWithIgnoreCaseAndWs(sql, "CREATE") //$NON-NLS-1$
                     ||StringUtils.startsWithIgnoreCaseAndWs(sql, "ALTER")) { //$NON-NLS-1$
                throw new SQLException(Messages.getString("Statement.57"), //$NON-NLS-1$
                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
            }
        }
    }

    /**
     * Method checkNullOrEmptyQuery.
     *
     * @param sql the SQL to check
     *
     * @throws SQLException if query is null or empty.
     */
01469     protected void checkNullOrEmptyQuery(String sql) throws SQLException {
        if (sql == null) {
            throw new SQLException(Messages.getString("Statement.59"), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
        }

        if (sql.length() == 0) {
            throw new SQLException(Messages.getString("Statement.61"), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
        }
    }

    /**
     * Close any open result sets that have been 'held open'
     */
01484     protected void closeAllOpenResults() {
        if (this.openResults != null) {
            for (Iterator iter = this.openResults.iterator(); iter.hasNext();) {
                ResultSet element = (ResultSet) iter.next();

                try {
                    element.realClose(false);
                } catch (SQLException sqlEx) {
                    AssertionFailedException.shouldNotHappen(sqlEx);
                }
            }

            this.openResults.clear();
        }
    }

    /**
     * We only stream result sets when they are forward-only, read-only, and
     * the fetch size has been set to Integer.MIN_VALUE
     *
     * @return true if this result set should be streamed row at-a-time, rather
     *         than read all at once.
     */
01507     protected boolean createStreamingResultSet() {
        return ((this.resultSetType == java.sql.ResultSet.TYPE_FORWARD_ONLY) &&
        (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY) &&
        (this.fetchSize == Integer.MIN_VALUE));
    }

    /**
     * Caches CachedResultSetMetaData that has been placed in the cache using
     * the given SQL as a key.
     *
     * @param sql DOCUMENT ME!
     * @param cachedMetaData DOCUMENT ME!
     * @param resultSet DOCUMENT ME!
     *
     * @throws SQLException DOCUMENT ME!
     */
01523     protected void initializeResultsMetadataFromCache(String sql,
        CachedResultSetMetaData cachedMetaData, ResultSet resultSet)
        throws SQLException {
        synchronized (resultSet) {
            if (cachedMetaData == null) {
                // read from results
                cachedMetaData = new CachedResultSetMetaData();
                cachedMetaData.fields = this.results.fields;

                // assume that users will use named-based
                // lookups
                resultSet.buildIndexMapping();

                cachedMetaData.columnNameToIndex = resultSet.columnNameToIndex;
                cachedMetaData.fullColumnNameToIndex = resultSet.fullColumnNameToIndex;

                cachedMetaData.metadata = resultSet.getMetaData();

                if (this.resultSetMetadataCache == null) {
                    this.resultSetMetadataCache = new LRUCache(this.connection.getMetadataCacheSize());
                }

                this.resultSetMetadataCache.put(sql, cachedMetaData);
            } else {
                // initialize results from cached data
                resultSet.fields = cachedMetaData.fields;
                resultSet.columnNameToIndex = cachedMetaData.columnNameToIndex;
                resultSet.fullColumnNameToIndex = cachedMetaData.fullColumnNameToIndex;
                resultSet.hasBuiltIndexMapping = true;

                // results.resultSetMetaData = cachedMetaData.metadata;
            }
        }
    }

    /**
     * Closes this statement, and frees resources.
     *
     * @param calledExplicitly was this called from close()?
     *
     * @throws SQLException if an error occurs
     */
01565     protected void realClose(boolean calledExplicitly)
        throws SQLException {
        if (this.isClosed) {
            return;
        }

        if (this.useUsageAdvisor) {
            if (!calledExplicitly) {
                String message = Messages.getString("Statement.63") //$NON-NLS-1$
                     +Messages.getString("Statement.64"); //$NON-NLS-1$

                this.eventSink.consumeEvent(new ProfilerEvent(
                        ProfilerEvent.TYPE_WARN, "", //$NON-NLS-1$
                        this.currentCatalog, this.connection.getId(),
                        this.getId(), -1, System.currentTimeMillis(), 0, null,
                        this.pointOfOrigin, message));
            }
        }

        if (this.results != null) {
            if (this.connection != null && 
                  !this.connection.getHoldResultsOpenOverStatementClose()) {
                  try {
                        this.results.close();
                  } catch (Exception ex) {
                        ;
                  }
            }
        }

        if (this.connection != null) {
            if (this.maxRowsChanged) {
                this.connection.unsetMaxRows(this);
            }

            if (!this.connection.getDontTrackOpenResources()) {
                  this.connection.unregisterStatement(this);
            }
        }

        this.closeAllOpenResults();
        
        this.results = null;
        this.connection = null;
        this.warningChain = null;     
        this.openResults = null;
        this.isClosed = true;
    }

    /**
     * Sets the concurrency for result sets generated by this statement
     *
     * @param concurrencyFlag DOCUMENT ME!
     */
01619     synchronized void setResultSetConcurrency(int concurrencyFlag) {
        this.resultSetConcurrency = concurrencyFlag;
    }

    /**
     * Sets the result set type for result sets generated by this statement
     *
     * @param typeFlag DOCUMENT ME!
     */
01628     synchronized void setResultSetType(int typeFlag) {
        this.resultSetType = typeFlag;
    }

    /**
     * Parses actual record count from 'info' message
     *
     * @param serverInfo DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
01639     private int getRecordCountFromInfo(String serverInfo) {
        StringBuffer recordsBuf = new StringBuffer();
        int recordsCount = 0;
        int duplicatesCount = 0;

        char c = (char) 0;

        int length = serverInfo.length();
        int i = 0;

        for (; i < length; i++) {
            c = serverInfo.charAt(i);

            if (Character.isDigit(c)) {
                break;
            }
        }

        recordsBuf.append(c);
        i++;

        for (; i < length; i++) {
            c = serverInfo.charAt(i);

            if (!Character.isDigit(c)) {
                break;
            }

            recordsBuf.append(c);
        }

        recordsCount = Integer.parseInt(recordsBuf.toString());

        StringBuffer duplicatesBuf = new StringBuffer();

        for (; i < length; i++) {
            c = serverInfo.charAt(i);

            if (Character.isDigit(c)) {
                break;
            }
        }

        duplicatesBuf.append(c);
        i++;

        for (; i < length; i++) {
            c = serverInfo.charAt(i);

            if (!Character.isDigit(c)) {
                break;
            }

            duplicatesBuf.append(c);
        }

        duplicatesCount = Integer.parseInt(duplicatesBuf.toString());

        return recordsCount - duplicatesCount;
    }

    class CachedResultSetMetaData {
        /** Map column names (and all of their permutations) to column indices */
        Map columnNameToIndex = null;

        /** Map of fully-specified column names to column indices */
        Map fullColumnNameToIndex = null;

        /** Cached ResultSetMetaData */
        java.sql.ResultSetMetaData metadata;

        /** Cached Field info */
        Field[] fields;
    }

}

Generated by  Doxygen 1.6.0   Back to index