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

StatementImpl.java

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

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

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

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

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

import java.io.InputStream;
import java.math.BigInteger;
import java.sql.BatchUpdateException;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.TimerTask;

import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
import com.mysql.jdbc.exceptions.MySQLTimeoutException;
import com.mysql.jdbc.profiler.ProfilerEvent;
import com.mysql.jdbc.profiler.ProfilerEventHandler;

/**
 * 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 4624 2005-11-28 14:24:29 -0600 (Mon, 28 Nov
 *          2005) mmatthews $
 *
 * @see java.sql.Statement
 * @see ResultSetInternalMethods
 */
00067 public class StatementImpl implements Statement {
        protected static final String PING_MARKER = "/* ping */";
      /**
       * Thread used to implement query timeouts...Eventually we could be more
       * efficient and have one thread with timers, but this is a straightforward
       * and simple way to implement a feature that isn't used all that often.
       */
00074       class CancelTask extends TimerTask {

            long connectionId = 0;
            String origHost = "";
            SQLException caughtWhileCancelling = null;
            StatementImpl toCancel;
            Properties origConnProps = null; 
            String origConnURL = "";
            
            CancelTask(StatementImpl cancellee) throws SQLException {
                  connectionId = cancellee.connectionId;
                  origHost = connection.getHost();
                  toCancel = cancellee;
                  origConnProps = new Properties();
                  
                  Properties props = connection.getProperties();
                  
                  Enumeration<?> keys = props.propertyNames();
                  
                  while (keys.hasMoreElements()) {
                        String key = keys.nextElement().toString();
                        origConnProps.setProperty(key, props.getProperty(key));
                  }
                  
                  origConnURL = connection.getURL();
            }

            public void run() {

                  Thread cancelThread = new Thread() {

                        public void run() {
                              if (connection.getQueryTimeoutKillsConnection()) {
                                    try {
                                          toCancel.wasCancelled = true;
                                          toCancel.wasCancelledByTimeout = true;
                                          connection.realClose(false, false, true, 
                                                      new MySQLStatementCancelledException(Messages.getString("Statement.ConnectionKilledDueToTimeout")));
                                    } catch (NullPointerException npe) {
                                          // not worth guarding against
                                    } catch (SQLException sqlEx) {
                                          caughtWhileCancelling = sqlEx;
                                    }
                              } else {
                                    Connection cancelConn = null;
                                    java.sql.Statement cancelStmt = null;
      
                                    try {
                                          synchronized (cancelTimeoutMutex) {
                                                if (origConnURL == connection.getURL()) {
                                                      //All's fine
                                                      cancelConn = connection.duplicate();
                                                      cancelStmt = cancelConn.createStatement();
                                                      cancelStmt.execute("KILL QUERY " + connectionId);
                                                } else {
                                                      try {
                                                            cancelConn = (Connection) DriverManager.getConnection(origConnURL, origConnProps);
                                                            cancelStmt = cancelConn.createStatement();
                                                            cancelStmt.execute("KILL QUERY " + connectionId);
                                                      } catch (NullPointerException npe){
                                                            //Log this? "Failed to connect to " + origConnURL + " and KILL query"
                                                      }
                                                }
                                                toCancel.wasCancelled = true;
                                                toCancel.wasCancelledByTimeout = true;
                                          }
                                    } catch (SQLException sqlEx) {
                                          caughtWhileCancelling = sqlEx;
                                    } catch (NullPointerException npe) {
                                          // Case when connection closed while starting to cancel
                                          // We can't easily synchronize this, because then one thread
                                          // can't cancel() a running query
      
                                          // ignore, we shouldn't re-throw this, because the connection's
                                          // already closed, so the statement has been timed out.
                                    } finally {
                                          if (cancelStmt != null) {
                                                try {
                                                      cancelStmt.close();
                                                } catch (SQLException sqlEx) {
                                                      throw new RuntimeException(sqlEx.toString());
                                                }
                                          }
      
                                          if (cancelConn != null) {
                                                try {
                                                      cancelConn.close();
                                                } catch (SQLException sqlEx) {
                                                      throw new RuntimeException(sqlEx.toString());
                                                }
                                          }
                                          
                                          toCancel = null;
                                          origConnProps = null;
                                          origConnURL = null;
                                    }
                              }
                        }
                  };

                  cancelThread.start();
            }
      }

      /** Mutex to prevent race between returning query results and noticing
    that we're timed-out or cancelled. */

00181       protected Object cancelTimeoutMutex = new Object();

      /** Used to generate IDs when profiling. */
00184       static int statementCounter = 1;

      public final static byte USES_VARIABLES_FALSE = 0;

      public final static byte USES_VARIABLES_TRUE = 1;

      public final static byte USES_VARIABLES_UNKNOWN = -1;

      protected boolean wasCancelled = false;
      protected boolean wasCancelledByTimeout = false;

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

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

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

      /** The connection that created us */
00205       protected MySQLConnection connection = null;

      protected long connectionId = 0;

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

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

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

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

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

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

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

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

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

      /** Set of currently-open ResultSets */
00240       protected Set openResults = new HashSet();

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

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

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

      /** The current results */
00255       protected ResultSetInternalMethods results = null;

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

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

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

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

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

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

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

      /**
       * Should this statement hold results open over .close() irregardless of
       * connection's setting?
       */
00282       protected boolean holdResultsOpenOverClose = false;

      protected ArrayList batchedGeneratedKeys = null;

      protected boolean retrieveGeneratedKeys = false;

      protected boolean continueBatchOnError = false;

      protected PingTarget pingTarget = null;
      
      protected boolean useLegacyDatetimeCode;
      
      private ExceptionInterceptor exceptionInterceptor;
      
      /** Whether or not the last query was of the form ON DUPLICATE KEY UPDATE */
00297       protected boolean lastQueryIsOnDupKeyUpdate = false;
      
      /**
       * 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.
       */
00310       public StatementImpl(MySQLConnection c, String catalog) throws SQLException {
            if ((c == null) || c.isClosed()) {
                  throw SQLError.createSQLException(
                              Messages.getString("Statement.0"), //$NON-NLS-1$
                              SQLError.SQL_STATE_CONNECTION_NOT_OPEN, null); //$NON-NLS-1$ //$NON-NLS-2$
            }

            this.connection = c;
            this.connectionId = this.connection.getId();
            this.exceptionInterceptor = this.connection
                        .getExceptionInterceptor();

            this.currentCatalog = catalog;
            this.pedantic = this.connection.getPedantic();
            this.continueBatchOnError = this.connection.getContinueBatchOnError();
            this.useLegacyDatetimeCode = this.connection.getUseLegacyDatetimeCode();
            
            if (!this.connection.getDontTrackOpenResources()) {
                  this.connection.registerStatement(this);
            }

            //
            // Adjust, if we know it
            //

            if (this.connection != null) {
                  this.maxFieldSize = this.connection.getMaxAllowedPacket();

                  int defaultFetchSize = this.connection.getDefaultFetchSize();

                  if (defaultFetchSize != 0) {
                        setFetchSize(defaultFetchSize);
                  }
                  
                  if (this.connection.getUseUnicode()) {
                        this.charEncoding = this.connection.getEncoding();

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

                  boolean profiling = this.connection.getProfileSql()
                              || this.connection.getUseUsageAdvisor() || this.connection.getLogSlowQueries();

                  if (this.connection.getAutoGenerateTestcaseScript() || profiling) {
                        this.statementId = statementCounter++;
                  }

                  if (profiling) {
                        this.pointOfOrigin = new Throwable();
                        this.profileSQL = this.connection.getProfileSql();
                        this.useUsageAdvisor = this.connection.getUseUsageAdvisor();
                        this.eventSink = ProfilerEventHandlerFactory.getInstance(this.connection);
                  }

                  int maxRowsConn = this.connection.getMaxRows();

                  if (maxRowsConn != -1) {
                        setMaxRows(maxRowsConn);
                  }
                  
                  this.holdResultsOpenOverClose = this.connection.getHoldResultsOpenOverStatementClose();
            }
      }

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

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

      /**
       * Cancels this Statement object if both the DBMS and driver support
       * aborting an SQL statement. This method can be used by one thread to
       * cancel a statement that is being executed by another thread.
       */
00400       public void cancel() throws SQLException {
            if (!this.isClosed &&
                        this.connection != null &&
                        this.connection.versionMeetsMinimum(5, 0, 0)) {
                  Connection cancelConn = null;
                  java.sql.Statement cancelStmt = null;

                  try {
                        cancelConn = this.connection.duplicate();
                        cancelStmt = cancelConn.createStatement();
                        cancelStmt.execute("KILL QUERY "
                                    + this.connection.getIO().getThreadId());
                        this.wasCancelled = true;
                  } finally {
                        if (cancelStmt != null) {
                              cancelStmt.close();
                        }

                        if (cancelConn != null) {
                              cancelConn.close();
                        }
                  }

            }
      }

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

      /**
       * Checks if closed() has been called, and throws an exception if so
       *
       * @throws SQLException
       *             if this statement has been closed
       */
00434       protected synchronized void checkClosed() throws SQLException {
            if (this.isClosed) {
                  throw SQLError.createSQLException(Messages
                              .getString("Statement.49"), //$NON-NLS-1$
                              SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor()); //$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
       */
00454       protected void checkForDml(String sql, char firstStatementChar)
                  throws SQLException {
            if ((firstStatementChar == 'I') || (firstStatementChar == 'U')
                        || (firstStatementChar == 'D') || (firstStatementChar == 'A')
                        || (firstStatementChar == 'C')) {
                  String noCommentSql = StringUtils.stripComments(sql,
                              "'\"", "'\"", true, false, true, true);

                  if (StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "INSERT") //$NON-NLS-1$
                              || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "UPDATE") //$NON-NLS-1$
                              || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DELETE") //$NON-NLS-1$
                              || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DROP") //$NON-NLS-1$
                              || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "CREATE") //$NON-NLS-1$
                              || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "ALTER")) { //$NON-NLS-1$
                        throw SQLError.createSQLException(Messages
                                    .getString("Statement.57"), //$NON-NLS-1$
                                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
                  }
            }
      }

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

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

      /**
       * 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
       */
00506       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?)
       */
