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

Statement.java

/*
 Copyright (C) 2002-2004 MySQL AB

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

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

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

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



 */
package com.mysql.jdbc;

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

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

import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimerTask;

/**
 * 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 ResultSet
 */
00065 public class Statement implements java.sql.Statement {
      class CachedResultSetMetaData {
            /** Map column names (and all of their permutations) to column indices */
            Map columnNameToIndex = null;

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

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

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

      /**
       * 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.
       */
00085       class CancelTask extends TimerTask {

            long connectionId = 0;

            CancelTask() throws SQLException {
                  connectionId = connection.getIO().getThreadId();
            }

            public void run() {

                  Thread cancelThread = new Thread() {

                        public void run() {
                              Connection cancelConn = null;
                              java.sql.Statement cancelStmt = null;

                              try {
                                    cancelConn = connection.duplicate();
                                    cancelStmt = cancelConn.createStatement();
                                    cancelStmt.execute("KILL QUERY " + connectionId);
                                    wasCancelled = true;
                              } catch (SQLException sqlEx) {
                                    throw new RuntimeException(sqlEx.toString());
                              } 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());
                                          }
                                    }
                              }
                        }
                  };

                  cancelThread.start();
            }
      }

      /** Used to generate IDs when profiling. */
00133       protected 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;

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

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

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

      /** The connection that created us */
00153       protected Connection connection = null;
      
      protected long connectionId = 0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      protected ArrayList batchedGeneratedKeys = null;

      protected boolean retrieveGeneratedKeys = false;

      protected boolean continueBatchOnError = 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.
       */
00252       public Statement(Connection 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); //$NON-NLS-1$ //$NON-NLS-2$
            }

            this.connection = c;
            this.connectionId = this.connection.getId();
            
            this.currentCatalog = catalog;
            this.pedantic = this.connection.getPedantic();
            this.continueBatchOnError = this.connection.getContinueBatchOnError();
            
            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();

            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 = ProfileEventSink.getInstance(this.connection);
            }

            int maxRowsConn = this.connection.getMaxRows();

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

      /**
       * DOCUMENT ME!
       * 
       * @param sql
       *            DOCUMENT ME!
       * 
       * @throws SQLException
       *             DOCUMENT ME!
       */
00321       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.
       */
00336       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
       */
00370       protected void checkClosed() throws SQLException {
            if (this.isClosed) {
                  throw SQLError.createSQLException(Messages
                              .getString("Statement.49"), //$NON-NLS-1$
                              SQLError.SQL_STATE_CONNECTION_NOT_OPEN); //$NON-NLS-1$
            }
      }

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

      /**
       * Method checkNullOrEmptyQuery.
       * 
       * @param sql
       *            the SQL to check
       * 
       * @throws SQLException
       *             if query is null or empty.
       */
00417       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); //$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); //$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
       */
00439       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?)
       */
00452       public 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
       */
00471       public void close() throws SQLException {
            realClose(true, true);
      }

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

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

                  this.openResults.clear();
            }
      }

      /**
       * @param sql
       * @return
       */
