source: Ballon/out/artifacts/geisa_artifact/WEB-INF/lib/mysql-connector-java-5.1.21/src/com/mysql/jdbc/StatementImpl.java @ 848

Last change on this file since 848 was 766, checked in by npipsl, 11 years ago
File size: 84.1 KB
Line 
1/*
2  Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
3
4  The MySQL Connector/J is licensed under the terms of the GPLv2
5  <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
6  There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
7  this software, see the FLOSS License Exception
8  <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
9
10  This program is free software; you can redistribute it and/or modify it under the terms
11  of the GNU General Public License as published by the Free Software Foundation; version 2
12  of the License.
13
14  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
15  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  See the GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License along with this
19  program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
20  Floor, Boston, MA 02110-1301  USA
21 */
22package com.mysql.jdbc;
23
24import java.io.InputStream;
25import java.math.BigInteger;
26import java.sql.BatchUpdateException;
27import java.sql.DriverManager;
28import java.sql.ResultSet;
29import java.sql.SQLException;
30import java.sql.SQLWarning;
31import java.sql.Types;
32import java.util.ArrayList;
33import java.util.Calendar;
34import java.util.Collections;
35import java.util.Enumeration;
36import java.util.GregorianCalendar;
37import java.util.HashSet;
38import java.util.List;
39import java.util.Properties;
40import java.util.Set;
41import java.util.TimerTask;
42import java.util.concurrent.atomic.AtomicBoolean;
43
44import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
45import com.mysql.jdbc.exceptions.MySQLTimeoutException;
46import com.mysql.jdbc.profiler.ProfilerEvent;
47import com.mysql.jdbc.profiler.ProfilerEventHandler;
48
49/**
50 * A Statement object is used for executing a static SQL statement and obtaining
51 * the results produced by it.
52 *
53 * <p>
54 * Only one ResultSet per Statement can be open at any point in time. Therefore,
55 * if the reading of one ResultSet is interleaved with the reading of another,
56 * each must have been generated by different Statements. All statement execute
57 * methods implicitly close a statement's current ResultSet if an open one
58 * exists.
59 * </p>
60 *
61 * @author Mark Matthews
62 * @version $Id: Statement.java 4624 2005-11-28 14:24:29 -0600 (Mon, 28 Nov
63 *          2005) mmatthews $
64 *
65 * @see java.sql.Statement
66 * @see ResultSetInternalMethods
67 */
68public class StatementImpl implements Statement {
69        protected static final String PING_MARKER = "/* ping */";
70        /**
71         * Thread used to implement query timeouts...Eventually we could be more
72         * efficient and have one thread with timers, but this is a straightforward
73         * and simple way to implement a feature that isn't used all that often.
74         */
75        class CancelTask extends TimerTask {
76
77                long connectionId = 0;
78                String origHost = "";
79                SQLException caughtWhileCancelling = null;
80                StatementImpl toCancel;
81                Properties origConnProps = null; 
82                String origConnURL = "";
83               
84                CancelTask(StatementImpl cancellee) throws SQLException {
85                        connectionId = cancellee.connectionId;
86                        origHost = connection.getHost();
87                        toCancel = cancellee;
88                        origConnProps = new Properties();
89                       
90                        Properties props = connection.getProperties();
91                       
92                        Enumeration<?> keys = props.propertyNames();
93                       
94                        while (keys.hasMoreElements()) {
95                                String key = keys.nextElement().toString();
96                                origConnProps.setProperty(key, props.getProperty(key));
97                        }
98                       
99                        origConnURL = connection.getURL();
100                }
101
102                public void run() {
103
104                        Thread cancelThread = new Thread() {
105
106                                public void run() {
107                                        if (connection.getQueryTimeoutKillsConnection()) {
108                                                try {
109                                                        toCancel.wasCancelled = true;
110                                                        toCancel.wasCancelledByTimeout = true;
111                                                        connection.realClose(false, false, true, 
112                                                                        new MySQLStatementCancelledException(Messages.getString("Statement.ConnectionKilledDueToTimeout")));
113                                                } catch (NullPointerException npe) {
114                                                        // not worth guarding against
115                                                } catch (SQLException sqlEx) {
116                                                        caughtWhileCancelling = sqlEx;
117                                                }
118                                        } else {
119                                                Connection cancelConn = null;
120                                                java.sql.Statement cancelStmt = null;
121       
122                                                try {
123                                                        synchronized (cancelTimeoutMutex) {
124                                                                if (origConnURL.equals(connection.getURL())) {
125                                                                        //All's fine
126                                                                        cancelConn = connection.duplicate();
127                                                                        cancelStmt = cancelConn.createStatement();
128                                                                        cancelStmt.execute("KILL QUERY " + connectionId);
129                                                                } else {
130                                                                        try {
131                                                                                cancelConn = (Connection) DriverManager.getConnection(origConnURL, origConnProps);
132                                                                                cancelStmt = cancelConn.createStatement();
133                                                                                cancelStmt.execute("KILL QUERY " + connectionId);
134                                                                        } catch (NullPointerException npe){
135                                                                                //Log this? "Failed to connect to " + origConnURL + " and KILL query"
136                                                                        }
137                                                                }
138                                                                toCancel.wasCancelled = true;
139                                                                toCancel.wasCancelledByTimeout = true;
140                                                        }
141                                                } catch (SQLException sqlEx) {
142                                                        caughtWhileCancelling = sqlEx;
143                                                } catch (NullPointerException npe) {
144                                                        // Case when connection closed while starting to cancel
145                                                        // We can't easily synchronize this, because then one thread
146                                                        // can't cancel() a running query
147       
148                                                        // ignore, we shouldn't re-throw this, because the connection's
149                                                        // already closed, so the statement has been timed out.
150                                                } finally {
151                                                        if (cancelStmt != null) {
152                                                                try {
153                                                                        cancelStmt.close();
154                                                                } catch (SQLException sqlEx) {
155                                                                        throw new RuntimeException(sqlEx.toString());
156                                                                }
157                                                        }
158       
159                                                        if (cancelConn != null) {
160                                                                try {
161                                                                        cancelConn.close();
162                                                                } catch (SQLException sqlEx) {
163                                                                        throw new RuntimeException(sqlEx.toString());
164                                                                }
165                                                        }
166                                                       
167                                                        toCancel = null;
168                                                        origConnProps = null;
169                                                        origConnURL = null;
170                                                }
171                                        }
172                                }
173                        };
174
175                        cancelThread.start();
176                }
177        }
178
179        /** Mutex to prevent race between returning query results and noticing
180    that we're timed-out or cancelled. */
181
182        protected Object cancelTimeoutMutex = new Object();
183
184        /** Used to generate IDs when profiling. */
185        static int statementCounter = 1;
186
187        public final static byte USES_VARIABLES_FALSE = 0;
188
189        public final static byte USES_VARIABLES_TRUE = 1;
190
191        public final static byte USES_VARIABLES_UNKNOWN = -1;
192
193        protected boolean wasCancelled = false;
194        protected boolean wasCancelledByTimeout = false;
195
196        /** Holds batched commands */
197        protected List<Object> batchedArgs;
198
199        /** The character converter to use (if available) */
200        protected SingleByteCharsetConverter charConverter = null;
201
202        /** The character encoding to use (if available) */
203        protected String charEncoding = null;
204
205        /** The connection that created us */
206        protected volatile MySQLConnection connection = null;
207
208        protected long connectionId = 0;
209
210        /** The catalog in use */
211        protected String currentCatalog = null;
212
213        /** Should we process escape codes? */
214        protected boolean doEscapeProcessing = true;
215
216        /** If we're profiling, where should events go to? */
217        protected ProfilerEventHandler eventSink = null;
218
219        /** The number of rows to fetch at a time (currently ignored) */
220        private int fetchSize = 0;
221
222        /** Has this statement been closed? */
223        protected boolean isClosed = false;
224
225        /** The auto_increment value for the last insert */
226        protected long lastInsertId = -1;
227
228        /** The max field size for this statement */
229        protected int maxFieldSize = MysqlIO.getMaxBuf();
230
231        /**
232         * The maximum number of rows to return for this statement (-1 means _all_
233         * rows)
234         */
235        protected int maxRows = -1;
236
237        /** Has someone changed this for this statement? */
238        protected boolean maxRowsChanged = false;
239
240        /** Set of currently-open ResultSets */
241        protected Set<ResultSetInternalMethods> openResults = new HashSet<ResultSetInternalMethods>();
242
243        /** Are we in pedantic mode? */
244        protected boolean pedantic = false;
245
246        /**
247         * Where this statement was created, only used if profileSql or
248         * useUsageAdvisor set to true.
249         */
250        protected Throwable pointOfOrigin;
251
252        /** Should we profile? */
253        protected boolean profileSQL = false;
254
255        /** The current results */
256        protected ResultSetInternalMethods results = null;
257
258        /** The concurrency for this result set (updatable or not) */
259        protected int resultSetConcurrency = 0;
260
261        /** The type of this result set (scroll sensitive or in-sensitive) */
262        protected int resultSetType = 0;
263
264        /** Used to identify this statement when profiling. */
265        protected int statementId;
266
267        /** The timeout for a query */
268        protected int timeoutInMillis = 0;
269
270        /** The update count for this statement */
271        protected long updateCount = -1;
272
273        /** Should we use the usage advisor? */
274        protected boolean useUsageAdvisor = false;
275
276        /** The warnings chain. */
277        protected SQLWarning warningChain = null;
278       
279        /** Has clearWarnings() been called? */
280        protected boolean clearWarningsCalled = false;
281
282        /**
283         * Should this statement hold results open over .close() irregardless of
284         * connection's setting?
285         */
286        protected boolean holdResultsOpenOverClose = false;
287
288        protected ArrayList<ResultSetRow> batchedGeneratedKeys = null;
289
290        protected boolean retrieveGeneratedKeys = false;
291
292        protected boolean continueBatchOnError = false;
293
294        protected PingTarget pingTarget = null;
295       
296        protected boolean useLegacyDatetimeCode;
297       
298        private ExceptionInterceptor exceptionInterceptor;
299       
300        /** Whether or not the last query was of the form ON DUPLICATE KEY UPDATE */
301        protected boolean lastQueryIsOnDupKeyUpdate = false;
302       
303        /** Are we currently executing a statement? */
304        protected final AtomicBoolean statementExecuting = new AtomicBoolean(false);
305       
306        /**
307         * Constructor for a Statement.
308         *
309         * @param c
310         *            the Connection instantation that creates us
311         * @param catalog
312         *            the database name in use when we were created
313         *
314         * @throws SQLException
315         *             if an error occurs.
316         */
317        public StatementImpl(MySQLConnection c, String catalog) throws SQLException {
318                if ((c == null) || c.isClosed()) {
319                        throw SQLError.createSQLException(
320                                        Messages.getString("Statement.0"), //$NON-NLS-1$
321                                        SQLError.SQL_STATE_CONNECTION_NOT_OPEN, null); //$NON-NLS-1$ //$NON-NLS-2$
322                }
323
324                this.connection = c;
325                this.connectionId = this.connection.getId();
326                this.exceptionInterceptor = this.connection
327                                .getExceptionInterceptor();
328
329                this.currentCatalog = catalog;
330                this.pedantic = this.connection.getPedantic();
331                this.continueBatchOnError = this.connection.getContinueBatchOnError();
332                this.useLegacyDatetimeCode = this.connection.getUseLegacyDatetimeCode();
333               
334                if (!this.connection.getDontTrackOpenResources()) {
335                        this.connection.registerStatement(this);
336                }
337
338                //
339                // Adjust, if we know it
340                //
341
342                if (this.connection != null) {
343                        this.maxFieldSize = this.connection.getMaxAllowedPacket();
344
345                        int defaultFetchSize = this.connection.getDefaultFetchSize();
346
347                        if (defaultFetchSize != 0) {
348                                setFetchSize(defaultFetchSize);
349                        }
350                       
351                        if (this.connection.getUseUnicode()) {
352                                this.charEncoding = this.connection.getEncoding();
353
354                                this.charConverter = this.connection.getCharsetConverter(this.charEncoding);
355                        }
356                       
357                       
358
359                        boolean profiling = this.connection.getProfileSql()
360                                        || this.connection.getUseUsageAdvisor() || this.connection.getLogSlowQueries();
361
362                        if (this.connection.getAutoGenerateTestcaseScript() || profiling) {
363                                this.statementId = statementCounter++;
364                        }
365
366                        if (profiling) {
367                                this.pointOfOrigin = new Throwable();
368                                this.profileSQL = this.connection.getProfileSql();
369                                this.useUsageAdvisor = this.connection.getUseUsageAdvisor();
370                                this.eventSink = ProfilerEventHandlerFactory.getInstance(this.connection);
371                        }
372
373                        int maxRowsConn = this.connection.getMaxRows();
374
375                        if (maxRowsConn != -1) {
376                                setMaxRows(maxRowsConn);
377                        }
378                       
379                        this.holdResultsOpenOverClose = this.connection.getHoldResultsOpenOverStatementClose();
380                }
381               
382                version5013OrNewer = this.connection.versionMeetsMinimum(5, 0, 13);
383        }
384
385        /**
386         * DOCUMENT ME!
387         *
388         * @param sql
389         *            DOCUMENT ME!
390         *
391         * @throws SQLException
392         *             DOCUMENT ME!
393         */
394        public void addBatch(String sql) throws SQLException {
395                synchronized (checkClosed()) {
396                        if (this.batchedArgs == null) {
397                                this.batchedArgs = new ArrayList<Object>();
398                        }
399       
400                        if (sql != null) {
401                                this.batchedArgs.add(sql);
402                        }
403                }
404        }
405
406        /** Get the batched args as added by the addBatch method(s).
407         * The list is unmodifiable and might contain any combination of String,
408         * BatchParams, or BatchedBindValues depending on how the parameters were
409         * batched.
410         * @return an unmodifiable List of batched args
411         */
412        public List<Object> getBatchedArgs() {
413                return batchedArgs==null?null:Collections.unmodifiableList(batchedArgs);
414        }
415
416        /**
417         * Cancels this Statement object if both the DBMS and driver support
418         * aborting an SQL statement. This method can be used by one thread to
419         * cancel a statement that is being executed by another thread.
420         */
421        public void cancel() throws SQLException {
422                if (!this.statementExecuting.get()) {
423                        return;
424                }
425               
426                if (!this.isClosed &&
427                                this.connection != null &&
428                                this.connection.versionMeetsMinimum(5, 0, 0)) {
429                        Connection cancelConn = null;
430                        java.sql.Statement cancelStmt = null;
431
432                        try {
433                                cancelConn = this.connection.duplicate();
434                                cancelStmt = cancelConn.createStatement();
435                                cancelStmt.execute("KILL QUERY "
436                                                + this.connection.getIO().getThreadId());
437                                this.wasCancelled = true;
438                        } finally {
439                                if (cancelStmt != null) {
440                                        cancelStmt.close();
441                                }
442
443                                if (cancelConn != null) {
444                                        cancelConn.close();
445                                }
446                        }
447
448                }
449        }
450
451        // --------------------------JDBC 2.0-----------------------------
452
453        /**
454         * Checks if closed() has been called, and throws an exception if so
455         *
456         * @throws SQLException
457         *             if this statement has been closed
458         */
459        protected MySQLConnection checkClosed() throws SQLException {
460                MySQLConnection c = this.connection;
461               
462                if (c == null) {
463                        throw SQLError.createSQLException(Messages
464                                        .getString("Statement.49"), //$NON-NLS-1$
465                                        SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor()); //$NON-NLS-1$
466                }
467               
468                return c;
469        }
470
471        /**
472         * Checks if the given SQL query with the given first non-ws char is a DML
473         * statement. Throws an exception if it is.
474         *
475         * @param sql
476         *            the SQL to check
477         * @param firstStatementChar
478         *            the UC first non-ws char of the statement
479         *
480         * @throws SQLException
481         *             if the statement contains DML
482         */
483        protected void checkForDml(String sql, char firstStatementChar)
484                        throws SQLException {
485                if ((firstStatementChar == 'I') || (firstStatementChar == 'U')
486                                || (firstStatementChar == 'D') || (firstStatementChar == 'A')
487                                || (firstStatementChar == 'C')) {
488                        String noCommentSql = StringUtils.stripComments(sql,
489                                        "'\"", "'\"", true, false, true, true);
490
491                        if (StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "INSERT") //$NON-NLS-1$
492                                        || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "UPDATE") //$NON-NLS-1$
493                                        || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DELETE") //$NON-NLS-1$
494                                        || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DROP") //$NON-NLS-1$
495                                        || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "CREATE") //$NON-NLS-1$
496                                        || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "ALTER")) { //$NON-NLS-1$
497                                throw SQLError.createSQLException(Messages
498                                                .getString("Statement.57"), //$NON-NLS-1$
499                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
500                        }
501                }
502        }
503
504        /**
505         * Method checkNullOrEmptyQuery.
506         *
507         * @param sql
508         *            the SQL to check
509         *
510         * @throws SQLException
511         *             if query is null or empty.
512         */
513        protected void checkNullOrEmptyQuery(String sql) throws SQLException {
514                if (sql == null) {
515                        throw SQLError.createSQLException(Messages
516                                        .getString("Statement.59"), //$NON-NLS-1$
517                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$ //$NON-NLS-2$
518                }
519
520                if (sql.length() == 0) {
521                        throw SQLError.createSQLException(Messages
522                                        .getString("Statement.61"), //$NON-NLS-1$
523                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$ //$NON-NLS-2$
524                }
525        }
526
527        /**
528         * JDBC 2.0 Make the set of commands in the current batch empty. This method
529         * is optional.
530         *
531         * @exception SQLException
532         *                if a database-access error occurs, or the driver does not
533         *                support batch statements
534         */
535        public void clearBatch() throws SQLException {
536                synchronized (checkClosed()) {
537                        if (this.batchedArgs != null) {
538                                this.batchedArgs.clear();
539                        }
540                }
541        }
542
543        /**
544         * After this call, getWarnings returns null until a new warning is reported
545         * for this Statement.
546         *
547         * @exception SQLException
548         *                if a database access error occurs (why?)
549         */
550        public void clearWarnings() throws SQLException {
551                synchronized (checkClosed()) {
552                        this.clearWarningsCalled = true;
553                        this.warningChain = null;
554                }
555        }
556
557        /**
558         * In many cases, it is desirable to immediately release a Statement's
559         * database and JDBC resources instead of waiting for this to happen when it
560         * is automatically closed. The close method provides this immediate
561         * release.
562         *
563         * <p>
564         * <B>Note:</B> A Statement is automatically closed when it is garbage
565         * collected. When a Statement is closed, its current ResultSet, if one
566         * exists, is also closed.
567         * </p>
568         *
569         * @exception SQLException
570         *                if a database access error occurs
571         */
572        public void close() throws SQLException {
573                try {
574                        synchronized (checkClosed()) {
575                                realClose(true, true);
576                        }
577                } catch (SQLException sqlEx) {
578                        if (SQLError.SQL_STATE_CONNECTION_NOT_OPEN.equals(sqlEx.getSQLState())) {
579                                return;
580                        }
581                       
582                        throw sqlEx;
583                }
584        }
585
586        /**
587         * Close any open result sets that have been 'held open'
588         */
589        protected void closeAllOpenResults() throws SQLException {
590                synchronized (checkClosed()) {
591                        if (this.openResults != null) {
592                                for (ResultSetInternalMethods element : this.openResults) {
593                                        try {
594                                                element.realClose(false);
595                                        } catch (SQLException sqlEx) {
596                                                AssertionFailedException.shouldNotHappen(sqlEx);
597                                        }
598                                }
599       
600                                this.openResults.clear();
601                        }
602                }
603        }
604
605        public void removeOpenResultSet(ResultSet rs) {
606                try {
607                        synchronized (checkClosed()) {
608                                if (this.openResults != null) {
609                                        this.openResults.remove(rs);
610                                }
611                        }
612                } catch (SQLException e) {
613                        // we can't break the interface, having this be no-op in case of error is ok
614                }
615        }
616       
617        public int getOpenResultSetCount() {
618                try {
619                        synchronized (checkClosed()) {
620                                if (this.openResults != null) {
621                                        return this.openResults.size();
622                                }
623                               
624                                return 0;
625                        }
626                } catch (SQLException e) {
627                        // we can't break the interface, having this be no-op in case of error is ok
628                       
629                        return 0;
630                }
631        }
632       
633        /**
634         * @param sql
635         * @return
636         */
637        private ResultSetInternalMethods createResultSetUsingServerFetch(String sql)
638                        throws SQLException {
639                synchronized (checkClosed()) {
640                        java.sql.PreparedStatement pStmt = this.connection.prepareStatement(
641                                        sql, this.resultSetType, this.resultSetConcurrency);
642       
643                        pStmt.setFetchSize(this.fetchSize);
644       
645                        if (this.maxRows > -1) {
646                                pStmt.setMaxRows(this.maxRows);
647                        }
648       
649                        statementBegins();
650       
651                        pStmt.execute();
652       
653                        //
654                        // Need to be able to get resultset irrespective if we issued DML or
655                        // not to make this work.
656                        //
657                        ResultSetInternalMethods rs = ((com.mysql.jdbc.StatementImpl) pStmt)
658                                        .getResultSetInternal();
659       
660                        rs
661                                        .setStatementUsedForFetchingRows((com.mysql.jdbc.PreparedStatement) pStmt);
662       
663                        this.results = rs;
664       
665                        return rs;
666                }
667        }
668
669        /**
670         * We only stream result sets when they are forward-only, read-only, and the
671         * fetch size has been set to Integer.MIN_VALUE
672         *
673         * @return true if this result set should be streamed row at-a-time, rather
674         *         than read all at once.
675         */
676        protected boolean createStreamingResultSet() {
677                try {
678                        synchronized (checkClosed()) {
679                                return ((this.resultSetType == java.sql.ResultSet.TYPE_FORWARD_ONLY)
680                                                && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY) && (this.fetchSize == Integer.MIN_VALUE));
681                        }
682                } catch (SQLException e) {
683                        // we can't break the interface, having this be no-op in case of error is ok
684                       
685                        return false;
686                }
687        }
688
689        private int originalResultSetType = 0;
690        private int originalFetchSize = 0;
691
692        /* (non-Javadoc)
693         * @see com.mysql.jdbc.IStatement#enableStreamingResults()
694         */
695        public void enableStreamingResults() throws SQLException {
696                synchronized (checkClosed()) {
697                        this.originalResultSetType = this.resultSetType;
698                        this.originalFetchSize = this.fetchSize;
699       
700                        setFetchSize(Integer.MIN_VALUE);
701                        setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
702                }
703        }
704
705        public void disableStreamingResults() throws SQLException {
706                synchronized (checkClosed()) {
707                        if (this.fetchSize == Integer.MIN_VALUE &&
708                                        this.resultSetType == ResultSet.TYPE_FORWARD_ONLY) {
709                                setFetchSize(this.originalFetchSize);
710                                setResultSetType(this.originalResultSetType);
711                        }
712                }
713        }
714
715        /**
716         * Execute a SQL statement that may return multiple results. We don't have
717         * to worry about this since we do not support multiple ResultSets. You can
718         * use getResultSet or getUpdateCount to retrieve the result.
719         *
720         * @param sql
721         *            any SQL statement
722         *
723         * @return true if the next result is a ResulSet, false if it is an update
724         *         count or there are no more results
725         *
726         * @exception SQLException
727         *                if a database access error occurs
728         */
729        public boolean execute(String sql) throws SQLException {
730                return execute(sql, false);
731        }
732       
733        private boolean execute(String sql, boolean returnGeneratedKeys) throws SQLException {
734                MySQLConnection locallyScopedConn = checkClosed();
735
736                synchronized (locallyScopedConn) {
737                        this.retrieveGeneratedKeys = returnGeneratedKeys;
738                        lastQueryIsOnDupKeyUpdate = false;
739                        if (returnGeneratedKeys)
740                                lastQueryIsOnDupKeyUpdate = containsOnDuplicateKeyInString(sql);
741                       
742                        resetCancelledState();
743
744                        checkNullOrEmptyQuery(sql);
745
746                        checkClosed();
747
748                        char firstNonWsChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql));
749
750                        boolean isSelect = true;
751
752                        if (firstNonWsChar != 'S') {
753                                isSelect = false;
754
755                                if (locallyScopedConn.isReadOnly()) {
756                                        throw SQLError.createSQLException(Messages
757                                                        .getString("Statement.27") //$NON-NLS-1$
758                                                        + Messages.getString("Statement.28"), //$NON-NLS-1$
759                                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
760                                }
761                        }
762
763                        boolean doStreaming = createStreamingResultSet();
764
765                        try {
766                                // Adjust net_write_timeout to a higher value if we're
767                                // streaming result sets. More often than not, someone runs into
768                                // an issue where they blow net_write_timeout when using this
769                                // feature, and if they're willing to hold a result set open
770                                // for 30 seconds or more, one more round-trip isn't going to hurt
771                                //
772                                // This is reset by RowDataDynamic.close().
773       
774                                if (doStreaming
775                                                && locallyScopedConn.getNetTimeoutForStreamingResults() > 0) {
776                                        executeSimpleNonQuery(locallyScopedConn, "SET net_write_timeout="
777                                                        + locallyScopedConn.getNetTimeoutForStreamingResults());
778                                }
779       
780                                if (this.doEscapeProcessing) {
781                                        Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
782                                                        locallyScopedConn.serverSupportsConvertFn(), locallyScopedConn);
783       
784                                        if (escapedSqlResult instanceof String) {
785                                                sql = (String) escapedSqlResult;
786                                        } else {
787                                                sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
788                                        }
789                                }
790       
791                                if (this.results != null) {
792                                        if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
793                                                this.results.realClose(false);
794                                        }
795                                }
796       
797                                if (sql.charAt(0) == '/') {
798                                        if (sql.startsWith(PING_MARKER)) {
799                                                doPingInstead();
800                                       
801                                                return true;
802                                        }
803                                }
804       
805                                CachedResultSetMetaData cachedMetaData = null;
806       
807                                ResultSetInternalMethods rs = null;
808       
809                                // If there isn't a limit clause in the SQL
810                                // then limit the number of rows to return in
811                                // an efficient manner. Only do this if
812                                // setMaxRows() hasn't been used on any Statements
813                                // generated from the current Connection (saves
814                                // a query, and network traffic).
815       
816                                this.batchedGeneratedKeys = null;
817       
818                                if (useServerFetch()) {
819                                        rs = createResultSetUsingServerFetch(sql);
820                                } else {
821                                        CancelTask timeoutTask = null;
822       
823                                        String oldCatalog = null;
824       
825                                        try {
826                                                if (locallyScopedConn.getEnableQueryTimeouts() &&
827                                                                this.timeoutInMillis != 0
828                                                                && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
829                                                        timeoutTask = new CancelTask(this);
830                                                        locallyScopedConn.getCancelTimer().schedule(timeoutTask,
831                                                                        this.timeoutInMillis);
832                                                }
833
834                                                if (!locallyScopedConn.getCatalog().equals(
835                                                                this.currentCatalog)) {
836                                                        oldCatalog = locallyScopedConn.getCatalog();
837                                                        locallyScopedConn.setCatalog(this.currentCatalog);
838                                                }
839       
840                                                //
841                                                // Check if we have cached metadata for this query...
842                                                //
843       
844                                                Field[] cachedFields = null;
845       
846                                                if (locallyScopedConn.getCacheResultSetMetadata()) {
847                                                        cachedMetaData = locallyScopedConn.getCachedMetaData(sql);
848       
849                                                        if (cachedMetaData != null) {
850                                                                cachedFields = cachedMetaData.fields;
851                                                        }
852                                                }
853       
854                                                //
855                                                // Only apply max_rows to selects
856                                                //
857                                                if (locallyScopedConn.useMaxRows()) {
858                                                        int rowLimit = -1;
859       
860                                                        if (isSelect) {
861                                                                if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
862                                                                        rowLimit = this.maxRows;
863                                                                } else {
864                                                                        if (this.maxRows <= 0) {
865                                                                                executeSimpleNonQuery(locallyScopedConn,
866                                                                                                "SET SQL_SELECT_LIMIT=DEFAULT");
867                                                                        } else {
868                                                                                executeSimpleNonQuery(locallyScopedConn,
869                                                                                                "SET SQL_SELECT_LIMIT=" + this.maxRows);
870                                                                        }
871                                                                }
872                                                        } else {
873                                                                executeSimpleNonQuery(locallyScopedConn,
874                                                                                "SET SQL_SELECT_LIMIT=DEFAULT");
875                                                        }
876       
877
878                                                        statementBegins();
879
880                                                        // Finally, execute the query
881                                                        rs = locallyScopedConn.execSQL(this, sql, rowLimit, null,
882                                                                        this.resultSetType, this.resultSetConcurrency,
883                                                                        doStreaming,
884                                                                        this.currentCatalog, cachedFields);
885                                                } else {
886                                                        statementBegins();
887
888                                                        rs = locallyScopedConn.execSQL(this, sql, -1, null,
889                                                                        this.resultSetType, this.resultSetConcurrency,
890                                                                        doStreaming,
891                                                                        this.currentCatalog, cachedFields);
892                                                }
893       
894                                                if (timeoutTask != null) {
895                                                        if (timeoutTask.caughtWhileCancelling != null) {
896                                                                throw timeoutTask.caughtWhileCancelling;
897                                                        }
898       
899                                                        timeoutTask.cancel();
900                                                        timeoutTask = null;
901                                                }
902       
903                                                synchronized (this.cancelTimeoutMutex) {
904                                                        if (this.wasCancelled) {
905                                                                SQLException cause = null;
906                                                               
907                                                                if (this.wasCancelledByTimeout) {
908                                                                        cause = new MySQLTimeoutException();
909                                                                } else {
910                                                                        cause = new MySQLStatementCancelledException();
911                                                                }
912                                                               
913                                                                resetCancelledState();
914                                                               
915                                                                throw cause;
916                                                        }
917                                                }
918                                        } finally {
919                                                if (timeoutTask != null) {
920                                                        timeoutTask.cancel();
921                                                        locallyScopedConn.getCancelTimer().purge();
922                                                }
923       
924                                                if (oldCatalog != null) {
925                                                        locallyScopedConn.setCatalog(oldCatalog);
926                                                }
927                                        }
928                                }
929       
930                                if (rs != null) {
931                                        this.lastInsertId = rs.getUpdateID();
932       
933                                        this.results = rs;
934       
935                                        rs.setFirstCharOfQuery(firstNonWsChar);
936       
937                                        if (rs.reallyResult()) {
938                                                if (cachedMetaData != null) {
939                                                        locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData,
940                                                                        this.results);
941                                                } else {
942                                                        if (this.connection.getCacheResultSetMetadata()) {
943                                                                locallyScopedConn.initializeResultsMetadataFromCache(sql,
944                                                                                null /* will be created */, this.results);
945                                                        }
946                                                }
947                                        }
948                                }
949       
950                                return ((rs != null) && rs.reallyResult());
951                        } finally {
952                                this.statementExecuting.set(false);
953                        }
954                }
955        }
956
957        protected void statementBegins() {
958                this.clearWarningsCalled = false;
959                this.statementExecuting.set(true);
960        }
961
962        protected void resetCancelledState() throws SQLException {
963                synchronized (checkClosed()) {
964                        if (this.cancelTimeoutMutex == null) {
965                                return;
966                        }
967                       
968                        synchronized (this.cancelTimeoutMutex) {
969                                this.wasCancelled = false;
970                                this.wasCancelledByTimeout = false;
971                        }
972                }
973        }
974
975        /**
976         * @see StatementImpl#execute(String, int)
977         */
978        public boolean execute(String sql, int returnGeneratedKeys)
979                        throws SQLException {
980
981
982                if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
983                        checkClosed();
984
985                        MySQLConnection locallyScopedConn = this.connection;
986
987                        synchronized (locallyScopedConn) {
988                                // If this is a 'REPLACE' query, we need to be able to parse
989                                // the 'info' message returned from the server to determine
990                                // the actual number of keys generated.
991                                boolean readInfoMsgState = this.connection
992                                                .isReadInfoMsgEnabled();
993                                locallyScopedConn.setReadInfoMsgEnabled(true);
994
995                                try {
996                                        return execute(sql, true);
997                                } finally {
998                                        locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
999                                }
1000                        }
1001                }
1002
1003                return execute(sql);
1004        }
1005
1006        /**
1007         * @see StatementImpl#execute(String, int[])
1008         */
1009        public boolean execute(String sql, int[] generatedKeyIndices)
1010                        throws SQLException {
1011                MySQLConnection locallyScopedConn = checkClosed();
1012
1013                synchronized (locallyScopedConn) {
1014                        if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
1015
1016                                this.retrieveGeneratedKeys = true;
1017                               
1018                                // If this is a 'REPLACE' query, we need to be able to parse
1019                                // the 'info' message returned from the server to determine
1020                                // the actual number of keys generated.
1021                                boolean readInfoMsgState = locallyScopedConn
1022                                                .isReadInfoMsgEnabled();
1023                                locallyScopedConn.setReadInfoMsgEnabled(true);
1024
1025                                try {
1026                                        return execute(sql, true);
1027                                } finally {
1028                                        locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
1029                                }
1030                        }
1031                       
1032                        return execute(sql);
1033                }       
1034        }
1035
1036        /**
1037         * @see StatementImpl#execute(String, String[])
1038         */
1039        public boolean execute(String sql, String[] generatedKeyNames)
1040                        throws SQLException {
1041                MySQLConnection locallyScopedConn = checkClosed();
1042
1043                synchronized (locallyScopedConn) {
1044                        if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
1045
1046                                this.retrieveGeneratedKeys = true;
1047                                // If this is a 'REPLACE' query, we need to be able to parse
1048                                // the 'info' message returned from the server to determine
1049                                // the actual number of keys generated.
1050                                boolean readInfoMsgState = this.connection
1051                                                .isReadInfoMsgEnabled();
1052                                locallyScopedConn.setReadInfoMsgEnabled(true);
1053
1054                                try {
1055                                        return execute(sql, true);
1056                                } finally {
1057                                        locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
1058                                }
1059                        }
1060
1061                        return execute(sql);
1062                }
1063        }
1064
1065        /**
1066         * JDBC 2.0 Submit a batch of commands to the database for execution. This
1067         * method is optional.
1068         *
1069         * @return an array of update counts containing one element for each command
1070         *         in the batch. The array is ordered according to the order in
1071         *         which commands were inserted into the batch
1072         *
1073         * @exception SQLException
1074         *                if a database-access error occurs, or the driver does not
1075         *                support batch statements
1076         * @throws java.sql.BatchUpdateException
1077         *             DOCUMENT ME!
1078         */
1079        public int[] executeBatch() throws SQLException {
1080                MySQLConnection locallyScopedConn = checkClosed();
1081
1082                synchronized (locallyScopedConn) {
1083                        if (locallyScopedConn.isReadOnly()) {
1084                                throw SQLError.createSQLException(Messages
1085                                                .getString("Statement.34") //$NON-NLS-1$
1086                                                + Messages.getString("Statement.35"), //$NON-NLS-1$
1087                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
1088                        }
1089       
1090                        if (this.results != null) {
1091                                if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
1092                                        this.results.realClose(false);
1093                                }
1094                        }
1095
1096               
1097                        if (this.batchedArgs == null || this.batchedArgs.size() == 0) {
1098                return new int[0];
1099            }
1100                       
1101                        // we timeout the entire batch, not individual statements
1102                        int individualStatementTimeout = this.timeoutInMillis;
1103                        this.timeoutInMillis = 0;
1104                       
1105                        CancelTask timeoutTask = null;
1106                       
1107                        try {
1108                                resetCancelledState();
1109
1110                                statementBegins();
1111                               
1112                                try {
1113                                        this.retrieveGeneratedKeys = true; // The JDBC spec doesn't forbid this, but doesn't provide for it either...we do..
1114       
1115                                        int[] updateCounts = null;
1116       
1117                                       
1118                                        if (this.batchedArgs != null) {
1119                                                int nbrCommands = this.batchedArgs.size();
1120       
1121                                                this.batchedGeneratedKeys = new ArrayList<ResultSetRow>(this.batchedArgs.size());
1122       
1123                                                boolean multiQueriesEnabled = locallyScopedConn.getAllowMultiQueries();
1124       
1125                                                if (locallyScopedConn.versionMeetsMinimum(4, 1, 1) &&
1126                                                                (multiQueriesEnabled ||
1127                                                                (locallyScopedConn.getRewriteBatchedStatements() &&
1128                                                                                nbrCommands > 4))) {
1129                                                        return executeBatchUsingMultiQueries(multiQueriesEnabled, nbrCommands, individualStatementTimeout);
1130                                                }
1131       
1132                                                if (locallyScopedConn.getEnableQueryTimeouts() &&
1133                                                                individualStatementTimeout != 0
1134                                                                && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
1135                                                        timeoutTask = new CancelTask(this);
1136                                                        locallyScopedConn.getCancelTimer().schedule(timeoutTask,
1137                                                                        individualStatementTimeout);
1138                                                }
1139                                               
1140                                                updateCounts = new int[nbrCommands];
1141       
1142                                                for (int i = 0; i < nbrCommands; i++) {
1143                                                        updateCounts[i] = -3;
1144                                                }
1145       
1146                                                SQLException sqlEx = null;
1147       
1148                                                int commandIndex = 0;
1149       
1150                                                for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
1151                                                        try {
1152                                                                String sql = (String) this.batchedArgs.get(commandIndex);
1153                                                                updateCounts[commandIndex] = executeUpdate(sql, true, true);
1154                                                                // limit one generated key per OnDuplicateKey statement
1155                                                                getBatchedGeneratedKeys(containsOnDuplicateKeyInString(sql) ? 1 : 0);
1156                                                        } catch (SQLException ex) {
1157                                                                updateCounts[commandIndex] = EXECUTE_FAILED;
1158       
1159                                                                if (this.continueBatchOnError && 
1160                                                                                !(ex instanceof MySQLTimeoutException) && 
1161                                                                                !(ex instanceof MySQLStatementCancelledException) &&
1162                                            !hasDeadlockOrTimeoutRolledBackTx(ex)) {
1163                                                                        sqlEx = ex;
1164                                                                } else {
1165                                                                        int[] newUpdateCounts = new int[commandIndex];
1166                                                                       
1167                                                                        if (hasDeadlockOrTimeoutRolledBackTx(ex)) {
1168                                                                                for (int i = 0; i < newUpdateCounts.length; i++) {
1169                                                                                        newUpdateCounts[i] = Statement.EXECUTE_FAILED;
1170                                                                                }
1171                                                                        } else {
1172                                                                                System.arraycopy(updateCounts, 0,
1173                                                                                        newUpdateCounts, 0, commandIndex);
1174                                                                        }
1175       
1176                                                                        throw new java.sql.BatchUpdateException(ex
1177                                                                                        .getMessage(), ex.getSQLState(), ex
1178                                                                                        .getErrorCode(), newUpdateCounts);
1179                                                                }
1180                                                        }
1181                                                }
1182       
1183                                                if (sqlEx != null) {
1184                                                        throw new java.sql.BatchUpdateException(sqlEx
1185                                                                        .getMessage(), sqlEx.getSQLState(), sqlEx
1186                                                                        .getErrorCode(), updateCounts);
1187                                                }
1188                                        }
1189       
1190                                        if (timeoutTask != null) {
1191                                                if (timeoutTask.caughtWhileCancelling != null) {
1192                                                        throw timeoutTask.caughtWhileCancelling;
1193                                                }
1194       
1195                                                timeoutTask.cancel();
1196                                               
1197                                                locallyScopedConn.getCancelTimer().purge();
1198                                                timeoutTask = null;
1199                                        }
1200                                       
1201                                        return (updateCounts != null) ? updateCounts : new int[0];
1202                                } finally {
1203                                        this.statementExecuting.set(false);
1204                                }
1205                        } finally {
1206                               
1207                                if (timeoutTask != null) {
1208                                        timeoutTask.cancel();
1209                                       
1210                                        locallyScopedConn.getCancelTimer().purge();
1211                                }
1212                               
1213                                resetCancelledState();
1214                               
1215                                this.timeoutInMillis = individualStatementTimeout;
1216
1217                                clearBatch();
1218                        }
1219                }
1220        }
1221
1222        protected final boolean hasDeadlockOrTimeoutRolledBackTx(SQLException ex) {
1223                int vendorCode = ex.getErrorCode();
1224               
1225                switch (vendorCode) {
1226                case MysqlErrorNumbers.ER_LOCK_DEADLOCK:
1227                case MysqlErrorNumbers.ER_LOCK_TABLE_FULL:
1228                        return true;
1229                case MysqlErrorNumbers.ER_LOCK_WAIT_TIMEOUT:
1230                                return !version5013OrNewer;
1231                default:
1232                        return false;
1233                }
1234        }
1235
1236        /**
1237         * Rewrites batch into a single query to send to the server. This method
1238         * will constrain each batch to be shorter than max_allowed_packet on the
1239         * server.
1240         *
1241         * @return update counts in the same manner as executeBatch()
1242         * @throws SQLException
1243         */
1244        private int[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled,
1245                        int nbrCommands, int individualStatementTimeout) throws SQLException {
1246               
1247                MySQLConnection locallyScopedConn = checkClosed();
1248
1249                synchronized (locallyScopedConn) {
1250                        if (!multiQueriesEnabled) {
1251                                locallyScopedConn.getIO().enableMultiQueries();
1252                        }
1253       
1254                        java.sql.Statement batchStmt = null;
1255       
1256                        CancelTask timeoutTask = null;
1257                       
1258                        try {
1259                                int[] updateCounts = new int[nbrCommands];
1260       
1261                                for (int i = 0; i < nbrCommands; i++) {
1262                                        updateCounts[i] = -3;
1263                                }
1264       
1265                                int commandIndex = 0;
1266       
1267                                StringBuffer queryBuf = new StringBuffer();
1268       
1269                                batchStmt = locallyScopedConn.createStatement();
1270       
1271                                if (locallyScopedConn.getEnableQueryTimeouts() &&
1272                                                individualStatementTimeout != 0
1273                                                && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
1274                                        timeoutTask = new CancelTask((StatementImpl)batchStmt);
1275                                        locallyScopedConn.getCancelTimer().schedule(timeoutTask,
1276                                                        individualStatementTimeout);
1277                                }
1278                               
1279                                int counter = 0;
1280       
1281                                int numberOfBytesPerChar = 1;
1282       
1283                                String connectionEncoding = locallyScopedConn.getEncoding();
1284       
1285                                if (StringUtils.startsWithIgnoreCase(connectionEncoding, "utf")) {
1286                                        numberOfBytesPerChar = 3;
1287                                } else if (CharsetMapping.isMultibyteCharset(connectionEncoding)) {
1288                                        numberOfBytesPerChar = 2;
1289                                }
1290       
1291                                int escapeAdjust = 1;
1292       
1293                                batchStmt.setEscapeProcessing(this.doEscapeProcessing);
1294                               
1295                                if (this.doEscapeProcessing) {
1296                                       
1297                                        escapeAdjust = 2; /* We assume packet _could_ grow by this amount, as we're not
1298                                                             sure how big statement will end up after
1299                                                             escape processing */
1300                                }
1301       
1302                                SQLException sqlEx = null;
1303                               
1304                                int argumentSetsInBatchSoFar = 0;
1305                               
1306                                for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
1307                                        String nextQuery = (String) this.batchedArgs.get(commandIndex);
1308       
1309                                        if (((((queryBuf.length() + nextQuery.length())
1310                                                        * numberOfBytesPerChar) + 1 /* for semicolon */
1311                                                        + MysqlIO.HEADER_LENGTH) * escapeAdjust)  + 32 > this.connection
1312                                                        .getMaxAllowedPacket()) {
1313                                                try {
1314                                                        batchStmt.execute(queryBuf.toString(), Statement.RETURN_GENERATED_KEYS);
1315                                                } catch (SQLException ex) {
1316                                                        sqlEx = handleExceptionForBatch(commandIndex,
1317                                                                        argumentSetsInBatchSoFar, updateCounts, ex);
1318                                                }
1319       
1320                                                counter = processMultiCountsAndKeys((StatementImpl)batchStmt, counter, 
1321                                                                updateCounts);
1322       
1323                                                queryBuf = new StringBuffer();
1324                                                argumentSetsInBatchSoFar = 0;
1325                                        }
1326       
1327                                        queryBuf.append(nextQuery);
1328                                        queryBuf.append(";");
1329                                        argumentSetsInBatchSoFar++;
1330                                }
1331       
1332                                if (queryBuf.length() > 0) {
1333                                        try {
1334                                                batchStmt.execute(queryBuf.toString(), Statement.RETURN_GENERATED_KEYS);
1335                                        } catch (SQLException ex) {
1336                                                sqlEx = handleExceptionForBatch(commandIndex - 1,
1337                                                                argumentSetsInBatchSoFar, updateCounts, ex);
1338                                        }
1339       
1340                                        counter = processMultiCountsAndKeys((StatementImpl)batchStmt, counter, 
1341                                                        updateCounts);
1342                                }
1343       
1344                                if (timeoutTask != null) {
1345                                        if (timeoutTask.caughtWhileCancelling != null) {
1346                                                throw timeoutTask.caughtWhileCancelling;
1347                                        }
1348       
1349                                        timeoutTask.cancel();
1350                                       
1351                                        locallyScopedConn.getCancelTimer().purge();
1352                                       
1353                                        timeoutTask = null;
1354                                }
1355                               
1356                                if (sqlEx != null) {
1357                                        throw new java.sql.BatchUpdateException(sqlEx
1358                                                        .getMessage(), sqlEx.getSQLState(), sqlEx
1359                                                        .getErrorCode(), updateCounts);
1360                                }
1361                               
1362                                return (updateCounts != null) ? updateCounts : new int[0];
1363                        } finally {
1364                                if (timeoutTask != null) {
1365                                        timeoutTask.cancel();
1366                                       
1367                                        locallyScopedConn.getCancelTimer().purge();
1368                                }
1369                               
1370                                resetCancelledState();
1371                               
1372                                try {
1373                                        if (batchStmt != null) {
1374                                                batchStmt.close();
1375                                        }
1376                                } finally {
1377                                        if (!multiQueriesEnabled) {
1378                                                locallyScopedConn.getIO().disableMultiQueries();
1379                                        }
1380                                }
1381                        }
1382                }
1383        }
1384       
1385        protected int processMultiCountsAndKeys(
1386                        StatementImpl batchedStatement,
1387                        int updateCountCounter, int[] updateCounts) throws SQLException {
1388                synchronized (checkClosed()) {
1389                        updateCounts[updateCountCounter++] = batchedStatement.getUpdateCount();
1390                       
1391                        boolean doGenKeys = this.batchedGeneratedKeys != null;
1392       
1393                        byte[][] row = null;
1394                       
1395                        if (doGenKeys) {
1396                                long generatedKey = batchedStatement.getLastInsertID();
1397                       
1398                                row = new byte[1][];
1399                                row[0] = StringUtils.getBytes(Long.toString(generatedKey));
1400                                this.batchedGeneratedKeys.add(new ByteArrayRow(row, getExceptionInterceptor()));
1401                        }
1402       
1403                        while (batchedStatement.getMoreResults()
1404                                        || batchedStatement.getUpdateCount() != -1) {
1405                                updateCounts[updateCountCounter++] = batchedStatement.getUpdateCount();
1406                               
1407                                if (doGenKeys) {
1408                                        long generatedKey = batchedStatement.getLastInsertID();
1409                                       
1410                                        row = new byte[1][];
1411                                        row[0] = StringUtils.getBytes(Long.toString(generatedKey));
1412                                        this.batchedGeneratedKeys.add(new ByteArrayRow(row, getExceptionInterceptor()));
1413                                }
1414                        }
1415                       
1416                        return updateCountCounter;
1417                }
1418        }
1419       
1420        protected SQLException handleExceptionForBatch(int endOfBatchIndex,
1421                        int numValuesPerBatch, int[] updateCounts, SQLException ex)
1422                        throws BatchUpdateException {
1423                SQLException sqlEx;
1424       
1425                for (int j = endOfBatchIndex; j > endOfBatchIndex - numValuesPerBatch; j--) {
1426                        updateCounts[j] = EXECUTE_FAILED;
1427                }
1428
1429                if (this.continueBatchOnError && 
1430                                !(ex instanceof MySQLTimeoutException) && 
1431                                !(ex instanceof MySQLStatementCancelledException) &&
1432                                !hasDeadlockOrTimeoutRolledBackTx(ex)) {
1433                        sqlEx = ex;
1434                } else {
1435                        int[] newUpdateCounts = new int[endOfBatchIndex];
1436                        System.arraycopy(updateCounts, 0,
1437                                        newUpdateCounts, 0, endOfBatchIndex);
1438
1439                        BatchUpdateException batchException = new BatchUpdateException(ex
1440                                        .getMessage(), ex.getSQLState(), ex
1441                                        .getErrorCode(), newUpdateCounts);
1442                        batchException.initCause(ex);
1443                        throw batchException;
1444                }
1445               
1446                return sqlEx;
1447        }
1448       
1449        /**
1450         * Execute a SQL statement that retruns a single ResultSet
1451         *
1452         * @param sql
1453         *            typically a static SQL SELECT statement
1454         *
1455         * @return a ResulSet that contains the data produced by the query
1456         *
1457         * @exception SQLException
1458         *                if a database access error occurs
1459         */
1460        public java.sql.ResultSet executeQuery(String sql)
1461                        throws SQLException {
1462                synchronized (checkClosed()) {
1463                        MySQLConnection locallyScopedConn = this.connection;
1464                       
1465                        this.retrieveGeneratedKeys = false;
1466                       
1467                        resetCancelledState();
1468
1469                        checkNullOrEmptyQuery(sql);
1470
1471                        boolean doStreaming = createStreamingResultSet();
1472
1473                        // Adjust net_write_timeout to a higher value if we're
1474                        // streaming result sets. More often than not, someone runs into
1475                        // an issue where they blow net_write_timeout when using this
1476                        // feature, and if they're willing to hold a result set open
1477                        // for 30 seconds or more, one more round-trip isn't going to hurt
1478                        //
1479                        // This is reset by RowDataDynamic.close().
1480
1481                        if (doStreaming
1482                                        && this.connection.getNetTimeoutForStreamingResults() > 0) {
1483                                executeSimpleNonQuery(locallyScopedConn, "SET net_write_timeout="
1484                                                + this.connection.getNetTimeoutForStreamingResults());
1485                        }
1486
1487                        if (this.doEscapeProcessing) {
1488                                Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
1489                                                locallyScopedConn.serverSupportsConvertFn(), this.connection);
1490
1491                                if (escapedSqlResult instanceof String) {
1492                                        sql = (String) escapedSqlResult;
1493                                } else {
1494                                        sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
1495                                }
1496                        }
1497
1498                        char firstStatementChar = StringUtils.firstNonWsCharUc(sql,
1499                                        findStartOfStatement(sql));
1500
1501                        if (sql.charAt(0) == '/') {
1502                                if (sql.startsWith(PING_MARKER)) {
1503                                        doPingInstead();
1504                               
1505                                        return this.results;
1506                                }
1507                        }
1508                       
1509                        checkForDml(sql, firstStatementChar);
1510
1511                        if (this.results != null) {
1512                                if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
1513                                        this.results.realClose(false);
1514                                }
1515                        }
1516
1517                        CachedResultSetMetaData cachedMetaData = null;
1518
1519                        // If there isn't a limit clause in the SQL
1520                        // then limit the number of rows to return in
1521                        // an efficient manner. Only do this if
1522                        // setMaxRows() hasn't been used on any Statements
1523                        // generated from the current Connection (saves
1524                        // a query, and network traffic).
1525
1526                        if (useServerFetch()) {
1527                                this.results = createResultSetUsingServerFetch(sql);
1528
1529                                return this.results;
1530                        }
1531
1532                        CancelTask timeoutTask = null;
1533
1534                        String oldCatalog = null;
1535
1536                        try {
1537                                if (locallyScopedConn.getEnableQueryTimeouts() &&
1538                                                this.timeoutInMillis != 0
1539                                                && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
1540                                        timeoutTask = new CancelTask(this);
1541                                        locallyScopedConn.getCancelTimer().schedule(timeoutTask,
1542                                                        this.timeoutInMillis);
1543                                }
1544
1545                                if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
1546                                        oldCatalog = locallyScopedConn.getCatalog();
1547                                        locallyScopedConn.setCatalog(this.currentCatalog);
1548                                }
1549
1550                                //
1551                                // Check if we have cached metadata for this query...
1552                                //
1553
1554                                Field[] cachedFields = null;
1555
1556                                if (locallyScopedConn.getCacheResultSetMetadata()) {
1557                                        cachedMetaData = locallyScopedConn.getCachedMetaData(sql);
1558
1559                                        if (cachedMetaData != null) {
1560                                                cachedFields = cachedMetaData.fields;
1561                                        }
1562                                }
1563
1564                                if (locallyScopedConn.useMaxRows()) {
1565                                        // We need to execute this all together
1566                                        // So synchronize on the Connection's mutex (because
1567                                        // even queries going through there synchronize
1568                                        // on the connection
1569                                        if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
1570                                                this.results = locallyScopedConn.execSQL(this, sql,
1571                                                                this.maxRows, null, this.resultSetType,
1572                                                                this.resultSetConcurrency,
1573                                                                doStreaming,
1574                                                                this.currentCatalog, cachedFields);
1575                                        } else {
1576                                                if (this.maxRows <= 0) {
1577                                                        executeSimpleNonQuery(locallyScopedConn,
1578                                                                        "SET SQL_SELECT_LIMIT=DEFAULT");
1579                                                } else {
1580                                                        executeSimpleNonQuery(locallyScopedConn,
1581                                                                        "SET SQL_SELECT_LIMIT=" + this.maxRows);
1582                                                }
1583
1584                                                statementBegins();
1585                                               
1586                                                this.results = locallyScopedConn.execSQL(this, sql, -1,
1587                                                                null, this.resultSetType,
1588                                                                this.resultSetConcurrency,
1589                                                                doStreaming,
1590                                                                this.currentCatalog, cachedFields);
1591
1592                                                if (oldCatalog != null) {
1593                                                        locallyScopedConn.setCatalog(oldCatalog);
1594                                                }
1595                                        }
1596                                } else {
1597                                        statementBegins();
1598                                       
1599                                        this.results = locallyScopedConn.execSQL(this, sql, -1, null,
1600                                                        this.resultSetType, this.resultSetConcurrency,
1601                                                        doStreaming,
1602                                                        this.currentCatalog, cachedFields);
1603                                }
1604
1605                                if (timeoutTask != null) {
1606                                        if (timeoutTask.caughtWhileCancelling != null) {
1607                                                throw timeoutTask.caughtWhileCancelling;
1608                                        }
1609
1610                                        timeoutTask.cancel();
1611                                       
1612                                        locallyScopedConn.getCancelTimer().purge();
1613                                       
1614                                        timeoutTask = null;
1615                                }
1616
1617                                synchronized (this.cancelTimeoutMutex) {
1618                                        if (this.wasCancelled) {
1619                                                SQLException cause = null;
1620                                               
1621                                                if (this.wasCancelledByTimeout) {
1622                                                        cause = new MySQLTimeoutException();
1623                                                } else {
1624                                                        cause = new MySQLStatementCancelledException();
1625                                                }
1626                                               
1627                                                resetCancelledState();
1628                                               
1629                                                throw cause;
1630                                        }
1631                                }
1632                        } finally {
1633                                this.statementExecuting.set(false);
1634                               
1635                                if (timeoutTask != null) {
1636                                        timeoutTask.cancel();
1637                                       
1638                                        locallyScopedConn.getCancelTimer().purge();
1639                                }
1640
1641                                if (oldCatalog != null) {
1642                                        locallyScopedConn.setCatalog(oldCatalog);
1643                                }
1644                        }
1645
1646                        this.lastInsertId = this.results.getUpdateID();
1647
1648                        if (cachedMetaData != null) {
1649                                locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData,
1650                                                this.results);
1651                        } else {
1652                                if (this.connection.getCacheResultSetMetadata()) {
1653                                        locallyScopedConn.initializeResultsMetadataFromCache(sql,
1654                                                        null /* will be created */, this.results);
1655                                }
1656                        }
1657
1658                        return this.results;
1659                }
1660        }
1661
1662        protected void doPingInstead() throws SQLException {
1663                synchronized (checkClosed()) {
1664                        if (this.pingTarget != null) {
1665                                this.pingTarget.doPing();
1666                        } else {
1667                                this.connection.ping();
1668                        }
1669       
1670                        ResultSetInternalMethods fakeSelectOneResultSet = generatePingResultSet();
1671                        this.results = fakeSelectOneResultSet;
1672                }
1673        }
1674
1675        protected ResultSetInternalMethods generatePingResultSet() throws SQLException {
1676                synchronized (checkClosed()) {
1677                        Field[] fields = { new Field(null, "1", Types.BIGINT, 1) };
1678                        ArrayList<ResultSetRow> rows = new ArrayList<ResultSetRow>();
1679                        byte[] colVal = new byte[] { (byte) '1' };
1680       
1681                        rows.add(new ByteArrayRow(new byte[][] { colVal }, getExceptionInterceptor()));
1682       
1683                        return (ResultSetInternalMethods) DatabaseMetaData.buildResultSet(fields, rows,
1684                                        this.connection);
1685                }
1686        }
1687       
1688        protected void executeSimpleNonQuery(MySQLConnection c, String nonQuery)
1689                        throws SQLException {
1690                c.execSQL(this, nonQuery,
1691                                -1, null, ResultSet.TYPE_FORWARD_ONLY,
1692                                ResultSet.CONCUR_READ_ONLY, false, this.currentCatalog,
1693                                null, false).close();
1694        }
1695
1696        /**
1697         * Execute a SQL INSERT, UPDATE or DELETE statement. In addition SQL
1698         * statements that return nothing such as SQL DDL statements can be executed
1699         * Any IDs generated for AUTO_INCREMENT fields can be retrieved by casting
1700         * this Statement to org.gjt.mm.mysql.Statement and calling the
1701         * getLastInsertID() method.
1702         *
1703         * @param sql
1704         *            a SQL statement
1705         *
1706         * @return either a row count, or 0 for SQL commands
1707         *
1708         * @exception SQLException
1709         *                if a database access error occurs
1710         */
1711        public int executeUpdate(String sql) throws SQLException {
1712                return executeUpdate(sql, false, false);
1713        }
1714
1715        protected int executeUpdate(String sql, boolean isBatch, boolean returnGeneratedKeys)
1716                throws SQLException {
1717               
1718                synchronized (checkClosed()) {
1719                        MySQLConnection locallyScopedConn = this.connection;
1720       
1721                        char firstStatementChar = StringUtils.firstAlphaCharUc(sql,
1722                                        findStartOfStatement(sql));
1723       
1724                        ResultSetInternalMethods rs = null;
1725               
1726                        this.retrieveGeneratedKeys = returnGeneratedKeys;
1727                       
1728                        resetCancelledState();
1729
1730                        checkNullOrEmptyQuery(sql);
1731
1732                        if (this.doEscapeProcessing) {
1733                                Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
1734                                                this.connection.serverSupportsConvertFn(), this.connection);
1735
1736                                if (escapedSqlResult instanceof String) {
1737                                        sql = (String) escapedSqlResult;
1738                                } else {
1739                                        sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
1740                                }
1741                        }
1742
1743                        if (locallyScopedConn.isReadOnly()) {
1744                                throw SQLError.createSQLException(Messages
1745                                                .getString("Statement.42") //$NON-NLS-1$
1746                                                + Messages.getString("Statement.43"), //$NON-NLS-1$
1747                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
1748                        }
1749
1750                        if (StringUtils.startsWithIgnoreCaseAndWs(sql, "select")) { //$NON-NLS-1$
1751                                throw SQLError.createSQLException(Messages
1752                                                .getString("Statement.46"), //$NON-NLS-1$
1753                                "01S03", getExceptionInterceptor()); //$NON-NLS-1$
1754                        }
1755
1756                        if (this.results != null) {
1757                                if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
1758                                        this.results.realClose(false);
1759                                }
1760                        }
1761
1762                        // The checking and changing of catalogs
1763                        // must happen in sequence, so synchronize
1764                        // on the same mutex that _conn is using
1765
1766                        CancelTask timeoutTask = null;
1767
1768                        String oldCatalog = null;
1769
1770                        try {
1771                                if (locallyScopedConn.getEnableQueryTimeouts() &&
1772                                                this.timeoutInMillis != 0
1773                                                && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
1774                                        timeoutTask = new CancelTask(this);
1775                                        locallyScopedConn.getCancelTimer().schedule(timeoutTask,
1776                                                        this.timeoutInMillis);
1777                                }
1778
1779                                if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
1780                                        oldCatalog = locallyScopedConn.getCatalog();
1781                                        locallyScopedConn.setCatalog(this.currentCatalog);
1782                                }
1783
1784                                //
1785                                // Only apply max_rows to selects
1786                                //
1787                                if (locallyScopedConn.useMaxRows()) {
1788                                        executeSimpleNonQuery(locallyScopedConn,
1789                                                        "SET SQL_SELECT_LIMIT=DEFAULT");
1790                                }
1791
1792                                statementBegins();
1793                               
1794                                rs = locallyScopedConn.execSQL(this, sql, -1, null,
1795                                                java.sql.ResultSet.TYPE_FORWARD_ONLY,
1796                                                java.sql.ResultSet.CONCUR_READ_ONLY, false,
1797                                                this.currentCatalog,
1798                                                null /* force read of field info on DML */,
1799                                                isBatch);
1800
1801                                if (timeoutTask != null) {
1802                                        if (timeoutTask.caughtWhileCancelling != null) {
1803                                                throw timeoutTask.caughtWhileCancelling;
1804                                        }
1805
1806                                        timeoutTask.cancel();
1807                                       
1808                                        locallyScopedConn.getCancelTimer().purge();
1809                                       
1810                                        timeoutTask = null;
1811                                }
1812
1813                                synchronized (this.cancelTimeoutMutex) {
1814                                        if (this.wasCancelled) {
1815                                                SQLException cause = null;
1816                                               
1817                                                if (this.wasCancelledByTimeout) {
1818                                                        cause = new MySQLTimeoutException();
1819                                                } else {
1820                                                        cause = new MySQLStatementCancelledException();
1821                                                }
1822                                               
1823                                                resetCancelledState();
1824                                               
1825                                                throw cause;
1826                                        }
1827                                }
1828                        } finally {
1829                                if (timeoutTask != null) {
1830                                        timeoutTask.cancel();
1831                                       
1832                                        locallyScopedConn.getCancelTimer().purge();
1833                                }
1834
1835                                if (oldCatalog != null) {
1836                                        locallyScopedConn.setCatalog(oldCatalog);
1837                                }
1838                               
1839                                if (!isBatch) {
1840                                        this.statementExecuting.set(false);
1841                                }
1842                        }
1843                       
1844                        this.results = rs;
1845
1846                        rs.setFirstCharOfQuery(firstStatementChar);
1847
1848                        this.updateCount = rs.getUpdateCount();
1849
1850                        int truncatedUpdateCount = 0;
1851
1852                        if (this.updateCount > Integer.MAX_VALUE) {
1853                                truncatedUpdateCount = Integer.MAX_VALUE;
1854                        } else {
1855                                truncatedUpdateCount = (int) this.updateCount;
1856                        }
1857
1858                        this.lastInsertId = rs.getUpdateID();
1859
1860                        return truncatedUpdateCount;
1861                }
1862        }
1863
1864
1865        /**
1866         * @see StatementImpl#executeUpdate(String, int)
1867         */
1868        public int executeUpdate(String sql, int returnGeneratedKeys)
1869                        throws SQLException {
1870                synchronized (checkClosed()) {
1871                        if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
1872                                MySQLConnection locallyScopedConn = this.connection;
1873                       
1874                                // If this is a 'REPLACE' query, we need to be able to parse
1875                                // the 'info' message returned from the server to determine
1876                                // the actual number of keys generated.
1877                                boolean readInfoMsgState = locallyScopedConn
1878                                                .isReadInfoMsgEnabled();
1879                                locallyScopedConn.setReadInfoMsgEnabled(true);
1880
1881                                try {
1882                                        return executeUpdate(sql, false, true);
1883                                } finally {
1884                                        locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
1885                                }
1886                        }
1887                       
1888                        return executeUpdate(sql);
1889                }
1890        }
1891
1892        /**
1893         * @see StatementImpl#executeUpdate(String, int[])
1894         */
1895        public int executeUpdate(String sql, int[] generatedKeyIndices)
1896                        throws SQLException {
1897                synchronized (checkClosed()) {
1898                        if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
1899                                checkClosed();
1900       
1901                                MySQLConnection locallyScopedConn = this.connection;
1902                               
1903                                // If this is a 'REPLACE' query, we need to be able to parse
1904                                // the 'info' message returned from the server to determine
1905                                // the actual number of keys generated.
1906                                boolean readInfoMsgState = locallyScopedConn
1907                                                .isReadInfoMsgEnabled();
1908                                locallyScopedConn.setReadInfoMsgEnabled(true);
1909
1910                                try {
1911                                        return executeUpdate(sql, false, true);
1912                                } finally {
1913                                        locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
1914                                }
1915                        }
1916                       
1917                        return executeUpdate(sql);
1918                }
1919        }
1920
1921        /**
1922         * @see StatementImpl#executeUpdate(String, String[])
1923         */
1924        public int executeUpdate(String sql, String[] generatedKeyNames)
1925                        throws SQLException {
1926                synchronized (checkClosed()) {
1927                        if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
1928                                MySQLConnection locallyScopedConn = this.connection;
1929                                // If this is a 'REPLACE' query, we need to be able to parse
1930                                // the 'info' message returned from the server to determine
1931                                // the actual number of keys generated.
1932                                boolean readInfoMsgState = this.connection
1933                                                .isReadInfoMsgEnabled();
1934                                locallyScopedConn.setReadInfoMsgEnabled(true);
1935
1936                                try {
1937                                        return executeUpdate(sql, false, true);
1938                                } finally {
1939                                        locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
1940                                }
1941                        }       
1942
1943                        return executeUpdate(sql);
1944                }
1945        }
1946
1947        /**
1948         * Optimization to only use one calendar per-session, or calculate it for
1949         * each call, depending on user configuration
1950         */
1951        protected Calendar getCalendarInstanceForSessionOrNew() throws SQLException {
1952                synchronized (checkClosed()) {
1953                        if (this.connection != null) {
1954                                return this.connection.getCalendarInstanceForSessionOrNew();
1955                        }
1956                        // punt, no connection around
1957                        return new GregorianCalendar();
1958                }
1959        }
1960
1961        /**
1962         * JDBC 2.0 Return the Connection that produced the Statement.
1963         *
1964         * @return the Connection that produced the Statement
1965         *
1966         * @throws SQLException
1967         *             if an error occurs
1968         */
1969        public java.sql.Connection getConnection() throws SQLException {
1970                synchronized (checkClosed()) {
1971                        return this.connection;
1972                }
1973        }
1974
1975        /**
1976         * JDBC 2.0 Determine the fetch direction.
1977         *
1978         * @return the default fetch direction
1979         *
1980         * @exception SQLException
1981         *                if a database-access error occurs
1982         */
1983        public int getFetchDirection() throws SQLException {
1984                return java.sql.ResultSet.FETCH_FORWARD;
1985        }
1986
1987        /**
1988         * JDBC 2.0 Determine the default fetch size.
1989         *
1990         * @return the number of rows to fetch at a time
1991         *
1992         * @throws SQLException
1993         *             if an error occurs
1994         */
1995        public int getFetchSize() throws SQLException {
1996                synchronized (checkClosed()) {
1997                        return this.fetchSize;
1998                }
1999        }
2000
2001        /**
2002         * DOCUMENT ME!
2003         *
2004         * @return DOCUMENT ME!
2005         *
2006         * @throws SQLException
2007         *             DOCUMENT ME!
2008         */
2009        public java.sql.ResultSet getGeneratedKeys()
2010                        throws SQLException {
2011                synchronized (checkClosed()) {
2012                        if (!this.retrieveGeneratedKeys) {
2013                                throw SQLError.createSQLException(Messages.getString("Statement.GeneratedKeysNotRequested"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
2014                        }
2015                       
2016                        if (this.batchedGeneratedKeys == null) {
2017                                if (lastQueryIsOnDupKeyUpdate) {
2018                                        return getGeneratedKeysInternal(1);
2019                                }
2020                                return getGeneratedKeysInternal();
2021                        }
2022       
2023                        Field[] fields = new Field[1];
2024                        fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$
2025                        fields[0].setConnection(this.connection);
2026       
2027                        return com.mysql.jdbc.ResultSetImpl.getInstance(this.currentCatalog, fields,
2028                                        new RowDataStatic(this.batchedGeneratedKeys), this.connection,
2029                                        this, false);
2030                }
2031        }
2032
2033        /*
2034         * Needed because there's no concept of super.super to get to this
2035         * implementation from ServerPreparedStatement when dealing with batched
2036         * updates.
2037         */
2038        protected java.sql.ResultSet getGeneratedKeysInternal()
2039                        throws SQLException {
2040                int numKeys = getUpdateCount();
2041                return getGeneratedKeysInternal(numKeys);
2042        }
2043
2044        protected java.sql.ResultSet getGeneratedKeysInternal(int numKeys)
2045                        throws SQLException {
2046                synchronized (checkClosed()) {
2047                        Field[] fields = new Field[1];
2048                        fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$
2049                        fields[0].setConnection(this.connection);
2050                        fields[0].setUseOldNameMetadata(true);
2051       
2052                        ArrayList<ResultSetRow> rowSet = new ArrayList<ResultSetRow>();
2053       
2054                        long beginAt = getLastInsertID();
2055                       
2056                        if (beginAt < 0) { // looking at an UNSIGNED BIGINT that has overflowed
2057                                fields[0].setUnsigned();
2058                        }
2059       
2060                        if (this.results != null) {
2061                                String serverInfo = this.results.getServerInfo();
2062       
2063                                //
2064                                // Only parse server info messages for 'REPLACE'
2065                                // queries
2066                                //
2067                                if ((numKeys > 0) && (this.results.getFirstCharOfQuery() == 'R')
2068                                                && (serverInfo != null) && (serverInfo.length() > 0)) {
2069                                        numKeys = getRecordCountFromInfo(serverInfo);
2070                                }
2071       
2072                                if ((beginAt != 0 /* BIGINT UNSIGNED can wrap the protocol representation */) && (numKeys > 0)) {
2073                                        for (int i = 0; i < numKeys; i++) {
2074                                                byte[][] row = new byte[1][];
2075                                                if (beginAt > 0) {
2076                                                        row[0] = StringUtils.getBytes(Long.toString(beginAt));
2077                                                } else {
2078                                                        byte[] asBytes = new byte[8];
2079                                                        asBytes[7] = (byte) (beginAt & 0xff);
2080                                                        asBytes[6] = (byte) (beginAt >>> 8);
2081                                                        asBytes[5] = (byte) (beginAt >>> 16);
2082                                                        asBytes[4] = (byte) (beginAt >>> 24);
2083                                                        asBytes[3] = (byte) (beginAt >>> 32);
2084                                                        asBytes[2] = (byte) (beginAt >>> 40);
2085                                                        asBytes[1] = (byte) (beginAt >>> 48);
2086                                                        asBytes[0] = (byte) (beginAt >>> 56);
2087                                                       
2088                                                        BigInteger val = new BigInteger(1, asBytes);
2089       
2090                                                        row[0] = val.toString().getBytes();
2091                                                }
2092                                                rowSet.add(new ByteArrayRow(row, getExceptionInterceptor()));
2093                                                beginAt  += this.connection.getAutoIncrementIncrement();
2094                                        }
2095                                }
2096                        }
2097                       
2098                        com.mysql.jdbc.ResultSetImpl gkRs = com.mysql.jdbc.ResultSetImpl.getInstance(this.currentCatalog, fields,
2099                                        new RowDataStatic(rowSet), this.connection, this, false);
2100                       
2101                        this.openResults.add(gkRs);
2102                       
2103                        return gkRs;
2104                }
2105        }
2106
2107        /**
2108         * Returns the id used when profiling
2109         *
2110         * @return the id used when profiling.
2111         */
2112        protected int getId() {
2113                return this.statementId;
2114        }
2115
2116        /**
2117         * getLastInsertID returns the value of the auto_incremented key after an
2118         * executeQuery() or excute() call.
2119         *
2120         * <p>
2121         * This gets around the un-threadsafe behavior of "select LAST_INSERT_ID()"
2122         * which is tied to the Connection that created this Statement, and
2123         * therefore could have had many INSERTS performed before one gets a chance
2124         * to call "select LAST_INSERT_ID()".
2125         * </p>
2126         *
2127         * @return the last update ID.
2128         */
2129        public long getLastInsertID() {
2130                try {
2131                        synchronized (checkClosed()) {
2132                                return this.lastInsertId;
2133                        }
2134                } catch (SQLException e) {
2135                        throw new RuntimeException(e); // evolve interface to throw SQLException
2136                }
2137        }
2138
2139        /**
2140         * getLongUpdateCount returns the current result as an update count, if the
2141         * result is a ResultSet or there are no more results, -1 is returned. It
2142         * should only be called once per result.
2143         *
2144         * <p>
2145         * This method returns longs as MySQL server versions newer than 3.22.4
2146         * return 64-bit values for update counts
2147         * </p>
2148         *
2149         * @return the current update count.
2150         */
2151        public long getLongUpdateCount() {
2152                try {
2153                        synchronized (checkClosed()) {
2154                                if (this.results == null) {
2155                                        return -1;
2156                                }
2157
2158                                if (this.results.reallyResult()) {
2159                                        return -1;
2160                                }
2161
2162                                return this.updateCount;
2163                        }
2164                } catch (SQLException e) {
2165                        throw new RuntimeException(e); // evolve interface to throw SQLException
2166                }
2167        }
2168
2169        /**
2170         * The maxFieldSize limit (in bytes) is the maximum amount of data returned
2171         * for any column value; it only applies to BINARY, VARBINARY,
2172         * LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR columns. If the limit is
2173         * exceeded, the excess data is silently discarded.
2174         *
2175         * @return the current max column size limit; zero means unlimited
2176         *
2177         * @exception SQLException
2178         *                if a database access error occurs
2179         */
2180        public int getMaxFieldSize() throws SQLException {
2181                synchronized (checkClosed()) {
2182                        return this.maxFieldSize;
2183                }
2184        }
2185
2186        /**
2187         * The maxRows limit is set to limit the number of rows that any ResultSet
2188         * can contain. If the limit is exceeded, the excess rows are silently
2189         * dropped.
2190         *
2191         * @return the current maximum row limit; zero means unlimited
2192         *
2193         * @exception SQLException
2194         *                if a database access error occurs
2195         */
2196        public int getMaxRows() throws SQLException {
2197                synchronized (checkClosed()) {
2198                        if (this.maxRows <= 0) {
2199                                return 0;
2200                        }
2201
2202                        return this.maxRows;
2203                }
2204        }
2205
2206        /**
2207         * getMoreResults moves to a Statement's next result. If it returns true,
2208         * this result is a ResulSet.
2209         *
2210         * @return true if the next ResultSet is valid
2211         *
2212         * @exception SQLException
2213         *                if a database access error occurs
2214         */
2215        public boolean getMoreResults() throws SQLException {
2216                return getMoreResults(CLOSE_CURRENT_RESULT);
2217        }
2218
2219        /**
2220         * @see StatementImpl#getMoreResults(int)
2221         */
2222        public boolean getMoreResults(int current) throws SQLException {
2223                synchronized (checkClosed()) {
2224                        if (this.results == null) {
2225                                return false;
2226                        }
2227       
2228                        boolean streamingMode = createStreamingResultSet();
2229                       
2230                        if (streamingMode) {
2231                                if (this.results.reallyResult()) {
2232                                        while (this.results.next()); // need to drain remaining rows to get to server status
2233                                                                                         // which tells us whether more results actually exist or not
2234                                }
2235                        }
2236                       
2237                        ResultSetInternalMethods nextResultSet = this.results.getNextResultSet();
2238       
2239                        switch (current) {
2240                        case java.sql.Statement.CLOSE_CURRENT_RESULT:
2241       
2242                                if (this.results != null) {
2243                                        if (!streamingMode) { 
2244                                                this.results.close();
2245                                        }
2246                                       
2247                                        this.results.clearNextResult();
2248                                }
2249       
2250                                break;
2251       
2252                        case java.sql.Statement.CLOSE_ALL_RESULTS:
2253       
2254                                if (this.results != null) {
2255                                        if (!streamingMode) { 
2256                                                this.results.close();
2257                                        }
2258                                       
2259                                        this.results.clearNextResult();
2260                                }
2261       
2262                                closeAllOpenResults();
2263       
2264                                break;
2265       
2266                        case java.sql.Statement.KEEP_CURRENT_RESULT:
2267                                if (!this.connection.getDontTrackOpenResources()) {
2268                                        this.openResults.add(this.results);
2269                                }
2270       
2271                                this.results.clearNextResult(); // nobody besides us should
2272                                // ever need this value...
2273                                break;
2274       
2275                        default:
2276                                throw SQLError.createSQLException(Messages
2277                                                .getString("Statement.19"), //$NON-NLS-1$
2278                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
2279                        }
2280       
2281                        this.results = nextResultSet;
2282       
2283                        if (this.results == null) {
2284                                this.updateCount = -1;
2285                                this.lastInsertId = -1;
2286                        } else if (this.results.reallyResult()) {
2287                                this.updateCount = -1;
2288                                this.lastInsertId = -1;
2289                        } else {
2290                                this.updateCount = this.results.getUpdateCount();
2291                                this.lastInsertId = this.results.getUpdateID();
2292                        }
2293       
2294                        return ((this.results != null) && this.results.reallyResult()) ? true
2295                                        : false;
2296                }
2297        }
2298
2299        /**
2300         * The queryTimeout limit is the number of seconds the driver will wait for
2301         * a Statement to execute. If the limit is exceeded, a SQLException is
2302         * thrown.
2303         *
2304         * @return the current query timeout limit in seconds; 0 = unlimited
2305         *
2306         * @exception SQLException
2307         *                if a database access error occurs
2308         */
2309        public int getQueryTimeout() throws SQLException {
2310                synchronized (checkClosed()) {
2311                        return this.timeoutInMillis / 1000;
2312                }
2313        }
2314
2315        /**
2316         * Parses actual record count from 'info' message
2317         *
2318         * @param serverInfo
2319         *            DOCUMENT ME!
2320         *
2321         * @return DOCUMENT ME!
2322         */
2323        private int getRecordCountFromInfo(String serverInfo) {
2324                StringBuffer recordsBuf = new StringBuffer();
2325                int recordsCount = 0;
2326                int duplicatesCount = 0;
2327
2328                char c = (char) 0;
2329
2330                int length = serverInfo.length();
2331                int i = 0;
2332
2333                for (; i < length; i++) {
2334                        c = serverInfo.charAt(i);
2335
2336                        if (Character.isDigit(c)) {
2337                                break;
2338                        }
2339                }
2340
2341                recordsBuf.append(c);
2342                i++;
2343
2344                for (; i < length; i++) {
2345                        c = serverInfo.charAt(i);
2346
2347                        if (!Character.isDigit(c)) {
2348                                break;
2349                        }
2350
2351                        recordsBuf.append(c);
2352                }
2353
2354                recordsCount = Integer.parseInt(recordsBuf.toString());
2355
2356                StringBuffer duplicatesBuf = new StringBuffer();
2357
2358                for (; i < length; i++) {
2359                        c = serverInfo.charAt(i);
2360
2361                        if (Character.isDigit(c)) {
2362                                break;
2363                        }
2364                }
2365
2366                duplicatesBuf.append(c);
2367                i++;
2368
2369                for (; i < length; i++) {
2370                        c = serverInfo.charAt(i);
2371
2372                        if (!Character.isDigit(c)) {
2373                                break;
2374                        }
2375
2376                        duplicatesBuf.append(c);
2377                }
2378
2379                duplicatesCount = Integer.parseInt(duplicatesBuf.toString());
2380
2381                return recordsCount - duplicatesCount;
2382        }
2383
2384        /**
2385         * getResultSet returns the current result as a ResultSet. It should only be
2386         * called once per result.
2387         *
2388         * @return the current result set; null if there are no more
2389         *
2390         * @exception SQLException
2391         *                if a database access error occurs (why?)
2392         */
2393        public java.sql.ResultSet getResultSet() throws SQLException {
2394                synchronized (checkClosed()) {
2395                        return ((this.results != null) && this.results.reallyResult()) ? (java.sql.ResultSet) this.results
2396                                        : null;
2397                }
2398        }
2399
2400        /**
2401         * JDBC 2.0 Determine the result set concurrency.
2402         *
2403         * @return CONCUR_UPDATABLE or CONCUR_READONLY
2404         *
2405         * @throws SQLException
2406         *             if an error occurs
2407         */
2408        public int getResultSetConcurrency() throws SQLException {
2409                synchronized (checkClosed()) {
2410                        return this.resultSetConcurrency;
2411                }
2412        }
2413
2414        /**
2415         * @see StatementImpl#getResultSetHoldability()
2416         */
2417        public int getResultSetHoldability() throws SQLException {
2418                return java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
2419        }
2420
2421        protected ResultSetInternalMethods getResultSetInternal() {
2422                try {
2423                        synchronized (checkClosed()) {
2424                                return this.results;
2425                        }
2426                } catch (SQLException e) {
2427                        return this.results; // you end up with the same thing as before, you'll get exception when actually trying to use it
2428                }
2429        }
2430
2431        /**
2432         * JDBC 2.0 Determine the result set type.
2433         *
2434         * @return the ResultSet type (SCROLL_SENSITIVE or SCROLL_INSENSITIVE)
2435         *
2436         * @throws SQLException
2437         *             if an error occurs.
2438         */
2439        public int getResultSetType() throws SQLException {
2440                synchronized (checkClosed()) {
2441                        return this.resultSetType;
2442                }
2443        }
2444
2445        /**
2446         * getUpdateCount returns the current result as an update count, if the
2447         * result is a ResultSet or there are no more results, -1 is returned. It
2448         * should only be called once per result.
2449         *
2450         * @return the current result as an update count.
2451         *
2452         * @exception SQLException
2453         *                if a database access error occurs
2454         */
2455        public int getUpdateCount() throws SQLException {
2456                synchronized (checkClosed()) {
2457                        if (this.results == null) {
2458                                return -1;
2459                        }
2460       
2461                        if (this.results.reallyResult()) {
2462                                return -1;
2463                        }
2464       
2465                        int truncatedUpdateCount = 0;
2466       
2467                        if (this.results.getUpdateCount() > Integer.MAX_VALUE) {
2468                                truncatedUpdateCount = Integer.MAX_VALUE;
2469                        } else {
2470                                truncatedUpdateCount = (int) this.results.getUpdateCount();
2471                        }
2472       
2473                        return truncatedUpdateCount;
2474                }
2475        }
2476
2477        /**
2478         * The first warning reported by calls on this Statement is returned. A
2479         * Statement's execute methods clear its java.sql.SQLWarning chain.
2480         * Subsequent Statement warnings will be chained to this
2481         * java.sql.SQLWarning.
2482         *
2483         * <p>
2484         * The Warning chain is automatically cleared each time a statement is
2485         * (re)executed.
2486         * </p>
2487         *
2488         * <p>
2489         * <B>Note:</B> If you are processing a ResultSet then any warnings
2490         * associated with ResultSet reads will be chained on the ResultSet object.
2491         * </p>
2492         *
2493         * @return the first java.sql.SQLWarning or null
2494         *
2495         * @exception SQLException
2496         *                if a database access error occurs
2497         */
2498        public java.sql.SQLWarning getWarnings() throws SQLException {
2499                synchronized (checkClosed()) {
2500
2501                        if (this.clearWarningsCalled) {
2502                                return null;
2503                        }
2504                       
2505                        if (this.connection.versionMeetsMinimum(4, 1, 0)) {
2506                                SQLWarning pendingWarningsFromServer = SQLError
2507                                                .convertShowWarningsToSQLWarnings(this.connection);
2508       
2509                                if (this.warningChain != null) {
2510                                        this.warningChain.setNextWarning(pendingWarningsFromServer);
2511                                } else {
2512                                        this.warningChain = pendingWarningsFromServer;
2513                                }
2514       
2515                                return this.warningChain;
2516                        }
2517       
2518                        return this.warningChain;
2519                }
2520        }
2521
2522        /**
2523         * Closes this statement, and frees resources.
2524         *
2525         * @param calledExplicitly
2526         *            was this called from close()?
2527         *
2528         * @throws SQLException
2529         *             if an error occurs
2530         */
2531        protected void realClose(boolean calledExplicitly, boolean closeOpenResults)
2532                        throws SQLException {
2533                MySQLConnection locallyScopedConn;
2534               
2535                try {
2536                        locallyScopedConn = checkClosed();
2537                } catch (SQLException sqlEx) {
2538                        return; // already closed
2539                }
2540               
2541                synchronized (locallyScopedConn) {
2542       
2543                        if (this.useUsageAdvisor) {
2544                                if (!calledExplicitly) {
2545                                        String message = Messages.getString("Statement.63") //$NON-NLS-1$
2546                                                        + Messages.getString("Statement.64"); //$NON-NLS-1$
2547       
2548                                        this.eventSink.consumeEvent(new ProfilerEvent(
2549                                                        ProfilerEvent.TYPE_WARN,
2550                                                        "", //$NON-NLS-1$
2551                                                        this.currentCatalog, this.connectionId, this.getId(),
2552                                                        -1, System.currentTimeMillis(), 0,
2553                                                        Constants.MILLIS_I18N, null, this.pointOfOrigin,
2554                                                        message));
2555                                }
2556                        }
2557       
2558                        if (closeOpenResults) {
2559                                closeOpenResults = !this.holdResultsOpenOverClose;
2560                        }
2561                       
2562                        if (closeOpenResults) {
2563                                if (this.results != null) {
2564                                       
2565                                        try {
2566                                                this.results.close();
2567                                        } catch (Exception ex) {
2568                                                ;
2569                                        }
2570                                }
2571                               
2572                                closeAllOpenResults();
2573                        }
2574       
2575                        if (this.connection != null) {
2576                                if (this.maxRowsChanged) {
2577                                        this.connection.unsetMaxRows(this);
2578                                }
2579       
2580                                if (!this.connection.getDontTrackOpenResources()) {
2581                                        this.connection.unregisterStatement(this);
2582                                }
2583                        }
2584       
2585                        this.isClosed = true;
2586       
2587                        this.results = null;
2588                        this.connection = null;
2589                        this.warningChain = null;
2590                        this.openResults = null;
2591                        this.batchedGeneratedKeys = null;
2592                        this.localInfileInputStream = null;
2593                        this.pingTarget = null;
2594                }
2595        }
2596
2597        /**
2598         * setCursorName defines the SQL cursor name that will be used by subsequent
2599         * execute methods. This name can then be used in SQL positioned
2600         * update/delete statements to identify the current row in the ResultSet
2601         * generated by this statement. If a database doesn't support positioned
2602         * update/delete, this method is a no-op.
2603         *
2604         * <p>
2605         * <b>Note:</b> This MySQL driver does not support cursors.
2606         * </p>
2607         *
2608         * @param name
2609         *            the new cursor name
2610         *
2611         * @exception SQLException
2612         *                if a database access error occurs
2613         */
2614        public void setCursorName(String name) throws SQLException {
2615                // No-op
2616        }
2617
2618        /**
2619         * If escape scanning is on (the default), the driver will do escape
2620         * substitution before sending the SQL to the database.
2621         *
2622         * @param enable
2623         *            true to enable; false to disable
2624         *
2625         * @exception SQLException
2626         *                if a database access error occurs
2627         */
2628        public void setEscapeProcessing(boolean enable)
2629                        throws SQLException {
2630                synchronized (checkClosed()) {
2631                        this.doEscapeProcessing = enable;
2632                }
2633        }
2634
2635        /**
2636         * JDBC 2.0 Give a hint as to the direction in which the rows in a result
2637         * set will be processed. The hint applies only to result sets created using
2638         * this Statement object. The default value is ResultSet.FETCH_FORWARD.
2639         *
2640         * @param direction
2641         *            the initial direction for processing rows
2642         *
2643         * @exception SQLException
2644         *                if a database-access error occurs or direction is not one
2645         *                of ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, or
2646         *                ResultSet.FETCH_UNKNOWN
2647         */
2648        public void setFetchDirection(int direction) throws SQLException {
2649                switch (direction) {
2650                case java.sql.ResultSet.FETCH_FORWARD:
2651                case java.sql.ResultSet.FETCH_REVERSE:
2652                case java.sql.ResultSet.FETCH_UNKNOWN:
2653                        break;
2654
2655                default:
2656                        throw SQLError.createSQLException(
2657                                        Messages.getString("Statement.5"), //$NON-NLS-1$
2658                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
2659                }
2660        }
2661
2662        /**
2663         * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that should
2664         * be fetched from the database when more rows are needed. The number of
2665         * rows specified only affects result sets created using this statement. If
2666         * the value specified is zero, then the hint is ignored. The default value
2667         * is zero.
2668         *
2669         * @param rows
2670         *            the number of rows to fetch
2671         *
2672         * @exception SQLException
2673         *                if a database-access error occurs, or the condition 0
2674         *                &lt;= rows &lt;= this.getMaxRows() is not satisfied.
2675         */
2676        public void setFetchSize(int rows) throws SQLException {
2677                synchronized (checkClosed()) {
2678                        if (((rows < 0) && (rows != Integer.MIN_VALUE))
2679                                        || ((this.maxRows != 0) && (this.maxRows != -1) && (rows > this
2680                                                        .getMaxRows()))) {
2681                                throw SQLError.createSQLException(
2682                                                Messages.getString("Statement.7"), //$NON-NLS-1$
2683                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$ //$NON-NLS-2$
2684                        }
2685       
2686                        this.fetchSize = rows;
2687                }
2688        }
2689
2690        public void setHoldResultsOpenOverClose(boolean holdResultsOpenOverClose) {
2691                try {
2692                        synchronized (checkClosed()) {
2693                                this.holdResultsOpenOverClose = holdResultsOpenOverClose;
2694                        }
2695                } catch (SQLException e) {
2696                        // FIXME: can't break interface at this point
2697                }
2698        }
2699
2700        /**
2701         * Sets the maxFieldSize
2702         *
2703         * @param max
2704         *            the new max column size limit; zero means unlimited
2705         *
2706         * @exception SQLException
2707         *                if size exceeds buffer size
2708         */
2709        public void setMaxFieldSize(int max) throws SQLException {
2710                synchronized (checkClosed()) {
2711                        if (max < 0) {
2712                                throw SQLError.createSQLException(Messages
2713                                                .getString("Statement.11"), //$NON-NLS-1$
2714                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
2715                        }
2716       
2717                        int maxBuf = (this.connection != null) ? this.connection
2718                                        .getMaxAllowedPacket() : MysqlIO.getMaxBuf();
2719       
2720                        if (max > maxBuf) {
2721                                throw SQLError.createSQLException(Messages.getString(
2722                                                "Statement.13", //$NON-NLS-1$
2723                                                new Object[] { Long.valueOf(maxBuf) }), //$NON-NLS-1$
2724                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
2725                        }
2726       
2727                        this.maxFieldSize = max;
2728                }
2729        }
2730
2731        /**
2732         * Set the maximum number of rows
2733         *
2734         * @param max
2735         *            the new max rows limit; zero means unlimited
2736         *
2737         * @exception SQLException
2738         *                if a database access error occurs
2739         *
2740         * @see getMaxRows
2741         */
2742        public void setMaxRows(int max) throws SQLException {
2743                synchronized (checkClosed()) {
2744                        if ((max > MysqlDefs.MAX_ROWS) || (max < 0)) {
2745                                throw SQLError
2746                                                .createSQLException(
2747                                                                Messages.getString("Statement.15") + max //$NON-NLS-1$
2748                                                                                + " > " //$NON-NLS-1$ //$NON-NLS-2$
2749                                                                                + MysqlDefs.MAX_ROWS + ".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$ //$NON-NLS-2$
2750                        }
2751       
2752                        if (max == 0) {
2753                                max = -1;
2754                        }
2755       
2756                        this.maxRows = max;
2757                        this.maxRowsChanged = true;
2758       
2759                        if (this.maxRows == -1) {
2760                                this.connection.unsetMaxRows(this);
2761                                this.maxRowsChanged = false;
2762                        } else {
2763                                // Most people don't use setMaxRows()
2764                                // so don't penalize them
2765                                // with the extra query it takes
2766                                // to do it efficiently unless we need
2767                                // to.
2768                                this.connection.maxRowsChanged(this);
2769                        }
2770                }
2771        }
2772
2773        /**
2774         * Sets the queryTimeout limit
2775         *
2776         * @param seconds -
2777         *            the new query timeout limit in seconds
2778         *
2779         * @exception SQLException
2780         *                if a database access error occurs
2781         */
2782        public void setQueryTimeout(int seconds) throws SQLException {
2783                synchronized (checkClosed()) {
2784                        if (seconds < 0) {
2785                                throw SQLError.createSQLException(Messages
2786                                                .getString("Statement.21"), //$NON-NLS-1$
2787                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
2788                        }
2789       
2790                        this.timeoutInMillis = seconds * 1000;
2791                }
2792        }
2793
2794        /**
2795         * Sets the concurrency for result sets generated by this statement
2796         *
2797         * @param concurrencyFlag
2798         *            DOCUMENT ME!
2799         */
2800        void setResultSetConcurrency(int concurrencyFlag) {
2801                try {
2802                        synchronized (checkClosed()) {
2803                                this.resultSetConcurrency = concurrencyFlag;
2804                        }
2805                } catch (SQLException e) {
2806                        // FIXME: Can't break interface atm, we'll get the exception later when
2807                        // you try and do something useful with a closed statement...
2808                }
2809        }
2810
2811        /**
2812         * Sets the result set type for result sets generated by this statement
2813         *
2814         * @param typeFlag
2815         *            DOCUMENT ME!
2816         */
2817        void setResultSetType(int typeFlag) {
2818                try {
2819                        synchronized (checkClosed()) {
2820                                this.resultSetType = typeFlag;
2821                        }
2822                } catch (SQLException e) {
2823                        // FIXME: Can't break interface atm, we'll get the exception later when
2824                        // you try and do something useful with a closed statement...
2825                }
2826        }
2827
2828        protected void getBatchedGeneratedKeys(java.sql.Statement batchedStatement) throws SQLException {
2829                synchronized (checkClosed()) {
2830                        if (this.retrieveGeneratedKeys) {
2831                                java.sql.ResultSet rs = null;
2832       
2833                                try {
2834                                        rs = batchedStatement.getGeneratedKeys();
2835       
2836                                        while (rs.next()) {
2837                                                this.batchedGeneratedKeys
2838                                                                .add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor()));
2839                                        }
2840                                } finally {
2841                                        if (rs != null) {
2842                                                rs.close();
2843                                        }
2844                                }
2845                        }
2846                }
2847        }
2848       
2849        protected void getBatchedGeneratedKeys(int maxKeys) throws SQLException {
2850                synchronized (checkClosed()) {
2851                        if (this.retrieveGeneratedKeys) {
2852                                java.sql.ResultSet rs = null;
2853       
2854                                try {
2855                                        if (maxKeys == 0)
2856                                                rs = getGeneratedKeysInternal();
2857                                        else
2858                                                rs = getGeneratedKeysInternal(maxKeys);
2859       
2860                                        while (rs.next()) {
2861                                                this.batchedGeneratedKeys
2862                                                                .add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor()));
2863                                        }
2864                                } finally {
2865                                        if (rs != null) {
2866                                                rs.close();
2867                                        }
2868                                }
2869                        }
2870                }
2871        }
2872
2873        /**
2874         * @return
2875         */
2876        private boolean useServerFetch() throws SQLException {
2877                synchronized (checkClosed()) {
2878                        return this.connection.isCursorFetchEnabled() && this.fetchSize > 0
2879                                        && this.resultSetConcurrency == ResultSet.CONCUR_READ_ONLY
2880                                        && this.resultSetType == ResultSet.TYPE_FORWARD_ONLY;
2881                }
2882        }
2883
2884        public boolean isClosed() throws SQLException {
2885                try {
2886                        synchronized (checkClosed()) {
2887                                return this.isClosed;
2888                        }
2889                } catch (SQLException sqlEx) {
2890                        if (SQLError.SQL_STATE_CONNECTION_NOT_OPEN.equals(sqlEx.getSQLState())) {
2891                                return true;
2892                        }
2893                       
2894                        throw sqlEx;
2895                }
2896        }
2897
2898        private boolean isPoolable = true;
2899
2900        public boolean isPoolable() throws SQLException {
2901                return this.isPoolable;
2902        }
2903
2904        public void setPoolable(boolean poolable) throws SQLException {
2905                this.isPoolable = poolable;
2906        }
2907
2908        /**
2909     * Returns true if this either implements the interface argument or is directly or indirectly a wrapper
2910     * for an object that does. Returns false otherwise. If this implements the interface then return true,
2911     * else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped
2912     * object. If this does not implement the interface and is not a wrapper, return false.
2913     * This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that
2914     * callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method
2915     * returns true then calling <code>unwrap</code> with the same argument should succeed.
2916     *
2917     * @param interfaces a Class defining an interface.
2918     * @return true if this implements the interface or directly or indirectly wraps an object that does.
2919     * @throws java.sql.SQLException  if an error occurs while determining whether this is a wrapper
2920     * for an object with the given interface.
2921     * @since 1.6
2922     */
2923        public boolean isWrapperFor(Class<?> iface) throws SQLException {
2924                checkClosed();
2925
2926                // This works for classes that aren't actually wrapping
2927                // anything
2928                return iface.isInstance(this);
2929        }
2930
2931    /**
2932     * Returns an object that implements the given interface to allow access to non-standard methods,
2933     * or standard methods not exposed by the proxy.
2934     * The result may be either the object found to implement the interface or a proxy for that object.
2935     * If the receiver implements the interface then that is the object. If the receiver is a wrapper
2936     * and the wrapped object implements the interface then that is the object. Otherwise the object is
2937     *  the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a
2938     * wrapper and does not implement the interface, then an <code>SQLException</code> is thrown.
2939     *
2940     * @param iface A Class defining an interface that the result must implement.
2941     * @return an object that implements the interface. May be a proxy for the actual implementing object.
2942     * @throws java.sql.SQLException If no object found that implements the interface
2943     * @since 1.6
2944     */
2945        public Object unwrap(Class<?> iface) throws java.sql.SQLException {
2946        try {
2947                // This works for classes that aren't actually wrapping
2948                // anything
2949            return Util.cast(iface, this);
2950        } catch (ClassCastException cce) {
2951            throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(),
2952                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
2953        }
2954    }
2955
2956        protected int findStartOfStatement(String sql) {
2957                int statementStartPos = 0;
2958
2959                if (StringUtils.startsWithIgnoreCaseAndWs(sql, "/*")) {
2960                        statementStartPos = sql.indexOf("*/");
2961
2962                        if (statementStartPos == -1) {
2963                                statementStartPos = 0;
2964                        } else {
2965                                statementStartPos += 2;
2966                        }
2967                } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "--")
2968                        || StringUtils.startsWithIgnoreCaseAndWs(sql, "#")) {
2969                        statementStartPos = sql.indexOf('\n');
2970
2971                        if (statementStartPos == -1) {
2972                                statementStartPos = sql.indexOf('\r');
2973
2974                                if (statementStartPos == -1) {
2975                                        statementStartPos = 0;
2976                                }
2977                        }
2978                }
2979
2980                return statementStartPos;
2981        }
2982
2983        private InputStream localInfileInputStream;
2984
2985        protected final boolean version5013OrNewer;
2986
2987    public InputStream getLocalInfileInputStream() {
2988        return this.localInfileInputStream;
2989    }
2990
2991    public void setLocalInfileInputStream(InputStream stream) {
2992        this.localInfileInputStream = stream;
2993    }
2994   
2995    public void setPingTarget(PingTarget pingTarget) {
2996                this.pingTarget = pingTarget;
2997        }
2998   
2999    public ExceptionInterceptor getExceptionInterceptor() {
3000        return this.exceptionInterceptor;
3001    }
3002       
3003        protected boolean containsOnDuplicateKeyInString(String sql) {
3004                return getOnDuplicateKeyLocation(sql) != -1;
3005        }
3006       
3007        protected int getOnDuplicateKeyLocation(String sql) {
3008                return StringUtils.indexOfIgnoreCaseRespectMarker(0, 
3009                                sql, "ON DUPLICATE KEY UPDATE ", "\"'`", "\"'`", !this.connection.isNoBackslashEscapesSet());
3010        }
3011       
3012        private boolean closeOnCompletion;
3013       
3014        public void closeOnCompletion() throws SQLException {
3015                synchronized (checkClosed()) {
3016                        closeOnCompletion = true;
3017                }
3018        }
3019       
3020        public boolean isCloseOnCompletion() throws SQLException {
3021                synchronized (checkClosed()) {
3022                        return closeOnCompletion;
3023                }
3024        }
3025}
Note: See TracBrowser for help on using the repository browser.