KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mysql > jdbc > Statement


1 /*
2    Copyright (C) 2002 MySQL AB
3
4       This program is free software; you can redistribute it and/or modify
5       it under the terms of the GNU General Public License as published by
6       the Free Software Foundation; either version 2 of the License, or
7       (at your option) any later version.
8
9       This program is distributed in the hope that it will be useful,
10       but WITHOUT ANY WARRANTY; without even the implied warranty of
11       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12       GNU General Public License for more details.
13
14       You should have received a copy of the GNU General Public License
15       along with this program; if not, write to the Free Software
16       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18  */

19 package com.mysql.jdbc;
20
21 import java.io.UnsupportedEncodingException JavaDoc;
22
23 import java.sql.SQLException JavaDoc;
24 import java.sql.SQLWarning JavaDoc;
25 import java.sql.Types JavaDoc;
26
27 import java.util.ArrayList JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30
31
32 /**
33  * A Statement object is used for executing a static SQL statement and
34  * obtaining the results produced by it.
35  *
36  * <p>
37  * Only one ResultSet per Statement can be open at any point in time.
38  * Therefore, if the reading of one ResultSet is interleaved with the reading
39  * of another, each must have been generated by different Statements. All
40  * statement execute methods implicitly close a statement's current ResultSet
41  * if an open one exists.
42  * </p>
43  *
44  * @author Mark Matthews
45  * @version $Id: Statement.java,v 1.20.2.17 2004/02/13 22:31:25 mmatthew Exp $
46  *
47  * @see java.sql.Statement
48  * @see ResultSet
49  */