00519       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
       */
00538       public synchronized void close() throws SQLException {
            realClose(true, true);
      }

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

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

                  this.openResults.clear();
            }
      }

00561       public synchronized void removeOpenResultSet(ResultSet rs) {
            if (this.openResults != null) {
                  this.openResults.remove(rs);
            }
      }
      
00567       public synchronized int getOpenResultSetCount() {
            if (this.openResults != null) {
                  return this.openResults.size();
            }
            
            return 0;
      }
      
      /**
       * @param sql
       * @return
       */
00579       private synchronized ResultSetInternalMethods createResultSetUsingServerFetch(String sql)
                  throws SQLException {
            java.sql.PreparedStatement pStmt = this.connection.prepareStatement(
                        sql, this.resultSetType, this.resultSetConcurrency);

            pStmt.setFetchSize(this.fetchSize);

            if (this.maxRows > -1) {
                  pStmt.setMaxRows(this.maxRows);
            }

            pStmt.execute();

            //
            // Need to be able to get resultset irrespective if we issued DML or
            // not to make this work.
            //
            ResultSetInternalMethods rs = ((com.mysql.jdbc.StatementImpl) pStmt)
                        .getResultSetInternal();

            rs
                        .setStatementUsedForFetchingRows((com.mysql.jdbc.PreparedStatement) pStmt);

            this.results = rs;

            return rs;
      }

      /**
       * 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.
       */
00614       protected synchronized boolean createStreamingResultSet() {
            return ((this.resultSetType == java.sql.ResultSet.TYPE_FORWARD_ONLY)
                        && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY) && (this.fetchSize == Integer.MIN_VALUE));
      }

      private int originalResultSetType = 0;
      private int originalFetchSize = 0;

      /* (non-Javadoc)
       * @see com.mysql.jdbc.IStatement#enableStreamingResults()
       */
00625       public synchronized void enableStreamingResults() throws SQLException {
            this.originalResultSetType = this.resultSetType;
            this.originalFetchSize = this.fetchSize;

            setFetchSize(Integer.MIN_VALUE);
            setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
      }

00633       public synchronized void disableStreamingResults() throws SQLException {
            if (this.fetchSize == Integer.MIN_VALUE &&
                        this.resultSetType == ResultSet.TYPE_FORWARD_ONLY) {
                  setFetchSize(this.originalFetchSize);
                  setResultSetType(this.originalResultSetType);
            }
      }

      /**
       * 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
       */
