Statement.java
上传用户:tanyanyong
上传日期:2013-06-23
资源大小:1355k
文件大小:49k
- /*
- Copyright (C) 2002 MySQL AB
- 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; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- package com.mysql.jdbc;
- import java.io.UnsupportedEncodingException;
- import java.sql.SQLException;
- import java.sql.SQLWarning;
- import java.sql.Types;
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
- /**
- * A Statement object is used for executing a static SQL statement and
- * obtaining the results produced by it.
- *
- * <p>
- * Only one ResultSet per Statement can be open at any point in time.
- * Therefore, if the reading of one ResultSet is interleaved with the reading
- * of another, each must have been generated by different Statements. All
- * statement execute methods implicitly close a statement's current ResultSet
- * if an open one exists.
- * </p>
- *
- * @author Mark Matthews
- * @version $Id: Statement.java,v 1.20.2.17 2004/02/13 22:31:25 mmatthew Exp $
- *
- * @see java.sql.Statement
- * @see ResultSet
- */
- public class Statement implements java.sql.Statement {
- /** The connection that created us */
- protected Connection connection = null;
- /** Holds batched commands */
- protected List batchedArgs;
- /** List of currently-open ResultSets */
- protected List openResults = new ArrayList();
- /** The next result set */
- protected ResultSet nextResults = null;
- /** The current results */
- protected ResultSet results = null;
- /** The warnings chain. */
- protected SQLWarning warningChain = null;
-
- /** The pending warnings chain */
- protected SQLWarning pendingWarnings = null;
- /** The character converter to use (if available) */
- protected SingleByteCharsetConverter charConverter = null;
- /** The character encoding to use (if available) */
- protected String charEncoding = null;
- /** The catalog in use */
- protected String currentCatalog = null;
- /** Should we process escape codes? */
- protected boolean doEscapeProcessing = true;
- /** Has this statement been closed? */
- protected boolean isClosed = false;
- /** Has someone changed this for this statement? */
- protected boolean maxRowsChanged = false;
- /** Are we in pedantic mode? */
- protected boolean pedantic = false;
- /** The max field size for this statement */
- protected int maxFieldSize = MysqlIO.getMaxBuf();
- /**
- * The maximum number of rows to return for this statement (-1 means _all_
- * rows)
- */
- protected int maxRows = -1;
- /** The concurrency for this result set (updatable or not) */
- protected int resultSetConcurrency = 0;
- /** The type of this result set (scroll sensitive or in-sensitive) */
- protected int resultSetType = 0;
- /** The timeout for a query */
- protected int timeout = 0;
- /** The auto_increment value for the last insert */
- protected long lastInsertId = -1;
- /** The update count for this statement */
- protected long updateCount = -1;
- /** The number of rows to fetch at a time (currently ignored) */
- private int fetchSize = 0;
- /** Does the server support CAST/CONVERT? */
- private boolean serverSupportsConvertFn;
-
- /**
- * 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.
- */
- public Statement(Connection c, String catalog) throws SQLException {
- if (Driver.TRACE) {
- Object[] args = { c };
- Debug.methodCall(this, "constructor", args);
- }
- if ((c == null) || ((com.mysql.jdbc.Connection) c).isClosed()) {
- throw new SQLException("Connection is closed.", "08003");
- }
- this.connection = c;
- this.currentCatalog = catalog;
- this.pedantic = this.connection.isPedantic();
- this.serverSupportsConvertFn = this.connection.getIO().versionMeetsMinimum(4, 0, 2);
- //
- // Adjust, if we know it
- //
- if (connection != null) {
- maxFieldSize = connection.getMaxAllowedPacket();
- }
- if (this.connection.useUnicode()) {
- this.charEncoding = connection.getEncoding();
- this.charConverter = this.connection.getCharsetConverter(this.charEncoding);
- }
-
- int maxRowsConn = this.connection.getMaxRows();
-
- if (maxRowsConn != -1) {
- setMaxRows(maxRowsConn);
- }
- }
- /**
- * JDBC 2.0 Return the Connection that produced the Statement.
- *
- * @return the Connection that produced the Statement
- *
- * @throws SQLException if an error occurs
- */
- public java.sql.Connection getConnection() throws SQLException {
- return (java.sql.Connection) connection;
- }
- /**
- * setCursorName defines the SQL cursor name that will be used by
- * subsequent execute methods. This name can then be used in SQL
- * positioned update/delete statements to identify the current row in the
- * ResultSet generated by this statement. If a database doesn't support
- * positioned update/delete, this method is a no-op.
- *
- * <p>
- * <b>Note:</b> This MySQL driver does not support cursors.
- * </p>
- *
- * @param name the new cursor name
- *
- * @exception java.sql.SQLException if a database access error occurs
- */
- public void setCursorName(String name) throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = { name };
- Debug.methodCall(this, "setCursorName", args);
- }
- // 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 java.sql.SQLException if a database access error occurs
- */
- public void setEscapeProcessing(boolean enable)
- throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = { new Boolean(enable) };
- Debug.methodCall(this, "setEscapeProcessing", args);
- }
- doEscapeProcessing = enable;
- }
- //--------------------------JDBC 2.0-----------------------------
- /**
- * JDBC 2.0 Give a hint as to the direction in which the rows in a result
- * set will be processed. The hint applies only to result sets created
- * using this Statement object. The default value is
- * ResultSet.FETCH_FORWARD.
- *
- * @param direction the initial direction for processing rows
- *
- * @exception SQLException if a database-access error occurs or direction
- * is not one of ResultSet.FETCH_FORWARD,
- * ResultSet.FETCH_REVERSE, or ResultSet.FETCH_UNKNOWN
- */
- public void setFetchDirection(int direction) throws SQLException {
- switch (direction) {
- case java.sql.ResultSet.FETCH_FORWARD:
- case java.sql.ResultSet.FETCH_REVERSE:
- case java.sql.ResultSet.FETCH_UNKNOWN:
- break;
- default:
- throw new SQLException("Illegal value for setFetchDirection()",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- }
- /**
- * JDBC 2.0 Determine the fetch direction.
- *
- * @return the default fetch direction
- *
- * @exception SQLException if a database-access error occurs
- */
- public int getFetchDirection() throws SQLException {
- return java.sql.ResultSet.FETCH_FORWARD;
- }
- /**
- * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that
- * should be fetched from the database when more rows are needed. The
- * number of rows specified only affects result sets created using this
- * statement. If the value specified is zero, then the hint is ignored.
- * The default value is zero.
- *
- * @param rows the number of rows to fetch
- *
- * @exception SQLException if a database-access error occurs, or the
- * condition 0 <= rows <= this.getMaxRows() is not
- * satisfied.
- */
- public void setFetchSize(int rows) throws SQLException {
- if (((rows < 0) && (rows != Integer.MIN_VALUE))
- || ((maxRows != 0) && (maxRows != -1)
- && (rows > this.getMaxRows()))) {
- throw new SQLException("Illegal value for setFetchSize()", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- fetchSize = rows;
- }
- /**
- * JDBC 2.0 Determine the default fetch size.
- *
- * @return the number of rows to fetch at a time
- *
- * @throws SQLException if an error occurs
- */
- public int getFetchSize() throws SQLException {
- return fetchSize;
- }
- /**
- * DOCUMENT ME!
- *
- * @return DOCUMENT ME!
- *
- * @throws SQLException DOCUMENT ME!
- */
- public java.sql.ResultSet getGeneratedKeys() throws SQLException {
- Field[] fields = new Field[1];
- fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17);
- ArrayList rowSet = new ArrayList();
- long beginAt = getLastInsertID();
- int numKeys = getUpdateCount();
- String serverInfo = this.results.getServerInfo();
- //
- // Only parse server info messages for 'REPLACE'
- // queries
- //
-
- if ((numKeys > 0)
- && this.results.getFirstCharOfQuery() == 'R'
- && (serverInfo != null)
- && (serverInfo.length() > 0)) {
- numKeys = getRecordCountFromInfo(serverInfo);
- }
- if ((beginAt > 0) && (numKeys > 0)) {
- for (int i = 0; i < numKeys; i++) {
- byte[][] row = new byte[1][];
- row[0] = Long.toString(beginAt++).getBytes();
- rowSet.add(row);
- }
- }
- return new com.mysql.jdbc.ResultSet(currentCatalog, fields,
- new RowDataStatic(rowSet), connection);
- }
- /**
- * 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.
- */
- public long getLastInsertID() {
- if (Driver.TRACE) {
- Object[] args = new Object[0];
- Debug.methodCall(this, "getLastInsertID", args);
- }
- return 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.
- */
- public long getLongUpdateCount() {
- if (Driver.TRACE) {
- Object[] args = new Object[0];
- Debug.methodCall(this, "getLongUpdateCount", args);
- }
- if (results == null) {
- return -1;
- }
- if (results.reallyResult()) {
- return -1;
- }
- return updateCount;
- }
- /**
- * Sets the maxFieldSize
- *
- * @param max the new max column size limit; zero means unlimited
- *
- * @exception SQLException if size exceeds buffer size
- * @throws SQLException DOCUMENT ME!
- */
- public void setMaxFieldSize(int max) throws SQLException {
- if (Driver.TRACE) {
- Object[] args = { new Integer(max) };
- Debug.methodCall(this, "setMaxFieldSize", args);
- }
- if (max < 0) {
- throw new SQLException("Illegal value for setMaxFieldSize()",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- int maxBuf = (connection != null) ? connection.getMaxAllowedPacket()
- : MysqlIO.getMaxBuf();
- if (max > maxBuf) {
- throw new java.sql.SQLException(
- "Can not set max field size > max allowed packet: " + maxBuf,
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- } else {
- maxFieldSize = max;
- }
- }
- /**
- * The maxFieldSize limit (in bytes) is the maximum amount of data returned
- * for any column value; it only applies to BINARY, VARBINARY,
- * LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR columns. If the limit is
- * exceeded, the excess data is silently discarded.
- *
- * @return the current max column size limit; zero means unlimited
- *
- * @exception java.sql.SQLException if a database access error occurs
- */
- public int getMaxFieldSize() throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = new Object[0];
- Debug.methodCall(this, "getMaxFieldSize", args);
- }
- return maxFieldSize;
- }
- /**
- * Set the maximum number of rows
- *
- * @param max the new max rows limit; zero means unlimited
- *
- * @exception java.sql.SQLException if a database access error occurs
- *
- * @see getMaxRows
- */
- public void setMaxRows(int max) throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = { new Integer(max) };
- Debug.methodCall(this, "setMaxRows", args);
- }
- if ((max > MysqlDefs.MAX_ROWS) || (max < 0)) {
- throw new java.sql.SQLException("setMaxRows() out of range. " + max
- + " > " + MysqlDefs.MAX_ROWS + ".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- if (max == 0) {
- max = -1;
- }
- this.maxRows = max;
- this.maxRowsChanged = true;
- if (maxRows == -1) {
- 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.
- connection.maxRowsChanged(this);
- }
- }
- /**
- * The maxRows limit is set to limit the number of rows that any ResultSet
- * can contain. If the limit is exceeded, the excess rows are silently
- * dropped.
- *
- * @return the current maximum row limit; zero means unlimited
- *
- * @exception java.sql.SQLException if a database access error occurs
- */
- public int getMaxRows() throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = new Object[0];
- Debug.methodCall(this, "getMaxRows", args);
- }
- if (maxRows <= 0) {
- return 0;
- } else {
- return 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 java.sql.SQLException if a database access error occurs
- */
- public boolean getMoreResults() throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = new Object[0];
- Debug.methodCall(this, "getMoreResults", args);
- }
- return getMoreResults(CLOSE_CURRENT_RESULT);
- }
- /**
- * @see Statement#getMoreResults(int)
- */
- public synchronized boolean getMoreResults(int current)
- throws SQLException {
- switch (current) {
- case Statement.CLOSE_CURRENT_RESULT:
- if (results != null) {
- results.close();
- }
- break;
- case Statement.CLOSE_ALL_RESULTS:
- if (results != null) {
- results.close();
- }
- closeAllOpenResults();
- break;
- case Statement.KEEP_CURRENT_RESULT:
- openResults.add(results);
- break;
- default:
- throw new SQLException("Illegal flag for getMoreResults(int)",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- results = nextResults;
- nextResults = null;
- return ((results != null) && results.reallyResult()) ? true : false;
- }
- /**
- * Sets the queryTimeout limit
- *
- * @param seconds - the new query timeout limit in seconds
- *
- * @exception SQLException if a database access error occurs
- */
- public void setQueryTimeout(int seconds) throws SQLException {
- if (Driver.TRACE) {
- Object[] args = { new Integer(seconds) };
- Debug.methodCall(this, "setQueryTimeout", args);
- }
- if (seconds < 0) {
- throw new SQLException("Illegal value for setQueryTimeout()",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- timeout = seconds;
- }
- /**
- * The queryTimeout limit is the number of seconds the driver will wait for
- * a Statement to execute. If the limit is exceeded, a
- * java.sql.SQLException is thrown.
- *
- * @return the current query timeout limit in seconds; 0 = unlimited
- *
- * @exception java.sql.SQLException if a database access error occurs
- */
- public int getQueryTimeout() throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = new Object[0];
- Debug.methodCall(this, "getQueryTimeout", args);
- }
- return timeout;
- }
- /**
- * getResultSet returns the current result as a ResultSet. It should only
- * be called once per result.
- *
- * @return the current result set; null if there are no more
- *
- * @exception java.sql.SQLException if a database access error occurs
- * (why?)
- */
- public synchronized java.sql.ResultSet getResultSet()
- throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = new Object[0];
- Debug.methodCall(this, "getResultSet", args);
- }
- return ((results != null) && results.reallyResult())
- ? (java.sql.ResultSet) results : null;
- }
- /**
- * JDBC 2.0 Determine the result set concurrency.
- *
- * @return CONCUR_UPDATABLE or CONCUR_READONLY
- *
- * @throws SQLException if an error occurs
- */
- public int getResultSetConcurrency() throws SQLException {
- return resultSetConcurrency;
- }
- /**
- * @see Statement#getResultSetHoldability()
- */
- public int getResultSetHoldability() throws SQLException {
- return ResultSet.HOLD_CURSORS_OVER_COMMIT;
- }
- /**
- * JDBC 2.0 Determine the result set type.
- *
- * @return the ResultSet type (SCROLL_SENSITIVE or SCROLL_INSENSITIVE)
- *
- * @throws SQLException if an error occurs.
- */
- public int getResultSetType() throws SQLException {
- return 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 java.sql.SQLException if a database access error occurs
- */
- public synchronized int getUpdateCount() throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = new Object[0];
- Debug.methodCall(this, "getUpdateCount", args);
- }
- if (results == null) {
- return -1;
- }
- if (results.reallyResult()) {
- return -1;
- }
- int truncatedUpdateCount = 0;
- if (results.getUpdateCount() > Integer.MAX_VALUE) {
- truncatedUpdateCount = Integer.MAX_VALUE;
- } else {
- truncatedUpdateCount = (int) 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 on null
- *
- * @exception java.sql.SQLException if a database access error occurs
- */
- public synchronized java.sql.SQLWarning getWarnings()
- throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = new Object[0];
- Debug.methodCall(this, "getWarnings", args);
- }
- return warningChain;
- }
- /**
- * DOCUMENT ME!
- *
- * @param sql DOCUMENT ME!
- *
- * @throws SQLException DOCUMENT ME!
- */
- public synchronized void addBatch(String sql) throws SQLException {
- if (batchedArgs == null) {
- batchedArgs = new ArrayList();
- }
- if (sql != null) {
- batchedArgs.add(sql);
- }
- }
- /**
- * Cancel can be used by one thread to cancel a statement that is being
- * executed by another thread. However this driver is synchronous, so
- * this really has no meaning - we define it as a no-op (i.e. you can't
- * cancel, but there is no error if you try.)
- *
- * @exception java.sql.SQLException only because thats the spec.
- */
- public void cancel() throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = new Object[0];
- Debug.methodCall(this, "cancel", args);
- }
- // No-op
- }
- /**
- * JDBC 2.0 Make the set of commands in the current batch empty. This
- * method is optional.
- *
- * @exception SQLException if a database-access error occurs, or the driver
- * does not support batch statements
- */
- public synchronized void clearBatch() throws SQLException {
- if (batchedArgs != null) {
- batchedArgs.clear();
- }
- }
- /**
- * After this call, getWarnings returns null until a new warning is
- * reported for this Statement.
- *
- * @exception java.sql.SQLException if a database access error occurs
- * (why?)
- */
- public synchronized void clearWarnings() throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = new Object[0];
- Debug.methodCall(this, "clearWarnings", args);
- }
- this.warningChain = this.pendingWarnings;
- this.pendingWarnings = 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 java.sql.SQLException if a database access error occurs
- */
- public synchronized void close() throws java.sql.SQLException {
- if (Driver.TRACE) {
- Object[] args = new Object[0];
- Debug.methodCall(this, "close", args);
- }
- if (this.isClosed) {
- return;
- }
-
- if (results != null) {
- try {
- results.close();
- } catch (Exception ex) {
- ;
- }
- }
- if (this.maxRowsChanged && this.connection != null) {
- this.connection.unsetMaxRows(this);
- }
- this.results = null;
- this.connection = null;
- this.warningChain = null;
- this.isClosed = true;
- this.closeAllOpenResults();
- this.openResults = null;
- }
- /**
- * 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
- */
- public synchronized boolean execute(String sql) throws SQLException {
- if (Driver.TRACE) {
- Object[] args = { sql };
- Debug.methodCall(this, "execute", args);
- }
- char firstNonWsChar = StringUtils.firstNonWsCharUc(sql);
- if (connection.isReadOnly()) {
- if (firstNonWsChar != 'S') {
- throw new SQLException("Connection is read-only. "
- + "Queries leading to data modification are not allowed",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- }
- checkClosed();
- if (this.doEscapeProcessing) {
- sql = EscapeProcessor.escapeSQL(sql, this.serverSupportsConvertFn);
- }
- if (results != null) {
- results.close();
- }
- ResultSet rs = null;
- // If there isn't a limit clause in the SQL
- // then limit the number of rows to return in
- // an efficient manner. Only do this if
- // setMaxRows() hasn't been used on any Statements
- // generated from the current Connection (saves
- // a query, and network traffic).
- synchronized (connection.getMutex()) {
- clearWarnings();
-
- String oldCatalog = null;
- if (!connection.getCatalog().equals(currentCatalog)) {
- oldCatalog = connection.getCatalog();
- connection.setCatalog(currentCatalog);
- }
- boolean isSelect = (firstNonWsChar == 'S');
- //
- // Only apply max_rows to selects
- //
- if (connection.useMaxRows()) {
- int rowLimit = -1;
-
- if (isSelect) {
- if (sql.toUpperCase().indexOf("LIMIT") != -1) {
- rowLimit = this.maxRows;
- } else {
- if (maxRows <= 0) {
- connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT",
- -1, this.currentCatalog);
- } else {
- connection.execSQL("SET OPTION SQL_SELECT_LIMIT="
- + maxRows, -1, this.currentCatalog);
- }
- }
- } else {
- connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT",
- -1, this.currentCatalog);
- }
- // Finally, execute the query
- rs = connection.execSQL(sql, rowLimit, resultSetConcurrency,
- createStreamingResultSet(), isSelect,
- this.currentCatalog);
- } else {
- rs = connection.execSQL(sql, -1, resultSetConcurrency,
- createStreamingResultSet(), isSelect,
- this.currentCatalog);
- }
- if (oldCatalog != null) {
- connection.setCatalog(oldCatalog);
- }
- }
- lastInsertId = rs.getUpdateID();
- if (rs != null) {
- this.results = rs;
- }
- rs.setFirstCharOfQuery(firstNonWsChar);
- rs.setConnection(connection);
- rs.setResultSetType(resultSetType);
- rs.setResultSetConcurrency(resultSetConcurrency);
- return ((rs != null) && rs.reallyResult());
- }
- /**
- * @see Statement#execute(String, int)
- */
- public boolean execute(String sql, int returnGeneratedKeys)
- throws SQLException {
- if (returnGeneratedKeys == Statement.RETURN_GENERATED_KEYS) {
- checkClosed();
- synchronized (this.connection.getMutex()) {
- // If this is a 'REPLACE' query, we need to be able to parse
- // the 'info' message returned from the server to determine
- // the actual number of keys generated.
- boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
- this.connection.setReadInfoMsgEnabled(true);
- try {
- return execute(sql);
- } finally {
- this.connection.setReadInfoMsgEnabled(readInfoMsgState);
- }
- }
- } else {
- return execute(sql);
- }
- }
- /**
- * @see Statement#execute(String, int[])
- */
- public boolean execute(String sql, int[] generatedKeyIndices)
- throws SQLException {
- if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
- checkClosed();
- synchronized (this.connection.getMutex()) {
- // If this is a 'REPLACE' query, we need to be able to parse
- // the 'info' message returned from the server to determine
- // the actual number of keys generated.
- boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
- this.connection.setReadInfoMsgEnabled(true);
- try {
- return execute(sql);
- } finally {
- this.connection.setReadInfoMsgEnabled(readInfoMsgState);
- }
- }
- } else {
- return execute(sql);
- }
- }
- /**
- * @see Statement#execute(String, String[])
- */
- public boolean execute(String sql, String[] generatedKeyNames)
- throws SQLException {
- if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
- checkClosed();
- synchronized (this.connection.getMutex()) {
- // If this is a 'REPLACE' query, we need to be able to parse
- // the 'info' message returned from the server to determine
- // the actual number of keys generated.
- boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
- this.connection.setReadInfoMsgEnabled(true);
- try {
- return execute(sql);
- } finally {
- this.connection.setReadInfoMsgEnabled(readInfoMsgState);
- }
- }
- } else {
- 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
- */
- public synchronized int[] executeBatch() throws SQLException {
- if (connection.isReadOnly()) {
- throw new SQLException("Connection is read-only. "
- + "Queries leading to data modification are not allowed",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- try {
- clearWarnings();
-
- int[] updateCounts = null;
- if (batchedArgs != null) {
- int nbrCommands = batchedArgs.size();
- updateCounts = new int[nbrCommands];
- for (int i = 0; i < nbrCommands; i++) {
- updateCounts[i] = -3;
- }
- SQLException sqlEx = null;
- int commandIndex = 0;
- for (commandIndex = 0; commandIndex < nbrCommands;
- commandIndex++) {
- try {
- updateCounts[commandIndex] = executeUpdate((String) batchedArgs
- .get(commandIndex), false);
- } catch (SQLException ex) {
- updateCounts[commandIndex] = EXECUTE_FAILED;
- if (this.connection.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 {
- clearBatch();
- }
- }
- /**
- * Execute a SQL statement that retruns a single ResultSet
- *
- * @param sql typically a static SQL SELECT statement
- *
- * @return a ResulSet that contains the data produced by the query
- *
- * @exception SQLException if a database access error occurs
- */
- public synchronized java.sql.ResultSet executeQuery(String sql)
- throws SQLException {
- if (Driver.TRACE) {
- Object[] args = { sql };
- Debug.methodCall(this, "executeQuery", args);
- }
- checkClosed();
- if (this.doEscapeProcessing) {
- sql = EscapeProcessor.escapeSQL(sql, this.serverSupportsConvertFn);
- }
- char firstStatementChar = StringUtils.firstNonWsCharUc(sql);
- if ((firstStatementChar == 'I') || (firstStatementChar == 'U')
- || (firstStatementChar == 'D') || (firstStatementChar == 'A')
- || (firstStatementChar == 'C')) {
- if (StringUtils.startsWithIgnoreCaseAndWs(sql, "INSERT")
- || StringUtils.startsWithIgnoreCaseAndWs(sql, "UPDATE")
- || StringUtils.startsWithIgnoreCaseAndWs(sql, "DELETE")
- || StringUtils.startsWithIgnoreCaseAndWs(sql, "DROP")
- || StringUtils.startsWithIgnoreCaseAndWs(sql, "CREATE")
- || StringUtils.startsWithIgnoreCaseAndWs(sql, "ALTER")) {
- throw new SQLException("Can not issue data manipulation statements with executeQuery()",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- }
- if (results != null) {
- results.close();
- }
- // If there isn't a limit clause in the SQL
- // then limit the number of rows to return in
- // an efficient manner. Only do this if
- // setMaxRows() hasn't been used on any Statements
- // generated from the current Connection (saves
- // a query, and network traffic).
- synchronized (connection.getMutex()) {
- clearWarnings();
-
- String oldCatalog = null;
- if (!connection.getCatalog().equals(currentCatalog)) {
- oldCatalog = connection.getCatalog();
- connection.setCatalog(currentCatalog);
- }
- if (connection.useMaxRows()) {
- // We need to execute this all together
- // So synchronize on the Connection's mutex (because
- // even queries going through there synchronize
- // on the connection
- if (sql.toUpperCase().indexOf("LIMIT") != -1) {
- results = connection.execSQL(sql, maxRows,
- resultSetConcurrency, createStreamingResultSet(),
- true, this.currentCatalog);
- } else {
- if (maxRows <= 0) {
- connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT",
- -1, this.currentCatalog);
- } else {
- connection.execSQL("SET OPTION SQL_SELECT_LIMIT="
- + maxRows, -1, this.currentCatalog);
- }
- results = connection.execSQL(sql, -1, resultSetConcurrency,
- createStreamingResultSet(), true,
- this.currentCatalog);
- if (oldCatalog != null) {
- connection.setCatalog(oldCatalog);
- }
- }
- } else {
- results = connection.execSQL(sql, -1, resultSetConcurrency,
- createStreamingResultSet(), true, this.currentCatalog);
- }
- if (oldCatalog != null) {
- connection.setCatalog(oldCatalog);
- }
- }
- lastInsertId = results.getUpdateID();
- nextResults = results;
- results.setConnection(connection);
- results.setResultSetType(resultSetType);
- results.setResultSetConcurrency(resultSetConcurrency);
- results.setStatement(this);
- if (!results.reallyResult()) {
- if (!connection.getAutoCommit()) {
- connection.rollback();
- }
- throw new SQLException("Can not issue INSERT/UPDATE/DELETE with executeQuery()",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- return (java.sql.ResultSet) 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
- */
- public synchronized int executeUpdate(String sql) throws SQLException {
- return executeUpdate(sql, true);
- }
-
- private synchronized int executeUpdate(String sql, boolean clearWarnings) throws SQLException {
- if (Driver.TRACE) {
- Object[] args = { sql };
- Debug.methodCall(this, "executeUpdate", args);
- }
- if (connection.isReadOnly()) {
- throw new SQLException("Connection is read-only. "
- + "Queries leading to data modification are not allowed",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- char firstStatementChar = StringUtils.firstNonWsCharUc(sql);
- if ((firstStatementChar == 'S')
- && StringUtils.startsWithIgnoreCaseAndWs(sql, "select")) {
- throw new SQLException("Can not issue SELECT via executeUpdate()",
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- checkClosed();
- if (this.doEscapeProcessing) {
- sql = EscapeProcessor.escapeSQL(sql, this.serverSupportsConvertFn);
- }
- // The checking and changing of catalogs
- // must happen in sequence, so synchronize
- // on the same mutex that _conn is using
- ResultSet rs = null;
- synchronized (connection.getMutex()) {
- if (clearWarnings) {
- clearWarnings();
- }
-
- String oldCatalog = null;
- if (!connection.getCatalog().equals(currentCatalog)) {
- oldCatalog = connection.getCatalog();
- connection.setCatalog(currentCatalog);
- }
- //
- // Only apply max_rows to selects
- //
- if (connection.useMaxRows()) {
- connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1,
- this.currentCatalog);
- }
- rs = connection.execSQL(sql, -1,
- java.sql.ResultSet.CONCUR_READ_ONLY, false, false,
- this.currentCatalog);
- rs.setConnection(connection);
- if (oldCatalog != null) {
- connection.setCatalog(oldCatalog);
- }
- }
- this.results = rs;
- rs.setFirstCharOfQuery(firstStatementChar);
-
- updateCount = rs.getUpdateCount();
- int truncatedUpdateCount = 0;
- if (updateCount > Integer.MAX_VALUE) {
- truncatedUpdateCount = Integer.MAX_VALUE;
- } else {
- truncatedUpdateCount = (int) updateCount;
- }
- lastInsertId = rs.getUpdateID();
- return truncatedUpdateCount;
- }
- /**
- * @see Statement#executeUpdate(String, int)
- */
- public int executeUpdate(String sql, int returnGeneratedKeys)
- throws SQLException {
- if (returnGeneratedKeys == Statement.RETURN_GENERATED_KEYS) {
- checkClosed();
- synchronized (this.connection.getMutex()) {
- // If this is a 'REPLACE' query, we need to be able to parse
- // the 'info' message returned from the server to determine
- // the actual number of keys generated.
- boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
- this.connection.setReadInfoMsgEnabled(true);
- try {
- return executeUpdate(sql);
- } finally {
- this.connection.setReadInfoMsgEnabled(readInfoMsgState);
- }
- }
- } else {
- return executeUpdate(sql);
- }
- }
- /**
- * @see Statement#executeUpdate(String, int[])
- */
- public int executeUpdate(String sql, int[] generatedKeyIndices)
- throws SQLException {
- if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
- checkClosed();
- synchronized (this.connection.getMutex()) {
- // If this is a 'REPLACE' query, we need to be able to parse
- // the 'info' message returned from the server to determine
- // the actual number of keys generated.
- boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
- this.connection.setReadInfoMsgEnabled(true);
- try {
- return executeUpdate(sql);
- } finally {
- this.connection.setReadInfoMsgEnabled(readInfoMsgState);
- }
- }
- } else {
- return executeUpdate(sql);
- }
- }
- /**
- * @see Statement#executeUpdate(String, String[])
- */
- public int executeUpdate(String sql, String[] generatedKeyNames)
- throws SQLException {
- if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
- checkClosed();
- synchronized (this.connection.getMutex()) {
- // If this is a 'REPLACE' query, we need to be able to parse
- // the 'info' message returned from the server to determine
- // the actual number of keys generated.
- boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
- this.connection.setReadInfoMsgEnabled(true);
- try {
- return executeUpdate(sql);
- } finally {
- this.connection.setReadInfoMsgEnabled(readInfoMsgState);
- }
- }
- } else {
- return executeUpdate(sql);
- }
- }
- /**
- * Checks if closed() has been called, and throws an exception if so
- *
- * @throws SQLException if this statement has been closed
- */
- protected void checkClosed() throws SQLException {
- if (this.isClosed) {
- throw new SQLException("No operations allowed after statement closed",
- SQLError.SQL_STATE_GENERAL_ERROR);
- }
- }
- /**
- * Close any open result sets that have been 'held open'
- */
- protected void closeAllOpenResults() {
- if (this.openResults != null) {
- for (Iterator iter = this.openResults.iterator(); iter.hasNext();) {
- ResultSet element = (ResultSet) iter.next();
- try {
- element.close();
- } catch (SQLException sqlEx) {
- AssertionFailedException.shouldNotHappen(sqlEx);
- }
- }
- this.openResults.clear();
- }
- }
- /**
- * We only stream result sets when they are forward-only, read-only, and
- * the fetch size has been set to Integer.MIN_VALUE
- *
- * @return true if this result set should be streamed row at-a-time, rather
- * than read all at once.
- */
- protected boolean createStreamingResultSet() {
- return ((resultSetType == java.sql.ResultSet.TYPE_FORWARD_ONLY)
- && (resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY)
- && (fetchSize == Integer.MIN_VALUE));
- }
- /**
- * Sets the concurrency for result sets generated by this statement
- *
- * @param concurrencyFlag DOCUMENT ME!
- */
- void setResultSetConcurrency(int concurrencyFlag) {
- resultSetConcurrency = concurrencyFlag;
- }
- /**
- * Sets the result set type for result sets generated by this statement
- *
- * @param typeFlag DOCUMENT ME!
- */
- void setResultSetType(int typeFlag) {
- resultSetType = typeFlag;
- }
-
- protected void addWarning(SQLWarning warning) {
- if (this.pendingWarnings == null) {
- this.pendingWarnings = warning;
- } else {
- // find the 'end'... this could be handled more
- // efficiently, but the only thing that currently
- // sets 'client-side' warnings is prepared statements
- // when clipping +/- INF and NaN.
-
- SQLWarning lastWarning = this.pendingWarnings;
-
- while (lastWarning.getNextWarning() != null) {
- lastWarning = lastWarning.getNextWarning();
- }
-
- lastWarning.setNextWarning(warning);
- }
-
- }
- /**
- * Parses actual record count from 'info' message
- *
- * @param serverInfo DOCUMENT ME!
- *
- * @return DOCUMENT ME!
- */
- 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;
- }
- }