00498       private ResultSet createResultSetUsingServerFetch(String sql)
                  throws SQLException {
            java.sql.PreparedStatement pStmt = this.connection.prepareStatement(
                        sql, this.resultSetType, this.resultSetConcurrency);

            pStmt.setFetchSize(this.fetchSize);

            pStmt.execute();

            //
            // Need to be able to get resultset irrespective if we issued DML or
            // not to make this work.
            //
            ResultSet rs = ((com.mysql.jdbc.Statement) 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.
       */
00529       protected boolean createStreamingResultSet() {
            return ((this.resultSetType == java.sql.ResultSet.TYPE_FORWARD_ONLY)
                        && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY) && (this.fetchSize == Integer.MIN_VALUE));
      }

      /**
       * Workaround for containers that 'check' for sane values of
       * Statement.setFetchSize().
       * 
       * @throws SQLException
       */
00540       public void enableStreamingResults() throws SQLException {
            setFetchSize(Integer.MIN_VALUE);
            setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
      }

      /**
       * Execute a SQL statement that may return multiple results. We don't have
       * to worry about this since we do not support multiple ResultSets. You can
       * use getResultSet or getUpdateCount to retrieve the result.
       * 
       * @param sql
       *            any SQL statement
       * 
       * @return true if the next result is a ResulSet, false if it is an update
       *         count or there are no more results
       * 
       * @exception SQLException
       *                if a database access error occurs
       */
00559       public boolean execute(String sql) throws SQLException {
            checkClosed();
            

            Connection locallyScopedConn = this.connection;
            
            synchronized (locallyScopedConn.getMutex()) {
                  this.wasCancelled = false;
      
                  checkNullOrEmptyQuery(sql);
      
                  checkClosed();
      
                  char firstNonWsChar = StringUtils.firstNonWsCharUc(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); //$NON-NLS-1$
                        }
                  }
      
                  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);
                        }
                  }
      
                  CachedResultSetMetaData cachedMetaData = null;
      
                  ResultSet rs = null;
      
                  // If there isn't a limit clause in the SQL
                  // then limit the number of rows to return in
                  // an efficient manner. Only do this if
                  // setMaxRows() hasn't been used on any Statements
                  // generated from the current Connection (saves
                  // a query, and network traffic).
                  
                  this.batchedGeneratedKeys = null;
                  
                  if (useServerFetch()) {
                        rs = createResultSetUsingServerFetch(sql);
                  } else {
                        CancelTask timeoutTask = null;

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

                              String oldCatalog = null;

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

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

                              //
                              // 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) {
                                                      locallyScopedConn
                                                                  .execSQL(
                                                                              this,
                                                                              "SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, //$NON-NLS-1$
                                                                              null,
                                                                              java.sql.ResultSet.TYPE_FORWARD_ONLY,
                                                                              java.sql.ResultSet.CONCUR_READ_ONLY,
                                                                              false, 
                                                                              this.currentCatalog, true); //$NON-NLS-1$
                                                } else {
                                                      locallyScopedConn
                                                                  .execSQL(
                                                                              this,
                                                                              "SET OPTION SQL_SELECT_LIMIT=" + this.maxRows, //$NON-NLS-1$
                                                                              -1,
                                                                              null,
                                                                              java.sql.ResultSet.TYPE_FORWARD_ONLY,
                                                                              java.sql.ResultSet.CONCUR_READ_ONLY,
                                                                              false, 
                                                                              this.currentCatalog, true); //$NON-NLS-1$
                                                }
                                          }
                                    } else {
                                          locallyScopedConn
                                                      .execSQL(
                                                                  this,
                                                                  "SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, //$NON-NLS-1$
                                                                  java.sql.ResultSet.TYPE_FORWARD_ONLY,
                                                                  java.sql.ResultSet.CONCUR_READ_ONLY,
                                                                  false, this.currentCatalog,
                                                                  true); //$NON-NLS-1$
                                    }

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

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

                              if (this.wasCancelled) {
                                    this.wasCancelled = false;
                                    throw new MySQLTimeoutException();
                              }
                        } finally {
                              if (timeoutTask != null) {
                                    timeoutTask.cancel();
                              }
                        }
                  }

                  this.lastInsertId = rs.getUpdateID();

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

                        rs.setFirstCharOfQuery(firstNonWsChar);

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

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

      /**
       * @see Statement#execute(String, int)
       */