00655       public boolean execute(String sql) throws SQLException {
            return execute(sql, false);
      }
      
      private synchronized boolean execute(String sql, boolean returnGeneratedKeys) throws SQLException {
            checkClosed();

            MySQLConnection locallyScopedConn = this.connection;

            synchronized (locallyScopedConn) {
                  this.retrieveGeneratedKeys = returnGeneratedKeys;
                  lastQueryIsOnDupKeyUpdate = false;
                  if (returnGeneratedKeys)
                        lastQueryIsOnDupKeyUpdate = containsOnDuplicateKeyInString(sql);
                  
                  resetCancelledState();

                  checkNullOrEmptyQuery(sql);

                  checkClosed();

                  char firstNonWsChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql));

                  boolean isSelect = true;

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

                        if (locallyScopedConn.isReadOnly()) {
                              throw SQLError.createSQLException(Messages
                                          .getString("Statement.27") //$NON-NLS-1$
                                          + Messages.getString("Statement.28"), //$NON-NLS-1$
                                          SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
                        }
                  }

                  boolean doStreaming = createStreamingResultSet();

                  // Adjust net_write_timeout to a higher value if we're
                  // streaming result sets. More often than not, someone runs into
                  // an issue where they blow net_write_timeout when using this
                  // feature, and if they're willing to hold a result set open
                  // for 30 seconds or more, one more round-trip isn't going to hurt
                  //
                  // This is reset by RowDataDynamic.close().

                  if (doStreaming
                              && locallyScopedConn.getNetTimeoutForStreamingResults() > 0) {
                        executeSimpleNonQuery(locallyScopedConn, "SET net_write_timeout="
                                    + locallyScopedConn.getNetTimeoutForStreamingResults());
                  }

                  if (this.doEscapeProcessing) {
                        Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
                                    locallyScopedConn.serverSupportsConvertFn(), locallyScopedConn);

                        if (escapedSqlResult instanceof String) {
                              sql = (String) escapedSqlResult;
                        } else {
                              sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
                        }
                  }

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

                  if (sql.charAt(0) == '/') {
                        if (sql.startsWith(PING_MARKER)) {
                              doPingInstead();
                        
                              return true;
                        }
                  }

                  CachedResultSetMetaData cachedMetaData = null;

                  ResultSetInternalMethods 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).

                  this.batchedGeneratedKeys = null;

                  if (useServerFetch()) {
                        rs = createResultSetUsingServerFetch(sql);
                  } else {
                        CancelTask timeoutTask = null;

                        String oldCatalog = null;

                        try {
                              if (locallyScopedConn.getEnableQueryTimeouts() &&
                                          this.timeoutInMillis != 0
                                          && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
                                    timeoutTask = new CancelTask(this);
                                    locallyScopedConn.getCancelTimer().schedule(timeoutTask,
                                                this.timeoutInMillis);
                              }



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

                              //
                              // Check if we have cached metadata for this query...
                              //

                              Field[] cachedFields = null;

                              if (locallyScopedConn.getCacheResultSetMetadata()) {
                                    cachedMetaData = locallyScopedConn.getCachedMetaData(sql);

                                    if (cachedMetaData != null) {
                                          cachedFields = cachedMetaData.fields;
                                    }
                              }

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

                                    if (isSelect) {
                                          if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
                                                rowLimit = this.maxRows;
                                          } else {
                                                if (this.maxRows <= 0) {
                                                      executeSimpleNonQuery(locallyScopedConn,
                                                                  "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
                                                } else {
                                                      executeSimpleNonQuery(locallyScopedConn,
                                                                  "SET OPTION SQL_SELECT_LIMIT="
                                                                              + this.maxRows);
                                                }
                                          }
                                    } else {
                                          executeSimpleNonQuery(locallyScopedConn,
                                                      "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
                                    }

                                    // Finally, execute the query
                                    rs = locallyScopedConn.execSQL(this, sql, rowLimit, null,
                                                this.resultSetType, this.resultSetConcurrency,
                                                doStreaming,
                                                this.currentCatalog, cachedFields);
                              } else {
                                    rs = locallyScopedConn.execSQL(this, sql, -1, null,
                                                this.resultSetType, this.resultSetConcurrency,
                                                doStreaming,
                                                this.currentCatalog, cachedFields);
                              }

                              if (timeoutTask != null) {
                                    if (timeoutTask.caughtWhileCancelling != null) {
                                          throw timeoutTask.caughtWhileCancelling;
                                    }

                                    timeoutTask.cancel();
                                    timeoutTask = null;
                              }

                              synchronized (this.cancelTimeoutMutex) {
                                    if (this.wasCancelled) {
                                          SQLException cause = null;
                                          
                                          if (this.wasCancelledByTimeout) {
                                                cause = new MySQLTimeoutException();
                                          } else {
                                                cause = new MySQLStatementCancelledException();
                                          }
                                          
                                          resetCancelledState();
                                          
                                          throw cause;
                                    }
                              }
                        } finally {
                              if (timeoutTask != null) {
                                    timeoutTask.cancel();
                                    locallyScopedConn.getCancelTimer().purge();
                              }

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

                  if (rs != null) {
                        this.lastInsertId = rs.getUpdateID();

                        this.results = rs;

                        rs.setFirstCharOfQuery(firstNonWsChar);

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

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

      protected synchronized void resetCancelledState() {
            if (this.cancelTimeoutMutex == null) {
                  return;
            }
            
            synchronized (this.cancelTimeoutMutex) {
                  this.wasCancelled = false;
                  this.wasCancelledByTimeout = false;
            }
      }

      /**
       * @see StatementImpl#execute(String, int)
       */
00893       public boolean execute(String sql, int returnGeneratedKeys)
                  throws SQLException {


            if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
                  checkClosed();

                  MySQLConnection locallyScopedConn = this.connection;

                  synchronized (locallyScopedConn) {
                        // 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();
                        locallyScopedConn.setReadInfoMsgEnabled(true);

                        try {
                              return execute(sql, true);
                        } finally {
                              locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
                        }
                  }
            }

            return execute(sql);
      }

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

                  MySQLConnection locallyScopedConn = this.connection;

                  synchronized (locallyScopedConn) {
                        this.retrieveGeneratedKeys = true;
                        
                        // 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 = locallyScopedConn
                                    .isReadInfoMsgEnabled();
                        locallyScopedConn.setReadInfoMsgEnabled(true);

                        try {
                              return execute(sql, true);
                        } finally {
                              locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
                        }
                  }
            }

            return execute(sql);
      }

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

                  MySQLConnection locallyScopedConn = this.connection;

                  synchronized (locallyScopedConn) {
                        this.retrieveGeneratedKeys = true;
                        // 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();
                        locallyScopedConn.setReadInfoMsgEnabled(true);

                        try {
                              return execute(sql, true);
                        } finally {
                              locallyScopedConn.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!
       */
00996       public synchronized int[] executeBatch() throws SQLException {
            checkClosed();

            MySQLConnection locallyScopedConn = this.connection;

            if (locallyScopedConn.isReadOnly()) {
                  throw SQLError.createSQLException(Messages
                              .getString("Statement.34") //$NON-NLS-1$
                              + Messages.getString("Statement.35"), //$NON-NLS-1$
                              SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
            }

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

            synchronized (locallyScopedConn) {
                  if (this.batchedArgs == null || this.batchedArgs.size() == 0) {
                return new int[0];
            }
                  
                  // we timeout the entire batch, not individual statements
                  int individualStatementTimeout = this.timeoutInMillis;
                  this.timeoutInMillis = 0;
                  
                  CancelTask timeoutTask = null;
                  
                  try {
                        resetCancelledState();

                        this.retrieveGeneratedKeys = true; // The JDBC spec doesn't forbid this, but doesn't provide for it either...we do..

                        int[] updateCounts = null;

                        
                        if (this.batchedArgs != null) {
                              int nbrCommands = this.batchedArgs.size();

                              this.batchedGeneratedKeys = new ArrayList(this.batchedArgs.size());

                              boolean multiQueriesEnabled = locallyScopedConn.getAllowMultiQueries();

                              if (locallyScopedConn.versionMeetsMinimum(4, 1, 1) &&
                                          (multiQueriesEnabled ||
                                          (locallyScopedConn.getRewriteBatchedStatements() &&
                                                      nbrCommands > 4))) {
                                    return executeBatchUsingMultiQueries(multiQueriesEnabled, nbrCommands, individualStatementTimeout);
                              }

                              if (locallyScopedConn.getEnableQueryTimeouts() &&
                                          individualStatementTimeout != 0
                                          && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
                                    timeoutTask = new CancelTask(this);
                                    locallyScopedConn.getCancelTimer().schedule(timeoutTask,
                                                individualStatementTimeout);
                              }
                              
                              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 {
                                          String sql = (String) this.batchedArgs.get(commandIndex);
                                          updateCounts[commandIndex] = executeUpdate(sql, true, true);
                                          // limit one generated key per OnDuplicateKey statement
                                          getBatchedGeneratedKeys(containsOnDuplicateKeyInString(sql) ? 1 : 0);
                                    } catch (SQLException ex) {
                                          updateCounts[commandIndex] = EXECUTE_FAILED;

                                          if (this.continueBatchOnError && 
                                                      !(ex instanceof MySQLTimeoutException) && 
                                                      !(ex instanceof MySQLStatementCancelledException) &&
                                    !hasDeadlockOrTimeoutRolledBackTx(ex)) {
                                                sqlEx = ex;
                                          } else {
                                                int[] newUpdateCounts = new int[commandIndex];
                                                
                                                if (hasDeadlockOrTimeoutRolledBackTx(ex)) {
                                                      for (int i = 0; i < newUpdateCounts.length; i++) {
                                                            newUpdateCounts[i] = Statement.EXECUTE_FAILED;
                                                      }
                                                } else {
                                                      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);
                              }
                        }

                        if (timeoutTask != null) {
                              if (timeoutTask.caughtWhileCancelling != null) {
                                    throw timeoutTask.caughtWhileCancelling;
                              }

                              timeoutTask.cancel();
                              
                              locallyScopedConn.getCancelTimer().purge();
                              timeoutTask = null;
                        }
                        
                        return (updateCounts != null) ? updateCounts : new int[0];
                  } finally {
                        
                        if (timeoutTask != null) {
                              timeoutTask.cancel();
                              
                              locallyScopedConn.getCancelTimer().purge();
                        }
                        
                        resetCancelledState();
                        
                        this.timeoutInMillis = individualStatementTimeout;

                        clearBatch();
                  }
            }
      }

      protected synchronized final boolean hasDeadlockOrTimeoutRolledBackTx(SQLException ex) {
            int vendorCode = ex.getErrorCode();
            
            switch (vendorCode) {
            case MysqlErrorNumbers.ER_LOCK_DEADLOCK:
            case MysqlErrorNumbers.ER_LOCK_TABLE_FULL:
                  return true;
            case MysqlErrorNumbers.ER_LOCK_WAIT_TIMEOUT:
                  try {
                        return !this.connection.versionMeetsMinimum(5, 0, 13);
                  } catch (SQLException sqlEx) {
                        // won't actually be thrown in this case
                        return false;
                  }
            default:
                  return false;
            }
      }

      /**
       * Rewrites batch into a single query to send to the server. This method
       * will constrain each batch to be shorter than max_allowed_packet on the
       * server.
       *
       * @return update counts in the same manner as executeBatch()
       * @throws SQLException
       */
01161       private synchronized int[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled,
                  int nbrCommands, int individualStatementTimeout) throws SQLException {

            MySQLConnection locallyScopedConn = this.connection;

            if (!multiQueriesEnabled) {
                  locallyScopedConn.getIO().enableMultiQueries();
            }

            java.sql.Statement batchStmt = null;

            CancelTask timeoutTask = null;
            
            try {
                  int[] updateCounts = new int[nbrCommands];

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

                  int commandIndex = 0;

                  StringBuffer queryBuf = new StringBuffer();

                  batchStmt = locallyScopedConn.createStatement();

                  if (locallyScopedConn.getEnableQueryTimeouts() &&
                              individualStatementTimeout != 0
                              && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
                        timeoutTask = new CancelTask((StatementImpl)batchStmt);
                        locallyScopedConn.getCancelTimer().schedule(timeoutTask,
                                    individualStatementTimeout);
                  }
                  
                  int counter = 0;

                  int numberOfBytesPerChar = 1;

                  String connectionEncoding = locallyScopedConn.getEncoding();

                  if (StringUtils.startsWithIgnoreCase(connectionEncoding, "utf")) {
                        numberOfBytesPerChar = 3;
                  } else if (CharsetMapping.isMultibyteCharset(connectionEncoding)) {
                        numberOfBytesPerChar = 2;
                  }

                  int escapeAdjust = 1;

                  batchStmt.setEscapeProcessing(this.doEscapeProcessing);
                  
                  if (this.doEscapeProcessing) {
                        
                        escapeAdjust = 2; /* We assume packet _could_ grow by this amount, as we're not
                                             sure how big statement will end up after
                                             escape processing */
                  }

                  SQLException sqlEx = null;
                  
                  int argumentSetsInBatchSoFar = 0;
                  
                  for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
                        String nextQuery = (String) this.batchedArgs.get(commandIndex);

                        if (((((queryBuf.length() + nextQuery.length())
                                    * numberOfBytesPerChar) + 1 /* for semicolon */
                                    + MysqlIO.HEADER_LENGTH) * escapeAdjust)  + 32 > this.connection
                                    .getMaxAllowedPacket()) {
                              try {
                                    batchStmt.execute(queryBuf.toString(), Statement.RETURN_GENERATED_KEYS);
                              } catch (SQLException ex) {
                                    sqlEx = handleExceptionForBatch(commandIndex,
                                                argumentSetsInBatchSoFar, updateCounts, ex);
                              }

                              counter = processMultiCountsAndKeys((StatementImpl)batchStmt, counter, 
                                          updateCounts);

                              queryBuf = new StringBuffer();
                              argumentSetsInBatchSoFar = 0;
                        }

                        queryBuf.append(nextQuery);
                        queryBuf.append(";");
                        argumentSetsInBatchSoFar++;
                  }

                  if (queryBuf.length() > 0) {
                        try {
                              batchStmt.execute(queryBuf.toString(), Statement.RETURN_GENERATED_KEYS);
                        } catch (SQLException ex) {
                              sqlEx = handleExceptionForBatch(commandIndex - 1,
                                          argumentSetsInBatchSoFar, updateCounts, ex);
                        }

                        counter = processMultiCountsAndKeys((StatementImpl)batchStmt, counter, 
                                    updateCounts);
                  }

                  if (timeoutTask != null) {
                        if (timeoutTask.caughtWhileCancelling != null) {
                              throw timeoutTask.caughtWhileCancelling;
                        }

                        timeoutTask.cancel();
                        
                        locallyScopedConn.getCancelTimer().purge();
                        
                        timeoutTask = null;
                  }
                  
                  if (sqlEx != null) {
                        throw new java.sql.BatchUpdateException(sqlEx
                                    .getMessage(), sqlEx.getSQLState(), sqlEx
                                    .getErrorCode(), updateCounts);
                  }
                  
                  return (updateCounts != null) ? updateCounts : new int[0];
            } finally {
                  if (timeoutTask != null) {
                        timeoutTask.cancel();
                        
                        locallyScopedConn.getCancelTimer().purge();
                  }
                  
                  resetCancelledState();
                  
                  try {
                        if (batchStmt != null) {
                              batchStmt.close();
                        }
                  } finally {
                        if (!multiQueriesEnabled) {
                              locallyScopedConn.getIO().disableMultiQueries();
                        }
                  }
            }
      }
      
      protected synchronized int processMultiCountsAndKeys(
                  StatementImpl batchedStatement,
                  int updateCountCounter, int[] updateCounts) throws SQLException {
            updateCounts[updateCountCounter++] = batchedStatement.getUpdateCount();
            
            boolean doGenKeys = this.batchedGeneratedKeys != null;

            byte[][] row = null;
            
            if (doGenKeys) {
                  long generatedKey = batchedStatement.getLastInsertID();
            
                  row = new byte[1][];
                  row[0] = Long.toString(generatedKey).getBytes();
                  this.batchedGeneratedKeys.add(new ByteArrayRow(row, getExceptionInterceptor()));
            }

            while (batchedStatement.getMoreResults()
                        || batchedStatement.getUpdateCount() != -1) {
                  updateCounts[updateCountCounter++] = batchedStatement.getUpdateCount();
                  
                  if (doGenKeys) {
                        long generatedKey = batchedStatement.getLastInsertID();
                        
                        row = new byte[1][];
                        row[0] = Long.toString(generatedKey).getBytes();
                        this.batchedGeneratedKeys.add(new ByteArrayRow(row, getExceptionInterceptor()));
                  }
            }
            
            return updateCountCounter;
      }
      
      protected SQLException handleExceptionForBatch(int endOfBatchIndex,
                  int numValuesPerBatch, int[] updateCounts, SQLException ex)
                  throws BatchUpdateException {
            SQLException sqlEx;
      
            for (int j = endOfBatchIndex; j > endOfBatchIndex - numValuesPerBatch; j--) {
                  updateCounts[j] = EXECUTE_FAILED;
            }

            if (this.continueBatchOnError && 
                        !(ex instanceof MySQLTimeoutException) && 
                        !(ex instanceof MySQLStatementCancelledException) &&
                        !hasDeadlockOrTimeoutRolledBackTx(ex)) {
                  sqlEx = ex;
            } else {
                  int[] newUpdateCounts = new int[endOfBatchIndex];
                  System.arraycopy(updateCounts, 0,
                              newUpdateCounts, 0, endOfBatchIndex);

                  BatchUpdateException batchException = new BatchUpdateException(ex
                              .getMessage(), ex.getSQLState(), ex
                              .getErrorCode(), newUpdateCounts);
                  batchException.initCause(ex);
                  throw batchException;
            }
            
            return sqlEx;
      }
      
      /**
       * 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
       */
01373       public synchronized java.sql.ResultSet executeQuery(String sql)
                  throws SQLException {
            checkClosed();

            MySQLConnection locallyScopedConn = this.connection;

            synchronized (locallyScopedConn) {
                  this.retrieveGeneratedKeys = false;
                  
                  resetCancelledState();

                  checkNullOrEmptyQuery(sql);

                  boolean doStreaming = createStreamingResultSet();

                  // Adjust net_write_timeout to a higher value if we're
                  // streaming result sets. More often than not, someone runs into
                  // an issue where they blow net_write_timeout when using this
                  // feature, and if they're willing to hold a result set open
                  // for 30 seconds or more, one more round-trip isn't going to hurt
                  //
                  // This is reset by RowDataDynamic.close().

                  if (doStreaming
                              && this.connection.getNetTimeoutForStreamingResults() > 0) {
                        executeSimpleNonQuery(locallyScopedConn, "SET net_write_timeout="
                                    + this.connection.getNetTimeoutForStreamingResults());
                  }

                  if (this.doEscapeProcessing) {
                        Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
                                    locallyScopedConn.serverSupportsConvertFn(), this.connection);

                        if (escapedSqlResult instanceof String) {
                              sql = (String) escapedSqlResult;
                        } else {
                              sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
                        }
                  }

                  char firstStatementChar = StringUtils.firstNonWsCharUc(sql,
                              findStartOfStatement(sql));

                  if (sql.charAt(0) == '/') {
                        if (sql.startsWith(PING_MARKER)) {
                              doPingInstead();
                        
                              return this.results;
                        }
                  }
                  
                  checkForDml(sql, firstStatementChar);

                  if (this.results != null) {
                        if (!locallyScopedConn.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).

                  if (useServerFetch()) {
                        this.results = createResultSetUsingServerFetch(sql);

                        return this.results;
                  }

                  CancelTask timeoutTask = null;

                  String oldCatalog = null;

                  try {
                        if (locallyScopedConn.getEnableQueryTimeouts() &&
                                    this.timeoutInMillis != 0
                                    && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
                              timeoutTask = new CancelTask(this);
                              locallyScopedConn.getCancelTimer().schedule(timeoutTask,
                                          this.timeoutInMillis);
                        }

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

                        //
                        // Check if we have cached metadata for this query...
                        //

                        Field[] cachedFields = null;

                        if (locallyScopedConn.getCacheResultSetMetadata()) {
                              cachedMetaData = locallyScopedConn.getCachedMetaData(sql);

                              if (cachedMetaData != null) {
                                    cachedFields = cachedMetaData.fields;
                              }
                        }

                        if (locallyScopedConn.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 = locallyScopedConn.execSQL(this, sql,
                                                this.maxRows, null, this.resultSetType,
                                                this.resultSetConcurrency,
                                                doStreaming,
                                                this.currentCatalog, cachedFields);
                              } else {
                                    if (this.maxRows <= 0) {
                                          executeSimpleNonQuery(locallyScopedConn,
                                                      "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
                                    } else {
                                          executeSimpleNonQuery(locallyScopedConn,
                                                                  "SET OPTION SQL_SELECT_LIMIT=" + this.maxRows);
                                    }

                                    this.results = locallyScopedConn.execSQL(this, sql, -1,
                                                null, this.resultSetType,
                                                this.resultSetConcurrency,
                                                doStreaming,
                                                this.currentCatalog, cachedFields);

                                    if (oldCatalog != null) {
                                          locallyScopedConn.setCatalog(oldCatalog);
                                    }
                              }
                        } else {
                              this.results = locallyScopedConn.execSQL(this, sql, -1, null,
                                          this.resultSetType, this.resultSetConcurrency,
                                          doStreaming,
                                          this.currentCatalog, cachedFields);
                        }

                        if (timeoutTask != null) {
                              if (timeoutTask.caughtWhileCancelling != null) {
                                    throw timeoutTask.caughtWhileCancelling;
                              }

                              timeoutTask.cancel();
                              
                              locallyScopedConn.getCancelTimer().purge();
                              
                              timeoutTask = null;
                        }

                        synchronized (this.cancelTimeoutMutex) {
                              if (this.wasCancelled) {
                                    SQLException cause = null;
                                    
                                    if (this.wasCancelledByTimeout) {
                                          cause = new MySQLTimeoutException();
                                    } else {
                                          cause = new MySQLStatementCancelledException();
                                    }
                                    
                                    resetCancelledState();
                                    
                                    throw cause;
                              }
                        }
                  } finally {
                        if (timeoutTask != null) {
                              timeoutTask.cancel();
                              
                              locallyScopedConn.getCancelTimer().purge();
                        }

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

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

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

                  return this.results;
            }
      }

      protected synchronized void doPingInstead() throws SQLException {
            if (this.pingTarget != null) {
                  this.pingTarget.doPing();
            } else {
                  this.connection.ping();
            }

            ResultSetInternalMethods fakeSelectOneResultSet = generatePingResultSet();
            this.results = fakeSelectOneResultSet;
      }

      protected synchronized ResultSetInternalMethods generatePingResultSet() throws SQLException {
            Field[] fields = { new Field(null, "1", Types.BIGINT, 1) };
            ArrayList rows = new ArrayList();
            byte[] colVal = new byte[] { (byte) '1' };

            rows.add(new ByteArrayRow(new byte[][] { colVal }, getExceptionInterceptor()));

            return (ResultSetInternalMethods) DatabaseMetaData.buildResultSet(fields, rows,
                        this.connection);
      }
      
      protected void executeSimpleNonQuery(MySQLConnection c, String nonQuery)
                  throws SQLException {
            c.execSQL(this, nonQuery,
                        -1, null, ResultSet.TYPE_FORWARD_ONLY,
                        ResultSet.CONCUR_READ_ONLY, false, this.currentCatalog,
                        null, false).close();
      }

      /**
       * 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
       */
01616       public int executeUpdate(String sql) throws SQLException {
            return executeUpdate(sql, false, false);
      }

      protected synchronized int executeUpdate(String sql, boolean isBatch, boolean returnGeneratedKeys)
            throws SQLException {
            checkClosed();

            MySQLConnection locallyScopedConn = this.connection;

            char firstStatementChar = StringUtils.firstAlphaCharUc(sql,
                        findStartOfStatement(sql));

            ResultSetInternalMethods rs = null;

            synchronized (locallyScopedConn) {
                  this.retrieveGeneratedKeys = returnGeneratedKeys;
                  
                  resetCancelledState();

                  checkNullOrEmptyQuery(sql);

                  if (this.doEscapeProcessing) {
                        Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
                                    this.connection.serverSupportsConvertFn(), this.connection);

                        if (escapedSqlResult instanceof String) {
                              sql = (String) escapedSqlResult;
                        } else {
                              sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
                        }
                  }

                  if (locallyScopedConn.isReadOnly()) {
                        throw SQLError.createSQLException(Messages
                                    .getString("Statement.42") //$NON-NLS-1$
                                    + Messages.getString("Statement.43"), //$NON-NLS-1$
                                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
                  }

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

                  if (this.results != null) {
                        if (!locallyScopedConn.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

                  CancelTask timeoutTask = null;

                  String oldCatalog = null;

                  try {
                        if (locallyScopedConn.getEnableQueryTimeouts() &&
                                    this.timeoutInMillis != 0
                                    && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
                              timeoutTask = new CancelTask(this);
                              locallyScopedConn.getCancelTimer().schedule(timeoutTask,
                                          this.timeoutInMillis);
                        }

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

                        //
                        // Only apply max_rows to selects
                        //
                        if (locallyScopedConn.useMaxRows()) {
                              executeSimpleNonQuery(locallyScopedConn,
                                          "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
                        }

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

                        if (timeoutTask != null) {
                              if (timeoutTask.caughtWhileCancelling != null) {
                                    throw timeoutTask.caughtWhileCancelling;
                              }

                              timeoutTask.cancel();
                              
                              locallyScopedConn.getCancelTimer().purge();
                              
                              timeoutTask = null;
                        }

                        synchronized (this.cancelTimeoutMutex) {
                              if (this.wasCancelled) {
                                    SQLException cause = null;
                                    
                                    if (this.wasCancelledByTimeout) {
                                          cause = new MySQLTimeoutException();
                                    } else {
                                          cause = new MySQLStatementCancelledException();
                                    }
                                    
                                    resetCancelledState();
                                    
                                    throw cause;
                              }
                        }
                  } finally {
                        if (timeoutTask != null) {
                              timeoutTask.cancel();
                              
                              locallyScopedConn.getCancelTimer().purge();
                        }

                        if (oldCatalog != null) {
                              locallyScopedConn.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 StatementImpl#executeUpdate(String, int)
       */
01768       public synchronized int executeUpdate(String sql, int returnGeneratedKeys)
                  throws SQLException {
            if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
                  checkClosed();

                  MySQLConnection locallyScopedConn = this.connection;

                  synchronized (locallyScopedConn) {
                        // 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 = locallyScopedConn
                                    .isReadInfoMsgEnabled();
                        locallyScopedConn.setReadInfoMsgEnabled(true);

                        try {
                              return executeUpdate(sql, false, true);
                        } finally {
                              locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
                        }
                  }
            }

            return executeUpdate(sql);
      }

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

                  MySQLConnection locallyScopedConn = this.connection;

                  synchronized (locallyScopedConn) {
                        // 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 = locallyScopedConn
                                    .isReadInfoMsgEnabled();
                        locallyScopedConn.setReadInfoMsgEnabled(true);

                        try {
                              return executeUpdate(sql, false, true);
                        } finally {
                              locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
                        }
                  }
            }

            return executeUpdate(sql);
      }

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

                  MySQLConnection locallyScopedConn = this.connection;

                  synchronized (locallyScopedConn) {
                        // 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();
                        locallyScopedConn.setReadInfoMsgEnabled(true);

                        try {
                              return executeUpdate(sql, false, true);
                        } finally {
                              locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
                        }
                  }
            }

            return executeUpdate(sql);
      }

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

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

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

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

      /**
       * DOCUMENT ME!
       *
       * @return DOCUMENT ME!
       *
       * @throws SQLException
       *             DOCUMENT ME!
       */
01909       public synchronized java.sql.ResultSet getGeneratedKeys()
                  throws SQLException {
            if (!this.retrieveGeneratedKeys) {
                  throw SQLError.createSQLException(Messages.getString("Statement.GeneratedKeysNotRequested"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
            }
            
            if (this.batchedGeneratedKeys == null) {
                  if (lastQueryIsOnDupKeyUpdate)
                        return getGeneratedKeysInternal(1);
                  else
                        return getGeneratedKeysInternal();
            }

            Field[] fields = new Field[1];
            fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$
            fields[0].setConnection(this.connection);

            return com.mysql.jdbc.ResultSetImpl.getInstance(this.currentCatalog, fields,
                        new RowDataStatic(this.batchedGeneratedKeys), this.connection,
                        this, false);
      }

      /*
       * Needed because there's no concept of super.super to get to this
       * implementation from ServerPreparedStatement when dealing with batched
       * updates.
       */
      protected java.sql.ResultSet getGeneratedKeysInternal()
                  throws SQLException {
            int numKeys = getUpdateCount();
            return getGeneratedKeysInternal(numKeys);
      }

      protected synchronized java.sql.ResultSet getGeneratedKeysInternal(int numKeys)
                  throws SQLException {
            Field[] fields = new Field[1];
            fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$
            fields[0].setConnection(this.connection);
            fields[0].setUseOldNameMetadata(true);

            ArrayList rowSet = new ArrayList();

            long beginAt = getLastInsertID();
            
            if (beginAt < 0) { // looking at an UNSIGNED BIGINT that has overflowed
                  fields[0].setUnsigned();
            }

            if (this.results != null) {
                  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 /* BIGINT UNSIGNED can wrap the protocol representation */) && (numKeys > 0)) {
                        for (int i = 0; i < numKeys; i++) {
                              byte[][] row = new byte[1][];
                              if (beginAt > 0) {
                                    row[0] = Long.toString(beginAt).getBytes();
                              } else {
                                    byte[] asBytes = new byte[8];
                                    asBytes[7] = (byte) (beginAt & 0xff);
                                    asBytes[6] = (byte) (beginAt >>> 8);
                                    asBytes[5] = (byte) (beginAt >>> 16);
                                    asBytes[4] = (byte) (beginAt >>> 24);
                                    asBytes[3] = (byte) (beginAt >>> 32);
                                    asBytes[2] = (byte) (beginAt >>> 40);
                                    asBytes[1] = (byte) (beginAt >>> 48);
                                    asBytes[0] = (byte) (beginAt >>> 56);
                                    
                                    BigInteger val = new BigInteger(1, asBytes);

                                    row[0] = val.toString().getBytes();
                              }
                              rowSet.add(new ByteArrayRow(row, getExceptionInterceptor()));
                              beginAt  += this.connection.getAutoIncrementIncrement();
                        }
                  }
            }

            com.mysql.jdbc.ResultSetImpl gkRs = com.mysql.jdbc.ResultSetImpl.getInstance(this.currentCatalog, fields,
                        new RowDataStatic(rowSet), this.connection, this, false);
            
            this.openResults.add(gkRs);
            
            return gkRs;
      }

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

      /**
       * 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.
       */
02025       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.
       */
02041       public synchronized long getLongUpdateCount() {
            if (this.results == null) {
                  return -1;
            }

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

            return this.updateCount;
      }

      /**
       * 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
       */
02064       public synchronized int getMaxFieldSize() throws SQLException {
            return this.maxFieldSize;
      }

      /**
       * 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
       */
02078       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
       */
02095       public boolean getMoreResults() throws SQLException {
            return getMoreResults(CLOSE_CURRENT_RESULT);
      }

      /**
       * @see StatementImpl#getMoreResults(int)
       */
02102       public synchronized boolean getMoreResults(int current) throws SQLException {

            if (this.results == null) {
                  return false;
            }

            boolean streamingMode = createStreamingResultSet();
            
            if (streamingMode) {
                  if (this.results.reallyResult()) {
                        while (this.results.next()); // need to drain remaining rows to get to server status 
                                                             // which tells us whether more results actually exist or not
                  }
            }
            
            ResultSetInternalMethods nextResultSet = this.results.getNextResultSet();

            switch (current) {
            case java.sql.Statement.CLOSE_CURRENT_RESULT:

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

                  break;

            case java.sql.Statement.CLOSE_ALL_RESULTS:

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

                  closeAllOpenResults();

                  break;

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

                  this.results.clearNextResult(); // nobody besides us should
                  // ever need this value...
                  break;

            default:
                  throw SQLError.createSQLException(Messages
                              .getString("Statement.19"), //$NON-NLS-1$
                              SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$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;
      }

      /**
       * 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
       */
02188       public synchronized int getQueryTimeout() throws SQLException {
            return this.timeoutInMillis / 1000;
      }

      /**
       * Parses actual record count from 'info' message
       *
       * @param serverInfo
       *            DOCUMENT ME!
       *
       * @return DOCUMENT ME!
       */
02200       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;
      }

      /**
       * 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?)
       */
02270       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
       */
02283       public synchronized int getResultSetConcurrency() throws SQLException {
            return this.resultSetConcurrency;
      }

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

      protected synchronized ResultSetInternalMethods getResultSetInternal() {
            return this.results;
      }

      /**
       * JDBC 2.0 Determine the result set type.
       *
       * @return the ResultSet type (SCROLL_SENSITIVE or SCROLL_INSENSITIVE)
       *
       * @throws SQLException
       *             if an error occurs.
       */
02306       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
       */
02320       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
       */
02361       public synchronized java.sql.SQLWarning getWarnings() throws SQLException {
            checkClosed();

            if (this.connection != null && !this.connection.isClosed()
                        && 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;
      }

      /**
       * Closes this statement, and frees resources.
       *
       * @param calledExplicitly
       *            was this called from close()?
       *
       * @throws SQLException
       *             if an error occurs
       */
02390       protected synchronized void realClose(boolean calledExplicitly, boolean closeOpenResults)
                  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.connectionId, this.getId(),
                                    -1, System.currentTimeMillis(), 0,
                                    Constants.MILLIS_I18N, null, this.pointOfOrigin,
                                    message));
                  }
            }

            if (closeOpenResults) {
                  closeOpenResults = !this.holdResultsOpenOverClose;
            }
            
            if (closeOpenResults) {
                  if (this.results != null) {
                        
                        try {
                              this.results.close();
                        } catch (Exception ex) {
                              ;
                        }
                  }
                  
                  closeAllOpenResults();
            }

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

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

            this.isClosed = true;

            this.results = null;
            this.connection = null;
            this.warningChain = null;
            this.openResults = null;
            this.batchedGeneratedKeys = null;
            this.localInfileInputStream = null;
            this.pingTarget = null;
      }

      /**
       * 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
       */
02466       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
       */
02480       public synchronized void setEscapeProcessing(boolean enable)
                  throws SQLException {
            this.doEscapeProcessing = enable;
      }

      /**
       * 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
       */
02498       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 SQLError.createSQLException(
                              Messages.getString("Statement.5"), //$NON-NLS-1$
                              SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
            }
      }

      /**
       * 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.
       */
02526       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 SQLError.createSQLException(
                              Messages.getString("Statement.7"), //$NON-NLS-1$
                              SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$ //$NON-NLS-2$
            }

            this.fetchSize = rows;
      }

      public synchronized void setHoldResultsOpenOverClose(boolean holdResultsOpenOverClose) {
            this.holdResultsOpenOverClose = holdResultsOpenOverClose;
      }

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

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

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

            this.maxFieldSize = max;
      }

      /**
       * 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
       */
