2
Copyright (C) 2002-2007 MySQL AB
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.
8
There are special exceptions to the terms and conditions of the GPL
9
as it is applied to this software. View the full text of the
10
exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
11
software distribution.
13
This program is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
GNU General Public License for more details.
18
You should have received a copy of the GNU General Public License
19
along with this program; if not, write to the Free Software
20
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
package com.mysql.jdbc;
27
import com.mysql.jdbc.exceptions.MySQLTimeoutException;
28
import com.mysql.jdbc.profiler.ProfileEventSink;
29
import com.mysql.jdbc.profiler.ProfilerEvent;
30
import com.mysql.jdbc.util.LRUCache;
32
import java.sql.DataTruncation;
33
import java.sql.SQLException;
34
import java.sql.SQLWarning;
35
import java.sql.Types;
37
import java.util.ArrayList;
38
import java.util.Calendar;
39
import java.util.GregorianCalendar;
40
import java.util.HashMap;
41
import java.util.Iterator;
42
import java.util.List;
43
import java.util.Locale;
44
import java.util.TimerTask;
47
* A Statement object is used for executing a static SQL statement and obtaining
48
* the results produced by it.
51
* Only one ResultSet per Statement can be open at any point in time. Therefore,
52
* if the reading of one ResultSet is interleaved with the reading of another,
53
* each must have been generated by different Statements. All statement execute
54
* methods implicitly close a statement's current ResultSet if an open one
58
* @author Mark Matthews
59
* @version $Id: Statement.java 4624 2005-11-28 14:24:29 -0600 (Mon, 28 Nov
62
* @see java.sql.Statement
65
public class Statement implements java.sql.Statement {
66
protected static final String PING_MARKER = "/* ping */";
69
* Thread used to implement query timeouts...Eventually we could be more
70
* efficient and have one thread with timers, but this is a straightforward
71
* and simple way to implement a feature that isn't used all that often.
73
class CancelTask extends TimerTask {
75
long connectionId = 0;
76
SQLException caughtWhileCancelling = null;
78
CancelTask() throws SQLException {
79
connectionId = connection.getIO().getThreadId();
84
Thread cancelThread = new Thread() {
87
Connection cancelConn = null;
88
java.sql.Statement cancelStmt = null;
91
synchronized (cancelTimeoutMutex) {
92
cancelConn = connection.duplicate();
93
cancelStmt = cancelConn.createStatement();
94
cancelStmt.execute("KILL QUERY " + connectionId);
97
} catch (SQLException sqlEx) {
98
caughtWhileCancelling = sqlEx;
99
} catch (NullPointerException npe) {
100
// Case when connection closed while starting to cancel
101
// We can't easily synchronize this, because then one thread
102
// can't cancel() a running query
104
// ignore, we shouldn't re-throw this, because the connection's
105
// already closed, so the statement has been timed out.
107
if (cancelStmt != null) {
110
} catch (SQLException sqlEx) {
111
throw new RuntimeException(sqlEx.toString());
115
if (cancelConn != null) {
118
} catch (SQLException sqlEx) {
119
throw new RuntimeException(sqlEx.toString());
126
cancelThread.start();
130
/** Mutex to prevent race between returning query results and noticing
131
that we're timed-out or cancelled. */
133
protected Object cancelTimeoutMutex = new Object();
135
/** Used to generate IDs when profiling. */
136
protected static int statementCounter = 1;
138
public final static byte USES_VARIABLES_FALSE = 0;
140
public final static byte USES_VARIABLES_TRUE = 1;
142
public final static byte USES_VARIABLES_UNKNOWN = -1;
144
protected boolean wasCancelled = false;
146
/** Holds batched commands */
147
protected List batchedArgs;
149
/** The character converter to use (if available) */
150
protected SingleByteCharsetConverter charConverter = null;
152
/** The character encoding to use (if available) */
153
protected String charEncoding = null;
155
/** The connection that created us */
156
protected Connection connection = null;
158
protected long connectionId = 0;
160
/** The catalog in use */
161
protected String currentCatalog = null;
163
/** Should we process escape codes? */
164
protected boolean doEscapeProcessing = true;
166
/** If we're profiling, where should events go to? */
167
protected ProfileEventSink eventSink = null;
169
/** The number of rows to fetch at a time (currently ignored) */
170
private int fetchSize = 0;
172
/** Has this statement been closed? */
173
protected boolean isClosed = false;
175
/** The auto_increment value for the last insert */
176
protected long lastInsertId = -1;
178
/** The max field size for this statement */
179
protected int maxFieldSize = MysqlIO.getMaxBuf();
182
* The maximum number of rows to return for this statement (-1 means _all_
185
protected int maxRows = -1;
187
/** Has someone changed this for this statement? */
188
protected boolean maxRowsChanged = false;
190
/** List of currently-open ResultSets */
191
protected List openResults = new ArrayList();
193
/** Are we in pedantic mode? */
194
protected boolean pedantic = false;
197
* Where this statement was created, only used if profileSql or
198
* useUsageAdvisor set to true.
200
protected Throwable pointOfOrigin;
202
/** Should we profile? */
203
protected boolean profileSQL = false;
205
/** The current results */
206
protected ResultSet results = null;
208
/** The concurrency for this result set (updatable or not) */
209
protected int resultSetConcurrency = 0;
211
/** The type of this result set (scroll sensitive or in-sensitive) */
212
protected int resultSetType = 0;
214
/** Used to identify this statement when profiling. */
215
protected int statementId;
217
/** The timeout for a query */
218
protected int timeoutInMillis = 0;
220
/** The update count for this statement */
221
protected long updateCount = -1;
223
/** Should we use the usage advisor? */
224
protected boolean useUsageAdvisor = false;
226
/** The warnings chain. */
227
protected SQLWarning warningChain = null;
230
* Should this statement hold results open over .close() irregardless of
231
* connection's setting?
233
protected boolean holdResultsOpenOverClose = false;
235
protected ArrayList batchedGeneratedKeys = null;
237
protected boolean retrieveGeneratedKeys = false;
239
protected boolean continueBatchOnError = false;
241
protected PingTarget pingTarget = null;
245
* Constructor for a Statement.
248
* the Connection instantation that creates us
250
* the database name in use when we were created
252
* @throws SQLException
253
* if an error occurs.
255
public Statement(Connection c, String catalog) throws SQLException {
256
if ((c == null) || c.isClosed()) {
257
throw SQLError.createSQLException(
258
Messages.getString("Statement.0"), //$NON-NLS-1$
259
SQLError.SQL_STATE_CONNECTION_NOT_OPEN); //$NON-NLS-1$ //$NON-NLS-2$
263
this.connectionId = this.connection.getId();
265
this.currentCatalog = catalog;
266
this.pedantic = this.connection.getPedantic();
267
this.continueBatchOnError = this.connection.getContinueBatchOnError();
269
if (!this.connection.getDontTrackOpenResources()) {
270
this.connection.registerStatement(this);
274
// Adjust, if we know it
277
if (this.connection != null) {
278
this.maxFieldSize = this.connection.getMaxAllowedPacket();
280
int defaultFetchSize = this.connection.getDefaultFetchSize();
282
if (defaultFetchSize != 0) {
283
setFetchSize(defaultFetchSize);
287
if (this.connection.getUseUnicode()) {
288
this.charEncoding = this.connection.getEncoding();
290
this.charConverter = this.connection
291
.getCharsetConverter(this.charEncoding);
294
boolean profiling = this.connection.getProfileSql()
295
|| this.connection.getUseUsageAdvisor();
297
if (this.connection.getAutoGenerateTestcaseScript() || profiling) {
298
this.statementId = statementCounter++;
302
this.pointOfOrigin = new Throwable();
303
this.profileSQL = this.connection.getProfileSql();
304
this.useUsageAdvisor = this.connection.getUseUsageAdvisor();
305
this.eventSink = ProfileEventSink.getInstance(this.connection);
308
int maxRowsConn = this.connection.getMaxRows();
310
if (maxRowsConn != -1) {
311
setMaxRows(maxRowsConn);
321
* @throws SQLException
324
public synchronized void addBatch(String sql) throws SQLException {
325
if (this.batchedArgs == null) {
326
this.batchedArgs = new ArrayList();
330
this.batchedArgs.add(sql);
335
* Cancels this Statement object if both the DBMS and driver support
336
* aborting an SQL statement. This method can be used by one thread to
337
* cancel a statement that is being executed by another thread.
339
public void cancel() throws SQLException {
340
if (!this.isClosed &&
341
this.connection != null &&
342
this.connection.versionMeetsMinimum(5, 0, 0)) {
343
Connection cancelConn = null;
344
java.sql.Statement cancelStmt = null;
347
synchronized (this.cancelTimeoutMutex) {
348
cancelConn = this.connection.duplicate();
349
cancelStmt = cancelConn.createStatement();
350
cancelStmt.execute("KILL QUERY "
351
+ this.connection.getIO().getThreadId());
352
this.wasCancelled = true;
354
} catch (NullPointerException npe) {
355
// Case when connection closed while starting to cancel
356
// We can't easily synchronize this, because then one thread
357
// can't cancel() a running query
359
throw SQLError.createSQLException(Messages
360
.getString("Statement.49"), //$NON-NLS-1$
361
SQLError.SQL_STATE_CONNECTION_NOT_OPEN); //$NON-NLS-1$
363
if (cancelStmt != null) {
367
if (cancelConn != null) {
375
// --------------------------JDBC 2.0-----------------------------
378
* Checks if closed() has been called, and throws an exception if so
380
* @throws SQLException
381
* if this statement has been closed
383
protected void checkClosed() throws SQLException {
385
throw SQLError.createSQLException(Messages
386
.getString("Statement.49"), //$NON-NLS-1$
387
SQLError.SQL_STATE_CONNECTION_NOT_OPEN); //$NON-NLS-1$
392
* Checks if the given SQL query with the given first non-ws char is a DML
393
* statement. Throws an exception if it is.
397
* @param firstStatementChar
398
* the UC first non-ws char of the statement
400
* @throws SQLException
401
* if the statement contains DML
403
protected void checkForDml(String sql, char firstStatementChar)
404
throws SQLException {
405
if ((firstStatementChar == 'I') || (firstStatementChar == 'U')
406
|| (firstStatementChar == 'D') || (firstStatementChar == 'A')
407
|| (firstStatementChar == 'C')) {
408
String noCommentSql = StringUtils.stripComments(sql,
409
"'\"", "'\"", true, false, true, true);
411
if (StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "INSERT") //$NON-NLS-1$
412
|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "UPDATE") //$NON-NLS-1$
413
|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DELETE") //$NON-NLS-1$
414
|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DROP") //$NON-NLS-1$
415
|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "CREATE") //$NON-NLS-1$
416
|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "ALTER")) { //$NON-NLS-1$
417
throw SQLError.createSQLException(Messages
418
.getString("Statement.57"), //$NON-NLS-1$
419
SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
425
* Method checkNullOrEmptyQuery.
430
* @throws SQLException
431
* if query is null or empty.
433
protected void checkNullOrEmptyQuery(String sql) throws SQLException {
435
throw SQLError.createSQLException(Messages
436
.getString("Statement.59"), //$NON-NLS-1$
437
SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
440
if (sql.length() == 0) {
441
throw SQLError.createSQLException(Messages
442
.getString("Statement.61"), //$NON-NLS-1$
443
SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
448
* JDBC 2.0 Make the set of commands in the current batch empty. This method
451
* @exception SQLException
452
* if a database-access error occurs, or the driver does not
453
* support batch statements
455
public synchronized void clearBatch() throws SQLException {
456
if (this.batchedArgs != null) {
457
this.batchedArgs.clear();
462
* After this call, getWarnings returns null until a new warning is reported
463
* for this Statement.
465
* @exception SQLException
466
* if a database access error occurs (why?)
468
public void clearWarnings() throws SQLException {
469
this.warningChain = null;
473
* In many cases, it is desirable to immediately release a Statement's
474
* database and JDBC resources instead of waiting for this to happen when it
475
* is automatically closed. The close method provides this immediate
479
* <B>Note:</B> A Statement is automatically closed when it is garbage
480
* collected. When a Statement is closed, its current ResultSet, if one
481
* exists, is also closed.
484
* @exception SQLException
485
* if a database access error occurs
487
public void close() throws SQLException {
488
realClose(true, true);
492
* Close any open result sets that have been 'held open'
494
protected void closeAllOpenResults() {
495
if (this.openResults != null) {
496
for (Iterator iter = this.openResults.iterator(); iter.hasNext();) {
497
ResultSet element = (ResultSet) iter.next();
500
element.realClose(false);
501
} catch (SQLException sqlEx) {
502
AssertionFailedException.shouldNotHappen(sqlEx);
506
this.openResults.clear();
514
private ResultSet createResultSetUsingServerFetch(String sql)
515
throws SQLException {
516
java.sql.PreparedStatement pStmt = this.connection.prepareStatement(
517
sql, this.resultSetType, this.resultSetConcurrency);
519
pStmt.setFetchSize(this.fetchSize);
521
if (this.maxRows > -1) {
522
pStmt.setMaxRows(this.maxRows);
528
// Need to be able to get resultset irrespective if we issued DML or
529
// not to make this work.
531
ResultSet rs = ((com.mysql.jdbc.Statement) pStmt)
532
.getResultSetInternal();
535
.setStatementUsedForFetchingRows((com.mysql.jdbc.PreparedStatement) pStmt);
543
* We only stream result sets when they are forward-only, read-only, and the
544
* fetch size has been set to Integer.MIN_VALUE
546
* @return true if this result set should be streamed row at-a-time, rather
547
* than read all at once.
549
protected boolean createStreamingResultSet() {
550
return ((this.resultSetType == java.sql.ResultSet.TYPE_FORWARD_ONLY)
551
&& (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY) && (this.fetchSize == Integer.MIN_VALUE));
555
* Workaround for containers that 'check' for sane values of
556
* Statement.setFetchSize().
558
* @throws SQLException
560
public void enableStreamingResults() throws SQLException {
561
setFetchSize(Integer.MIN_VALUE);
562
setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
566
* Execute a SQL statement that may return multiple results. We don't have
567
* to worry about this since we do not support multiple ResultSets. You can
568
* use getResultSet or getUpdateCount to retrieve the result.
573
* @return true if the next result is a ResulSet, false if it is an update
574
* count or there are no more results
576
* @exception SQLException
577
* if a database access error occurs
579
public boolean execute(String sql) throws SQLException {
582
Connection locallyScopedConn = this.connection;
584
synchronized (locallyScopedConn.getMutex()) {
585
synchronized (this.cancelTimeoutMutex) {
586
this.wasCancelled = false;
589
checkNullOrEmptyQuery(sql);
593
char firstNonWsChar = StringUtils.firstNonWsCharUc(sql);
595
boolean isSelect = true;
597
if (firstNonWsChar != 'S') {
600
if (locallyScopedConn.isReadOnly()) {
601
throw SQLError.createSQLException(Messages
602
.getString("Statement.27") //$NON-NLS-1$
603
+ Messages.getString("Statement.28"), //$NON-NLS-1$
604
SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
608
if (this.doEscapeProcessing) {
609
Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
610
locallyScopedConn.serverSupportsConvertFn(), locallyScopedConn);
612
if (escapedSqlResult instanceof String) {
613
sql = (String) escapedSqlResult;
615
sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
619
if (this.results != null) {
620
if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
621
this.results.realClose(false);
625
if (firstNonWsChar == '/') {
626
if (sql.startsWith(PING_MARKER)) {
633
CachedResultSetMetaData cachedMetaData = null;
637
// If there isn't a limit clause in the SQL
638
// then limit the number of rows to return in
639
// an efficient manner. Only do this if
640
// setMaxRows() hasn't been used on any Statements
641
// generated from the current Connection (saves
642
// a query, and network traffic).
644
this.batchedGeneratedKeys = null;
646
if (useServerFetch()) {
647
rs = createResultSetUsingServerFetch(sql);
649
CancelTask timeoutTask = null;
651
String oldCatalog = null;
654
if (locallyScopedConn.getEnableQueryTimeouts() &&
655
this.timeoutInMillis != 0
656
&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
657
timeoutTask = new CancelTask();
658
Connection.getCancelTimer().schedule(timeoutTask,
659
this.timeoutInMillis);
664
if (!locallyScopedConn.getCatalog().equals(
665
this.currentCatalog)) {
666
oldCatalog = locallyScopedConn.getCatalog();
667
locallyScopedConn.setCatalog(this.currentCatalog);
671
// Check if we have cached metadata for this query...
673
if (locallyScopedConn.getCacheResultSetMetadata()) {
674
cachedMetaData = locallyScopedConn.getCachedMetaData(sql);
678
// Only apply max_rows to selects
680
if (locallyScopedConn.useMaxRows()) {
684
if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
685
rowLimit = this.maxRows;
687
if (this.maxRows <= 0) {
691
"SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, //$NON-NLS-1$
693
java.sql.ResultSet.TYPE_FORWARD_ONLY,
694
java.sql.ResultSet.CONCUR_READ_ONLY,
696
this.currentCatalog, true); //$NON-NLS-1$
701
"SET OPTION SQL_SELECT_LIMIT=" + this.maxRows, //$NON-NLS-1$
704
java.sql.ResultSet.TYPE_FORWARD_ONLY,
705
java.sql.ResultSet.CONCUR_READ_ONLY,
707
this.currentCatalog, true); //$NON-NLS-1$
714
"SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, //$NON-NLS-1$
715
java.sql.ResultSet.TYPE_FORWARD_ONLY,
716
java.sql.ResultSet.CONCUR_READ_ONLY,
717
false, this.currentCatalog,
721
// Finally, execute the query
722
rs = locallyScopedConn.execSQL(this, sql, rowLimit, null,
723
this.resultSetType, this.resultSetConcurrency,
724
createStreamingResultSet(),
725
this.currentCatalog, (cachedMetaData == null));
727
rs = locallyScopedConn.execSQL(this, sql, -1, null,
728
this.resultSetType, this.resultSetConcurrency,
729
createStreamingResultSet(),
730
this.currentCatalog, (cachedMetaData == null));
733
if (timeoutTask != null) {
734
if (timeoutTask.caughtWhileCancelling != null) {
735
throw timeoutTask.caughtWhileCancelling;
738
timeoutTask.cancel();
742
synchronized (this.cancelTimeoutMutex) {
743
if (this.wasCancelled) {
744
this.wasCancelled = false;
745
throw new MySQLTimeoutException();
749
if (timeoutTask != null) {
750
timeoutTask.cancel();
753
if (oldCatalog != null) {
754
locallyScopedConn.setCatalog(oldCatalog);
759
this.lastInsertId = rs.getUpdateID();
764
rs.setFirstCharOfQuery(firstNonWsChar);
766
if (rs.reallyResult()) {
767
if (cachedMetaData != null) {
768
locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData,
771
if (this.connection.getCacheResultSetMetadata()) {
772
locallyScopedConn.initializeResultsMetadataFromCache(sql,
773
null /* will be created */, this.results);
779
return ((rs != null) && rs.reallyResult());
784
* @see Statement#execute(String, int)
786
public boolean execute(String sql, int returnGeneratedKeys)
787
throws SQLException {
790
if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
793
Connection locallyScopedConn = this.connection;
795
synchronized (locallyScopedConn.getMutex()) {
796
// If this is a 'REPLACE' query, we need to be able to parse
797
// the 'info' message returned from the server to determine
798
// the actual number of keys generated.
799
boolean readInfoMsgState = this.connection
800
.isReadInfoMsgEnabled();
801
locallyScopedConn.setReadInfoMsgEnabled(true);
806
locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
815
* @see Statement#execute(String, int[])
817
public boolean execute(String sql, int[] generatedKeyIndices)
818
throws SQLException {
819
if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
822
Connection locallyScopedConn = this.connection;
824
synchronized (locallyScopedConn.getMutex()) {
825
// If this is a 'REPLACE' query, we need to be able to parse
826
// the 'info' message returned from the server to determine
827
// the actual number of keys generated.
828
boolean readInfoMsgState = locallyScopedConn
829
.isReadInfoMsgEnabled();
830
locallyScopedConn.setReadInfoMsgEnabled(true);
835
locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
844
* @see Statement#execute(String, String[])
846
public boolean execute(String sql, String[] generatedKeyNames)
847
throws SQLException {
848
if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
851
Connection locallyScopedConn = this.connection;
853
synchronized (locallyScopedConn.getMutex()) {
854
// If this is a 'REPLACE' query, we need to be able to parse
855
// the 'info' message returned from the server to determine
856
// the actual number of keys generated.
857
boolean readInfoMsgState = this.connection
858
.isReadInfoMsgEnabled();
859
locallyScopedConn.setReadInfoMsgEnabled(true);
864
locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
873
* JDBC 2.0 Submit a batch of commands to the database for execution. This
874
* method is optional.
876
* @return an array of update counts containing one element for each command
877
* in the batch. The array is ordered according to the order in
878
* which commands were inserted into the batch
880
* @exception SQLException
881
* if a database-access error occurs, or the driver does not
882
* support batch statements
883
* @throws java.sql.BatchUpdateException
886
public synchronized int[] executeBatch() throws SQLException {
889
Connection locallyScopedConn = this.connection;
891
if (locallyScopedConn.isReadOnly()) {
892
throw SQLError.createSQLException(Messages
893
.getString("Statement.34") //$NON-NLS-1$
894
+ Messages.getString("Statement.35"), //$NON-NLS-1$
895
SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
898
if (this.results != null) {
899
if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
900
this.results.realClose(false);
904
synchronized (locallyScopedConn.getMutex()) {
905
if (this.batchedArgs == null || this.batchedArgs.size() == 0) {
910
this.retrieveGeneratedKeys = true;
912
int[] updateCounts = null;
914
if (this.batchedArgs != null) {
915
int nbrCommands = this.batchedArgs.size();
917
this.batchedGeneratedKeys = new ArrayList(this.batchedArgs.size());
919
boolean multiQueriesEnabled = locallyScopedConn.getAllowMultiQueries();
921
if (locallyScopedConn.versionMeetsMinimum(4, 1, 1) &&
922
(multiQueriesEnabled ||
923
(locallyScopedConn.getRewriteBatchedStatements() &&
925
return executeBatchUsingMultiQueries(multiQueriesEnabled, nbrCommands);
928
updateCounts = new int[nbrCommands];
930
for (int i = 0; i < nbrCommands; i++) {
931
updateCounts[i] = -3;
934
SQLException sqlEx = null;
936
int commandIndex = 0;
938
for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
940
updateCounts[commandIndex] = executeUpdate((String) this.batchedArgs
941
.get(commandIndex), true);
942
getBatchedGeneratedKeys();
943
} catch (SQLException ex) {
944
updateCounts[commandIndex] = EXECUTE_FAILED;
946
if (this.continueBatchOnError) {
949
int[] newUpdateCounts = new int[commandIndex];
950
System.arraycopy(updateCounts, 0,
951
newUpdateCounts, 0, commandIndex);
953
throw new java.sql.BatchUpdateException(ex
954
.getMessage(), ex.getSQLState(), ex
955
.getErrorCode(), newUpdateCounts);
961
throw new java.sql.BatchUpdateException(sqlEx
962
.getMessage(), sqlEx.getSQLState(), sqlEx
963
.getErrorCode(), updateCounts);
967
return (updateCounts != null) ? updateCounts : new int[0];
969
this.retrieveGeneratedKeys = false;
977
* Rewrites batch into a single query to send to the server. This method
978
* will constrain each batch to be shorter than max_allowed_packet on the
981
* @return update counts in the same manner as executeBatch()
982
* @throws SQLException
984
private int[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled,
985
int nbrCommands) throws SQLException {
987
Connection locallyScopedConn = this.connection;
989
if (!multiQueriesEnabled) {
990
locallyScopedConn.getIO().enableMultiQueries();
993
java.sql.Statement batchStmt = null;
996
int[] updateCounts = new int[nbrCommands];
998
for (int i = 0; i < nbrCommands; i++) {
999
updateCounts[i] = -3;
1002
int commandIndex = 0;
1004
StringBuffer queryBuf = new StringBuffer();
1008
batchStmt = locallyScopedConn.createStatement();
1013
int numberOfBytesPerChar = 1;
1015
String connectionEncoding = locallyScopedConn.getEncoding();
1017
if (StringUtils.startsWithIgnoreCase(connectionEncoding, "utf")) {
1018
numberOfBytesPerChar = 3;
1019
} else if (CharsetMapping.isMultibyteCharset(connectionEncoding)) {
1020
numberOfBytesPerChar = 2;
1023
int escapeAdjust = 1;
1025
if (this.doEscapeProcessing) {
1026
escapeAdjust = 2; /* We assume packet _could_ grow by this amount, as we're not
1027
sure how big statement will end up after
1028
escape processing */
1031
for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
1032
String nextQuery = (String) this.batchedArgs.get(commandIndex);
1034
if (((((queryBuf.length() + nextQuery.length())
1035
* numberOfBytesPerChar) + 1 /* for semicolon */
1036
+ MysqlIO.HEADER_LENGTH) * escapeAdjust) + 32 > this.connection
1037
.getMaxAllowedPacket()) {
1038
batchStmt.execute(queryBuf.toString());
1040
updateCounts[counter++] = batchStmt.getUpdateCount();
1041
long generatedKeyStart = ((com.mysql.jdbc.Statement)batchStmt).getLastInsertID();
1042
byte[][] row = new byte[1][];
1043
row[0] = Long.toString(generatedKeyStart++).getBytes();
1044
this.batchedGeneratedKeys.add(row);
1046
while (batchStmt.getMoreResults()
1047
|| batchStmt.getUpdateCount() != -1) {
1048
updateCounts[counter++] = batchStmt.getUpdateCount();
1049
row = new byte[1][];
1050
row[0] = Long.toString(generatedKeyStart++).getBytes();
1051
this.batchedGeneratedKeys.add(row);
1054
queryBuf = new StringBuffer();
1057
queryBuf.append(nextQuery);
1058
queryBuf.append(";");
1061
if (queryBuf.length() > 0) {
1062
batchStmt.execute(queryBuf.toString());
1064
long generatedKeyStart = ((com.mysql.jdbc.Statement)batchStmt).getLastInsertID();
1065
byte[][] row = new byte[1][];
1066
row[0] = Long.toString(generatedKeyStart++).getBytes();
1067
this.batchedGeneratedKeys.add(row);
1069
updateCounts[counter++] = batchStmt.getUpdateCount();
1071
while (batchStmt.getMoreResults()
1072
|| batchStmt.getUpdateCount() != -1) {
1073
updateCounts[counter++] = batchStmt.getUpdateCount();
1074
row = new byte[1][];
1075
row[0] = Long.toString(generatedKeyStart++).getBytes();
1076
this.batchedGeneratedKeys.add(row);
1080
return (updateCounts != null) ? updateCounts : new int[0];
1083
if (batchStmt != null) {
1087
if (!multiQueriesEnabled) {
1088
locallyScopedConn.getIO().disableMultiQueries();
1095
* Execute a SQL statement that retruns a single ResultSet
1098
* typically a static SQL SELECT statement
1100
* @return a ResulSet that contains the data produced by the query
1102
* @exception SQLException
1103
* if a database access error occurs
1105
public java.sql.ResultSet executeQuery(String sql)
1106
throws SQLException {
1109
Connection locallyScopedConn = this.connection;
1111
synchronized (locallyScopedConn.getMutex()) {
1112
synchronized (this.cancelTimeoutMutex) {
1113
this.wasCancelled = false;
1116
checkNullOrEmptyQuery(sql);
1118
if (this.doEscapeProcessing) {
1119
Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
1120
locallyScopedConn.serverSupportsConvertFn(), this.connection);
1122
if (escapedSqlResult instanceof String) {
1123
sql = (String) escapedSqlResult;
1125
sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
1129
char firstStatementChar = StringUtils.firstNonWsCharUc(sql,
1130
findStartOfStatement(sql));
1132
if (sql.charAt(0) == '/') {
1133
if (sql.startsWith(PING_MARKER)) {
1136
return this.results;
1140
checkForDml(sql, firstStatementChar);
1142
if (this.results != null) {
1143
if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
1144
this.results.realClose(false);
1148
CachedResultSetMetaData cachedMetaData = null;
1150
// If there isn't a limit clause in the SQL
1151
// then limit the number of rows to return in
1152
// an efficient manner. Only do this if
1153
// setMaxRows() hasn't been used on any Statements
1154
// generated from the current Connection (saves
1155
// a query, and network traffic).
1157
if (useServerFetch()) {
1158
this.results = createResultSetUsingServerFetch(sql);
1160
return this.results;
1163
CancelTask timeoutTask = null;
1165
String oldCatalog = null;
1168
if (locallyScopedConn.getEnableQueryTimeouts() &&
1169
this.timeoutInMillis != 0
1170
&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
1171
timeoutTask = new CancelTask();
1172
Connection.getCancelTimer().schedule(timeoutTask,
1173
this.timeoutInMillis);
1176
if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
1177
oldCatalog = locallyScopedConn.getCatalog();
1178
locallyScopedConn.setCatalog(this.currentCatalog);
1182
// Check if we have cached metadata for this query...
1184
if (locallyScopedConn.getCacheResultSetMetadata()) {
1185
cachedMetaData = locallyScopedConn.getCachedMetaData(sql);
1188
if (locallyScopedConn.useMaxRows()) {
1189
// We need to execute this all together
1190
// So synchronize on the Connection's mutex (because
1191
// even queries going through there synchronize
1192
// on the connection
1193
if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
1194
this.results = locallyScopedConn.execSQL(this, sql,
1195
this.maxRows, null, this.resultSetType,
1196
this.resultSetConcurrency,
1197
createStreamingResultSet(),
1198
this.currentCatalog, (cachedMetaData == null));
1200
if (this.maxRows <= 0) {
1204
"SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, //$NON-NLS-1$
1205
java.sql.ResultSet.TYPE_FORWARD_ONLY,
1206
java.sql.ResultSet.CONCUR_READ_ONLY,
1207
false, this.currentCatalog,
1208
true); //$NON-NLS-1$
1213
"SET OPTION SQL_SELECT_LIMIT=" + this.maxRows, -1, //$NON-NLS-1$
1215
java.sql.ResultSet.TYPE_FORWARD_ONLY,
1216
java.sql.ResultSet.CONCUR_READ_ONLY,
1217
false, this.currentCatalog,
1218
true); //$NON-NLS-1$
1221
this.results = locallyScopedConn.execSQL(this, sql, -1,
1222
null, this.resultSetType,
1223
this.resultSetConcurrency,
1224
createStreamingResultSet(),
1225
this.currentCatalog, (cachedMetaData == null));
1227
if (oldCatalog != null) {
1228
locallyScopedConn.setCatalog(oldCatalog);
1232
this.results = locallyScopedConn.execSQL(this, sql, -1, null,
1233
this.resultSetType, this.resultSetConcurrency,
1234
createStreamingResultSet(),
1235
this.currentCatalog, (cachedMetaData == null));
1238
if (timeoutTask != null) {
1239
if (timeoutTask.caughtWhileCancelling != null) {
1240
throw timeoutTask.caughtWhileCancelling;
1243
timeoutTask.cancel();
1247
synchronized (this.cancelTimeoutMutex) {
1248
if (this.wasCancelled) {
1249
this.wasCancelled = false;
1250
throw new MySQLTimeoutException();
1254
if (timeoutTask != null) {
1255
timeoutTask.cancel();
1258
if (oldCatalog != null) {
1259
locallyScopedConn.setCatalog(oldCatalog);
1263
this.lastInsertId = this.results.getUpdateID();
1265
if (cachedMetaData != null) {
1266
locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData,
1269
if (this.connection.getCacheResultSetMetadata()) {
1270
locallyScopedConn.initializeResultsMetadataFromCache(sql,
1271
null /* will be created */, this.results);
1275
return this.results;
1279
protected void doPingInstead() throws SQLException {
1280
if (this.pingTarget != null) {
1281
this.pingTarget.doPing();
1283
this.connection.ping();
1286
ResultSet fakeSelectOneResultSet = generatePingResultSet();
1287
this.results = fakeSelectOneResultSet;
1290
protected ResultSet generatePingResultSet() throws SQLException {
1291
Field[] fields = { new Field(null, "1", Types.BIGINT, 1) };
1292
ArrayList rows = new ArrayList();
1293
byte[] colVal = new byte[] { (byte) '1' };
1295
rows.add(new byte[][] { colVal });
1297
return (ResultSet) DatabaseMetaData.buildResultSet(fields, rows,
1302
* Execute a SQL INSERT, UPDATE or DELETE statement. In addition SQL
1303
* statements that return nothing such as SQL DDL statements can be executed
1304
* Any IDs generated for AUTO_INCREMENT fields can be retrieved by casting
1305
* this Statement to org.gjt.mm.mysql.Statement and calling the
1306
* getLastInsertID() method.
1311
* @return either a row count, or 0 for SQL commands
1313
* @exception SQLException
1314
* if a database access error occurs
1316
public int executeUpdate(String sql) throws SQLException {
1317
return executeUpdate(sql, false);
1320
protected int executeUpdate(String sql, boolean isBatch)
1321
throws SQLException {
1324
Connection locallyScopedConn = this.connection;
1326
char firstStatementChar = StringUtils.firstNonWsCharUc(sql,
1327
findStartOfStatement(sql));
1329
ResultSet rs = null;
1331
synchronized (locallyScopedConn.getMutex()) {
1332
synchronized (this.cancelTimeoutMutex) {
1333
this.wasCancelled = false;
1336
checkNullOrEmptyQuery(sql);
1338
if (this.doEscapeProcessing) {
1339
Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
1340
this.connection.serverSupportsConvertFn(), this.connection);
1342
if (escapedSqlResult instanceof String) {
1343
sql = (String) escapedSqlResult;
1345
sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
1349
if (locallyScopedConn.isReadOnly()) {
1350
throw SQLError.createSQLException(Messages
1351
.getString("Statement.42") //$NON-NLS-1$
1352
+ Messages.getString("Statement.43"), //$NON-NLS-1$
1353
SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
1356
if (StringUtils.startsWithIgnoreCaseAndWs(sql, "select")) { //$NON-NLS-1$
1357
throw SQLError.createSQLException(Messages
1358
.getString("Statement.46"), //$NON-NLS-1$
1359
"01S03"); //$NON-NLS-1$
1362
if (this.results != null) {
1363
if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
1364
this.results.realClose(false);
1368
// The checking and changing of catalogs
1369
// must happen in sequence, so synchronize
1370
// on the same mutex that _conn is using
1372
CancelTask timeoutTask = null;
1374
String oldCatalog = null;
1377
if (locallyScopedConn.getEnableQueryTimeouts() &&
1378
this.timeoutInMillis != 0
1379
&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
1380
timeoutTask = new CancelTask();
1381
Connection.getCancelTimer().schedule(timeoutTask,
1382
this.timeoutInMillis);
1385
if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
1386
oldCatalog = locallyScopedConn.getCatalog();
1387
locallyScopedConn.setCatalog(this.currentCatalog);
1391
// Only apply max_rows to selects
1393
if (locallyScopedConn.useMaxRows()) {
1394
locallyScopedConn.execSQL(
1396
"SET OPTION SQL_SELECT_LIMIT=DEFAULT", //$NON-NLS-1$
1397
-1, null, java.sql.ResultSet.TYPE_FORWARD_ONLY,
1398
java.sql.ResultSet.CONCUR_READ_ONLY, false,
1399
this.currentCatalog, true);
1402
rs = locallyScopedConn.execSQL(this, sql, -1, null,
1403
java.sql.ResultSet.TYPE_FORWARD_ONLY,
1404
java.sql.ResultSet.CONCUR_READ_ONLY, false,
1405
this.currentCatalog,
1406
true /* force read of field info on DML */,
1409
if (timeoutTask != null) {
1410
if (timeoutTask.caughtWhileCancelling != null) {
1411
throw timeoutTask.caughtWhileCancelling;
1414
timeoutTask.cancel();
1418
synchronized (this.cancelTimeoutMutex) {
1419
if (this.wasCancelled) {
1420
this.wasCancelled = false;
1421
throw new MySQLTimeoutException();
1425
if (timeoutTask != null) {
1426
timeoutTask.cancel();
1429
if (oldCatalog != null) {
1430
locallyScopedConn.setCatalog(oldCatalog);
1437
rs.setFirstCharOfQuery(firstStatementChar);
1439
this.updateCount = rs.getUpdateCount();
1441
int truncatedUpdateCount = 0;
1443
if (this.updateCount > Integer.MAX_VALUE) {
1444
truncatedUpdateCount = Integer.MAX_VALUE;
1446
truncatedUpdateCount = (int) this.updateCount;
1449
this.lastInsertId = rs.getUpdateID();
1451
return truncatedUpdateCount;
1455
* @see Statement#executeUpdate(String, int)
1457
public int executeUpdate(String sql, int returnGeneratedKeys)
1458
throws SQLException {
1459
if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
1462
Connection locallyScopedConn = this.connection;
1464
synchronized (locallyScopedConn.getMutex()) {
1465
// If this is a 'REPLACE' query, we need to be able to parse
1466
// the 'info' message returned from the server to determine
1467
// the actual number of keys generated.
1468
boolean readInfoMsgState = locallyScopedConn
1469
.isReadInfoMsgEnabled();
1470
locallyScopedConn.setReadInfoMsgEnabled(true);
1473
return executeUpdate(sql);
1475
locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
1480
return executeUpdate(sql);
1484
* @see Statement#executeUpdate(String, int[])
1486
public int executeUpdate(String sql, int[] generatedKeyIndices)
1487
throws SQLException {
1488
if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
1491
Connection locallyScopedConn = this.connection;
1493
synchronized (locallyScopedConn.getMutex()) {
1494
// If this is a 'REPLACE' query, we need to be able to parse
1495
// the 'info' message returned from the server to determine
1496
// the actual number of keys generated.
1497
boolean readInfoMsgState = locallyScopedConn
1498
.isReadInfoMsgEnabled();
1499
locallyScopedConn.setReadInfoMsgEnabled(true);
1502
return executeUpdate(sql);
1504
locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
1509
return executeUpdate(sql);
1513
* @see Statement#executeUpdate(String, String[])
1515
public int executeUpdate(String sql, String[] generatedKeyNames)
1516
throws SQLException {
1517
if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
1520
Connection locallyScopedConn = this.connection;
1522
synchronized (locallyScopedConn.getMutex()) {
1523
// If this is a 'REPLACE' query, we need to be able to parse
1524
// the 'info' message returned from the server to determine
1525
// the actual number of keys generated.
1526
boolean readInfoMsgState = this.connection
1527
.isReadInfoMsgEnabled();
1528
locallyScopedConn.setReadInfoMsgEnabled(true);
1531
return executeUpdate(sql);
1533
locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
1538
return executeUpdate(sql);
1544
* Optimization to only use one calendar per-session, or calculate it for
1545
* each call, depending on user configuration
1547
protected Calendar getCalendarInstanceForSessionOrNew() {
1548
if (this.connection != null) {
1549
return this.connection.getCalendarInstanceForSessionOrNew();
1551
// punt, no connection around
1552
return new GregorianCalendar();
1557
* JDBC 2.0 Return the Connection that produced the Statement.
1559
* @return the Connection that produced the Statement
1561
* @throws SQLException
1562
* if an error occurs
1564
public java.sql.Connection getConnection() throws SQLException {
1565
return this.connection;
1569
* JDBC 2.0 Determine the fetch direction.
1571
* @return the default fetch direction
1573
* @exception SQLException
1574
* if a database-access error occurs
1576
public int getFetchDirection() throws SQLException {
1577
return java.sql.ResultSet.FETCH_FORWARD;
1581
* JDBC 2.0 Determine the default fetch size.
1583
* @return the number of rows to fetch at a time
1585
* @throws SQLException
1586
* if an error occurs
1588
public int getFetchSize() throws SQLException {
1589
return this.fetchSize;
1595
* @return DOCUMENT ME!
1597
* @throws SQLException
1600
public java.sql.ResultSet getGeneratedKeys()
1601
throws SQLException {
1602
if (this.batchedGeneratedKeys == null) {
1603
return getGeneratedKeysInternal();
1606
Field[] fields = new Field[1];
1607
fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$
1608
fields[0].setConnection(this.connection);
1610
return new com.mysql.jdbc.ResultSet(this.currentCatalog, fields,
1611
new RowDataStatic(this.batchedGeneratedKeys), this.connection,
1616
* Needed because there's no concept of super.super to get to this
1617
* implementation from ServerPreparedStatement when dealing with batched
1620
protected java.sql.ResultSet getGeneratedKeysInternal()
1621
throws SQLException {
1622
Field[] fields = new Field[1];
1623
fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$
1624
fields[0].setConnection(this.connection);
1626
ArrayList rowSet = new ArrayList();
1628
long beginAt = getLastInsertID();
1629
int numKeys = getUpdateCount();
1631
if (this.results != null) {
1632
String serverInfo = this.results.getServerInfo();
1635
// Only parse server info messages for 'REPLACE'
1638
if ((numKeys > 0) && (this.results.getFirstCharOfQuery() == 'R')
1639
&& (serverInfo != null) && (serverInfo.length() > 0)) {
1640
numKeys = getRecordCountFromInfo(serverInfo);
1643
if ((beginAt > 0) && (numKeys > 0)) {
1644
for (int i = 0; i < numKeys; i++) {
1645
byte[][] row = new byte[1][];
1646
row[0] = Long.toString(beginAt++).getBytes();
1652
return new com.mysql.jdbc.ResultSet(this.currentCatalog, fields,
1653
new RowDataStatic(rowSet), this.connection, this);
1657
* Returns the id used when profiling
1659
* @return the id used when profiling.
1661
protected int getId() {
1662
return this.statementId;
1666
* getLastInsertID returns the value of the auto_incremented key after an
1667
* executeQuery() or excute() call.
1670
* This gets around the un-threadsafe behavior of "select LAST_INSERT_ID()"
1671
* which is tied to the Connection that created this Statement, and
1672
* therefore could have had many INSERTS performed before one gets a chance
1673
* to call "select LAST_INSERT_ID()".
1676
* @return the last update ID.
1678
public long getLastInsertID() {
1679
return this.lastInsertId;
1683
* getLongUpdateCount returns the current result as an update count, if the
1684
* result is a ResultSet or there are no more results, -1 is returned. It
1685
* should only be called once per result.
1688
* This method returns longs as MySQL server versions newer than 3.22.4
1689
* return 64-bit values for update counts
1692
* @return the current update count.
1694
public long getLongUpdateCount() {
1695
if (this.results == null) {
1699
if (this.results.reallyResult()) {
1703
return this.updateCount;
1707
* The maxFieldSize limit (in bytes) is the maximum amount of data returned
1708
* for any column value; it only applies to BINARY, VARBINARY,
1709
* LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR columns. If the limit is
1710
* exceeded, the excess data is silently discarded.
1712
* @return the current max column size limit; zero means unlimited
1714
* @exception SQLException
1715
* if a database access error occurs
1717
public int getMaxFieldSize() throws SQLException {
1718
return this.maxFieldSize;
1722
* The maxRows limit is set to limit the number of rows that any ResultSet
1723
* can contain. If the limit is exceeded, the excess rows are silently
1726
* @return the current maximum row limit; zero means unlimited
1728
* @exception SQLException
1729
* if a database access error occurs
1731
public int getMaxRows() throws SQLException {
1732
if (this.maxRows <= 0) {
1736
return this.maxRows;
1740
* getMoreResults moves to a Statement's next result. If it returns true,
1741
* this result is a ResulSet.
1743
* @return true if the next ResultSet is valid
1745
* @exception SQLException
1746
* if a database access error occurs
1748
public boolean getMoreResults() throws SQLException {
1749
return getMoreResults(CLOSE_CURRENT_RESULT);
1753
* @see Statement#getMoreResults(int)
1755
public boolean getMoreResults(int current) throws SQLException {
1757
if (this.results == null) {
1761
ResultSet nextResultSet = this.results.getNextResultSet();
1764
case java.sql.Statement.CLOSE_CURRENT_RESULT:
1766
if (this.results != null) {
1767
this.results.close();
1768
this.results.clearNextResult();
1773
case java.sql.Statement.CLOSE_ALL_RESULTS:
1775
if (this.results != null) {
1776
this.results.close();
1777
this.results.clearNextResult();
1780
closeAllOpenResults();
1784
case java.sql.Statement.KEEP_CURRENT_RESULT:
1785
if (!this.connection.getDontTrackOpenResources()) {
1786
this.openResults.add(this.results);
1789
this.results.clearNextResult(); // nobody besides us should
1790
// ever need this value...
1794
throw SQLError.createSQLException(Messages
1795
.getString("Statement.19"), //$NON-NLS-1$
1796
SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
1799
this.results = nextResultSet;
1801
if (this.results == null) {
1802
this.updateCount = -1;
1803
this.lastInsertId = -1;
1804
} else if (this.results.reallyResult()) {
1805
this.updateCount = -1;
1806
this.lastInsertId = -1;
1808
this.updateCount = this.results.getUpdateCount();
1809
this.lastInsertId = this.results.getUpdateID();
1812
return ((this.results != null) && this.results.reallyResult()) ? true
1817
* The queryTimeout limit is the number of seconds the driver will wait for
1818
* a Statement to execute. If the limit is exceeded, a SQLException is
1821
* @return the current query timeout limit in seconds; 0 = unlimited
1823
* @exception SQLException
1824
* if a database access error occurs
1826
public int getQueryTimeout() throws SQLException {
1827
return this.timeoutInMillis / 1000;
1831
* Parses actual record count from 'info' message
1836
* @return DOCUMENT ME!
1838
private int getRecordCountFromInfo(String serverInfo) {
1839
StringBuffer recordsBuf = new StringBuffer();
1840
int recordsCount = 0;
1841
int duplicatesCount = 0;
1845
int length = serverInfo.length();
1848
for (; i < length; i++) {
1849
c = serverInfo.charAt(i);
1851
if (Character.isDigit(c)) {
1856
recordsBuf.append(c);
1859
for (; i < length; i++) {
1860
c = serverInfo.charAt(i);
1862
if (!Character.isDigit(c)) {
1866
recordsBuf.append(c);
1869
recordsCount = Integer.parseInt(recordsBuf.toString());
1871
StringBuffer duplicatesBuf = new StringBuffer();
1873
for (; i < length; i++) {
1874
c = serverInfo.charAt(i);
1876
if (Character.isDigit(c)) {
1881
duplicatesBuf.append(c);
1884
for (; i < length; i++) {
1885
c = serverInfo.charAt(i);
1887
if (!Character.isDigit(c)) {
1891
duplicatesBuf.append(c);
1894
duplicatesCount = Integer.parseInt(duplicatesBuf.toString());
1896
return recordsCount - duplicatesCount;
1900
* getResultSet returns the current result as a ResultSet. It should only be
1901
* called once per result.
1903
* @return the current result set; null if there are no more
1905
* @exception SQLException
1906
* if a database access error occurs (why?)
1908
public java.sql.ResultSet getResultSet() throws SQLException {
1909
return ((this.results != null) && this.results.reallyResult()) ? (java.sql.ResultSet) this.results
1914
* JDBC 2.0 Determine the result set concurrency.
1916
* @return CONCUR_UPDATABLE or CONCUR_READONLY
1918
* @throws SQLException
1919
* if an error occurs
1921
public int getResultSetConcurrency() throws SQLException {
1922
return this.resultSetConcurrency;
1926
* @see Statement#getResultSetHoldability()
1928
public int getResultSetHoldability() throws SQLException {
1929
return java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
1932
protected ResultSet getResultSetInternal() {
1933
return this.results;
1937
* JDBC 2.0 Determine the result set type.
1939
* @return the ResultSet type (SCROLL_SENSITIVE or SCROLL_INSENSITIVE)
1941
* @throws SQLException
1942
* if an error occurs.
1944
public int getResultSetType() throws SQLException {
1945
return this.resultSetType;
1949
* getUpdateCount returns the current result as an update count, if the
1950
* result is a ResultSet or there are no more results, -1 is returned. It
1951
* should only be called once per result.
1953
* @return the current result as an update count.
1955
* @exception SQLException
1956
* if a database access error occurs
1958
public int getUpdateCount() throws SQLException {
1959
if (this.results == null) {
1963
if (this.results.reallyResult()) {
1967
int truncatedUpdateCount = 0;
1969
if (this.results.getUpdateCount() > Integer.MAX_VALUE) {
1970
truncatedUpdateCount = Integer.MAX_VALUE;
1972
truncatedUpdateCount = (int) this.results.getUpdateCount();
1975
return truncatedUpdateCount;
1979
* The first warning reported by calls on this Statement is returned. A
1980
* Statement's execute methods clear its java.sql.SQLWarning chain.
1981
* Subsequent Statement warnings will be chained to this
1982
* java.sql.SQLWarning.
1985
* The Warning chain is automatically cleared each time a statement is
1990
* <B>Note:</B> If you are processing a ResultSet then any warnings
1991
* associated with ResultSet reads will be chained on the ResultSet object.
1994
* @return the first java.sql.SQLWarning or null
1996
* @exception SQLException
1997
* if a database access error occurs
1999
public java.sql.SQLWarning getWarnings() throws SQLException {
2002
if (this.connection != null && !this.connection.isClosed()
2003
&& this.connection.versionMeetsMinimum(4, 1, 0)) {
2004
SQLWarning pendingWarningsFromServer = SQLError
2005
.convertShowWarningsToSQLWarnings(this.connection);
2007
if (this.warningChain != null) {
2008
this.warningChain.setNextWarning(pendingWarningsFromServer);
2010
this.warningChain = pendingWarningsFromServer;
2013
return this.warningChain;
2016
return this.warningChain;
2022
* Closes this statement, and frees resources.
2024
* @param calledExplicitly
2025
* was this called from close()?
2027
* @throws SQLException
2028
* if an error occurs
2030
protected void realClose(boolean calledExplicitly, boolean closeOpenResults)
2031
throws SQLException {
2032
if (this.isClosed) {
2036
if (this.useUsageAdvisor) {
2037
if (!calledExplicitly) {
2038
String message = Messages.getString("Statement.63") //$NON-NLS-1$
2039
+ Messages.getString("Statement.64"); //$NON-NLS-1$
2041
this.eventSink.consumeEvent(new ProfilerEvent(
2042
ProfilerEvent.TYPE_WARN,
2044
this.currentCatalog, this.connectionId, this.getId(),
2045
-1, System.currentTimeMillis(), 0,
2046
Constants.MILLIS_I18N, null, this.pointOfOrigin,
2051
if (this.results != null) {
2052
if (closeOpenResults) {
2053
closeOpenResults = !this.holdResultsOpenOverClose;
2056
if (closeOpenResults && this.connection != null
2057
&& !this.connection.getHoldResultsOpenOverStatementClose()) {
2059
this.results.close();
2060
} catch (Exception ex) {
2064
this.closeAllOpenResults();
2068
if (this.connection != null) {
2069
if (this.maxRowsChanged) {
2070
this.connection.unsetMaxRows(this);
2073
if (!this.connection.getDontTrackOpenResources()) {
2074
this.connection.unregisterStatement(this);
2078
this.isClosed = true;
2080
this.results = null;
2081
this.connection = null;
2082
this.warningChain = null;
2083
this.openResults = null;
2084
this.batchedGeneratedKeys = null;
2085
this.cancelTimeoutMutex = null;
2086
this.pingTarget = null;
2090
* setCursorName defines the SQL cursor name that will be used by subsequent
2091
* execute methods. This name can then be used in SQL positioned
2092
* update/delete statements to identify the current row in the ResultSet
2093
* generated by this statement. If a database doesn't support positioned
2094
* update/delete, this method is a no-op.
2097
* <b>Note:</b> This MySQL driver does not support cursors.
2101
* the new cursor name
2103
* @exception SQLException
2104
* if a database access error occurs
2106
public void setCursorName(String name) throws SQLException {
2111
* If escape scanning is on (the default), the driver will do escape
2112
* substitution before sending the SQL to the database.
2115
* true to enable; false to disable
2117
* @exception SQLException
2118
* if a database access error occurs
2120
public void setEscapeProcessing(boolean enable)
2121
throws SQLException {
2122
this.doEscapeProcessing = enable;
2126
* JDBC 2.0 Give a hint as to the direction in which the rows in a result
2127
* set will be processed. The hint applies only to result sets created using
2128
* this Statement object. The default value is ResultSet.FETCH_FORWARD.
2131
* the initial direction for processing rows
2133
* @exception SQLException
2134
* if a database-access error occurs or direction is not one
2135
* of ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, or
2136
* ResultSet.FETCH_UNKNOWN
2138
public void setFetchDirection(int direction) throws SQLException {
2139
switch (direction) {
2140
case java.sql.ResultSet.FETCH_FORWARD:
2141
case java.sql.ResultSet.FETCH_REVERSE:
2142
case java.sql.ResultSet.FETCH_UNKNOWN:
2146
throw SQLError.createSQLException(
2147
Messages.getString("Statement.5"), //$NON-NLS-1$
2148
SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
2153
* JDBC 2.0 Give the JDBC driver a hint as to the number of rows that should
2154
* be fetched from the database when more rows are needed. The number of
2155
* rows specified only affects result sets created using this statement. If
2156
* the value specified is zero, then the hint is ignored. The default value
2160
* the number of rows to fetch
2162
* @exception SQLException
2163
* if a database-access error occurs, or the condition 0
2164
* <= rows <= this.getMaxRows() is not satisfied.
2166
public void setFetchSize(int rows) throws SQLException {
2167
if (((rows < 0) && (rows != Integer.MIN_VALUE))
2168
|| ((this.maxRows != 0) && (this.maxRows != -1) && (rows > this
2170
throw SQLError.createSQLException(
2171
Messages.getString("Statement.7"), //$NON-NLS-1$
2172
SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
2175
this.fetchSize = rows;
2178
protected void setHoldResultsOpenOverClose(boolean holdResultsOpenOverClose) {
2179
this.holdResultsOpenOverClose = holdResultsOpenOverClose;
2183
* Sets the maxFieldSize
2186
* the new max column size limit; zero means unlimited
2188
* @exception SQLException
2189
* if size exceeds buffer size
2191
public void setMaxFieldSize(int max) throws SQLException {
2193
throw SQLError.createSQLException(Messages
2194
.getString("Statement.11"), //$NON-NLS-1$
2195
SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
2198
int maxBuf = (this.connection != null) ? this.connection
2199
.getMaxAllowedPacket() : MysqlIO.getMaxBuf();
2202
throw SQLError.createSQLException(Messages.getString(
2203
"Statement.13", //$NON-NLS-1$
2204
new Object[] { new Long(maxBuf) }), //$NON-NLS-1$
2205
SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
2208
this.maxFieldSize = max;
2212
* Set the maximum number of rows
2215
* the new max rows limit; zero means unlimited
2217
* @exception SQLException
2218
* if a database access error occurs
2222
public void setMaxRows(int max) throws SQLException {
2223
if ((max > MysqlDefs.MAX_ROWS) || (max < 0)) {
2225
.createSQLException(
2226
Messages.getString("Statement.15") + max //$NON-NLS-1$
2227
+ " > " //$NON-NLS-1$ //$NON-NLS-2$
2228
+ MysqlDefs.MAX_ROWS + ".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
2236
this.maxRowsChanged = true;
2238
if (this.maxRows == -1) {
2239
this.connection.unsetMaxRows(this);
2240
this.maxRowsChanged = false;
2242
// Most people don't use setMaxRows()
2243
// so don't penalize them
2244
// with the extra query it takes
2245
// to do it efficiently unless we need
2247
this.connection.maxRowsChanged(this);
2252
* Sets the queryTimeout limit
2255
* the new query timeout limit in seconds
2257
* @exception SQLException
2258
* if a database access error occurs
2260
public void setQueryTimeout(int seconds) throws SQLException {
2262
throw SQLError.createSQLException(Messages
2263
.getString("Statement.21"), //$NON-NLS-1$
2264
SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
2267
this.timeoutInMillis = seconds * 1000;
2271
* Sets the concurrency for result sets generated by this statement
2273
* @param concurrencyFlag
2276
void setResultSetConcurrency(int concurrencyFlag) {
2277
this.resultSetConcurrency = concurrencyFlag;
2281
* Sets the result set type for result sets generated by this statement
2286
void setResultSetType(int typeFlag) {
2287
this.resultSetType = typeFlag;
2290
protected void getBatchedGeneratedKeys(java.sql.Statement batchedStatement) throws SQLException {
2291
if (this.retrieveGeneratedKeys) {
2292
java.sql.ResultSet rs = null;
2295
rs = batchedStatement.getGeneratedKeys();
2298
this.batchedGeneratedKeys
2299
.add(new byte[][] { rs.getBytes(1) });
2309
protected void getBatchedGeneratedKeys() throws SQLException {
2310
if (this.retrieveGeneratedKeys) {
2311
java.sql.ResultSet rs = null;
2314
rs = getGeneratedKeysInternal();
2317
this.batchedGeneratedKeys
2318
.add(new byte[][] { rs.getBytes(1) });
2331
private boolean useServerFetch() throws SQLException {
2333
return this.connection.isCursorFetchEnabled() && this.fetchSize > 0
2334
&& this.resultSetConcurrency == ResultSet.CONCUR_READ_ONLY
2335
&& this.resultSetType == ResultSet.TYPE_FORWARD_ONLY;
2338
protected int findStartOfStatement(String sql) {
2339
int statementStartPos = 0;
2341
if (StringUtils.startsWithIgnoreCaseAndWs(sql, "/*")) {
2342
statementStartPos = sql.indexOf("*/");
2344
if (statementStartPos == -1) {
2345
statementStartPos = 0;
2347
statementStartPos += 2;
2349
} else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "--")
2350
|| StringUtils.startsWithIgnoreCaseAndWs(sql, "#")) {
2351
statementStartPos = sql.indexOf('\n');
2353
if (statementStartPos == -1) {
2354
statementStartPos = sql.indexOf('\r');
2356
if (statementStartPos == -1) {
2357
statementStartPos = 0;
2362
return statementStartPos;
2365
protected synchronized void setPingTarget(PingTarget pingTarget) {
2366
this.pingTarget = pingTarget;