00748       public boolean execute(String sql, int returnGeneratedKeys)
                  throws SQLException {
            
            
            if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
                  checkClosed();
                  
                  Connection locallyScopedConn = this.connection;
                  
                  synchronized (locallyScopedConn.getMutex()) {
                        // If this is a 'REPLACE' query, we need to be able to parse
                        // the 'info' message returned from the server to determine
                        // the actual number of keys generated.
                        boolean readInfoMsgState = this.connection
                                    .isReadInfoMsgEnabled();
                        locallyScopedConn.setReadInfoMsgEnabled(true);

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

            return execute(sql);
      }

      /**
       * @see Statement#execute(String, int[])
       */
00779       public boolean execute(String sql, int[] generatedKeyIndices)
                  throws SQLException {
            if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
                  checkClosed();
                  
                  Connection locallyScopedConn = this.connection;
                  
                  synchronized (locallyScopedConn.getMutex()) {
                        // If this is a 'REPLACE' query, we need to be able to parse
                        // the 'info' message returned from the server to determine
                        // the actual number of keys generated.
                        boolean readInfoMsgState = locallyScopedConn
                                    .isReadInfoMsgEnabled();
                        locallyScopedConn.setReadInfoMsgEnabled(true);

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

            return execute(sql);
      }

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

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

                        try {
                              return execute(sql);
                        } 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!
       */
00848       public synchronized int[] executeBatch() throws SQLException {
            checkClosed();
            
            Connection 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); //$NON-NLS-1$
            }

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

            synchronized (locallyScopedConn.getMutex()) {
                  try {
                        this.retrieveGeneratedKeys = true;
                        
                        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);
                              }
                              
                              updateCounts = new int[nbrCommands];

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

                              SQLException sqlEx = null;

                              int commandIndex = 0;

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

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

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

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

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

      /**
       * 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
       */
00942       private int[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled,
                  int nbrCommands) throws SQLException {

            Connection locallyScopedConn = this.connection;
            
            if (!multiQueriesEnabled) {
                  locallyScopedConn.getIO().enableMultiQueries();
            }

            try {
                  int[] updateCounts = new int[nbrCommands];

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

                  int commandIndex = 0;

                  StringBuffer queryBuf = new StringBuffer();

                  java.sql.Statement batchStmt = locallyScopedConn.createStatement();

                  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;
                  
                  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 */
                  }
                  
                  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()) {
                              batchStmt.execute(queryBuf.toString());

                              updateCounts[counter++] = batchStmt.getUpdateCount();
                              long generatedKeyStart = ((com.mysql.jdbc.Statement)batchStmt).getLastInsertID();
                              byte[][] row = new byte[1][];
                              row[0] = Long.toString(generatedKeyStart++).getBytes();
                              this.batchedGeneratedKeys.add(row);

                              while (batchStmt.getMoreResults()
                                          || batchStmt.getUpdateCount() != -1) {
                                    updateCounts[counter++] = batchStmt.getUpdateCount();
                                    row = new byte[1][];
                                    row[0] = Long.toString(generatedKeyStart++).getBytes();
                                    this.batchedGeneratedKeys.add(row);
                              }

                              queryBuf = new StringBuffer();
                        }

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

                  if (queryBuf.length() > 0) {
                        batchStmt.execute(queryBuf.toString());

                        long generatedKeyStart = ((com.mysql.jdbc.Statement)batchStmt).getLastInsertID();
                        byte[][] row = new byte[1][];
                        row[0] = Long.toString(generatedKeyStart++).getBytes();
                        this.batchedGeneratedKeys.add(row);
                        
                        updateCounts[counter++] = batchStmt.getUpdateCount();

                        while (batchStmt.getMoreResults()
                                    || batchStmt.getUpdateCount() != -1) {
                              updateCounts[counter++] = batchStmt.getUpdateCount();
                              row = new byte[1][];
                              row[0] = Long.toString(generatedKeyStart++).getBytes();
                              this.batchedGeneratedKeys.add(row);
                        }
                  }

                  return (updateCounts != null) ? updateCounts : new int[0];
            } finally {
                  if (!multiQueriesEnabled) {
                        locallyScopedConn.getIO().disableMultiQueries();
                  }
            }
      }
      
      /**
       * 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
       */