02582       public synchronized void setMaxRows(int max) throws SQLException {
            if ((max > MysqlDefs.MAX_ROWS) || (max < 0)) {
                  throw SQLError
                              .createSQLException(
                                          Messages.getString("Statement.15") + max //$NON-NLS-1$
                                                      + " > " //$NON-NLS-1$ //$NON-NLS-2$
                                                      + MysqlDefs.MAX_ROWS + ".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$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);
            }
      }

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

            this.timeoutInMillis = seconds * 1000;
      }

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

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

      protected synchronized void getBatchedGeneratedKeys(java.sql.Statement batchedStatement) throws SQLException {
            if (this.retrieveGeneratedKeys) {
                  java.sql.ResultSet rs = null;

                  try {
                        rs = batchedStatement.getGeneratedKeys();

                        while (rs.next()) {
                              this.batchedGeneratedKeys
                                          .add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor()));
                        }
                  } finally {
                        if (rs != null) {
                              rs.close();
                        }
                  }
            }
      }
      
      protected synchronized void getBatchedGeneratedKeys(int maxKeys) throws SQLException {
            if (this.retrieveGeneratedKeys) {
                  java.sql.ResultSet rs = null;

                  try {
                        if (maxKeys == 0)
                              rs = getGeneratedKeysInternal();
                        else
                              rs = getGeneratedKeysInternal(maxKeys);

                        while (rs.next()) {
                              this.batchedGeneratedKeys
                                          .add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor()));
                        }
                  } finally {
                        if (rs != null) {
                              rs.close();
                        }
                  }
            }
      }

      /**
       * @return
       */
