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 <= rows <= 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