01052       public java.sql.ResultSet executeQuery(String sql)
                  throws SQLException {
            checkClosed();
            
            Connection locallyScopedConn = this.connection;
            
            synchronized (locallyScopedConn.getMutex()) {
                  this.wasCancelled = false;
      
                  checkNullOrEmptyQuery(sql);
      
                  
      
                  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);
      
                  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;

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

                        String oldCatalog = null;

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

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

                        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,
                                                createStreamingResultSet(),
                                                this.currentCatalog, (cachedMetaData == null));
                              } else {
                                    if (this.maxRows <= 0) {
                                          locallyScopedConn
                                                      .execSQL(
                                                                  this,
                                                                  "SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, //$NON-NLS-1$
                                                                  java.sql.ResultSet.TYPE_FORWARD_ONLY,
                                                                  java.sql.ResultSet.CONCUR_READ_ONLY,
                                                                  false, this.currentCatalog,
                                                                  true); //$NON-NLS-1$
                                    } else {
                                          locallyScopedConn
                                                      .execSQL(
                                                                  this,
                                                                  "SET OPTION SQL_SELECT_LIMIT=" + this.maxRows, -1, //$NON-NLS-1$
                                                                  null,
                                                                  java.sql.ResultSet.TYPE_FORWARD_ONLY,
                                                                  java.sql.ResultSet.CONCUR_READ_ONLY,
                                                                  false, this.currentCatalog,
                                                                  true); //$NON-NLS-1$
                                    }

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

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

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

                        if (this.wasCancelled) {
                              this.wasCancelled = false;

                              throw new MySQLTimeoutException();
                        }
                  } finally {
                        if (timeoutTask != null) {
                              timeoutTask.cancel();
                        }
                  }

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

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

                  return this.results;
            }
      }

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

      protected int executeUpdate(String sql, boolean isBatch)
                  throws SQLException {
            checkClosed();
            
            Connection locallyScopedConn = this.connection;
            
            char firstStatementChar = StringUtils.firstNonWsCharUc(sql);

            ResultSet rs = null;

            synchronized (locallyScopedConn.getMutex()) {
                  this.wasCancelled = false;
      
                  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); //$NON-NLS-1$
                  }
      
                  if (StringUtils.startsWithIgnoreCaseAndWs(sql, "select")) { //$NON-NLS-1$
                        throw SQLError.createSQLException(Messages
                                    .getString("Statement.46"), //$NON-NLS-1$
                                    "01S03"); //$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;

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

                        String oldCatalog = null;

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

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

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

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

                        if (this.wasCancelled) {
                              this.wasCancelled = false;
                              throw new MySQLTimeoutException();
                        }
                  } finally {
                        if (timeoutTask != null) {
                              timeoutTask.cancel();
                        }
                  }
            }

            this.results = rs;

            rs.setFirstCharOfQuery(firstStatementChar);

            this.updateCount = rs.getUpdateCount();

            int truncatedUpdateCount = 0;

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

            this.lastInsertId = rs.getUpdateID();

            return truncatedUpdateCount;
      }

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

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

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

            return executeUpdate(sql);
      }

      /**
       * @see Statement#executeUpdate(String, int[])
       */
01394       public int executeUpdate(String sql, int[] generatedKeyIndices)
                  throws SQLException {
            if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
                  checkClosed();
                  
                  Connection locallyScopedConn = this.connection;
                  
                  synchronized (locallyScopedConn.getMutex()) {
                        // If this is a 'REPLACE' query, we need to be able to parse
                        // the 'info' message returned from the server to determine
                        // the actual number of keys generated.
                        boolean readInfoMsgState = locallyScopedConn
                                    .isReadInfoMsgEnabled();
                        locallyScopedConn.setReadInfoMsgEnabled(true);

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

            return executeUpdate(sql);
      }

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

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

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

            return executeUpdate(sql);
      }

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

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

      /**
       * Optimization to only use one calendar per-session, or calculate it for
       * each call, depending on user configuration
       */
01473       protected 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
       */
01490       public 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
       */
01502       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
       */
01514       public int getFetchSize() throws SQLException {
            return this.fetchSize;
      }

      /**
       * DOCUMENT ME!
       * 
       * @return DOCUMENT ME!
       * 
       * @throws SQLException
       *             DOCUMENT ME!
       */