02694       private synchronized boolean useServerFetch() throws SQLException {

            return this.connection.isCursorFetchEnabled() && this.fetchSize > 0
                        && this.resultSetConcurrency == ResultSet.CONCUR_READ_ONLY
                        && this.resultSetType == ResultSet.TYPE_FORWARD_ONLY;
      }

      public synchronized boolean isClosed() throws SQLException {
            return this.isClosed;
      }

      private boolean isPoolable = true;

      public boolean isPoolable() throws SQLException {
            return this.isPoolable;
      }

      public void setPoolable(boolean poolable) throws SQLException {
            this.isPoolable = poolable;
      }

      /**
     * Returns true if this either implements the interface argument or is directly or indirectly a wrapper
     * for an object that does. Returns false otherwise. If this implements the interface then return true,
     * else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped
     * object. If this does not implement the interface and is not a wrapper, return false.
     * This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that
     * callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method
     * returns true then calling <code>unwrap</code> with the same argument should succeed.
     *
     * @param interfaces a Class defining an interface.
     * @return true if this implements the interface or directly or indirectly wraps an object that does.
     * @throws java.sql.SQLException  if an error occurs while determining whether this is a wrapper
     * for an object with the given interface.
     * @since 1.6
     */
02730       public boolean isWrapperFor(Class iface) throws SQLException {
            checkClosed();

            // This works for classes that aren't actually wrapping
            // anything
            return iface.isInstance(this);
      }

    /**
     * Returns an object that implements the given interface to allow access to non-standard methods,
     * or standard methods not exposed by the proxy.
     * The result may be either the object found to implement the interface or a proxy for that object.
     * If the receiver implements the interface then that is the object. If the receiver is a wrapper
     * and the wrapped object implements the interface then that is the object. Otherwise the object is
     *  the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a
     * wrapper and does not implement the interface, then an <code>SQLException</code> is thrown.
     *
     * @param iface A Class defining an interface that the result must implement.
     * @return an object that implements the interface. May be a proxy for the actual implementing object.
     * @throws java.sql.SQLException If no object found that implements the interface
     * @since 1.6
     */
