Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/mysql/jdbc/Statement.java


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