Statement.java
上传用户:sxlinghang
上传日期:2022-07-20
资源大小:1405k
文件大小:49k
源码类别:

数据库编程

开发平台:

Java

  1. /*
  2.    Copyright (C) 2002 MySQL AB
  3.       This program is free software; you can redistribute it and/or modify
  4.       it under the terms of the GNU General Public License as published by
  5.       the Free Software Foundation; either version 2 of the License, or
  6.       (at your option) any later version.
  7.       This program is distributed in the hope that it will be useful,
  8.       but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.       GNU General Public License for more details.
  11.       You should have received a copy of the GNU General Public License
  12.       along with this program; if not, write to the Free Software
  13.       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  14.  */
  15. package com.mysql.jdbc;
  16. import java.io.UnsupportedEncodingException;
  17. import java.sql.SQLException;
  18. import java.sql.SQLWarning;
  19. import java.sql.Types;
  20. import java.util.ArrayList;
  21. import java.util.Iterator;
  22. import java.util.List;
  23. /**
  24.  * A Statement object is used for executing a static SQL statement and
  25.  * obtaining the results produced by it.
  26.  * 
  27.  * <p>
  28.  * Only one ResultSet per Statement can be open at any point in time.
  29.  * Therefore, if the reading of one ResultSet is interleaved with the reading
  30.  * of another, each must have been generated by different Statements.  All
  31.  * statement execute methods implicitly close a statement's current ResultSet
  32.  * if an open one exists.
  33.  * </p>
  34.  *
  35.  * @author Mark Matthews
  36.  * @version $Id: Statement.java,v 1.20.2.17 2004/02/13 22:31:25 mmatthew Exp $
  37.  *
  38.  * @see java.sql.Statement
  39.  * @see ResultSet
  40.  */
  41. public class Statement implements java.sql.Statement {
  42.     /** The connection that created us */
  43.     protected Connection connection = null;
  44.     /** Holds batched commands */
  45.     protected List batchedArgs;
  46.     /** List of currently-open ResultSets */
  47.     protected List openResults = new ArrayList();
  48.     /** The next result set */
  49.     protected ResultSet nextResults = null;
  50.     /** The current results */
  51.     protected ResultSet results = null;
  52.     /** The warnings chain. */
  53.     protected SQLWarning warningChain = null;
  54.     
  55.     /** The pending warnings chain */
  56.     protected SQLWarning pendingWarnings = null;
  57.     /** The character converter to use (if available) */
  58.     protected SingleByteCharsetConverter charConverter = null;
  59.     /** The character encoding to use (if available) */
  60.     protected String charEncoding = null;
  61.     /** The catalog in use */
  62.     protected String currentCatalog = null;
  63.     /** Should we process escape codes? */
  64.     protected boolean doEscapeProcessing = true;
  65.     /** Has this statement been closed? */
  66.     protected boolean isClosed = false;
  67.     /** Has someone changed this for this statement? */
  68.     protected boolean maxRowsChanged = false;
  69.     /** Are we in pedantic mode? */
  70.     protected boolean pedantic = false;
  71.     /** The max field size for this statement */
  72.     protected int maxFieldSize = MysqlIO.getMaxBuf();
  73.     /**
  74.      * The maximum number of rows to return for this statement (-1 means _all_
  75.      * rows)
  76.      */
  77.     protected int maxRows = -1;
  78.     /** The concurrency for this result set (updatable or not) */
  79.     protected int resultSetConcurrency = 0;
  80.     /** The type of this result set (scroll sensitive or in-sensitive) */
  81.     protected int resultSetType = 0;
  82.     /** The timeout for a query */
  83.     protected int timeout = 0;
  84.     /** The auto_increment value for the last insert */
  85.     protected long lastInsertId = -1;
  86.     /** The update count for this statement */
  87.     protected long updateCount = -1;
  88.     /** The number of rows to fetch at a time (currently ignored) */
  89.     private int fetchSize = 0;
  90.     /** Does the server support CAST/CONVERT? */
  91.     private boolean serverSupportsConvertFn;
  92.     
  93.     /**
  94.      * Constructor for a Statement.
  95.      *
  96.      * @param c the Connection instantation that creates us
  97.      * @param catalog the database name in use when we were created
  98.      *
  99.      * @throws SQLException if an error occurs.
  100.      */
  101.     public Statement(Connection c, String catalog) throws SQLException {
  102.         if (Driver.TRACE) {
  103.             Object[] args = { c };
  104.             Debug.methodCall(this, "constructor", args);
  105.         }
  106.         if ((c == null) || ((com.mysql.jdbc.Connection) c).isClosed()) {
  107.             throw new SQLException("Connection is closed.", "08003");
  108.         }
  109.         this.connection = c;
  110.         this.currentCatalog = catalog;
  111.         this.pedantic = this.connection.isPedantic();
  112.         this.serverSupportsConvertFn = this.connection.getIO().versionMeetsMinimum(4, 0, 2);
  113.         //
  114.         // Adjust, if we know it
  115.         //
  116.         if (connection != null) {
  117.             maxFieldSize = connection.getMaxAllowedPacket();
  118.         }
  119.         if (this.connection.useUnicode()) {
  120.             this.charEncoding = connection.getEncoding();
  121.             this.charConverter = this.connection.getCharsetConverter(this.charEncoding); 
  122.         }
  123.         
  124.         int maxRowsConn = this.connection.getMaxRows();
  125.         
  126.         if (maxRowsConn != -1) {
  127.          setMaxRows(maxRowsConn);
  128.         }
  129.     }
  130.     /**
  131.      * JDBC 2.0  Return the Connection that produced the Statement.
  132.      *
  133.      * @return the Connection that produced the Statement
  134.      *
  135.      * @throws SQLException if an error occurs
  136.      */
  137.     public java.sql.Connection getConnection() throws SQLException {
  138.         return (java.sql.Connection) connection;
  139.     }
  140.     /**
  141.      * setCursorName defines the SQL cursor name that will be used by
  142.      * subsequent execute methods.  This name can then be used in SQL
  143.      * positioned update/delete statements to identify the current row in the
  144.      * ResultSet generated by this statement.  If a database doesn't support
  145.      * positioned update/delete, this method is a no-op.
  146.      * 
  147.      * <p>
  148.      * <b>Note:</b> This MySQL driver does not support cursors.
  149.      * </p>
  150.      *
  151.      * @param name the new cursor name
  152.      *
  153.      * @exception java.sql.SQLException if a database access error occurs
  154.      */
  155.     public void setCursorName(String name) throws java.sql.SQLException {
  156.         if (Driver.TRACE) {
  157.             Object[] args = { name };
  158.             Debug.methodCall(this, "setCursorName", args);
  159.         }
  160.         // No-op
  161.     }
  162.     /**
  163.      * If escape scanning is on (the default), the driver will do escape
  164.      * substitution before sending the SQL to the database.
  165.      *
  166.      * @param enable true to enable; false to disable
  167.      *
  168.      * @exception java.sql.SQLException if a database access error occurs
  169.      */
  170.     public void setEscapeProcessing(boolean enable)
  171.         throws java.sql.SQLException {
  172.         if (Driver.TRACE) {
  173.             Object[] args = { new Boolean(enable) };
  174.             Debug.methodCall(this, "setEscapeProcessing", args);
  175.         }
  176.         doEscapeProcessing = enable;
  177.     }
  178.     //--------------------------JDBC 2.0-----------------------------
  179.     /**
  180.      * JDBC 2.0 Give a hint as to the direction in which the rows in a result
  181.      * set will be processed. The hint applies only to result sets created
  182.      * using this Statement object.  The default value is
  183.      * ResultSet.FETCH_FORWARD.
  184.      *
  185.      * @param direction the initial direction for processing rows
  186.      *
  187.      * @exception SQLException if a database-access error occurs or direction
  188.      *            is not one of ResultSet.FETCH_FORWARD,
  189.      *            ResultSet.FETCH_REVERSE, or ResultSet.FETCH_UNKNOWN
  190.      */
  191.     public void setFetchDirection(int direction) throws SQLException {
  192.         switch (direction) {
  193.         case java.sql.ResultSet.FETCH_FORWARD:
  194.         case java.sql.ResultSet.FETCH_REVERSE:
  195.         case java.sql.ResultSet.FETCH_UNKNOWN:
  196.             break;
  197.         default:
  198.             throw new SQLException("Illegal value for setFetchDirection()",
  199.                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  200.         }
  201.     }
  202.     /**
  203.      * JDBC 2.0 Determine the fetch direction.
  204.      *
  205.      * @return the default fetch direction
  206.      *
  207.      * @exception SQLException if a database-access error occurs
  208.      */
  209.     public int getFetchDirection() throws SQLException {
  210.         return java.sql.ResultSet.FETCH_FORWARD;
  211.     }
  212.     /**
  213.      * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that
  214.      * should  be fetched from the database when more rows are needed.  The
  215.      * number  of rows specified only affects result sets created using this
  216.      * statement. If the value specified is zero, then the hint is ignored.
  217.      * The default value is zero.
  218.      *
  219.      * @param rows the number of rows to fetch
  220.      *
  221.      * @exception SQLException if a database-access error occurs, or the
  222.      *            condition 0 &lt;= rows &lt;= this.getMaxRows() is not
  223.      *            satisfied.
  224.      */
  225.     public void setFetchSize(int rows) throws SQLException {
  226.         if (((rows < 0) && (rows != Integer.MIN_VALUE))
  227.                 || ((maxRows != 0) && (maxRows != -1)
  228.                 && (rows > this.getMaxRows()))) {
  229.             throw new SQLException("Illegal value for setFetchSize()", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  230.         }
  231.         fetchSize = rows;
  232.     }
  233.     /**
  234.      * JDBC 2.0 Determine the default fetch size.
  235.      *
  236.      * @return the number of rows to fetch at a time
  237.      *
  238.      * @throws SQLException if an error occurs
  239.      */
  240.     public int getFetchSize() throws SQLException {
  241.         return fetchSize;
  242.     }
  243.     /**
  244.      * DOCUMENT ME!
  245.      *
  246.      * @return DOCUMENT ME!
  247.      *
  248.      * @throws SQLException DOCUMENT ME!
  249.      */
  250.     public java.sql.ResultSet getGeneratedKeys() throws SQLException {
  251.         Field[] fields = new Field[1];
  252.         fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17);
  253.         ArrayList rowSet = new ArrayList();
  254.         long beginAt = getLastInsertID();
  255.         int numKeys = getUpdateCount();
  256.         String serverInfo = this.results.getServerInfo();
  257. // 
  258. // Only parse server info messages for 'REPLACE'
  259. // queries
  260. //
  261.         if ((numKeys > 0) 
  262.          && this.results.getFirstCharOfQuery() == 'R'
  263.          && (serverInfo != null)
  264.                 && (serverInfo.length() > 0)) {
  265.             numKeys = getRecordCountFromInfo(serverInfo);
  266.         }
  267.         if ((beginAt > 0) && (numKeys > 0)) {
  268.             for (int i = 0; i < numKeys; i++) {
  269.                 byte[][] row = new byte[1][];
  270.                 row[0] = Long.toString(beginAt++).getBytes();
  271.                 rowSet.add(row);
  272.             }
  273.         }
  274.         return new com.mysql.jdbc.ResultSet(currentCatalog, fields,
  275.             new RowDataStatic(rowSet), connection);
  276.     }
  277.     /**
  278.      * getLastInsertID returns the value of the auto_incremented key after an
  279.      * executeQuery() or excute() call.
  280.      * 
  281.      * <p>
  282.      * This gets around the un-threadsafe behavior of "select LAST_INSERT_ID()"
  283.      * which is tied to the Connection that created this Statement, and
  284.      * therefore could have had many INSERTS performed before one gets a
  285.      * chance to call "select LAST_INSERT_ID()".
  286.      * </p>
  287.      *
  288.      * @return the last update ID.
  289.      */
  290.     public long getLastInsertID() {
  291.         if (Driver.TRACE) {
  292.             Object[] args = new Object[0];
  293.             Debug.methodCall(this, "getLastInsertID", args);
  294.         }
  295.         return lastInsertId;
  296.     }
  297.     /**
  298.      * getLongUpdateCount returns the current result as an update count, if the
  299.      * result is a ResultSet or there are no more results, -1 is returned.  It
  300.      * should only be called once per result.
  301.      * 
  302.      * <p>
  303.      * This method returns longs as MySQL server versions newer than  3.22.4
  304.      * return 64-bit values for update counts
  305.      * </p>
  306.      *
  307.      * @return the current update count.
  308.      */
  309.     public long getLongUpdateCount() {
  310.         if (Driver.TRACE) {
  311.             Object[] args = new Object[0];
  312.             Debug.methodCall(this, "getLongUpdateCount", args);
  313.         }
  314.         if (results == null) {
  315.             return -1;
  316.         }
  317.         if (results.reallyResult()) {
  318.             return -1;
  319.         }
  320.         return updateCount;
  321.     }
  322.     /**
  323.      * Sets the maxFieldSize
  324.      *
  325.      * @param max the new max column size limit; zero means unlimited
  326.      *
  327.      * @exception SQLException if size exceeds buffer size
  328.      * @throws SQLException DOCUMENT ME!
  329.      */
  330.     public void setMaxFieldSize(int max) throws SQLException {
  331.         if (Driver.TRACE) {
  332.             Object[] args = { new Integer(max) };
  333.             Debug.methodCall(this, "setMaxFieldSize", args);
  334.         }
  335.         if (max < 0) {
  336.             throw new SQLException("Illegal value for setMaxFieldSize()",
  337.                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  338.         }
  339.         int maxBuf = (connection != null) ? connection.getMaxAllowedPacket()
  340.                                           : MysqlIO.getMaxBuf();
  341.         if (max > maxBuf) {
  342.             throw new java.sql.SQLException(
  343.                 "Can not set max field size > max allowed packet: " + maxBuf,
  344.                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  345.         } else {
  346.             maxFieldSize = max;
  347.         }
  348.     }
  349.     /**
  350.      * The maxFieldSize limit (in bytes) is the maximum amount of data returned
  351.      * for any column value; it only applies to BINARY, VARBINARY,
  352.      * LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR columns.  If the limit is
  353.      * exceeded, the excess data is silently discarded.
  354.      *
  355.      * @return the current max column size limit; zero means unlimited
  356.      *
  357.      * @exception java.sql.SQLException if a database access error occurs
  358.      */
  359.     public int getMaxFieldSize() throws java.sql.SQLException {
  360.         if (Driver.TRACE) {
  361.             Object[] args = new Object[0];
  362.             Debug.methodCall(this, "getMaxFieldSize", args);
  363.         }
  364.         return maxFieldSize;
  365.     }
  366.     /**
  367.      * Set the maximum number of rows
  368.      *
  369.      * @param max the new max rows limit; zero means unlimited
  370.      *
  371.      * @exception java.sql.SQLException if a database access error occurs
  372.      *
  373.      * @see getMaxRows
  374.      */
  375.     public void setMaxRows(int max) throws java.sql.SQLException {
  376.         if (Driver.TRACE) {
  377.             Object[] args = { new Integer(max) };
  378.             Debug.methodCall(this, "setMaxRows", args);
  379.         }
  380.         if ((max > MysqlDefs.MAX_ROWS) || (max < 0)) {
  381.             throw new java.sql.SQLException("setMaxRows() out of range. " + max
  382.                 + " > " + MysqlDefs.MAX_ROWS + ".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  383.         }
  384.         if (max == 0) {
  385.             max = -1;
  386.         }
  387.         this.maxRows = max;
  388.         this.maxRowsChanged = true;
  389.         if (maxRows == -1) {
  390.             connection.unsetMaxRows(this);
  391.             this.maxRowsChanged = false;
  392.         } else {
  393.             // Most people don't use setMaxRows()
  394.             // so don't penalize them
  395.             // with the extra query it takes
  396.             // to do it efficiently unless we need
  397.             // to.
  398.             connection.maxRowsChanged(this);
  399.         }
  400.     }
  401.     /**
  402.      * The maxRows limit is set to limit the number of rows that any ResultSet
  403.      * can contain.  If the limit is exceeded, the excess rows are silently
  404.      * dropped.
  405.      *
  406.      * @return the current maximum row limit; zero means unlimited
  407.      *
  408.      * @exception java.sql.SQLException if a database access error occurs
  409.      */
  410.     public int getMaxRows() throws java.sql.SQLException {
  411.         if (Driver.TRACE) {
  412.             Object[] args = new Object[0];
  413.             Debug.methodCall(this, "getMaxRows", args);
  414.         }
  415.         if (maxRows <= 0) {
  416.             return 0;
  417.         } else {
  418.             return maxRows;
  419.         }
  420.     }
  421.     /**
  422.      * getMoreResults moves to a Statement's next result.  If it returns true,
  423.      * this result is a ResulSet.
  424.      *
  425.      * @return true if the next ResultSet is valid
  426.      *
  427.      * @exception java.sql.SQLException if a database access error occurs
  428.      */
  429.     public boolean getMoreResults() throws java.sql.SQLException {
  430.         if (Driver.TRACE) {
  431.             Object[] args = new Object[0];
  432.             Debug.methodCall(this, "getMoreResults", args);
  433.         }
  434.         return getMoreResults(CLOSE_CURRENT_RESULT);
  435.     }
  436.     /**
  437.      * @see Statement#getMoreResults(int)
  438.      */
  439.     public synchronized boolean getMoreResults(int current)
  440.         throws SQLException {
  441.         switch (current) {
  442.         case Statement.CLOSE_CURRENT_RESULT:
  443.             if (results != null) {
  444.                 results.close();
  445.             }
  446.             break;
  447.         case Statement.CLOSE_ALL_RESULTS:
  448.             if (results != null) {
  449.                 results.close();
  450.             }
  451.             closeAllOpenResults();
  452.             break;
  453.         case Statement.KEEP_CURRENT_RESULT:
  454.             openResults.add(results);
  455.             break;
  456.         default:
  457.             throw new SQLException("Illegal flag for getMoreResults(int)",
  458.                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  459.         }
  460.         results = nextResults;
  461.         nextResults = null;
  462.         return ((results != null) && results.reallyResult()) ? true : false;
  463.     }
  464.     /**
  465.      * Sets the queryTimeout limit
  466.      *
  467.      * @param seconds - the new query timeout limit in seconds
  468.      *
  469.      * @exception SQLException if a database access error occurs
  470.      */
  471.     public void setQueryTimeout(int seconds) throws SQLException {
  472.         if (Driver.TRACE) {
  473.             Object[] args = { new Integer(seconds) };
  474.             Debug.methodCall(this, "setQueryTimeout", args);
  475.         }
  476.         if (seconds < 0) {
  477.             throw new SQLException("Illegal value for setQueryTimeout()",
  478.                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  479.         }
  480.         timeout = seconds;
  481.     }
  482.     /**
  483.      * The queryTimeout limit is the number of seconds the driver will wait for
  484.      * a Statement to execute.  If the limit is exceeded, a
  485.      * java.sql.SQLException is thrown.
  486.      *
  487.      * @return the current query timeout limit in seconds; 0 = unlimited
  488.      *
  489.      * @exception java.sql.SQLException if a database access error occurs
  490.      */
  491.     public int getQueryTimeout() throws java.sql.SQLException {
  492.         if (Driver.TRACE) {
  493.             Object[] args = new Object[0];
  494.             Debug.methodCall(this, "getQueryTimeout", args);
  495.         }
  496.         return timeout;
  497.     }
  498.     /**
  499.      * getResultSet returns the current result as a ResultSet.  It should only
  500.      * be called once per result.
  501.      *
  502.      * @return the current result set; null if there are no more
  503.      *
  504.      * @exception java.sql.SQLException if a database access error occurs
  505.      *            (why?)
  506.      */
  507.     public synchronized java.sql.ResultSet getResultSet()
  508.         throws java.sql.SQLException {
  509.         if (Driver.TRACE) {
  510.             Object[] args = new Object[0];
  511.             Debug.methodCall(this, "getResultSet", args);
  512.         }
  513.         return ((results != null) && results.reallyResult())
  514.         ? (java.sql.ResultSet) results : null;
  515.     }
  516.     /**
  517.      * JDBC 2.0 Determine the result set concurrency.
  518.      *
  519.      * @return CONCUR_UPDATABLE or CONCUR_READONLY
  520.      *
  521.      * @throws SQLException if an error occurs
  522.      */
  523.     public int getResultSetConcurrency() throws SQLException {
  524.         return resultSetConcurrency;
  525.     }
  526.     /**
  527.      * @see Statement#getResultSetHoldability()
  528.      */
  529.     public int getResultSetHoldability() throws SQLException {
  530.         return ResultSet.HOLD_CURSORS_OVER_COMMIT;
  531.     }
  532.     /**
  533.      * JDBC 2.0 Determine the result set type.
  534.      *
  535.      * @return the ResultSet type (SCROLL_SENSITIVE or SCROLL_INSENSITIVE)
  536.      *
  537.      * @throws SQLException if an error occurs.
  538.      */
  539.     public int getResultSetType() throws SQLException {
  540.         return resultSetType;
  541.     }
  542.     /**
  543.      * getUpdateCount returns the current result as an update count, if the
  544.      * result is a ResultSet or there are no more results, -1 is returned.  It
  545.      * should only be called once per result.
  546.      *
  547.      * @return the current result as an update count.
  548.      *
  549.      * @exception java.sql.SQLException if a database access error occurs
  550.      */
  551.     public synchronized int getUpdateCount() throws java.sql.SQLException {
  552.         if (Driver.TRACE) {
  553.             Object[] args = new Object[0];
  554.             Debug.methodCall(this, "getUpdateCount", args);
  555.         }
  556.         if (results == null) {
  557.             return -1;
  558.         }
  559.         if (results.reallyResult()) {
  560.             return -1;
  561.         }
  562.         int truncatedUpdateCount = 0;
  563.         if (results.getUpdateCount() > Integer.MAX_VALUE) {
  564.             truncatedUpdateCount = Integer.MAX_VALUE;
  565.         } else {
  566.             truncatedUpdateCount = (int) results.getUpdateCount();
  567.         }
  568.         return truncatedUpdateCount;
  569.     }
  570.     /**
  571.      * The first warning reported by calls on this Statement is returned.  A
  572.      * Statement's execute methods clear its java.sql.SQLWarning chain.
  573.      * Subsequent Statement warnings will be chained to this
  574.      * java.sql.SQLWarning.
  575.      * 
  576.      * <p>
  577.      * The Warning chain is automatically cleared each time a statement is
  578.      * (re)executed.
  579.      * </p>
  580.      * 
  581.      * <p>
  582.      * <B>Note:</B>  If you are processing a ResultSet then any warnings
  583.      * associated with ResultSet reads will be chained on the ResultSet
  584.      * object.
  585.      * </p>
  586.      *
  587.      * @return the first java.sql.SQLWarning on null
  588.      *
  589.      * @exception java.sql.SQLException if a database access error occurs
  590.      */
  591.     public synchronized java.sql.SQLWarning getWarnings()
  592.         throws java.sql.SQLException {
  593.         if (Driver.TRACE) {
  594.             Object[] args = new Object[0];
  595.             Debug.methodCall(this, "getWarnings", args);
  596.         }
  597.         return warningChain;
  598.     }
  599.     /**
  600.      * DOCUMENT ME!
  601.      *
  602.      * @param sql DOCUMENT ME!
  603.      *
  604.      * @throws SQLException DOCUMENT ME!
  605.      */
  606.     public synchronized void addBatch(String sql) throws SQLException {
  607.         if (batchedArgs == null) {
  608.             batchedArgs = new ArrayList();
  609.         }
  610.         if (sql != null) {
  611.             batchedArgs.add(sql);
  612.         }
  613.     }
  614.     /**
  615.      * Cancel can be used by one thread to cancel a statement that is being
  616.      * executed by another thread.  However this driver is synchronous, so
  617.      * this really has no meaning - we define it as a no-op (i.e. you can't
  618.      * cancel, but there is no error if you try.)
  619.      *
  620.      * @exception java.sql.SQLException only because thats the spec.
  621.      */
  622.     public void cancel() throws java.sql.SQLException {
  623.         if (Driver.TRACE) {
  624.             Object[] args = new Object[0];
  625.             Debug.methodCall(this, "cancel", args);
  626.         }
  627.         // No-op
  628.     }
  629.     /**
  630.      * JDBC 2.0 Make the set of commands in the current batch empty. This
  631.      * method is optional.
  632.      *
  633.      * @exception SQLException if a database-access error occurs, or the driver
  634.      *            does not support batch statements
  635.      */
  636.     public synchronized void clearBatch() throws SQLException {
  637.         if (batchedArgs != null) {
  638.             batchedArgs.clear();
  639.         }
  640.     }
  641.     /**
  642.      * After this call, getWarnings returns null until a new warning is
  643.      * reported for this Statement.
  644.      *
  645.      * @exception java.sql.SQLException if a database access error occurs
  646.      *            (why?)
  647.      */
  648.     public synchronized void clearWarnings() throws java.sql.SQLException {
  649.         if (Driver.TRACE) {
  650.             Object[] args = new Object[0];
  651.             Debug.methodCall(this, "clearWarnings", args);
  652.         }
  653.         this.warningChain = this.pendingWarnings;
  654.         this.pendingWarnings = null;
  655.     }
  656.     /**
  657.      * In many cases, it is desirable to immediately release a Statement's
  658.      * database and JDBC resources instead of waiting for this to happen when
  659.      * it is automatically closed.  The close method provides this immediate
  660.      * release.
  661.      * 
  662.      * <p>
  663.      * <B>Note:</B> A Statement is automatically closed when it is garbage
  664.      * collected.  When a Statement is closed, its current ResultSet, if one
  665.      * exists, is also closed.
  666.      * </p>
  667.      *
  668.      * @exception java.sql.SQLException if a database access error occurs
  669.      */
  670.     public synchronized void close() throws java.sql.SQLException {
  671.         if (Driver.TRACE) {
  672.             Object[] args = new Object[0];
  673.             Debug.methodCall(this, "close", args);
  674.         }
  675. if (this.isClosed) {
  676. return;
  677. }
  678.         if (results != null) {
  679.             try {
  680.                 results.close();
  681.             } catch (Exception ex) {
  682.                 ;
  683.             }
  684.         }
  685.         if (this.maxRowsChanged && this.connection != null) {
  686.             this.connection.unsetMaxRows(this);
  687.         }
  688.         this.results = null;
  689.         this.connection = null;
  690.         this.warningChain = null;
  691.         this.isClosed = true;
  692.         this.closeAllOpenResults();
  693.         this.openResults = null;
  694.     }
  695.     /**
  696.      * Execute a SQL statement that may return multiple results. We don't have
  697.      * to worry about this since we do not support multiple ResultSets.   You
  698.      * can use getResultSet or getUpdateCount to retrieve the result.
  699.      *
  700.      * @param sql any SQL statement
  701.      *
  702.      * @return true if the next result is a ResulSet, false if it is an update
  703.      *         count or there are no more results
  704.      *
  705.      * @exception SQLException if a database access error occurs
  706.      */
  707.     public synchronized boolean execute(String sql) throws SQLException {
  708.         if (Driver.TRACE) {
  709.             Object[] args = { sql };
  710.             Debug.methodCall(this, "execute", args);
  711.         }
  712.         char firstNonWsChar = StringUtils.firstNonWsCharUc(sql);
  713.         if (connection.isReadOnly()) {
  714.             if (firstNonWsChar != 'S') {
  715.                 throw new SQLException("Connection is read-only. "
  716.                     + "Queries leading to data modification are not allowed",
  717.                     SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  718.             }
  719.         }
  720.         checkClosed();
  721.         if (this.doEscapeProcessing) {
  722.             sql = EscapeProcessor.escapeSQL(sql, this.serverSupportsConvertFn);
  723.         }
  724.         if (results != null) {
  725.             results.close();
  726.         }
  727.         ResultSet rs = null;
  728.         // If there isn't a limit clause in the SQL
  729.         // then limit the number of rows to return in
  730.         // an efficient manner. Only do this if
  731.         // setMaxRows() hasn't been used on any Statements
  732.         // generated from the current Connection (saves
  733.         // a query, and network traffic).
  734.         synchronized (connection.getMutex()) {
  735.          clearWarnings();
  736.         
  737.             String oldCatalog = null;
  738.             if (!connection.getCatalog().equals(currentCatalog)) {
  739.                 oldCatalog = connection.getCatalog();
  740.                 connection.setCatalog(currentCatalog);
  741.             }
  742.             boolean isSelect = (firstNonWsChar == 'S');
  743.             //
  744.             // Only apply max_rows to selects
  745.             //
  746.             if (connection.useMaxRows()) {
  747.              int rowLimit = -1;
  748.             
  749.                 if (isSelect) {
  750.                     if (sql.toUpperCase().indexOf("LIMIT") != -1) {
  751.                        rowLimit = this.maxRows;
  752.                     } else {
  753.                         if (maxRows <= 0) {
  754.                             connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT",
  755.                                 -1, this.currentCatalog);
  756.                         } else {
  757.                             connection.execSQL("SET OPTION SQL_SELECT_LIMIT="
  758.                                 + maxRows, -1, this.currentCatalog);
  759.                         }
  760.                     }
  761.                 } else {
  762.                     connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT",
  763.                         -1, this.currentCatalog);
  764.                 }
  765.                 // Finally, execute the query
  766.                 rs = connection.execSQL(sql, rowLimit, resultSetConcurrency,
  767.                         createStreamingResultSet(), isSelect,
  768.                         this.currentCatalog);
  769.             } else {
  770.                 rs = connection.execSQL(sql, -1, resultSetConcurrency,
  771.                         createStreamingResultSet(), isSelect,
  772.                         this.currentCatalog);
  773.             }
  774.             if (oldCatalog != null) {
  775.                 connection.setCatalog(oldCatalog);
  776.             }
  777.         }
  778.         lastInsertId = rs.getUpdateID();
  779.         if (rs != null) {
  780.             this.results = rs;
  781.         }
  782. rs.setFirstCharOfQuery(firstNonWsChar);
  783.         rs.setConnection(connection);
  784.         rs.setResultSetType(resultSetType);
  785.         rs.setResultSetConcurrency(resultSetConcurrency);
  786.         return ((rs != null) && rs.reallyResult());
  787.     }
  788.     /**
  789.      * @see Statement#execute(String, int)
  790.      */
  791.     public boolean execute(String sql, int returnGeneratedKeys)
  792.         throws SQLException {
  793.         if (returnGeneratedKeys == Statement.RETURN_GENERATED_KEYS) {
  794.             checkClosed();
  795.             synchronized (this.connection.getMutex()) {
  796.                 // If this is a 'REPLACE' query, we need to be able to parse
  797.                 // the 'info' message returned from the server to determine
  798.                 // the actual number of keys generated.
  799.                 boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
  800.                 this.connection.setReadInfoMsgEnabled(true);
  801.                 try {
  802.                     return execute(sql);
  803.                 } finally {
  804.                     this.connection.setReadInfoMsgEnabled(readInfoMsgState);
  805.                 }
  806.             }
  807.         } else {
  808.             return execute(sql);
  809.         }
  810.     }
  811.     /**
  812.      * @see Statement#execute(String, int[])
  813.      */
  814.     public boolean execute(String sql, int[] generatedKeyIndices)
  815.         throws SQLException {
  816.         if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
  817.             checkClosed();
  818.             synchronized (this.connection.getMutex()) {
  819.                 // If this is a 'REPLACE' query, we need to be able to parse
  820.                 // the 'info' message returned from the server to determine
  821.                 // the actual number of keys generated.
  822.                 boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
  823.                 this.connection.setReadInfoMsgEnabled(true);
  824.                 try {
  825.                     return execute(sql);
  826.                 } finally {
  827.                     this.connection.setReadInfoMsgEnabled(readInfoMsgState);
  828.                 }
  829.             }
  830.         } else {
  831.             return execute(sql);
  832.         }
  833.     }
  834.     /**
  835.      * @see Statement#execute(String, String[])
  836.      */
  837.     public boolean execute(String sql, String[] generatedKeyNames)
  838.         throws SQLException {
  839.         if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
  840.             checkClosed();
  841.             synchronized (this.connection.getMutex()) {
  842.                 // If this is a 'REPLACE' query, we need to be able to parse
  843.                 // the 'info' message returned from the server to determine
  844.                 // the actual number of keys generated.
  845.                 boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
  846.                 this.connection.setReadInfoMsgEnabled(true);
  847.                 try {
  848.                     return execute(sql);
  849.                 } finally {
  850.                     this.connection.setReadInfoMsgEnabled(readInfoMsgState);
  851.                 }
  852.             }
  853.         } else {
  854.             return execute(sql);
  855.         }
  856.     }
  857.     /**
  858.      * JDBC 2.0  Submit a batch of commands to the database for execution. This
  859.      * method is optional.
  860.      *
  861.      * @return an array of update counts containing one element for each
  862.      *         command in the batch.  The array is ordered according  to the
  863.      *         order in which commands were inserted into the batch
  864.      *
  865.      * @exception SQLException if a database-access error occurs, or the driver
  866.      *            does not support batch statements
  867.      */
  868.     public synchronized int[] executeBatch() throws SQLException {
  869.         if (connection.isReadOnly()) {
  870.             throw new SQLException("Connection is read-only. "
  871.                 + "Queries leading to data modification are not allowed",
  872.                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  873.         }
  874.         try {
  875.          clearWarnings();
  876.         
  877.             int[] updateCounts = null;
  878.             if (batchedArgs != null) {
  879.                 int nbrCommands = batchedArgs.size();
  880.                 updateCounts = new int[nbrCommands];
  881.                 for (int i = 0; i < nbrCommands; i++) {
  882.                     updateCounts[i] = -3;
  883.                 }
  884.                 SQLException sqlEx = null;
  885.                 int commandIndex = 0;
  886.                 for (commandIndex = 0; commandIndex < nbrCommands;
  887.                         commandIndex++) {
  888.                     try {
  889.                         updateCounts[commandIndex] = executeUpdate((String) batchedArgs
  890.                                 .get(commandIndex), false);
  891.                     } catch (SQLException ex) {
  892.                         updateCounts[commandIndex] = EXECUTE_FAILED;
  893.                         if (this.connection.continueBatchOnError()) {
  894.                             sqlEx = ex;
  895.                         } else {
  896.                             int[] newUpdateCounts = new int[commandIndex];
  897.                             System.arraycopy(updateCounts, 0, newUpdateCounts,
  898.                                 0, commandIndex);
  899.                             throw new java.sql.BatchUpdateException(ex
  900.                                 .getMessage(), ex.getSQLState(),
  901.                                 ex.getErrorCode(), newUpdateCounts);
  902.                         }
  903.                     }
  904.                 }
  905.                 if (sqlEx != null) {
  906.                     throw new java.sql.BatchUpdateException(sqlEx.getMessage(),
  907.                         sqlEx.getSQLState(), sqlEx.getErrorCode(), updateCounts);
  908.                 }
  909.             }
  910.             return (updateCounts != null) ? updateCounts : new int[0];
  911.         } finally {
  912.             clearBatch();
  913.         }
  914.     }
  915.     /**
  916.      * Execute a SQL statement that retruns a single ResultSet
  917.      *
  918.      * @param sql typically a static SQL SELECT statement
  919.      *
  920.      * @return a ResulSet that contains the data produced by the query
  921.      *
  922.      * @exception SQLException if a database access error occurs
  923.      */
  924.     public synchronized java.sql.ResultSet executeQuery(String sql)
  925.         throws SQLException {
  926.         if (Driver.TRACE) {
  927.             Object[] args = { sql };
  928.             Debug.methodCall(this, "executeQuery", args);
  929.         }
  930.         checkClosed();
  931.         if (this.doEscapeProcessing) {
  932.             sql = EscapeProcessor.escapeSQL(sql, this.serverSupportsConvertFn);
  933.         }
  934.         char firstStatementChar = StringUtils.firstNonWsCharUc(sql);
  935.         if ((firstStatementChar == 'I') || (firstStatementChar == 'U')
  936.                 || (firstStatementChar == 'D') || (firstStatementChar == 'A')
  937.                 || (firstStatementChar == 'C')) {
  938.             if (StringUtils.startsWithIgnoreCaseAndWs(sql, "INSERT")
  939.                     || StringUtils.startsWithIgnoreCaseAndWs(sql, "UPDATE")
  940.                     || StringUtils.startsWithIgnoreCaseAndWs(sql, "DELETE")
  941.                     || StringUtils.startsWithIgnoreCaseAndWs(sql, "DROP")
  942.                     || StringUtils.startsWithIgnoreCaseAndWs(sql, "CREATE")
  943.                     || StringUtils.startsWithIgnoreCaseAndWs(sql, "ALTER")) {
  944.                 throw new SQLException("Can not issue data manipulation statements with executeQuery()",
  945.                     SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  946.             }
  947.         }
  948.         if (results != null) {
  949.             results.close();
  950.         }
  951.         // If there isn't a limit clause in the SQL
  952.         // then limit the number of rows to return in
  953.         // an efficient manner. Only do this if
  954.         // setMaxRows() hasn't been used on any Statements
  955.         // generated from the current Connection (saves
  956.         // a query, and network traffic).
  957.         synchronized (connection.getMutex()) {
  958.          clearWarnings();
  959.         
  960.             String oldCatalog = null;
  961.             if (!connection.getCatalog().equals(currentCatalog)) {
  962.                 oldCatalog = connection.getCatalog();
  963.                 connection.setCatalog(currentCatalog);
  964.             }
  965.             if (connection.useMaxRows()) {
  966.                 // We need to execute this all together
  967.                 // So synchronize on the Connection's mutex (because
  968.                 // even queries going through there synchronize
  969.                 // on the connection
  970.                 if (sql.toUpperCase().indexOf("LIMIT") != -1) {
  971.                     results = connection.execSQL(sql, maxRows,
  972.                             resultSetConcurrency, createStreamingResultSet(),
  973.                             true, this.currentCatalog);
  974.                 } else {
  975.                     if (maxRows <= 0) {
  976.                         connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT",
  977.                             -1, this.currentCatalog);
  978.                     } else {
  979.                         connection.execSQL("SET OPTION SQL_SELECT_LIMIT="
  980.                             + maxRows, -1, this.currentCatalog);
  981.                     }
  982.                     results = connection.execSQL(sql, -1, resultSetConcurrency,
  983.                             createStreamingResultSet(), true,
  984.                             this.currentCatalog);
  985.                     if (oldCatalog != null) {
  986.                         connection.setCatalog(oldCatalog);
  987.                     }
  988.                 }
  989.             } else {
  990.                 results = connection.execSQL(sql, -1, resultSetConcurrency,
  991.                         createStreamingResultSet(), true, this.currentCatalog);
  992.             }
  993.             if (oldCatalog != null) {
  994.                 connection.setCatalog(oldCatalog);
  995.             }
  996.         }
  997.         lastInsertId = results.getUpdateID();
  998.         nextResults = results;
  999.         results.setConnection(connection);
  1000.         results.setResultSetType(resultSetType);
  1001.         results.setResultSetConcurrency(resultSetConcurrency);
  1002.         results.setStatement(this);
  1003.         if (!results.reallyResult()) {
  1004.             if (!connection.getAutoCommit()) {
  1005.                 connection.rollback();
  1006.             }
  1007.             throw new SQLException("Can not issue INSERT/UPDATE/DELETE with executeQuery()",
  1008.                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  1009.         }
  1010.         return (java.sql.ResultSet) results;
  1011.     }
  1012.     /**
  1013.      * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition SQL
  1014.      * statements that return nothing such as SQL DDL statements can be
  1015.      * executed Any IDs generated for AUTO_INCREMENT fields can be retrieved
  1016.      * by casting this Statement to org.gjt.mm.mysql.Statement and calling the
  1017.      * getLastInsertID() method.
  1018.      *
  1019.      * @param sql a SQL statement
  1020.      *
  1021.      * @return either a row count, or 0 for SQL commands
  1022.      *
  1023.      * @exception SQLException if a database access error occurs
  1024.      */
  1025.     public synchronized int executeUpdate(String sql) throws SQLException {
  1026.      return executeUpdate(sql, true);
  1027.     }
  1028.     
  1029.     private synchronized int executeUpdate(String sql, boolean clearWarnings) throws SQLException {
  1030.         if (Driver.TRACE) {
  1031.             Object[] args = { sql };
  1032.             Debug.methodCall(this, "executeUpdate", args);
  1033.         }
  1034.         if (connection.isReadOnly()) {
  1035.             throw new SQLException("Connection is read-only. "
  1036.                 + "Queries leading to data modification are not allowed",
  1037.                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  1038.         }
  1039.         char firstStatementChar = StringUtils.firstNonWsCharUc(sql);
  1040.         if ((firstStatementChar == 'S')
  1041.                 && StringUtils.startsWithIgnoreCaseAndWs(sql, "select")) {
  1042.             throw new SQLException("Can not issue SELECT via executeUpdate()",
  1043.                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  1044.         }
  1045.         checkClosed();
  1046.         if (this.doEscapeProcessing) {
  1047.             sql = EscapeProcessor.escapeSQL(sql, this.serverSupportsConvertFn);
  1048.         }
  1049.         // The checking and changing of catalogs
  1050.         // must happen in sequence, so synchronize
  1051.         // on the same mutex that _conn is using
  1052.         ResultSet rs = null;
  1053.         synchronized (connection.getMutex()) {
  1054.          if (clearWarnings) {
  1055.          clearWarnings();
  1056.          }
  1057.         
  1058.             String oldCatalog = null;
  1059.             if (!connection.getCatalog().equals(currentCatalog)) {
  1060.                 oldCatalog = connection.getCatalog();
  1061.                 connection.setCatalog(currentCatalog);
  1062.             }
  1063.             //
  1064.             // Only apply max_rows to selects
  1065.             //
  1066.             if (connection.useMaxRows()) {
  1067.                 connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1,
  1068.                     this.currentCatalog);
  1069.             }
  1070.             rs = connection.execSQL(sql, -1,
  1071.                     java.sql.ResultSet.CONCUR_READ_ONLY, false, false,
  1072.                     this.currentCatalog);
  1073.             rs.setConnection(connection);
  1074.             if (oldCatalog != null) {
  1075.                 connection.setCatalog(oldCatalog);
  1076.             }
  1077.         }
  1078.         this.results = rs;
  1079. rs.setFirstCharOfQuery(firstStatementChar);
  1080.         updateCount = rs.getUpdateCount();
  1081.         int truncatedUpdateCount = 0;
  1082.         if (updateCount > Integer.MAX_VALUE) {
  1083.             truncatedUpdateCount = Integer.MAX_VALUE;
  1084.         } else {
  1085.             truncatedUpdateCount = (int) updateCount;
  1086.         }
  1087.         lastInsertId = rs.getUpdateID();
  1088.         return truncatedUpdateCount;
  1089.     }
  1090.     /**
  1091.      * @see Statement#executeUpdate(String, int)
  1092.      */
  1093.     public int executeUpdate(String sql, int returnGeneratedKeys)
  1094.         throws SQLException {
  1095.         if (returnGeneratedKeys == Statement.RETURN_GENERATED_KEYS) {
  1096.             checkClosed();
  1097.             synchronized (this.connection.getMutex()) {
  1098.                 // If this is a 'REPLACE' query, we need to be able to parse
  1099.                 // the 'info' message returned from the server to determine
  1100.                 // the actual number of keys generated.
  1101.                 boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
  1102.                 this.connection.setReadInfoMsgEnabled(true);
  1103.                 try {
  1104.                     return executeUpdate(sql);
  1105.                 } finally {
  1106.                     this.connection.setReadInfoMsgEnabled(readInfoMsgState);
  1107.                 }
  1108.             }
  1109.         } else {
  1110.             return executeUpdate(sql);
  1111.         }
  1112.     }
  1113.     /**
  1114.      * @see Statement#executeUpdate(String, int[])
  1115.      */
  1116.     public int executeUpdate(String sql, int[] generatedKeyIndices)
  1117.         throws SQLException {
  1118.         if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
  1119.             checkClosed();
  1120.             synchronized (this.connection.getMutex()) {
  1121.                 // If this is a 'REPLACE' query, we need to be able to parse
  1122.                 // the 'info' message returned from the server to determine
  1123.                 // the actual number of keys generated.
  1124.                 boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
  1125.                 this.connection.setReadInfoMsgEnabled(true);
  1126.                 try {
  1127.                     return executeUpdate(sql);
  1128.                 } finally {
  1129.                     this.connection.setReadInfoMsgEnabled(readInfoMsgState);
  1130.                 }
  1131.             }
  1132.         } else {
  1133.             return executeUpdate(sql);
  1134.         }
  1135.     }
  1136.     /**
  1137.      * @see Statement#executeUpdate(String, String[])
  1138.      */
  1139.     public int executeUpdate(String sql, String[] generatedKeyNames)
  1140.         throws SQLException {
  1141.         if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
  1142.             checkClosed();
  1143.             synchronized (this.connection.getMutex()) {
  1144.                 // If this is a 'REPLACE' query, we need to be able to parse
  1145.                 // the 'info' message returned from the server to determine
  1146.                 // the actual number of keys generated.
  1147.                 boolean readInfoMsgState = this.connection.isReadInfoMsgEnabled();
  1148.                 this.connection.setReadInfoMsgEnabled(true);
  1149.                 try {
  1150.                     return executeUpdate(sql);
  1151.                 } finally {
  1152.                     this.connection.setReadInfoMsgEnabled(readInfoMsgState);
  1153.                 }
  1154.             }
  1155.         } else {
  1156.             return executeUpdate(sql);
  1157.         }
  1158.     }
  1159.     /**
  1160.      * Checks if closed() has been called, and throws an exception if so
  1161.      *
  1162.      * @throws SQLException if this statement has been closed
  1163.      */
  1164.     protected void checkClosed() throws SQLException {
  1165.         if (this.isClosed) {
  1166.             throw new SQLException("No operations allowed after statement closed",
  1167.                 SQLError.SQL_STATE_GENERAL_ERROR);
  1168.         }
  1169.     }
  1170.     /**
  1171.      * Close any open result sets that have been 'held open'
  1172.      */
  1173.     protected void closeAllOpenResults() {
  1174.         if (this.openResults != null) {
  1175.             for (Iterator iter = this.openResults.iterator(); iter.hasNext();) {
  1176.                 ResultSet element = (ResultSet) iter.next();
  1177.                 try {
  1178.                     element.close();
  1179.                 } catch (SQLException sqlEx) {
  1180.                     AssertionFailedException.shouldNotHappen(sqlEx);
  1181.                 }
  1182.             }
  1183.             this.openResults.clear();
  1184.         }
  1185.     }
  1186.     /**
  1187.      * We only stream result sets when they are forward-only, read-only, and
  1188.      * the fetch size has been set to Integer.MIN_VALUE
  1189.      *
  1190.      * @return true if this result set should be streamed row at-a-time, rather
  1191.      *         than read all at once.
  1192.      */
  1193.     protected boolean createStreamingResultSet() {
  1194.         return ((resultSetType == java.sql.ResultSet.TYPE_FORWARD_ONLY)
  1195.         && (resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY)
  1196.         && (fetchSize == Integer.MIN_VALUE));
  1197.     }
  1198.     /**
  1199.      * Sets the concurrency for result sets generated by this statement
  1200.      *
  1201.      * @param concurrencyFlag DOCUMENT ME!
  1202.      */
  1203.     void setResultSetConcurrency(int concurrencyFlag) {
  1204.         resultSetConcurrency = concurrencyFlag;
  1205.     }
  1206.     /**
  1207.      * Sets the result set type for result sets generated by this statement
  1208.      *
  1209.      * @param typeFlag DOCUMENT ME!
  1210.      */
  1211.     void setResultSetType(int typeFlag) {
  1212.         resultSetType = typeFlag;
  1213.     }
  1214.     
  1215.     protected void addWarning(SQLWarning warning) {
  1216.      if (this.pendingWarnings == null) {
  1217. this.pendingWarnings = warning;
  1218.      } else {
  1219.      // find the 'end'... this could be handled more
  1220.      // efficiently, but the only thing that currently
  1221.      // sets 'client-side' warnings is prepared statements
  1222.      // when clipping +/- INF and NaN.
  1223.     
  1224.      SQLWarning lastWarning = this.pendingWarnings;
  1225.     
  1226.      while (lastWarning.getNextWarning() != null) {
  1227.      lastWarning = lastWarning.getNextWarning();
  1228.      }
  1229.     
  1230.      lastWarning.setNextWarning(warning);
  1231.      }
  1232.     
  1233.     }
  1234.     /**
  1235.      * Parses actual record count from 'info' message
  1236.      *
  1237.      * @param serverInfo DOCUMENT ME!
  1238.      *
  1239.      * @return DOCUMENT ME!
  1240.      */
  1241.     private int getRecordCountFromInfo(String serverInfo) {
  1242.         StringBuffer recordsBuf = new StringBuffer();
  1243.         int recordsCount = 0;
  1244.         int duplicatesCount = 0;
  1245.         char c = (char) 0;
  1246.         int length = serverInfo.length();
  1247.         int i = 0;
  1248.         for (; i < length; i++) {
  1249.             c = serverInfo.charAt(i);
  1250.             if (Character.isDigit(c)) {
  1251.                 break;
  1252.             }
  1253.         }
  1254.         recordsBuf.append(c);
  1255.         i++;
  1256.         for (; i < length; i++) {
  1257.             c = serverInfo.charAt(i);
  1258.             if (!Character.isDigit(c)) {
  1259.                 break;
  1260.             }
  1261.             recordsBuf.append(c);
  1262.         }
  1263.         recordsCount = Integer.parseInt(recordsBuf.toString());
  1264.         StringBuffer duplicatesBuf = new StringBuffer();
  1265.         for (; i < length; i++) {
  1266.             c = serverInfo.charAt(i);
  1267.             if (Character.isDigit(c)) {
  1268.                 break;
  1269.             }
  1270.         }
  1271.         duplicatesBuf.append(c);
  1272.         i++;
  1273.         for (; i < length; i++) {
  1274.             c = serverInfo.charAt(i);
  1275.             if (!Character.isDigit(c)) {
  1276.                 break;
  1277.             }
  1278.             duplicatesBuf.append(c);
  1279.         }
  1280.         duplicatesCount = Integer.parseInt(duplicatesBuf.toString());
  1281.         return recordsCount - duplicatesCount;
  1282.     }
  1283. }