02752       public Object unwrap(Class iface) throws java.sql.SQLException {
      try {
            // This works for classes that aren't actually wrapping
            // anything
            return Util.cast(iface, this);
        } catch (ClassCastException cce) {
            throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(),
                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
        }
    }

      protected int findStartOfStatement(String sql) {
            int statementStartPos = 0;

            if (StringUtils.startsWithIgnoreCaseAndWs(sql, "/*")) {
                  statementStartPos = sql.indexOf("*/");

                  if (statementStartPos == -1) {
                        statementStartPos = 0;
                  } else {
                        statementStartPos += 2;
                  }
            } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "--")
                  || StringUtils.startsWithIgnoreCaseAndWs(sql, "#")) {
                  statementStartPos = sql.indexOf('\n');

                  if (statementStartPos == -1) {
                        statementStartPos = sql.indexOf('\r');

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

            return statementStartPos;
      }

      private InputStream localInfileInputStream;

02792     public synchronized InputStream getLocalInfileInputStream() {
        return this.localInfileInputStream;
    }

02796     public synchronized void setLocalInfileInputStream(InputStream stream) {
        this.localInfileInputStream = stream;
    }
    
    public synchronized void setPingTarget(PingTarget pingTarget) {
            this.pingTarget = pingTarget;
      }
    
    public ExceptionInterceptor getExceptionInterceptor() {
      return this.exceptionInterceptor;
    }
      
      protected boolean containsOnDuplicateKeyInString(String sql) {
            return getOnDuplicateKeyLocation(sql) != -1;
      }
      
      protected synchronized int getOnDuplicateKeyLocation(String sql) {
            return StringUtils.indexOfIgnoreCaseRespectMarker(0, 
                        sql, " ON DUPLICATE KEY UPDATE ", "\"'`", "\"'`", !this.connection.isNoBackslashEscapesSet());
      }
}

Generated by  Doxygen 1.6.0   Back to index