50 public class Statement implements java.sql.Statement JavaDoc {
51     /** The connection that created us */
52     protected Connection connection = null;
53
54     /** Holds batched commands */
55     protected List JavaDoc batchedArgs;
56
57     /** List of currently-open ResultSets */
58     protected List JavaDoc openResults = new ArrayList JavaDoc();
59
60     /** The next result set */
61     protected ResultSet nextResults = null;
62
63     /** The current results */
64     protected ResultSet results = null;
65
66     /** The warnings chain. */
67     protected SQLWarning JavaDoc warningChain = null;
68     
69     /** The pending warnings chain */
70     protected SQLWarning JavaDoc pendingWarnings = null;
71
72     /** The character converter to use (if available) */
73     protected SingleByteCharsetConverter charConverter = null;
74
75     /** The character encoding to use (if available) */
76     protected String JavaDoc charEncoding = null;
77
78     /** The catalog in use */
79     protected String JavaDoc currentCatalog = null;
80
81     /** Should we process escape codes? */
82     protected boolean doEscapeProcessing = true;
83
84     /** Has this statement been closed? */
85     protected boolean isClosed = false;
86
87     /** Has someone changed this for this statement? */
88     protected boolean maxRowsChanged = false;
89
90     /** Are we in pedantic mode? */
91     protected boolean pedantic = false;
92
93     /** The max field size for this statement */
94     protected int maxFieldSize = MysqlIO.getMaxBuf();
95
96     /**
97      * The maximum number of rows to return for this statement (-1 means _all_
98      * rows)
99      */

100     protected int maxRows = -1;
101
102     /** The concurrency for this result set (updatable or not) */
103     protected int resultSetConcurrency = 0;
104
105     /** The type of this result set (scroll sensitive or in-sensitive) */
106     protected int resultSetType = 0;
107
108     /** The timeout for a query */
109     protected int timeout = 0;
110
111     /** The auto_increment value for the last insert */
112     protected long lastInsertId = -1;
113
114     /** The update count for this statement */
115     protected long updateCount = -1;
116
117     /** The number of rows to fetch at a time (currently ignored) */
118     private int fetchSize = 0;
119
120     /** Does the server support CAST/CONVERT? */
121     private boolean serverSupportsConvertFn;
122     
123     /**
124      * Constructor for a Statement.
125      *
126      * @param c the Connection instantation that creates us
127      * @param catalog the database name in use when we were created
128      *
129      * @throws SQLException if an error occurs.
130      */

131     public Statement(Connection c, String JavaDoc catalog) throws SQLException JavaDoc {
132         if (Driver.TRACE) {
133             Object JavaDoc[] args = { c };
134             Debug.methodCall(this, "constructor", args);
135         }
136
137         if ((c == null) || ((com.mysql.jdbc.Connection) c).isClosed()) {
138             throw new SQLException JavaDoc("Connection is closed.", "08003");
139         }
140
141         this.connection = c;
142         this.currentCatalog = catalog;
143         this.pedantic = this.connection.isPedantic();
144         this.serverSupportsConvertFn = this.connection.getIO().versionMeetsMinimum(4, 0, 2);
145
146         //
147
// Adjust, if we know it
148
//
149
if (connection != null) {
150             maxFieldSize = connection.getMaxAllowedPacket();
151         }
152
153         if (this.connection.useUnicode()) {
154             this.charEncoding = connection.getEncoding();
155             this.charConverter = this.connection.getCharsetConverter(this.charEncoding);
156         }
157         
158         int maxRowsConn = this.connection.getMaxRows();
159         
160         if (maxRowsConn != -1) {
161             setMaxRows(maxRowsConn);
162         }
163     }
164
165     /**
166      * JDBC 2.0 Return the Connection that produced the Statement.
167      *
168      * @return the Connection that produced the Statement
169      *
170      * @throws SQLException if an error occurs
171      */

172     public java.sql.Connection JavaDoc getConnection() throws SQLException JavaDoc {
173         return (java.sql.Connection JavaDoc) connection;
174     }
175
176     /**
177      * setCursorName defines the SQL cursor name that will be used by
178      * subsequent execute methods. This name can then be used in SQL
179      * positioned update/delete statements to identify the current row in the
180      * ResultSet generated by this statement. If a database doesn't support
181      * positioned update/delete, this method is a no-op.
182      *
183      * <p>
184      * <b>Note:</b> This MySQL driver does not support cursors.
185      * </p>
186      *
187      * @param name the new cursor name
188      *
189      * @exception java.sql.SQLException if a database access error occurs
190      */

191     public void setCursorName(String JavaDoc name) throws java.sql.SQLException JavaDoc {
192         if (Driver.TRACE) {
193             Object JavaDoc[] args = { name };
194             Debug.methodCall(this, "setCursorName", args);
195         }
196
197         // No-op
198
}
199
200     /**
201      * If escape scanning is on (the default), the driver will do escape
202      * substitution before sending the SQL to the database.
203      *
204      * @param enable true to enable; false to disable
205      *
206      * @exception java.sql.SQLException if a database access error occurs
207      */

208     public void setEscapeProcessing(boolean enable)
209         throws java.sql.SQLException JavaDoc {
210         if (Driver.TRACE) {
211             Object JavaDoc[] args = { new Boolean JavaDoc(enable) };
212             Debug.methodCall(this, "setEscapeProcessing", args);
213         }
214
215         doEscapeProcessing = enable;
216     }
217
218     //--------------------------JDBC 2.0-----------------------------
219

220     /**
221      * JDBC 2.0 Give a hint as to the direction in which the rows in a result
222      * set will be processed. The hint applies only to result sets created
223      * using this Statement object. The default value is
224      * ResultSet.FETCH_FORWARD.
225      *
226      * @param direction the initial direction for processing rows
227      *
228      * @exception SQLException if a database-access error occurs or direction
229      * is not one of ResultSet.FETCH_FORWARD,
230      * ResultSet.FETCH_REVERSE, or ResultSet.FETCH_UNKNOWN
231      */

232     public void setFetchDirection(int direction) throws SQLException JavaDoc {
233         switch (direction) {
234         case java.sql.ResultSet.FETCH_FORWARD:
235         case java.sql.ResultSet.FETCH_REVERSE:
236         case java.sql.ResultSet.FETCH_UNKNOWN:
237             break;
238
239         default:
240             throw new SQLException JavaDoc("Illegal value for setFetchDirection()",
241                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
242         }
243     }
244
245     /**
246      * JDBC 2.0 Determine the fetch direction.
247      *
248      * @return the default fetch direction
249      *
250      * @exception SQLException if a database-access error occurs
251      */

252     public int getFetchDirection() throws SQLException JavaDoc {
253         return java.sql.ResultSet.FETCH_FORWARD;
254     }
255
256     /**
257      * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that
258      * should be fetched from the database when more rows are needed. The
259      * number of rows specified only affects result sets created using this
260      * statement. If the value specified is zero, then the hint is ignored.
261      * The default value is zero.
262      *
263      * @param rows the number of rows to fetch
264      *
265      * @exception SQLException if a database-access error occurs, or the
266      * condition 0 &lt;= rows &lt;= this.getMaxRows() is not
267      * satisfied.
268      */

269     public void setFetchSize(int rows) throws SQLException JavaDoc {
270         if (((rows < 0) && (rows != Integer.MIN_VALUE))
271                 || ((maxRows != 0) && (maxRows != -1)
272                 && (rows > this.getMaxRows()))) {
273             throw new SQLException JavaDoc("Illegal value for setFetchSize()", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
274         }
275
276         fetchSize = rows;
277     }
278
279     /**
280      * JDBC 2.0 Determine the default fetch size.
281      *
282      * @return the number of rows to fetch at a time
283      *
284      * @throws SQLException if an error occurs
285      */

286     public int getFetchSize() throws SQLException JavaDoc {
287         return fetchSize;
288     }
289
290     /**
291      * DOCUMENT ME!
292      *
293      * @return DOCUMENT ME!
294      *
295      * @throws SQLException DOCUMENT ME!
296      */

297     public java.sql.ResultSet JavaDoc getGeneratedKeys() throws SQLException JavaDoc {
298         Field[] fields = new Field[1];
299         fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17);
300
301         ArrayList JavaDoc rowSet = new ArrayList JavaDoc();
302
303         long beginAt = getLastInsertID();
304         int numKeys = getUpdateCount();
305
306         String JavaDoc serverInfo = this.results.getServerInfo();
307
308         //
309
// Only parse server info messages for 'REPLACE'
310
// queries
311
//
312

313         if ((numKeys > 0)
314                 && this.results.getFirstCharOfQuery() == 'R'
315                 && (serverInfo != null)
316                 && (serverInfo.length() > 0)) {
317             numKeys = getRecordCountFromInfo(serverInfo);
318         }
319
320         if ((beginAt > 0) && (numKeys > 0)) {
321             for (int i = 0; i < numKeys; i++) {
322                 byte[][] row = new byte[1][];
323                 row[0] = Long.toString(beginAt++).getBytes();
324                 rowSet.add(row);
325             }
326         }
327
328         return new com.mysql.jdbc.ResultSet(currentCatalog, fields,
329             new RowDataStatic(rowSet), connection);
330     }
331
332     /**
333      * getLastInsertID returns the value of the auto_incremented key after an
334      * executeQuery() or excute() call.
335      *
336      * <p>
337      * This gets around the un-threadsafe behavior of "select LAST_INSERT_ID()"
338      * which is tied to the Connection that created this Statement, and
339      * therefore could have had many INSERTS performed before one gets a
340      * chance to call "select LAST_INSERT_ID()".
341      * </p>
342      *
343      * @return the last update ID.
344      */

345     public long getLastInsertID() {
346         if (Driver.TRACE) {
347             Object JavaDoc[] args = new Object JavaDoc[0];
348             Debug.methodCall(this, "getLastInsertID", args);
349         }
350
351         return lastInsertId;
352     }
353
354     /**
355      * getLongUpdateCount returns the current result as an update count, if the
356      * result is a ResultSet or there are no more results, -1 is returned. It
357      * should only be called once per result.
358      *
359      * <p>
360      * This method returns longs as MySQL server versions newer than 3.22.4
361      * return 64-bit values for update counts
362      * </p>
363      *
364      * @return the current update count.
365      */

366     public long getLongUpdateCount() {
367         if (Driver.TRACE) {
368             Object JavaDoc[] args = new Object JavaDoc[0];
369             Debug.methodCall(this, "getLongUpdateCount", args);
370         }
371
372         if (results == null) {
373             return -1;
374         }
375
376         if (results.reallyResult()) {
377             return -1;
378         }
379
380         return updateCount;
381     }
382
383     /**
384      * Sets the maxFieldSize
385      *
386      * @param max the new max column size limit; zero means unlimited
387      *
388      * @exception SQLException if size exceeds buffer size
389      * @throws SQLException DOCUMENT ME!
390      */

391     public void setMaxFieldSize(int max) throws SQLException JavaDoc {
392         if (Driver.TRACE) {
393             Object JavaDoc[] args = { new Integer JavaDoc(max) };
394             Debug.methodCall(this, "setMaxFieldSize", args);
395         }
396
397         if (max < 0) {
398             throw new SQLException JavaDoc("Illegal value for setMaxFieldSize()",
399                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
400         }
401
402         int maxBuf = (connection != null) ? connection.getMaxAllowedPacket()
403                                           : MysqlIO.getMaxBuf();
404
405         if (max > maxBuf) {
406             throw new java.sql.SQLException JavaDoc(
407                 "Can not set max field size > max allowed packet: " + maxBuf,
408                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
409         } else {
410             maxFieldSize = max;
411         }
412     }
413
414     /**
415      * The maxFieldSize limit (in bytes) is the maximum amount of data returned
416      * for any column value; it only applies to BINARY, VARBINARY,
417      * LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR columns. If the limit is
418      * exceeded, the excess data is silently discarded.
419      *
420      * @return the current max column size limit; zero means unlimited
421      *
422      * @exception java.sql.SQLException if a database access error occurs
423      */

424     public int getMaxFieldSize() throws java.sql.SQLException JavaDoc {
425         if (Driver.TRACE) {
426             Object JavaDoc[] args = new Object JavaDoc[0];
427             Debug.methodCall(this, "getMaxFieldSize", args);
428         }
429
430         return maxFieldSize;
431     }
432
433     /**
434      * Set the maximum number of rows
435      *
436      * @param max the new max rows limit; zero means unlimited
437      *
438      * @exception java.sql.SQLException if a database access error occurs
439      *
440      * @see getMaxRows
441      */

442     public void setMaxRows(int max) throws java.sql.SQLException JavaDoc {
443         if (Driver.TRACE) {
444             Object JavaDoc[] args = { new Integer JavaDoc(max) };
445             Debug.methodCall(this, "setMaxRows", args);
446         }
447
448         if ((max > MysqlDefs.MAX_ROWS) || (max < 0)) {
449             throw new java.sql.SQLException JavaDoc("setMaxRows() out of range. " + max
450                 + " > " + MysqlDefs.MAX_ROWS + ".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
451         }
452
453         if (max == 0) {
454             max = -1;
455         }
456
457         this.maxRows = max;
458         this.maxRowsChanged = true;
459
460         if (maxRows == -1) {
461             connection.unsetMaxRows(this);
462             this.maxRowsChanged = false;
463         } else {
464             // Most people don't use setMaxRows()
465
// so don't penalize them
466
// with the extra query it takes
467
// to do it efficiently unless we need
468
// to.
469
connection.maxRowsChanged(this);
470         }
471     }
472
473     /**
474      * The maxRows limit is set to limit the number of rows that any ResultSet
475      * can contain. If the limit is exceeded, the excess rows are silently
476      * dropped.
477      *
478      * @return the current maximum row limit; zero means unlimited
479      *
480      * @exception java.sql.SQLException if a database access error occurs
481      */

482     public int getMaxRows() throws java.sql.SQLException JavaDoc {
483         if (Driver.TRACE) {
484             Object JavaDoc[] args = new Object JavaDoc[0];
485             Debug.methodCall(this, "getMaxRows", args);
486         }
487
488         if (maxRows <= 0) {
489             return 0;
490         } else {
491             return maxRows;
492         }
493     }
494
495     /**
496      * getMoreResults moves to a Statement's next result. If it returns true,
497      * this result is a ResulSet.
498      *
499      * @return true if the next ResultSet is valid
500      *
501      * @exception java.sql.SQLException if a database access error occurs
502      */

503     public boolean getMoreResults() throws java.sql.SQLException JavaDoc {
504         if (Driver.TRACE) {
505             Object JavaDoc[] args = new Object JavaDoc[0];
506             Debug.methodCall(this, "getMoreResults", args);
507         }
508
509         return getMoreResults(CLOSE_CURRENT_RESULT);
510     }
511
512     /**
513      * @see Statement#getMoreResults(int)
514      */

515     public synchronized boolean getMoreResults(int current)
516         throws SQLException JavaDoc {
517         switch (current) {
518         case Statement.CLOSE_CURRENT_RESULT:
519
520             if (results != null) {
521                 results.close();
522             }
523
524             break;
525
526         case Statement.CLOSE_ALL_RESULTS:
527
528             if (results != null) {
529                 results.close();
530             }
531
532             closeAllOpenResults();
533
534             break;
535
536         case Statement.KEEP_CURRENT_RESULT:
537             openResults.add(results);
538
539             break;
540
541         default:
542             throw new SQLException JavaDoc("Illegal flag for getMoreResults(int)",
543                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
544         }
545
546         results = nextResults;
547
548         nextResults = null;
549
550         return ((results != null) && results.reallyResult()) ? true : false;
551     }
552
553     /**
554      * Sets the queryTimeout limit
555      *
556      * @param seconds - the new query timeout limit in seconds
557      *
558      * @exception SQLException if a database access error occurs
559      */

560     public void setQueryTimeout(int seconds) throws SQLException JavaDoc {
561         if (Driver.TRACE) {
562             Object JavaDoc[] args = { new Integer JavaDoc(seconds) };
563             Debug.methodCall(this, "setQueryTimeout", args);
564         }
565
566         if (seconds < 0) {
567             throw new SQLException JavaDoc("Illegal value for setQueryTimeout()",
568                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
569         }
570
571         timeout = seconds;
572     }
573
574     /**
575      * The queryTimeout limit is the number of seconds the driver will wait for
576      * a Statement to execute. If the limit is exceeded, a
577      * java.sql.SQLException is thrown.
578      *
579      * @return the current query timeout limit in seconds; 0 = unlimited
580      *
581      * @exception java.sql.SQLException if a database access error occurs
582      */

583     public int getQueryTimeout() throws java.sql.SQLException JavaDoc {
584         if (Driver.TRACE) {
585             Object JavaDoc[] args = new Object JavaDoc[0];
586             Debug.methodCall(this, "getQueryTimeout", args);
587         }
588
589         return timeout;
590     }
591
592     /**
593      * getResultSet returns the current result as a ResultSet. It should only
594      * be called once per result.
595      *
596      * @return the current result set; null if there are no more
597      *
598      * @exception java.sql.SQLException if a database access error occurs
599      * (why?)
600      */

601     public synchronized java.sql.ResultSet JavaDoc getResultSet()
602         throws java.sql.SQLException JavaDoc {
603         if (Driver.TRACE) {
604             Object JavaDoc[] args = new Object JavaDoc[0];
605             Debug.methodCall(this, "getResultSet", args);
606         }
607
608         return ((results != null) && results.reallyResult())
609         ? (java.sql.ResultSet JavaDoc) results : null;
610     }
611
612     /**
613      * JDBC 2.0 Determine the result set concurrency.
614      *
615      * @return CONCUR_UPDATABLE or CONCUR_READONLY
616      *
617      * @throws SQLException if an error occurs
618      */

619     public int getResultSetConcurrency() throws SQLException JavaDoc {
620         return resultSetConcurrency;
621     }
622
623     /**
624      * @see Statement#getResultSetHoldability()
625      */

626     public int getResultSetHoldability() throws SQLException JavaDoc {
627         return ResultSet.HOLD_CURSORS_OVER_COMMIT;
628     }
629
630     /**
631      * JDBC 2.0 Determine the result set type.
632      *
633      * @return the ResultSet type (SCROLL_SENSITIVE or SCROLL_INSENSITIVE)
634      *
635      * @throws SQLException if an error occurs.
636      */

637     public int getResultSetType() throws SQLException JavaDoc {
638         return resultSetType;
639     }
640
641     /**
642      * getUpdateCount returns the current result as an update count, if the
643      * result is a ResultSet or there are no more results, -1 is returned. It
644      * should only be called once per result.
645      *
646      * @return the current result as an update count.
647      *
648      * @exception java.sql.SQLException if a database access error occurs
649      */

650     public synchronized int getUpdateCount() throws java.sql.SQLException JavaDoc {
651         if (Driver.TRACE) {
652             Object JavaDoc[] args = new Object JavaDoc[0];
653             Debug.methodCall(this, "getUpdateCount", args);
654         }
655
656         if (results == null) {
657             return -1;
658         }
659
660         if (results.reallyResult()) {
661             return -1;
662         }
663
664         int truncatedUpdateCount = 0;
665
666         if (results.getUpdateCount() > Integer.MAX_VALUE) {
667             truncatedUpdateCount = Integer.MAX_VALUE;
668         } else {
669             truncatedUpdateCount = (int) results.getUpdateCount();
670         }
671
672         return truncatedUpdateCount;
673     }
674
675     /**
676      * The first warning reported by calls on this Statement is returned. A
677      * Statement's execute methods clear its java.sql.SQLWarning chain.
678      * Subsequent Statement warnings will be chained to this
679      * java.sql.SQLWarning.
680      *
681      * <p>
682      * The Warning chain is automatically cleared each time a statement is
683      * (re)executed.
684      * </p>
685      *
686      * <p>
687      * <B>Note:</B> If you are processing a ResultSet then any warnings
688      * associated with ResultSet reads will be chained on the ResultSet
689      * object.
690      * </p>
691      *
692      * @return the first java.sql.SQLWarning on null
693      *
694      * @exception java.sql.SQLException if a database access error occurs
695      */

696     public synchronized java.sql.SQLWarning JavaDoc getWarnings()
697         throws java.sql.SQLException JavaDoc {
698         if (Driver.TRACE) {
699             Object JavaDoc[] args = new Object JavaDoc[0];
700             Debug.methodCall(this, "getWarnings", args);
701         }
702
703         return warningChain;
704     }
705
706     /**
707      * DOCUMENT ME!
708      *
709      * @param sql DOCUMENT ME!
710      *
711      * @throws SQLException DOCUMENT ME!
712      */

713     public synchronized void addBatch(String JavaDoc sql) throws SQLException JavaDoc {
714         if (batchedArgs == null) {
715             batchedArgs = new ArrayList JavaDoc();
716         }
717
718         if (sql != null) {
719             batchedArgs.add(sql);
720         }
721     }
722
723     /**
724      * Cancel can be used by one thread to cancel a statement that is being
725      * executed by another thread. However this driver is synchronous, so
726      * this really has no meaning - we define it as a no-op (i.e. you can't
727      * cancel, but there is no error if you try.)
728      *
729      * @exception java.sql.SQLException only because thats the spec.
730      */

731     public void cancel() throws java.sql.SQLException JavaDoc {
732         if (Driver.TRACE) {
733             Object JavaDoc[] args = new Object JavaDoc[0];
734             Debug.methodCall(this, "cancel", args);
735         }
736
737         // No-op
738
}
739
740     /**
741      * JDBC 2.0 Make the set of commands in the current batch empty. This
742      * method is optional.
743      *
744      * @exception SQLException if a database-access error occurs, or the driver
745      * does not support batch statements
746      */

747     public synchronized void clearBatch() throws SQLException JavaDoc {
748         if (batchedArgs != null) {
749             batchedArgs.clear();
750         }
751     }
752
753     /**
754      * After this call, getWarnings returns null until a new warning is
755      * reported for this Statement.
756      *
757      * @exception java.sql.SQLException if a database access error occurs
758      * (why?)
759      */

760     public synchronized void clearWarnings() throws java.sql.SQLException JavaDoc {
761         if (Driver.TRACE) {
762             Object JavaDoc[] args = new Object JavaDoc[0];
763             Debug.methodCall(this, "clearWarnings", args);
764         }
765
766         this.warningChain = this.pendingWarnings;
767         this.pendingWarnings = null;
768     }
769
770     /**
771      * In many cases, it is desirable to immediately release a Statement's
772      * database and JDBC resources instead of waiting for this to happen when
773      * it is automatically closed. The close method provides this immediate
774      * release.
775      *
776      * <p>
777      * <B>Note:</B> A Statement is automatically closed when it is garbage
778      * collected. When a Statement is closed, its current ResultSet, if one
779      * exists, is also closed.
780      * </p>
781      *
782      * @exception java.sql.SQLException if a database access error occurs
783      */

784     public synchronized void close() throws java.sql.SQLException JavaDoc {
785         if (Driver.TRACE) {
786             Object JavaDoc[] args = new Object JavaDoc[0];
787             Debug.methodCall(this, "close", args);
788         }
789
790         if (this.isClosed) {
791             return;
792         }
793         
794         if (results != null) {
795             try {
796                 results.close();
797             } catch (Exception JavaDoc ex) {
798                 ;
799             }
800         }
801
802         if (this.maxRowsChanged && this.connection != null) {
803             this.connection.unsetMaxRows(this);
804         }
805
806         this.results = null;
807         this.connection = null;
808         this.warningChain = null;
809         this.isClosed = true;
810         this.closeAllOpenResults();
811         this.openResults = null;
812     }
813
814     /**
815      * Execute a SQL statement that may return multiple results. We don't have
816      * to worry about this since we do not support multiple ResultSets. You
817      * can use getResultSet or getUpdateCount to retrieve the result.
818      *
819      * @param sql any SQL statement
820      *
821      * @return true if the next result is a ResulSet, false if it is an update
822      * count or there are no more results
823      *
824      * @exception SQLException if a database access error occurs
825      */

826     public synchronized boolean execute(String JavaDoc sql) throws SQLException JavaDoc {
827         if (Driver.TRACE) {
828             Object JavaDoc[] args = { sql };
829             Debug.methodCall(this, "execute", args);
830         }
831
832         char firstNonWsChar = StringUtils.firstNonWsCharUc(sql);
833
834         if (connection.isReadOnly()) {
835             if (firstNonWsChar != 'S') {
836                 throw new SQLException JavaDoc("Connection is read-only. "
837                     + "Queries leading to data modification are not allowed",
838                     SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
839             }
840         }
841
842         checkClosed();
843
844         if (this.doEscapeProcessing) {
845             sql = EscapeProcessor.escapeSQL(sql, this.serverSupportsConvertFn);
846         }
847
848         if (results != null) {
849             results.close();
850         }
851
852         ResultSet rs = null;
853
854         // If there isn't a limit clause in the SQL
855
// then limit the number of rows to return in
856
// an efficient manner. Only do this if
857
// setMaxRows() hasn't been used on any Statements
858
// generated from the current Connection (saves
859
// a query, and network traffic).
860
synchronized (connection.getMutex()) {
861             clearWarnings();
862             
863             String JavaDoc oldCatalog = null;
864
865             if (!connection.getCatalog().equals(currentCatalog)) {
866                 oldCatalog = connection.getCatalog();
867                 connection.setCatalog(currentCatalog);
868             }
869
870             boolean isSelect = (firstNonWsChar == 'S');
871
872             //
873
// Only apply max_rows to selects
874
//
875
if (connection.useMaxRows()) {
876                 int rowLimit = -1;
877                 
878                 if (isSelect) {
879                     if (sql.toUpperCase().indexOf("LIMIT") != -1) {
880                        rowLimit = this.maxRows;
881                     } else {
882                         if (maxRows <= 0) {
883                             connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT",
884                                 -1, this.currentCatalog);
885                         } else {
886                             connection.execSQL("SET OPTION SQL_SELECT_LIMIT="
887                                 + maxRows, -1, this.currentCatalog);
888                         }
889                     }
890                 } else {
891                     connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT",
892                         -1, this.currentCatalog);
893                 }
894
895      &nbs