01526       public java.sql.ResultSet getGeneratedKeys()
                  throws SQLException {
            if (this.batchedGeneratedKeys == null) {
                  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 new com.mysql.jdbc.ResultSet(this.currentCatalog, fields,
                        new RowDataStatic(this.batchedGeneratedKeys), this.connection,
                        this);
      }
      
      /*
       * 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 {
            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);

            ArrayList rowSet = new ArrayList();

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

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

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

      /**
       * Returns the id used when profiling
       * 
       * @return the id used when profiling.
       */
01587       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.
       */
01604       public 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.
       */
01620       public 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
       */
01643       public 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
       */
01657       public 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
       */
01674       public boolean getMoreResults() throws SQLException {
            return getMoreResults(CLOSE_CURRENT_RESULT);
      }

      /**
       * @see Statement#getMoreResults(int)
       */
01681       public boolean getMoreResults(int current) throws SQLException {

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

            ResultSet nextResultSet = this.results.getNextResultSet();

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

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

                  break;

            case java.sql.Statement.CLOSE_ALL_RESULTS:

                  if (this.results != null) {
                        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); //$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
       */
01752       public int getQueryTimeout() throws SQLException {
            return this.timeoutInMillis / 1000;
      }

      /**
       * Parses actual record count from 'info' message
       * 
       * @param serverInfo
       *            DOCUMENT ME!
       * 
       * @return DOCUMENT ME!
       */
01764       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?)
       */
01834       public 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
       */
01847       public int getResultSetConcurrency() throws SQLException {
            return this.resultSetConcurrency;
      }

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

      protected ResultSet 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.
       */
01870       public 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
       */
01884       public 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
       */
01925       public 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;
      }

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

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

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

                        cachedMetaData.metadata = resultSet.getMetaData();

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

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

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

      /**
       * Closes this statement, and frees resources.
       * 
       * @param calledExplicitly
       *            was this called from close()?
       * 
       * @throws SQLException
       *             if an error occurs
       */
02004       protected 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,
                                    null, this.pointOfOrigin, message));
                  }
            }

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

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

                        this.closeAllOpenResults();
                  }
            }

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

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

            this.results = null;
            this.connection = null;
            this.warningChain = null;
            this.openResults = null;
            this.batchedGeneratedKeys = null;
            this.isClosed = true;
      }

      /**
       * 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
       */
02076       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
       */
02090       public 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
       */
02108       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); //$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.
       */
02136       public 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); //$NON-NLS-1$ //$NON-NLS-2$
            }

            this.fetchSize = rows;
      }

      protected 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
       */
02161       public void setMaxFieldSize(int max) throws SQLException {
            if (max < 0) {
                  throw SQLError.createSQLException(Messages
                              .getString("Statement.11"), //$NON-NLS-1$
                              SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
            }

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

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

            this.maxFieldSize = max;
      }

      /**
       * 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
       */
02192       public 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); //$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
       */
02230       public void setQueryTimeout(int seconds) throws SQLException {
            if (seconds < 0) {
                  throw SQLError.createSQLException(Messages
                              .getString("Statement.21"), //$NON-NLS-1$
                              SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
            }

            this.timeoutInMillis = seconds * 1000;
      }

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

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

      protected 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 byte[][] { rs.getBytes(1) });
                        }
                  } finally {
                        if (rs != null) {
                              rs.close();
                        }
                  }
            }
      }
      
      protected void getBatchedGeneratedKeys() throws SQLException {
            if (this.retrieveGeneratedKeys) {
                  java.sql.ResultSet rs = null;
      
                  try {
                        rs = getGeneratedKeysInternal();
      
                        while (rs.next()) {
                              this.batchedGeneratedKeys
                                          .add(new byte[][] { rs.getBytes(1) });
                        }
                  } finally {
                        if (rs != null) {
                              rs.close();
                        }
                  }
            }
      }
      
      /**
       * @return
       */
02301       private boolean useServerFetch() throws SQLException {

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

Generated by  Doxygen 1.6.0   Back to index