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

Last change on this file since 848 was 766, checked in by npipsl, 11 years ago
File size: 165.4 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 
22 */
23package com.mysql.jdbc;
24
25import java.io.IOException;
26import java.io.UnsupportedEncodingException;
27import java.lang.reflect.Array;
28import java.lang.reflect.Constructor;
29import java.lang.reflect.Method;
30import java.nio.ByteBuffer;
31import java.nio.CharBuffer;
32import java.nio.charset.Charset;
33import java.nio.charset.CharsetEncoder;
34import java.sql.Blob;
35import java.sql.DatabaseMetaData;
36import java.sql.ResultSet;
37import java.sql.SQLException;
38import java.sql.SQLPermission;
39import java.sql.SQLWarning;
40import java.sql.Savepoint;
41import java.util.ArrayList;
42import java.util.Calendar;
43import java.util.Collections;
44import java.util.Enumeration;
45import java.util.GregorianCalendar;
46import java.util.HashMap;
47import java.util.Iterator;
48import java.util.List;
49import java.util.Locale;
50import java.util.Map;
51import java.util.Properties;
52import java.util.Random;
53import java.util.Stack;
54import java.util.TimeZone;
55import java.util.Timer;
56import java.util.TreeMap;
57import java.util.concurrent.Executor;
58
59import com.mysql.jdbc.PreparedStatement.ParseInfo;
60import com.mysql.jdbc.log.Log;
61import com.mysql.jdbc.log.LogFactory;
62import com.mysql.jdbc.log.NullLogger;
63import com.mysql.jdbc.profiler.ProfilerEvent;
64import com.mysql.jdbc.profiler.ProfilerEventHandler;
65import com.mysql.jdbc.util.LRUCache;
66
67/**
68 * A Connection represents a session with a specific database. Within the
69 * context of a Connection, SQL statements are executed and results are
70 * returned.
71 * <P>
72 * A Connection's database is able to provide information describing its tables,
73 * its supported SQL grammar, its stored procedures, the capabilities of this
74 * connection, etc. This information is obtained with the getMetaData method.
75 * </p>
76 *
77 * @author Mark Matthews
78 * @version $Id$
79 * @see java.sql.Connection
80 */
81public class ConnectionImpl extends ConnectionPropertiesImpl implements
82                MySQLConnection {
83
84        private static final long serialVersionUID = 2877471301981509474L;
85
86        private static final SQLPermission SET_NETWORK_TIMEOUT_PERM = new SQLPermission("setNetworkTimeout");
87       
88        private static final SQLPermission ABORT_PERM = new SQLPermission("abort");
89
90        private static final String JDBC_LOCAL_CHARACTER_SET_RESULTS = "jdbc.local.character_set_results";
91
92   public String getHost() {
93      return host;
94   }
95
96   private MySQLConnection proxy = null;
97   
98   public boolean isProxySet(){
99           return this.proxy != null;
100   }
101
102   public void setProxy(MySQLConnection proxy) {
103      this.proxy = proxy;
104   }
105
106   // We have to proxy ourselves when we're load balanced so that
107   // statements get routed to the right physical connection
108   // (when load balanced, we're a "logical" connection)
109   private MySQLConnection getProxy() {
110      return (proxy != null) ? proxy : (MySQLConnection) this;
111   }
112   
113   public MySQLConnection getLoadBalanceSafeProxy() {
114           return this.getProxy();
115   }
116   
117
118        class ExceptionInterceptorChain implements ExceptionInterceptor {
119                List<Extension> interceptors;
120               
121                ExceptionInterceptorChain(String interceptorClasses) throws SQLException {
122                        interceptors = Util.loadExtensions(ConnectionImpl.this, props, interceptorClasses, "Connection.BadExceptionInterceptor",  this);
123                }
124               
125                public SQLException interceptException(SQLException sqlEx, Connection conn) {
126                        if (interceptors != null) {
127                                Iterator<Extension> iter = interceptors.iterator();
128                               
129                                while (iter.hasNext()) {
130                                        sqlEx = ((ExceptionInterceptor)iter.next()).interceptException(sqlEx, ConnectionImpl.this);
131                                }
132                        }
133                       
134                        return sqlEx;
135                }
136
137                public void destroy() {
138                        if (interceptors != null) {
139                                Iterator<Extension> iter = interceptors.iterator();
140                               
141                                while (iter.hasNext()) {
142                                        ((ExceptionInterceptor)iter.next()).destroy();
143                                }
144                        }
145                       
146                }
147
148                public void init(Connection conn, Properties properties) throws SQLException {
149                        if (interceptors != null) {
150                                Iterator<Extension> iter = interceptors.iterator();
151                               
152                                while (iter.hasNext()) {
153                                        ((ExceptionInterceptor)iter.next()).init(conn, properties);
154                                }
155                        }
156                }
157        }
158       
159        /**
160         * Used as a key for caching callable statements which (may) depend on
161         * current catalog...In 5.0.x, they don't (currently), but stored procedure
162         * names soon will, so current catalog is a (hidden) component of the name.
163         */
164        static class CompoundCacheKey {
165                String componentOne;
166
167                String componentTwo;
168
169                int hashCode;
170
171                CompoundCacheKey(String partOne, String partTwo) {
172                        this.componentOne = partOne;
173                        this.componentTwo = partTwo;
174
175                        // Handle first component (in most cases, currentCatalog)
176                        // being NULL....
177                        this.hashCode = (((this.componentOne != null) ? this.componentOne
178                                        : "") + this.componentTwo).hashCode();
179                }
180
181                /*
182                 * (non-Javadoc)
183                 *
184                 * @see java.lang.Object#equals(java.lang.Object)
185                 */
186                public boolean equals(Object obj) {
187                        if (obj instanceof CompoundCacheKey) {
188                                CompoundCacheKey another = (CompoundCacheKey) obj;
189
190                                boolean firstPartEqual = false;
191
192                                if (this.componentOne == null) {
193                                        firstPartEqual = (another.componentOne == null);
194                                } else {
195                                        firstPartEqual = this.componentOne
196                                                        .equals(another.componentOne);
197                                }
198
199                                return (firstPartEqual && this.componentTwo
200                                                .equals(another.componentTwo));
201                        }
202
203                        return false;
204                }
205
206                /*
207                 * (non-Javadoc)
208                 *
209                 * @see java.lang.Object#hashCode()
210                 */
211                public int hashCode() {
212                        return this.hashCode;
213                }
214        }
215
216        /**
217         * Marker for character set converter not being available (not written,
218         * multibyte, etc) Used to prevent multiple instantiation requests.
219         */
220        private static final Object CHARSET_CONVERTER_NOT_AVAILABLE_MARKER = new Object();
221
222        /**
223         * The mapping between MySQL charset names and Java charset names.
224         * Initialized by loadCharacterSetMapping()
225         */
226        public static Map<?, ?> charsetMap;
227
228        /** Default logger class name */
229        protected static final String DEFAULT_LOGGER_CLASS = "com.mysql.jdbc.log.StandardLogger";
230
231        private final static int HISTOGRAM_BUCKETS = 20;
232
233        /** Logger instance name */
234        private static final String LOGGER_INSTANCE_NAME = "MySQL";
235
236        /**
237         * Map mysql transaction isolation level name to
238         * java.sql.Connection.TRANSACTION_XXX
239         */
240        private static Map<String, Integer> mapTransIsolationNameToValue = null;
241
242        /** Null logger shared by all connections at startup */
243        private static final Log NULL_LOGGER = new NullLogger(LOGGER_INSTANCE_NAME);
244
245        protected static Map<?, ?> roundRobinStatsMap;
246
247        private static final Map<String, Map<Long, String>> serverCollationByUrl = new HashMap<String, Map<Long,String>>();
248
249        /**
250         * Map for Java charsets of user defined charsets. We can't map them statically, because
251         * they can be different for different server URLs.
252         */
253        private static final Map<String, Map<Integer, String>> serverJavaCharsetByUrl = new HashMap<String, Map<Integer,String>>();
254        /**
255         * Map for user defined charsets. We can't map them statically, because
256         * they can be different for different server URLs.
257         */
258        private static final Map<String, Map<Integer, String>> serverCustomCharsetByUrl = new HashMap<String, Map<Integer,String>>();
259        /**
260         * Map for user defined charsets. We can't map them statically, because
261         * they can be different for different server URLs.
262         */
263        private static final Map<String, Map<String, Integer>> serverCustomMblenByUrl = new HashMap<String, Map<String, Integer>>();
264       
265        private static final Map<String, Map<String, String>> serverConfigByUrl = new HashMap<String, Map<String,String>>();
266
267        private long queryTimeCount;
268        private double queryTimeSum;
269        private double queryTimeSumSquares;
270        private double queryTimeMean;
271       
272        private transient Timer cancelTimer;
273       
274        private List<Extension> connectionLifecycleInterceptors;
275       
276        private static final Constructor<?> JDBC_4_CONNECTION_CTOR;
277       
278        private static final int DEFAULT_RESULT_SET_TYPE = ResultSet.TYPE_FORWARD_ONLY;
279       
280        private static final int DEFAULT_RESULT_SET_CONCURRENCY = ResultSet.CONCUR_READ_ONLY;
281       
282        static {
283                mapTransIsolationNameToValue = new HashMap<String, Integer>(8);
284                mapTransIsolationNameToValue.put("READ-UNCOMMITED", TRANSACTION_READ_UNCOMMITTED);
285                mapTransIsolationNameToValue.put("READ-UNCOMMITTED", TRANSACTION_READ_UNCOMMITTED);
286                mapTransIsolationNameToValue.put("READ-COMMITTED", TRANSACTION_READ_COMMITTED);
287                mapTransIsolationNameToValue.put("REPEATABLE-READ", TRANSACTION_REPEATABLE_READ);
288                mapTransIsolationNameToValue.put("SERIALIZABLE", TRANSACTION_SERIALIZABLE);
289
290                if (Util.isJdbc4()) {
291                        try {
292                                JDBC_4_CONNECTION_CTOR = Class.forName(
293                                                "com.mysql.jdbc.JDBC4Connection").getConstructor(
294                                                new Class[] { String.class, Integer.TYPE,
295                                                                Properties.class, String.class, String.class });
296                        } catch (SecurityException e) {
297                                throw new RuntimeException(e);
298                        } catch (NoSuchMethodException e) {
299                                throw new RuntimeException(e);
300                        } catch (ClassNotFoundException e) {
301                                throw new RuntimeException(e);
302                        }
303                } else {
304                        JDBC_4_CONNECTION_CTOR = null;
305                }
306        }
307
308        protected static SQLException appendMessageToException(SQLException sqlEx,
309                        String messageToAppend, ExceptionInterceptor interceptor) {
310                String origMessage = sqlEx.getMessage();
311                String sqlState = sqlEx.getSQLState();
312                int vendorErrorCode = sqlEx.getErrorCode();
313
314                StringBuffer messageBuf = new StringBuffer(origMessage.length()
315                                + messageToAppend.length());
316                messageBuf.append(origMessage);
317                messageBuf.append(messageToAppend);
318
319                SQLException sqlExceptionWithNewMessage = SQLError.createSQLException(messageBuf
320                                .toString(), sqlState, vendorErrorCode, interceptor);
321
322                //
323                // Try and maintain the original stack trace,
324                // only works on JDK-1.4 and newer
325                //
326
327                try {
328                        // Have to do this with reflection, otherwise older JVMs croak
329                        Method getStackTraceMethod = null;
330                        Method setStackTraceMethod = null;
331                        Object theStackTraceAsObject = null;
332
333                        Class<?> stackTraceElementClass = Class.forName("java.lang.StackTraceElement");
334                        Class<?> stackTraceElementArrayClass = Array.newInstance(
335                                        stackTraceElementClass, new int[] { 0 }).getClass();
336
337                        getStackTraceMethod = Throwable.class.getMethod("getStackTrace",
338                                        new Class[] {});
339
340                        setStackTraceMethod = Throwable.class.getMethod("setStackTrace",
341                                        new Class[] { stackTraceElementArrayClass });
342
343                        if (getStackTraceMethod != null && setStackTraceMethod != null) {
344                                theStackTraceAsObject = getStackTraceMethod.invoke(sqlEx,
345                                                new Object[0]);
346                                setStackTraceMethod.invoke(sqlExceptionWithNewMessage,
347                                                new Object[] { theStackTraceAsObject });
348                        }
349                } catch (NoClassDefFoundError noClassDefFound) {
350
351                } catch (NoSuchMethodException noSuchMethodEx) {
352
353                } catch (Throwable catchAll) {
354
355                }
356
357                return sqlExceptionWithNewMessage;
358        }
359
360        public synchronized Timer getCancelTimer() {
361                if (cancelTimer == null) {
362                        boolean createdNamedTimer = false;
363                       
364                        // Use reflection magic to try this on JDK's 1.5 and newer, fallback to non-named
365                        // timer on older VMs.
366                        try {
367                                Constructor<Timer> ctr = Timer.class.getConstructor(new Class[] {String.class, Boolean.TYPE});
368                               
369                                cancelTimer = ctr.newInstance(new Object[] { "MySQL Statement Cancellation Timer", Boolean.TRUE});
370                                createdNamedTimer = true;
371                        } catch (Throwable t) {
372                                createdNamedTimer = false;
373                        }
374                       
375                        if (!createdNamedTimer) {
376                                cancelTimer = new Timer(true);
377                        }
378                }
379               
380                return cancelTimer;
381        }
382
383       
384        /**
385         * Creates a connection instance -- We need to provide factory-style methods
386         * so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise
387         * the class verifier complains when it tries to load JDBC4-only interface
388         * classes that are present in JDBC4 method signatures.
389         */
390
391        protected static Connection getInstance(String hostToConnectTo,
392                        int portToConnectTo, Properties info, String databaseToConnectTo,
393                        String url) throws SQLException {
394                if (!Util.isJdbc4()) {
395                        return new ConnectionImpl(hostToConnectTo, portToConnectTo, info,
396                                        databaseToConnectTo, url);
397                }
398
399                return (Connection) Util.handleNewInstance(JDBC_4_CONNECTION_CTOR,
400                                new Object[] {
401                                                        hostToConnectTo, Integer.valueOf(portToConnectTo), info,
402                                                        databaseToConnectTo, url }, null);
403        }
404
405        private static final Random random = new Random();
406       
407        /**
408         *
409         * @param url
410         * @param hostList
411         * @return
412         */
413        protected static synchronized int getNextRoundRobinHostIndex(String url,
414                        List<?> hostList) {
415                // we really do "random" here, because you don't get even
416                // distribution when this is coupled with connection pools
417               
418                int indexRange = hostList.size();
419               
420                int index = random.nextInt(indexRange);
421               
422                return index;
423        }
424
425        private static boolean nullSafeCompare(String s1, String s2) {
426                if (s1 == null && s2 == null) {
427                        return true;
428                }
429
430                if (s1 == null && s2 != null) {
431                        return false;
432                }
433
434                return s1 != null && s1.equals(s2);
435        }
436
437        /** Are we in autoCommit mode? */
438        private boolean autoCommit = true;
439
440        /** A cache of SQL to parsed prepared statement parameters. */
441        private CacheAdapter<String, ParseInfo> cachedPreparedStatementParams;
442
443        /**
444         * For servers > 4.1.0, what character set is the metadata returned in?
445         */
446        private String characterSetMetadata = null;
447
448        /**
449         * The character set we want results and result metadata returned in (null ==
450         * results in any charset, metadata in UTF-8).
451         */
452        private String characterSetResultsOnServer = null;
453
454        /**
455         * Holds cached mappings to charset converters to avoid static
456         * synchronization and at the same time save memory (each charset converter
457         * takes approx 65K of static data).
458         */
459        private Map<String, Object> charsetConverterMap = new HashMap<String, Object>(CharsetMapping
460                        .getNumberOfCharsetsConfigured());
461
462        /** The point in time when this connection was created */
463        private long connectionCreationTimeMillis = 0;
464
465        /** ID used when profiling */
466        private long connectionId;
467
468        /** The database we're currently using (called Catalog in JDBC terms). */
469        private String database = null;
470
471        /** Internal DBMD to use for various database-version specific features */
472        private DatabaseMetaData dbmd = null;
473
474        private TimeZone defaultTimeZone;
475
476        /** The event sink to use for profiling */
477        private ProfilerEventHandler eventSink;
478
479        /** Why was this connection implicitly closed, if known? (for diagnostics) */
480        private Throwable forceClosedReason;
481
482        /** Does the server suuport isolation levels? */
483        private boolean hasIsolationLevels = false;
484
485        /** Does this version of MySQL support quoted identifiers? */
486        private boolean hasQuotedIdentifiers = false;
487
488        /** The hostname we're connected to */
489        private String host = null;
490       
491        /**
492         * We need this 'bootstrapped', because 4.1 and newer will send fields back
493         * with this even before we fill this dynamically from the server.
494         */
495        public Map<Integer, String> indexToJavaCharset = new HashMap<Integer, String>();
496
497        public Map<Integer, String> indexToCustomMysqlCharset = new HashMap<Integer, String>();
498
499        private Map<String, Integer> mysqlCharsetToCustomMblen = new HashMap<String, Integer>();
500       
501        /** The I/O abstraction interface (network conn to MySQL server */
502        private transient MysqlIO io = null;
503       
504        private boolean isClientTzUTC = false;
505
506        /** Has this connection been closed? */
507        private boolean isClosed = true;
508
509        /** Is this connection associated with a global tx? */
510        private boolean isInGlobalTx = false;
511
512        /** Is this connection running inside a JDK-1.3 VM? */
513        private boolean isRunningOnJDK13 = false;
514
515        /** isolation level */
516        private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED;
517
518        private boolean isServerTzUTC = false;
519
520        /** When did the last query finish? */
521        private long lastQueryFinishedTime = 0;
522
523        /** The logger we're going to use */
524        private transient Log log = NULL_LOGGER;
525
526        /**
527         * If gathering metrics, what was the execution time of the longest query so
528         * far ?
529         */
530        private long longestQueryTimeMs = 0;
531
532        /** Is the server configured to use lower-case table names only? */
533        private boolean lowerCaseTableNames = false;
534
535        /** When did the master fail? */
536//      private long masterFailTimeMillis = 0L;
537
538        private long maximumNumberTablesAccessed = 0;
539
540        /** Has the max-rows setting been changed from the default? */
541        private boolean maxRowsChanged = false;
542
543        /** When was the last time we reported metrics? */
544        private long metricsLastReportedMs;
545
546        private long minimumNumberTablesAccessed = Long.MAX_VALUE;
547
548        /** The JDBC URL we're using */
549        private String myURL = null;
550
551        /** Does this connection need to be tested? */
552        private boolean needsPing = false;
553
554        private int netBufferLength = 16384;
555
556        private boolean noBackslashEscapes = false;
557
558        private long numberOfPreparedExecutes = 0;
559
560        private long numberOfPrepares = 0;
561
562        private long numberOfQueriesIssued = 0;
563
564        private long numberOfResultSetsCreated = 0;
565
566        private long[] numTablesMetricsHistBreakpoints;
567
568        private int[] numTablesMetricsHistCounts;
569
570        private long[] oldHistBreakpoints = null;
571
572        private int[] oldHistCounts = null;
573
574        /** A map of currently open statements */
575        private Map<Statement, Statement> openStatements;
576
577        private LRUCache parsedCallableStatementCache;
578
579        private boolean parserKnowsUnicode = false;
580
581        /** The password we used */
582        private String password = null;
583
584        private long[] perfMetricsHistBreakpoints;
585
586        private int[] perfMetricsHistCounts;
587
588        /** Point of origin where this Connection was created */
589        private Throwable pointOfOrigin;
590
591        /** The port number we're connected to (defaults to 3306) */
592        private int port = 3306;
593
594        /** Properties for this connection specified by user */
595        protected Properties props = null;
596
597        /** Should we retrieve 'info' messages from the server? */
598        private boolean readInfoMsg = false;
599
600        /** Are we in read-only mode? */
601        private boolean readOnly = false;
602
603        /** Cache of ResultSet metadata */
604        protected LRUCache resultSetMetadataCache;
605       
606        /** The timezone of the server */
607        private TimeZone serverTimezoneTZ = null;
608
609        /** The map of server variables that we retrieve at connection init. */
610        private Map<String, String> serverVariables = null;
611
612        private long shortestQueryTimeMs = Long.MAX_VALUE;
613
614        /** A map of statements that have had setMaxRows() called on them */
615        private Map<Statement, Statement> statementsUsingMaxRows;
616
617        private double totalQueryTimeMs = 0;
618
619        /** Are transactions supported by the MySQL server we are connected to? */
620        private boolean transactionsSupported = false;
621
622        /**
623         * The type map for UDTs (not implemented, but used by some third-party
624         * vendors, most notably IBM WebSphere)
625         */
626        private Map<String,Class<?>> typeMap;
627
628        /** Has ANSI_QUOTES been enabled on the server? */
629        private boolean useAnsiQuotes = false;
630
631        /** The user we're connected as */
632        private String user = null;
633       
634        /**
635         * Should we use server-side prepared statements? (auto-detected, but can be
636         * disabled by user)
637         */
638        private boolean useServerPreparedStmts = false;
639
640        private LRUCache serverSideStatementCheckCache;
641        private LRUCache serverSideStatementCache;
642        private Calendar sessionCalendar;
643       
644        private Calendar utcCalendar;
645       
646        private String origHostToConnectTo;
647
648        // we don't want to be able to publicly clone this...
649       
650        private int origPortToConnectTo;
651
652        private String origDatabaseToConnectTo;
653
654        private String errorMessageEncoding = "Cp1252"; // to begin with, changes after we talk to the server
655       
656        private boolean usePlatformCharsetConverters;
657       
658        /*
659         * For testing failover scenarios
660         */
661        private boolean hasTriedMasterFlag = false;
662
663        /**
664         * The comment (if any) that we'll prepend to all statements
665         * sent to the server (to show up in "SHOW PROCESSLIST")
666         */
667        private String statementComment = null;
668
669        private boolean storesLowerCaseTableName;
670
671        private List<StatementInterceptorV2> statementInterceptors;
672       
673        /**
674         * If a CharsetEncoder is required for escaping. Needed for SJIS and related
675         * problems with \u00A5.
676         */
677        private boolean requiresEscapingEncoder;
678
679        private String hostPortPair;
680       
681        /**'
682         * For the delegate only
683         */
684        protected ConnectionImpl() {   
685        }
686       
687        /**
688         * Creates a connection to a MySQL Server.
689         *
690         * @param hostToConnectTo
691         *            the hostname of the database server
692         * @param portToConnectTo
693         *            the port number the server is listening on
694         * @param info
695         *            a Properties[] list holding the user and password
696         * @param databaseToConnectTo
697         *            the database to connect to
698         * @param url
699         *            the URL of the connection
700         * @param d
701         *            the Driver instantation of the connection
702         * @exception SQLException
703         *                if a database access error occurs
704         */
705        protected ConnectionImpl(String hostToConnectTo, int portToConnectTo, Properties info,
706                        String databaseToConnectTo, String url)
707                        throws SQLException {
708       
709                this.connectionCreationTimeMillis = System.currentTimeMillis();
710                this.pointOfOrigin = new Throwable();
711
712      if (databaseToConnectTo == null) {
713                        databaseToConnectTo = "";
714                }
715
716                // Stash away for later, used to clone this connection for Statement.cancel
717                // and Statement.setQueryTimeout().
718                //
719               
720                this.origHostToConnectTo = hostToConnectTo;
721                this.origPortToConnectTo = portToConnectTo;
722                this.origDatabaseToConnectTo = databaseToConnectTo;
723
724                try {
725                        Blob.class.getMethod("truncate", new Class[] {Long.TYPE});
726                       
727                        this.isRunningOnJDK13 = false;
728                } catch (NoSuchMethodException nsme) {
729                        this.isRunningOnJDK13 = true;
730                }
731               
732                this.sessionCalendar = new GregorianCalendar();
733                this.utcCalendar = new GregorianCalendar();
734                this.utcCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
735               
736                //
737                // Normally, this code would be in initializeDriverProperties,
738                // but we need to do this as early as possible, so we can start
739                // logging to the 'correct' place as early as possible...this.log
740                // points to 'NullLogger' for every connection at startup to avoid
741                // NPEs and the overhead of checking for NULL at every logging call.
742                //
743                // We will reset this to the configured logger during properties
744                // initialization.
745                //
746                this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME, getExceptionInterceptor());
747
748                // We store this per-connection, due to static synchronization
749                // issues in Java's built-in TimeZone class...
750                this.defaultTimeZone = Util.getDefaultTimeZone();
751               
752                if ("GMT".equalsIgnoreCase(this.defaultTimeZone.getID())) {
753                        this.isClientTzUTC = true;
754                } else {
755                        this.isClientTzUTC = false;
756                }
757
758                this.openStatements = new HashMap<Statement, Statement>();
759               
760                if (NonRegisteringDriver.isHostPropertiesList(hostToConnectTo)) {
761                        Properties hostSpecificProps = NonRegisteringDriver.expandHostKeyValues(hostToConnectTo);
762                       
763                        Enumeration<?> propertyNames = hostSpecificProps.propertyNames();
764                       
765                        while (propertyNames.hasMoreElements()) {
766                                String propertyName = propertyNames.nextElement().toString();
767                                String propertyValue = hostSpecificProps.getProperty(propertyName);
768                               
769                                info.setProperty(propertyName, propertyValue);
770                        }
771                } else {
772               
773                        if (hostToConnectTo == null) {
774                                this.host = "localhost";
775                                this.hostPortPair = this.host + ":" + portToConnectTo;
776                        } else {
777                                this.host = hostToConnectTo;
778                               
779                                if (hostToConnectTo.indexOf(":") == -1) {
780                                        this.hostPortPair = this.host + ":" + portToConnectTo;
781                                } else {
782                                        this.hostPortPair = this.host;
783                                }
784                        }
785                }
786
787                this.port = portToConnectTo;
788
789                this.database = databaseToConnectTo;
790                this.myURL = url;
791                this.user = info.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY);
792                this.password = info
793                                .getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY);
794
795                if ((this.user == null) || this.user.equals("")) {
796                        this.user = "";
797                }
798
799                if (this.password == null) {
800                        this.password = "";
801                }
802
803                this.props = info;
804               
805               
806               
807                initializeDriverProperties(info);
808               
809               
810                try {
811                        this.dbmd = getMetaData(false, false);
812                        initializeSafeStatementInterceptors();
813                        createNewIO(false);
814                        unSafeStatementInterceptors();
815                } catch (SQLException ex) {
816                        cleanup(ex);
817
818                        // don't clobber SQL exceptions
819                        throw ex;
820                } catch (Exception ex) {
821                        cleanup(ex);
822
823                        StringBuffer mesg = new StringBuffer(128);
824
825                        if (!getParanoid()) {
826                                mesg.append("Cannot connect to MySQL server on ");
827                                mesg.append(this.host);
828                                mesg.append(":");
829                                mesg.append(this.port);
830                                mesg.append(".\n\n");
831                                mesg.append("Make sure that there is a MySQL server ");
832                                mesg.append("running on the machine/port you are trying ");
833                                mesg
834                                                .append("to connect to and that the machine this software is "
835                                                                + "running on ");
836                                mesg.append("is able to connect to this host/port "
837                                                + "(i.e. not firewalled). ");
838                                mesg
839                                                .append("Also make sure that the server has not been started "
840                                                                + "with the --skip-networking ");
841                                mesg.append("flag.\n\n");
842                        } else {
843                                mesg.append("Unable to connect to database.");
844                        }
845
846                        SQLException sqlEx = SQLError.createSQLException(mesg.toString(),
847                                        SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, getExceptionInterceptor());
848                       
849                        sqlEx.initCause(ex);
850                       
851                        throw sqlEx;
852                }
853               
854                NonRegisteringDriver.trackConnection(this);
855        }
856
857    public void unSafeStatementInterceptors() throws SQLException {
858       
859        ArrayList<StatementInterceptorV2> unSafedStatementInterceptors = new ArrayList<StatementInterceptorV2>(this.statementInterceptors.size());
860
861        for (int i = 0; i < this.statementInterceptors.size(); i++) {
862                NoSubInterceptorWrapper wrappedInterceptor = (NoSubInterceptorWrapper) this.statementInterceptors.get(i);
863               
864                unSafedStatementInterceptors.add(wrappedInterceptor.getUnderlyingInterceptor());
865        }
866       
867        this.statementInterceptors = unSafedStatementInterceptors;
868       
869        if (this.io != null) {
870                this.io.setStatementInterceptors(this.statementInterceptors);
871        }
872        }
873   
874    public void initializeSafeStatementInterceptors() throws SQLException {
875        this.isClosed = false;
876       
877        List<Extension> unwrappedInterceptors = Util.loadExtensions(this, this.props, 
878                                getStatementInterceptors(),
879                                "MysqlIo.BadStatementInterceptor", getExceptionInterceptor());
880       
881        this.statementInterceptors = new ArrayList<StatementInterceptorV2>(unwrappedInterceptors.size());
882
883        for (int i = 0; i < unwrappedInterceptors.size(); i++) {
884                Extension interceptor = unwrappedInterceptors.get(i);
885               
886                // adapt older versions of statement interceptors, handle the case where something wants v2
887                // functionality but wants to run with an older driver
888                if (interceptor instanceof StatementInterceptor) {
889                        if (ReflectiveStatementInterceptorAdapter.getV2PostProcessMethod(interceptor.getClass()) != null) {
890                                this.statementInterceptors.add(new NoSubInterceptorWrapper(new ReflectiveStatementInterceptorAdapter((StatementInterceptor) interceptor)));
891                        } else {
892                                this.statementInterceptors.add(new NoSubInterceptorWrapper(new V1toV2StatementInterceptorAdapter((StatementInterceptor) interceptor)));
893                        }
894                } else {
895                        this.statementInterceptors.add(new NoSubInterceptorWrapper((StatementInterceptorV2)interceptor));
896                }
897        }
898       
899       
900    }
901   
902    public List<StatementInterceptorV2> getStatementInterceptorsInstances() {
903        return this.statementInterceptors;
904    }
905   
906        private void addToHistogram(int[] histogramCounts,
907                        long[] histogramBreakpoints, long value, int numberOfTimes,
908                        long currentLowerBound, long currentUpperBound) {
909                if (histogramCounts == null) {
910                        createInitialHistogram(histogramBreakpoints,
911                                        currentLowerBound, currentUpperBound);
912                } else {
913                        for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
914                                if (histogramBreakpoints[i] >= value) {
915                                        histogramCounts[i] += numberOfTimes;
916       
917                                        break;
918                                }
919                        }
920                }
921        }
922
923        private void addToPerformanceHistogram(long value, int numberOfTimes) {
924                checkAndCreatePerformanceHistogram();
925
926                addToHistogram(this.perfMetricsHistCounts,
927                                this.perfMetricsHistBreakpoints, value, numberOfTimes,
928                                this.shortestQueryTimeMs == Long.MAX_VALUE ? 0
929                                                : this.shortestQueryTimeMs, this.longestQueryTimeMs);
930        }
931
932        private void addToTablesAccessedHistogram(long value, int numberOfTimes) {
933                checkAndCreateTablesAccessedHistogram();
934
935                addToHistogram(this.numTablesMetricsHistCounts,
936                                this.numTablesMetricsHistBreakpoints, value, numberOfTimes,
937                                this.minimumNumberTablesAccessed == Long.MAX_VALUE ? 0
938                                                : this.minimumNumberTablesAccessed,
939                                this.maximumNumberTablesAccessed);
940        }
941       
942        /**
943         * Builds the map needed for 4.1.0 and newer servers that maps field-level
944         * charset/collation info to a java character encoding name.
945         *
946         * @throws SQLException
947         *             DOCUMENT ME!
948         */
949        private void buildCollationMapping() throws SQLException {
950
951                HashMap<Integer, String> javaCharset = null;
952
953                if (versionMeetsMinimum(4, 1, 0)) {
954
955                        TreeMap<Long, String> sortedCollationMap = null;
956                        HashMap<Integer, String> customCharset = null;
957                        HashMap<String, Integer> customMblen = null;
958
959                        if (getCacheServerConfiguration()) {
960                                synchronized (serverConfigByUrl) {
961                                        sortedCollationMap = (TreeMap<Long, String>) serverCollationByUrl.get(getURL());
962                                        javaCharset = (HashMap<Integer, String>) serverJavaCharsetByUrl.get(getURL());
963                                        customCharset = (HashMap<Integer, String>) serverCustomCharsetByUrl.get(getURL());
964                                        customMblen = (HashMap<String, Integer>) serverCustomMblenByUrl.get(getURL());
965                                }
966                        }
967
968                        java.sql.Statement stmt = null;
969                        java.sql.ResultSet results = null;
970
971                        try {
972                                if (sortedCollationMap == null) {
973                                        sortedCollationMap = new TreeMap<Long, String>();
974                                        javaCharset = new HashMap<Integer, String>();
975                                        customCharset = new HashMap<Integer, String>();
976                                        customMblen = new HashMap<String, Integer>();
977
978                                        stmt = getMetadataSafeStatement();
979
980                                        results = stmt.executeQuery("SHOW COLLATION");
981                                        if (versionMeetsMinimum(5, 0, 0)) {
982                                                Util.resultSetToMap(sortedCollationMap, results, 3, 2);
983                                        } else {
984                                                while (results.next()) {
985                                                        sortedCollationMap.put(results.getLong(3), results.getString(2));
986                                                }
987                                        }
988
989                                        for (Iterator<Map.Entry<Long, String>> indexIter = sortedCollationMap.entrySet().iterator(); indexIter.hasNext();) {
990                                                Map.Entry<Long, String> indexEntry = indexIter.next();
991
992                                                int collationIndex = indexEntry.getKey().intValue();
993                                                String charsetName = indexEntry.getValue();
994
995                                                javaCharset.put(collationIndex, getJavaEncodingForMysqlEncoding(charsetName));
996
997                                                // if no static map for charsetIndex
998                                                // or server has a different mapping then our static map,
999                                                // adding it to custom map
1000                                                if (collationIndex >= CharsetMapping.MAP_SIZE ||
1001                                                        !charsetName.equals(CharsetMapping.STATIC_INDEX_TO_MYSQL_CHARSET_MAP.get(collationIndex))) {
1002                                                        customCharset.put(collationIndex, charsetName);
1003                                                }
1004
1005                                                // if no static map for charsetName adding to custom map
1006                                                if (!CharsetMapping.STATIC_CHARSET_TO_NUM_BYTES_MAP.containsKey(charsetName) &&
1007                                                        !CharsetMapping.STATIC_4_0_CHARSET_TO_NUM_BYTES_MAP.containsKey(charsetName)) {
1008                                                        customMblen.put(charsetName, null);
1009                                                }
1010                                        }
1011                                       
1012                                        // if there is a number of custom charsets we should execute SHOW CHARACTER SET to know theirs mblen
1013                                        if (customMblen.size() > 0) {
1014                                                results = stmt.executeQuery("SHOW CHARACTER SET");
1015                                                while (results.next()) {
1016                                                        String charsetName = results.getString("Charset");
1017                                                        if (customMblen.containsKey(charsetName)) {
1018                                                                customMblen.put(charsetName, results.getInt("Maxlen"));
1019                                                        }
1020                                                }
1021                                        }
1022
1023                                        if (getCacheServerConfiguration()) {
1024                                                synchronized (serverConfigByUrl) {
1025                                                        serverCollationByUrl.put(getURL(), sortedCollationMap);
1026                                                        serverJavaCharsetByUrl.put(getURL(), javaCharset);
1027                                                        serverCustomCharsetByUrl.put(getURL(), customCharset);
1028                                                        serverCustomMblenByUrl.put(getURL(), customMblen);
1029                                                }
1030                                        }
1031
1032                                }
1033
1034                                this.indexToJavaCharset = Collections.unmodifiableMap(javaCharset);
1035                                this.indexToCustomMysqlCharset = Collections.unmodifiableMap(customCharset);
1036                                this.mysqlCharsetToCustomMblen = Collections.unmodifiableMap(customMblen);
1037                               
1038                        } catch (SQLException ex) {
1039                                throw ex;
1040                        } catch (RuntimeException ex) {
1041                                SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
1042                                sqlEx.initCause(ex);
1043                                throw sqlEx;
1044                        } finally {
1045                                if (results != null) {
1046                                        try {
1047                                                results.close();
1048                                        } catch (java.sql.SQLException sqlE) {
1049                                                // ignore
1050                                        }
1051                                }
1052
1053                                if (stmt != null) {
1054                                        try {
1055                                                stmt.close();
1056                                        } catch (java.sql.SQLException sqlE) {
1057                                                // ignore
1058                                        }
1059                                }
1060                        }
1061                } else {
1062                        javaCharset = new HashMap<Integer, String>();
1063                        for (int i = 0; i < CharsetMapping.INDEX_TO_CHARSET.length; i++) {
1064                                javaCharset.put(i, CharsetMapping.INDEX_TO_CHARSET[i]);
1065                        }
1066                        this.indexToJavaCharset = Collections.unmodifiableMap(javaCharset);
1067                }
1068        }
1069
1070        public String getJavaEncodingForMysqlEncoding(String mysqlEncoding) throws SQLException {
1071               
1072                if (versionMeetsMinimum(4, 1, 0) && "latin1".equalsIgnoreCase(mysqlEncoding)) {
1073                        return "Cp1252";
1074                }
1075               
1076                return CharsetMapping.MYSQL_TO_JAVA_CHARSET_MAP.get(mysqlEncoding);
1077        }
1078
1079        private boolean canHandleAsServerPreparedStatement(String sql) 
1080                throws SQLException {
1081                if (sql == null || sql.length() == 0) {
1082                        return true;
1083                }
1084
1085                if (!this.useServerPreparedStmts) {
1086                        return false;
1087                }
1088               
1089                if (getCachePreparedStatements()) {
1090                        synchronized (this.serverSideStatementCheckCache) {
1091                                Boolean flag = (Boolean)this.serverSideStatementCheckCache.get(sql);
1092                               
1093                                if (flag != null) {
1094                                        return flag.booleanValue();
1095                                }
1096                                       
1097                                boolean canHandle = canHandleAsServerPreparedStatementNoCache(sql);
1098                               
1099                                if (sql.length() < getPreparedStatementCacheSqlLimit()) {
1100                                        this.serverSideStatementCheckCache.put(sql, 
1101                                                        canHandle ? Boolean.TRUE : Boolean.FALSE);
1102                                }
1103                                       
1104                                return canHandle;
1105                        }
1106                }
1107               
1108                return canHandleAsServerPreparedStatementNoCache(sql);
1109        }
1110
1111        private boolean canHandleAsServerPreparedStatementNoCache(String sql) 
1112                throws SQLException {
1113               
1114                // Can't use server-side prepare for CALL
1115                if (StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "CALL")) {
1116                        return false;
1117                }
1118               
1119                boolean canHandleAsStatement = true;
1120               
1121                if (!versionMeetsMinimum(5, 0, 7) && 
1122                                (StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "SELECT")
1123                                || StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
1124                                                "DELETE")
1125                                || StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
1126                                                "INSERT")
1127                                || StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
1128                                                "UPDATE")
1129                                || StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
1130                                                "REPLACE"))) {
1131
1132                        // check for limit ?[,?]
1133
1134                        /*
1135                         * The grammar for this (from the server) is: ULONG_NUM | ULONG_NUM
1136                         * ',' ULONG_NUM | ULONG_NUM OFFSET_SYM ULONG_NUM
1137                         */
1138
1139                        int currentPos = 0;
1140                        int statementLength = sql.length();
1141                        int lastPosToLook = statementLength - 7; // "LIMIT ".length()
1142                        boolean allowBackslashEscapes = !this.noBackslashEscapes;
1143                        char quoteChar = this.useAnsiQuotes ? '"' : '\'';
1144                        boolean foundLimitWithPlaceholder = false;
1145
1146                        while (currentPos < lastPosToLook) {
1147                                int limitStart = StringUtils.indexOfIgnoreCaseRespectQuotes(
1148                                                currentPos, sql, "LIMIT ", quoteChar,
1149                                                allowBackslashEscapes);
1150
1151                                if (limitStart == -1) {
1152                                        break;
1153                                }
1154
1155                                currentPos = limitStart + 7;
1156
1157                                while (currentPos < statementLength) {
1158                                        char c = sql.charAt(currentPos);
1159
1160                                        //
1161                                        // Have we reached the end
1162                                        // of what can be in a LIMIT clause?
1163                                        //
1164
1165                                        if (!Character.isDigit(c) && !Character.isWhitespace(c)
1166                                                        && c != ',' && c != '?') {
1167                                                break;
1168                                        }
1169
1170                                        if (c == '?') {
1171                                                foundLimitWithPlaceholder = true;
1172                                                break;
1173                                        }
1174
1175                                        currentPos++;
1176                                }
1177                        }
1178
1179                        canHandleAsStatement = !foundLimitWithPlaceholder;
1180                } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "CREATE TABLE")) {
1181                        canHandleAsStatement = false;
1182                } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "DO")) {
1183                        canHandleAsStatement = false;
1184                } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "SET")) {
1185                        canHandleAsStatement = false;
1186                }
1187
1188               
1189               
1190                return canHandleAsStatement;
1191        }
1192
1193        /**
1194         * Changes the user on this connection by performing a re-authentication. If
1195         * authentication fails, the connection will remain under the context of the
1196         * current user.
1197         *
1198         * @param userName
1199         *            the username to authenticate with
1200         * @param newPassword
1201         *            the password to authenticate with
1202         * @throws SQLException
1203         *             if authentication fails, or some other error occurs while
1204         *             performing the command.
1205         */
1206        public synchronized void changeUser(String userName, String newPassword)
1207                        throws SQLException {
1208                checkClosed();
1209               
1210                if ((userName == null) || userName.equals("")) {
1211                        userName = "";
1212                }
1213
1214                if (newPassword == null) {
1215                        newPassword = "";
1216                }
1217
1218                this.io.changeUser(userName, newPassword, this.database);
1219                this.user = userName;
1220                this.password = newPassword;
1221
1222                if (versionMeetsMinimum(4, 1, 0)) {
1223                        configureClientCharacterSet(true);
1224                }
1225               
1226                setSessionVariables();
1227               
1228                setupServerForTruncationChecks();
1229        }
1230
1231        private boolean characterSetNamesMatches(String mysqlEncodingName) {
1232                // set names is equivalent to character_set_client ..._results and ..._connection,
1233                // but we set _results later, so don't check it here.
1234                return (mysqlEncodingName != null && 
1235                                mysqlEncodingName.equalsIgnoreCase(this.serverVariables.get("character_set_client")) &&
1236                                mysqlEncodingName.equalsIgnoreCase(this.serverVariables.get("character_set_connection")));
1237        }
1238
1239        private void checkAndCreatePerformanceHistogram() {
1240                if (this.perfMetricsHistCounts == null) {
1241                        this.perfMetricsHistCounts = new int[HISTOGRAM_BUCKETS];
1242                }
1243
1244                if (this.perfMetricsHistBreakpoints == null) {
1245                        this.perfMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS];
1246                }
1247        }
1248
1249        private void checkAndCreateTablesAccessedHistogram() {
1250                if (this.numTablesMetricsHistCounts == null) {
1251                        this.numTablesMetricsHistCounts = new int[HISTOGRAM_BUCKETS];
1252                }
1253
1254                if (this.numTablesMetricsHistBreakpoints == null) {
1255                        this.numTablesMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS];
1256                }
1257        }
1258
1259        public void checkClosed() throws SQLException {
1260                if (this.isClosed) {
1261                        throwConnectionClosedException();
1262                }
1263        }
1264
1265        public void throwConnectionClosedException() throws SQLException {
1266                StringBuffer messageBuf = new StringBuffer(
1267                                "No operations allowed after connection closed.");
1268
1269                SQLException ex = SQLError.createSQLException(messageBuf.toString(),
1270                                SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor());
1271               
1272                if (this.forceClosedReason != null) {
1273                        ex.initCause(this.forceClosedReason);
1274                }
1275
1276                throw ex;
1277        }
1278
1279        /**
1280         * If useUnicode flag is set and explicit client character encoding isn't
1281         * specified then assign encoding from server if any.
1282         *
1283         * @throws SQLException
1284         *             DOCUMENT ME!
1285         */
1286        private void checkServerEncoding() throws SQLException {
1287                if (getUseUnicode() && (getEncoding() != null)) {
1288                        // spec'd by client, don't map
1289                        return;
1290                }
1291
1292                String serverEncoding = this.serverVariables.get("character_set");
1293
1294                if (serverEncoding == null) {
1295                        // must be 4.1.1 or newer?
1296                        serverEncoding = this.serverVariables.get("character_set_server");
1297                }
1298
1299                String mappedServerEncoding = null;
1300
1301                if (serverEncoding != null) {
1302                        try {
1303                                mappedServerEncoding = getJavaEncodingForMysqlEncoding(serverEncoding
1304                                                        .toUpperCase(Locale.ENGLISH));
1305                        } catch (SQLException ex) {
1306                                throw ex;
1307                        } catch (RuntimeException ex) {
1308                                SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
1309                                sqlEx.initCause(ex);
1310                                throw sqlEx;
1311                        }
1312                }
1313
1314                //
1315                // First check if we can do the encoding ourselves
1316                //
1317                if (!getUseUnicode() && (mappedServerEncoding != null)) {
1318                        SingleByteCharsetConverter converter = getCharsetConverter(mappedServerEncoding);
1319
1320                        if (converter != null) { // we know how to convert this ourselves
1321                                setUseUnicode(true); // force the issue
1322                                setEncoding(mappedServerEncoding);
1323
1324                                return;
1325                        }
1326                }
1327
1328                //
1329                // Now, try and find a Java I/O converter that can do
1330                // the encoding for us
1331                //
1332                if (serverEncoding != null) {
1333                        if (mappedServerEncoding == null) {
1334                                // We don't have a mapping for it, so try
1335                                // and canonicalize the name....
1336                                if (Character.isLowerCase(serverEncoding.charAt(0))) {
1337                                        char[] ach = serverEncoding.toCharArray();
1338                                        ach[0] = Character.toUpperCase(serverEncoding.charAt(0));
1339                                        setEncoding(new String(ach));
1340                                }
1341                        }
1342
1343                        if (mappedServerEncoding == null) {
1344                                throw SQLError.createSQLException("Unknown character encoding on server '"
1345                                                + serverEncoding
1346                                                + "', use 'characterEncoding=' property "
1347                                                + " to provide correct mapping",
1348                                                SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
1349                        }
1350
1351                        //
1352                        // Attempt to use the encoding, and bail out if it
1353                        // can't be used
1354                        //
1355                        try {
1356                                StringUtils.getBytes("abc",mappedServerEncoding);
1357                                setEncoding(mappedServerEncoding);
1358                                setUseUnicode(true);
1359                        } catch (UnsupportedEncodingException UE) {
1360                                throw SQLError.createSQLException(
1361                                                "The driver can not map the character encoding '"
1362                                                                + getEncoding()
1363                                                                + "' that your server is using "
1364                                                                + "to a character encoding your JVM understands. You "
1365                                                                + "can specify this mapping manually by adding \"useUnicode=true\" "
1366                                                                + "as well as \"characterEncoding=[an_encoding_your_jvm_understands]\" "
1367                                                                + "to your JDBC URL.", "0S100", getExceptionInterceptor());
1368                        }
1369                }
1370        }
1371
1372        /**
1373         * Set transaction isolation level to the value received from server if any.
1374         * Is called by connectionInit(...)
1375         *
1376         * @throws SQLException
1377         *             DOCUMENT ME!
1378         */
1379        private void checkTransactionIsolationLevel() throws SQLException {
1380                String txIsolationName = null;
1381
1382                if (versionMeetsMinimum(4, 0, 3)) {
1383                        txIsolationName = "tx_isolation";
1384                } else {
1385                        txIsolationName = "transaction_isolation";
1386                }
1387
1388                String s = this.serverVariables.get(txIsolationName);
1389
1390                if (s != null) {
1391                        Integer intTI = mapTransIsolationNameToValue.get(s);
1392
1393                        if (intTI != null) {
1394                                this.isolationLevel = intTI.intValue();
1395                        }
1396                }
1397        }
1398
1399        /**
1400         * Clobbers the physical network connection and marks
1401         * this connection as closed.
1402         *
1403         * @throws SQLException
1404         */
1405        public void abortInternal() throws SQLException {
1406                if (this.io != null) {
1407                        try {
1408                                this.io.forceClose();
1409                        } catch (Throwable t) {
1410                                // can't do anything about it, and we're forcibly aborting
1411                        }
1412                        this.io = null;
1413                }
1414               
1415                this.isClosed = true;
1416        }
1417       
1418        /**
1419         * Destroys this connection and any underlying resources
1420         *
1421         * @param fromWhere
1422         *            DOCUMENT ME!
1423         * @param whyCleanedUp
1424         *            DOCUMENT ME!
1425         */
1426        private void cleanup(Throwable whyCleanedUp) {
1427                try {
1428                        if ((this.io != null) && !isClosed()) {
1429                                realClose(false, false, false, whyCleanedUp);
1430                        } else if (this.io != null) {
1431                                this.io.forceClose();
1432                        }
1433                } catch (SQLException sqlEx) {
1434                        // ignore, we're going away.
1435                        ;
1436                }
1437
1438                this.isClosed = true;
1439        }
1440
1441        public void clearHasTriedMaster() {
1442                this.hasTriedMasterFlag = false;
1443        }
1444       
1445        /**
1446         * After this call, getWarnings returns null until a new warning is reported
1447         * for this connection.
1448         *
1449         * @exception SQLException
1450         *                if a database access error occurs
1451         */
1452        public void clearWarnings() throws SQLException {
1453                // firstWarning = null;
1454        }
1455
1456        /**
1457         * DOCUMENT ME!
1458         *
1459         * @param sql
1460         *            DOCUMENT ME!
1461         * @return DOCUMENT ME!
1462         * @throws SQLException
1463         *             DOCUMENT ME!
1464         */
1465        public java.sql.PreparedStatement clientPrepareStatement(String sql)
1466                        throws SQLException {
1467                return clientPrepareStatement(sql,
1468                                DEFAULT_RESULT_SET_TYPE,
1469                                DEFAULT_RESULT_SET_CONCURRENCY);
1470        }
1471
1472        /**
1473         * @see Connection#prepareStatement(String, int)
1474         */
1475        public java.sql.PreparedStatement clientPrepareStatement(String sql,
1476                        int autoGenKeyIndex) throws SQLException {
1477                java.sql.PreparedStatement pStmt = clientPrepareStatement(sql);
1478
1479                ((com.mysql.jdbc.PreparedStatement) pStmt)
1480                                .setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
1481
1482                return pStmt;
1483        }
1484
1485        /**
1486         * DOCUMENT ME!
1487         *
1488         * @param sql
1489         *            DOCUMENT ME!
1490         * @param resultSetType
1491         *            DOCUMENT ME!
1492         * @param resultSetConcurrency
1493         *            DOCUMENT ME!
1494         * @return DOCUMENT ME!
1495         * @throws SQLException
1496         *             DOCUMENT ME!
1497         */
1498        public java.sql.PreparedStatement clientPrepareStatement(String sql,
1499                        int resultSetType, int resultSetConcurrency) throws SQLException {
1500                return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true);
1501        }
1502
1503
1504       
1505        public java.sql.PreparedStatement clientPrepareStatement(String sql,
1506                        int resultSetType, int resultSetConcurrency, 
1507                        boolean processEscapeCodesIfNeeded) throws SQLException {
1508                checkClosed();
1509
1510                String nativeSql = processEscapeCodesIfNeeded && getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
1511               
1512                PreparedStatement pStmt = null;
1513
1514                if (getCachePreparedStatements()) {
1515                        PreparedStatement.ParseInfo pStmtInfo = this.cachedPreparedStatementParams.get(nativeSql);
1516 
1517                        if (pStmtInfo == null) {
1518                                pStmt = com.mysql.jdbc.PreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql,
1519                                                this.database);
1520
1521                                this.cachedPreparedStatementParams.put(nativeSql, pStmt
1522                                                        .getParseInfo());
1523                        } else {
1524                                pStmt = new com.mysql.jdbc.PreparedStatement(getLoadBalanceSafeProxy(), nativeSql,
1525                                                this.database, pStmtInfo);
1526                        }
1527                } else {
1528                        pStmt = com.mysql.jdbc.PreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql,
1529                                        this.database);
1530                }
1531
1532                pStmt.setResultSetType(resultSetType);
1533                pStmt.setResultSetConcurrency(resultSetConcurrency);
1534
1535                return pStmt;
1536        }
1537       
1538        /**
1539         * @see java.sql.Connection#prepareStatement(String, int[])
1540         */
1541        public java.sql.PreparedStatement clientPrepareStatement(String sql,
1542                        int[] autoGenKeyIndexes) throws SQLException {
1543               
1544                PreparedStatement pStmt = (PreparedStatement) clientPrepareStatement(sql);
1545               
1546                pStmt
1547                                .setRetrieveGeneratedKeys((autoGenKeyIndexes != null)
1548                                                && (autoGenKeyIndexes.length > 0));
1549
1550                return pStmt;
1551        }
1552
1553        /**
1554         * @see java.sql.Connection#prepareStatement(String, String[])
1555         */
1556        public java.sql.PreparedStatement clientPrepareStatement(String sql,
1557                        String[] autoGenKeyColNames) throws SQLException {
1558                PreparedStatement pStmt = (PreparedStatement) clientPrepareStatement(sql);
1559
1560                pStmt
1561                                .setRetrieveGeneratedKeys((autoGenKeyColNames != null)
1562                                                && (autoGenKeyColNames.length > 0));
1563
1564                return pStmt;
1565        }
1566
1567        public java.sql.PreparedStatement clientPrepareStatement(String sql,
1568                        int resultSetType, int resultSetConcurrency,
1569                        int resultSetHoldability) throws SQLException {
1570                return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true);
1571        }
1572       
1573        // --------------------------JDBC 2.0-----------------------------
1574
1575        /**
1576         * In some cases, it is desirable to immediately release a Connection's
1577         * database and JDBC resources instead of waiting for them to be
1578         * automatically released (cant think why off the top of my head) <B>Note:</B>
1579         * A Connection is automatically closed when it is garbage collected.
1580         * Certain fatal errors also result in a closed connection.
1581         *
1582         * @exception SQLException
1583         *                if a database access error occurs
1584         */
1585        public synchronized void close() throws SQLException {
1586                if (this.connectionLifecycleInterceptors != null) {
1587                        new IterateBlock<Extension>(this.connectionLifecycleInterceptors.iterator()) {
1588                                void forEach(Extension each) throws SQLException {
1589                                        ((ConnectionLifecycleInterceptor)each).close();
1590                                }
1591                        }.doForAll();
1592                }
1593       
1594                realClose(true, true, false, null);
1595        }
1596
1597        /**
1598         * Closes all currently open statements.
1599         *
1600         * @throws SQLException
1601         *             DOCUMENT ME!
1602         */
1603        private void closeAllOpenStatements() throws SQLException {
1604                SQLException postponedException = null;
1605
1606                if (this.openStatements != null) {
1607                        List<Statement> currentlyOpenStatements = new ArrayList<Statement>(); // we need this to
1608                        // avoid
1609                        // ConcurrentModificationEx
1610
1611                        for (Iterator<Statement> iter = this.openStatements.keySet().iterator(); iter.hasNext();) {
1612                                currentlyOpenStatements.add(iter.next());
1613                        }
1614
1615                        int numStmts = currentlyOpenStatements.size();
1616
1617                        for (int i = 0; i < numStmts; i++) {
1618                                StatementImpl stmt = (StatementImpl) currentlyOpenStatements.get(i);
1619
1620                                try {
1621                                        stmt.realClose(false, true);
1622                                } catch (SQLException sqlEx) {
1623                                        postponedException = sqlEx; // throw it later, cleanup all
1624                                        // statements first
1625                                }
1626                        }
1627
1628                        if (postponedException != null) {
1629                                throw postponedException;
1630                        }
1631                }
1632        }
1633
1634        private void closeStatement(java.sql.Statement stmt) {
1635                if (stmt != null) {
1636                        try {
1637                                stmt.close();
1638                        } catch (SQLException sqlEx) {
1639                                ; // ignore
1640                        }
1641
1642                        stmt = null;
1643                }
1644        }
1645
1646        /**
1647         * The method commit() makes all changes made since the previous
1648         * commit/rollback permanent and releases any database locks currently held
1649         * by the Connection. This method should only be used when auto-commit has
1650         * been disabled.
1651         * <p>
1652         * <b>Note:</b> MySQL does not support transactions, so this method is a
1653         * no-op.
1654         * </p>
1655         *
1656         * @exception SQLException
1657         *                if a database access error occurs
1658         * @see setAutoCommit
1659         */
1660        public synchronized void commit() throws SQLException {
1661                checkClosed();
1662
1663                try {
1664                        if (this.connectionLifecycleInterceptors != null) {
1665                                IterateBlock<Extension> iter = new IterateBlock<Extension>(this.connectionLifecycleInterceptors.iterator()) {
1666
1667                                        void forEach(Extension each) throws SQLException {
1668                                                if (!((ConnectionLifecycleInterceptor)each).commit()) {
1669                                                        this.stopIterating = true;
1670                                                }
1671                                        }
1672                                };
1673                               
1674                                iter.doForAll();
1675                               
1676                                if (!iter.fullIteration()) {
1677                                        return;
1678                                }
1679                        }
1680                       
1681                        // no-op if _relaxAutoCommit == true
1682                        if (this.autoCommit && !getRelaxAutoCommit()) {
1683                                throw SQLError.createSQLException("Can't call commit when autocommit=true", getExceptionInterceptor());
1684                        } else if (this.transactionsSupported) {
1685                                if (getUseLocalTransactionState() && versionMeetsMinimum(5, 0, 0)) {
1686                                        if (!this.io.inTransactionOnServer()) {
1687                                                return; // effectively a no-op
1688                                        }
1689                                }
1690
1691                                execSQL(null, "commit", -1, null,
1692                                                DEFAULT_RESULT_SET_TYPE,
1693                                                DEFAULT_RESULT_SET_CONCURRENCY, false,
1694                                                this.database, null,
1695                                                false);
1696                        }
1697                } catch (SQLException sqlException) {
1698                        if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
1699                                        .equals(sqlException.getSQLState())) {
1700                                throw SQLError
1701                                                .createSQLException(
1702                                                                "Communications link failure during commit(). Transaction resolution unknown.",
1703                                                                SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN,
1704                                                                getExceptionInterceptor());
1705                        }
1706
1707                        throw sqlException;
1708                } finally {
1709                        this.needsPing = this.getReconnectAtTxEnd();
1710                }
1711
1712                return;
1713        }
1714       
1715        /**
1716         * Configures client-side properties for character set information.
1717         *
1718         * @throws SQLException
1719         *             if unable to configure the specified character set.
1720         */
1721        private void configureCharsetProperties() throws SQLException {
1722                if (getEncoding() != null) {
1723                        // Attempt to use the encoding, and bail out if it
1724                        // can't be used
1725                        try {
1726                                String testString = "abc";
1727                                StringUtils.getBytes(testString, getEncoding());
1728                        } catch (UnsupportedEncodingException UE) {
1729                                // Try the MySQL character encoding, then....
1730                                String oldEncoding = getEncoding();
1731
1732                                try {
1733                                        setEncoding(getJavaEncodingForMysqlEncoding(oldEncoding));
1734                                } catch (SQLException ex) {
1735                                        throw ex;
1736                                } catch (RuntimeException ex) {
1737                                        SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
1738                                        sqlEx.initCause(ex);
1739                                        throw sqlEx;
1740                                }
1741
1742                                if (getEncoding() == null) {
1743                                        throw SQLError.createSQLException(
1744                                                        "Java does not support the MySQL character encoding "
1745                                                                        + " " + "encoding '" + oldEncoding + "'.",
1746                                                        SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
1747                                }
1748
1749                                try {
1750                                        String testString = "abc";
1751                                        StringUtils.getBytes(testString, getEncoding());
1752                                } catch (UnsupportedEncodingException encodingEx) {
1753                                        throw SQLError.createSQLException("Unsupported character "
1754                                                        + "encoding '" + getEncoding() + "'.",
1755                                                        SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
1756                                }
1757                        }
1758                }
1759        }
1760
1761        /**
1762         * Sets up client character set for MySQL-4.1 and newer if the user This
1763         * must be done before any further communication with the server!
1764         *
1765         * @return true if this routine actually configured the client character
1766         *         set, or false if the driver needs to use 'older' methods to
1767         *         detect the character set, as it is connected to a MySQL server
1768         *         older than 4.1.0
1769         * @throws SQLException
1770         *             if an exception happens while sending 'SET NAMES' to the
1771         *             server, or the server sends character set information that
1772         *             the client doesn't know about.
1773         */
1774        private boolean configureClientCharacterSet(boolean dontCheckServerMatch) throws SQLException {
1775                String realJavaEncoding = getEncoding();
1776                boolean characterSetAlreadyConfigured = false;
1777
1778                try {
1779                        if (versionMeetsMinimum(4, 1, 0)) {
1780                                characterSetAlreadyConfigured = true;
1781
1782                                setUseUnicode(true);
1783
1784                                configureCharsetProperties();
1785                                realJavaEncoding = getEncoding(); // we need to do this again
1786                                // to grab this for
1787                                // versions > 4.1.0
1788
1789                                try {
1790
1791                                        // Fault injection for testing server character set indices
1792                           
1793                            if (props != null && props.getProperty("com.mysql.jdbc.faultInjection.serverCharsetIndex") != null) {
1794                                this.io.serverCharsetIndex = Integer.parseInt(
1795                                                props.getProperty(
1796                                                                "com.mysql.jdbc.faultInjection.serverCharsetIndex"));   
1797                            }
1798                           
1799                                        String serverEncodingToSet = 
1800                                                CharsetMapping.INDEX_TO_CHARSET[this.io.serverCharsetIndex];
1801                                       
1802                                        if (serverEncodingToSet == null || serverEncodingToSet.length() == 0) {
1803                                                if (realJavaEncoding != null) {
1804                                                        // user knows best, try it
1805                                                        setEncoding(realJavaEncoding);
1806                                                } else {
1807                                                        throw SQLError.createSQLException(
1808                                                                        "Unknown initial character set index '"
1809                                                                                        + this.io.serverCharsetIndex
1810                                                                                        + "' received from server. Initial client character set can be forced via the 'characterEncoding' property.",
1811                                                                        SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
1812                                                }
1813                                        }
1814                                       
1815                                        // "latin1" on MySQL-4.1.0+ is actually CP1252, not ISO8859_1
1816                                        if (versionMeetsMinimum(4, 1, 0) && 
1817                                                        "ISO8859_1".equalsIgnoreCase(serverEncodingToSet)) {
1818                                                serverEncodingToSet = "Cp1252";
1819                                        }
1820                                       
1821                                        setEncoding(serverEncodingToSet);
1822                               
1823                                } catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
1824                                        if (realJavaEncoding != null) {
1825                                                // user knows best, try it
1826                                                setEncoding(realJavaEncoding);
1827                                        } else {
1828                                                throw SQLError.createSQLException(
1829                                                                "Unknown initial character set index '"
1830                                                                                + this.io.serverCharsetIndex
1831                                                                                + "' received from server. Initial client character set can be forced via the 'characterEncoding' property.",
1832                                                                SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
1833                                        }
1834                                } catch (SQLException ex) {
1835                                        throw ex;
1836                                } catch (RuntimeException ex) {
1837                                        SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
1838                                        sqlEx.initCause(ex);
1839                                        throw sqlEx;
1840                                }
1841
1842                                if (getEncoding() == null) {
1843                                        // punt?
1844                                        setEncoding("ISO8859_1");
1845                                }
1846
1847                                //
1848                                // Has the user has 'forced' the character encoding via
1849                                // driver properties?
1850                                //
1851                                if (getUseUnicode()) {
1852                                        if (realJavaEncoding != null) {
1853
1854                                                //
1855                                                // Now, inform the server what character set we
1856                                                // will be using from now-on...
1857                                                //
1858                                                if (realJavaEncoding.equalsIgnoreCase("UTF-8")
1859                                                                || realJavaEncoding.equalsIgnoreCase("UTF8")) {
1860                                                        // charset names are case-sensitive
1861
1862                                                        boolean utf8mb4Supported = versionMeetsMinimum(5, 5, 2);
1863                                                        boolean useutf8mb4 = false;
1864                                                       
1865                                                        if (utf8mb4Supported) {
1866                                                                useutf8mb4 = (this.io.serverCharsetIndex == 45);
1867                                                        }
1868                                                       
1869                                                        if (!getUseOldUTF8Behavior()) {
1870                                                                if (dontCheckServerMatch || !characterSetNamesMatches("utf8") 
1871                                                                                || (utf8mb4Supported && !characterSetNamesMatches("utf8mb4"))) {
1872                                                                        execSQL(null, "SET NAMES " + (useutf8mb4 ? "utf8mb4" : "utf8"), -1, null,
1873                                                                                        DEFAULT_RESULT_SET_TYPE,
1874                                                                                        DEFAULT_RESULT_SET_CONCURRENCY,
1875                                                                                        false, this.database, null, false);
1876                                                                }
1877                                                        } else {
1878                                                                execSQL(null, "SET NAMES latin1", -1, null,
1879                                                                                DEFAULT_RESULT_SET_TYPE,
1880                                                                                DEFAULT_RESULT_SET_CONCURRENCY,
1881                                                                                false, this.database, null, false);
1882                                                        }
1883
1884                                                        setEncoding(realJavaEncoding);
1885                                                } /* not utf-8 */else {
1886                                                        String mysqlEncodingName = CharsetMapping
1887                                                                        .getMysqlEncodingForJavaEncoding(
1888                                                                                        realJavaEncoding
1889                                                                                                        .toUpperCase(Locale.ENGLISH),
1890                                                                                        this);
1891
1892                                                        /*
1893                                                         * if ("koi8_ru".equals(mysqlEncodingName)) { //
1894                                                         * This has a _different_ name in 4.1...
1895                                                         * mysqlEncodingName = "ko18r"; } else if
1896                                                         * ("euc_kr".equals(mysqlEncodingName)) { //
1897                                                         * Different name in 4.1 mysqlEncodingName =
1898                                                         * "euckr"; }
1899                                                         */
1900
1901                                                        if (mysqlEncodingName != null) {
1902                                                               
1903                                                                if (dontCheckServerMatch || !characterSetNamesMatches(mysqlEncodingName)) {
1904                                                                        execSQL(null, "SET NAMES " + mysqlEncodingName,
1905                                                                                -1, null,
1906                                                                                DEFAULT_RESULT_SET_TYPE,
1907                                                                                DEFAULT_RESULT_SET_CONCURRENCY,
1908                                                                                false, this.database, null, false);
1909                                                                }
1910                                                        }
1911
1912                                                        // Switch driver's encoding now, since the server
1913                                                        // knows what we're sending...
1914                                                        //
1915                                                        setEncoding(realJavaEncoding);
1916                                                }
1917                                        } else if (getEncoding() != null) {
1918                                                // Tell the server we'll use the server default charset
1919                                                // to send our
1920                                                // queries from now on....
1921                                                String mysqlEncodingName = CharsetMapping
1922                                                                .getMysqlEncodingForJavaEncoding(getEncoding()
1923                                                                                .toUpperCase(Locale.ENGLISH), this);
1924                                               
1925                                                if(getUseOldUTF8Behavior()){
1926                                                        mysqlEncodingName = "latin1";
1927                                                }
1928
1929                                                if (dontCheckServerMatch || !characterSetNamesMatches(mysqlEncodingName)) {
1930                                                        execSQL(null, "SET NAMES " + mysqlEncodingName, -1,
1931                                                                null, DEFAULT_RESULT_SET_TYPE,
1932                                                                DEFAULT_RESULT_SET_CONCURRENCY, false,
1933                                                                this.database, null, false);
1934                                                }
1935
1936                                                realJavaEncoding = getEncoding();
1937                                        }
1938
1939                                }
1940
1941                                //
1942                                // We know how to deal with any charset coming back from
1943                                // the database, so tell the server not to do conversion
1944                                // if the user hasn't 'forced' a result-set character set
1945                                //
1946
1947                                String onServer = null;
1948                                boolean isNullOnServer = false;
1949                               
1950                                if (this.serverVariables != null) {
1951                                        onServer = this.serverVariables.get("character_set_results");
1952                                       
1953                                        isNullOnServer = onServer == null || "NULL".equalsIgnoreCase(onServer) || onServer.length() == 0;
1954                                }
1955                               
1956                                if (getCharacterSetResults() == null) {
1957                                       
1958                                        //
1959                                        // Only send if needed, if we're caching server variables
1960                                        // we -have- to send, because we don't know what it was
1961                                        // before we cached them.
1962                                        //
1963                                        if (!isNullOnServer) {
1964                                                execSQL(null, "SET character_set_results = NULL", -1, null,
1965                                                                DEFAULT_RESULT_SET_TYPE,
1966                                                                DEFAULT_RESULT_SET_CONCURRENCY, false,
1967                                                                this.database, null, 
1968                                                                false);
1969                                                if (!this.usingCachedConfig) {
1970                                                        this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, null);
1971                                                }
1972                                        } else {
1973                                                if (!this.usingCachedConfig) {
1974                                                        this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer);
1975                                                }
1976                                        }
1977                                } else {
1978
1979                                        if(getUseOldUTF8Behavior()){
1980                                                execSQL(null, "SET NAMES " + "latin1", -1,
1981                                                        null, DEFAULT_RESULT_SET_TYPE,
1982                                                        DEFAULT_RESULT_SET_CONCURRENCY, false,
1983                                                        this.database, null, false);
1984                                        }
1985                                        String charsetResults = getCharacterSetResults();
1986                                        String mysqlEncodingName = null;
1987
1988                                        if ("UTF-8".equalsIgnoreCase(charsetResults)
1989                                                        || "UTF8".equalsIgnoreCase(charsetResults)) {
1990                                                mysqlEncodingName = "utf8";
1991                                        } else if ("null".equalsIgnoreCase(charsetResults)) {
1992                                                mysqlEncodingName = "NULL";
1993                                        } else {
1994                                                mysqlEncodingName = CharsetMapping
1995                                                                .getMysqlEncodingForJavaEncoding(charsetResults
1996                                                                                .toUpperCase(Locale.ENGLISH), this);
1997                                        }
1998
1999                                        //
2000                                        // Only change the value if needed
2001                                        //
2002                                       
2003                                        if (mysqlEncodingName == null) {
2004                                                throw SQLError.createSQLException(
2005                                                                "Can't map "+charsetResults+" given for characterSetResults to a supported MySQL encoding.",
2006                                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
2007                                        }
2008
2009                                        if (!mysqlEncodingName.equalsIgnoreCase(
2010                                                        this.serverVariables.get("character_set_results"))) {
2011                                                StringBuffer setBuf = new StringBuffer(
2012                                                                "SET character_set_results = ".length()
2013                                                                                + mysqlEncodingName.length());
2014                                                setBuf.append("SET character_set_results = ").append(
2015                                                                mysqlEncodingName);
2016       
2017                                                execSQL(null, setBuf.toString(), -1, null,
2018                                                                DEFAULT_RESULT_SET_TYPE,
2019                                                                DEFAULT_RESULT_SET_CONCURRENCY, false,
2020                                                                this.database, null, false);
2021                                               
2022                                                if (!this.usingCachedConfig) {
2023                                                        this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, 
2024                                                                mysqlEncodingName);
2025                                                }
2026
2027                                                // We have to set errorMessageEncoding according to new value
2028                                                // of charsetResults for server version 5.5 and higher
2029                                                if (versionMeetsMinimum(5, 5, 0)) {
2030                                                        this.errorMessageEncoding = charsetResults;
2031                                                }
2032
2033                                        } else {
2034                                                if (!this.usingCachedConfig) {
2035                                                        this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer);
2036                                                }
2037                                        }
2038                                }
2039
2040                                if (getConnectionCollation() != null) {
2041                                        StringBuffer setBuf = new StringBuffer(
2042                                                        "SET collation_connection = ".length()
2043                                                                        + getConnectionCollation().length());
2044                                        setBuf.append("SET collation_connection = ").append(
2045                                                        getConnectionCollation());
2046
2047                                        execSQL(null, setBuf.toString(), -1, null,
2048                                                        DEFAULT_RESULT_SET_TYPE,
2049                                                        DEFAULT_RESULT_SET_CONCURRENCY, false,
2050                                                        this.database, null, false);
2051                                }
2052                        } else {
2053                                // Use what the server has specified
2054                                realJavaEncoding = getEncoding(); // so we don't get
2055                                // swapped out in the finally
2056                                // block....
2057                        }
2058                } finally {
2059                        // Failsafe, make sure that the driver's notion of character
2060                        // encoding matches what the user has specified.
2061                        //
2062                        setEncoding(realJavaEncoding);
2063                }
2064               
2065                /**
2066                 * Check if we need a CharsetEncoder for escaping codepoints that are
2067                 * transformed to backslash (0x5c) in the connection encoding.
2068                 */
2069                try {
2070                        CharsetEncoder enc = Charset.forName(getEncoding()).newEncoder();
2071                        CharBuffer cbuf = CharBuffer.allocate(1);
2072                        ByteBuffer bbuf = ByteBuffer.allocate(1);
2073
2074                        cbuf.put("\u00a5");
2075                        cbuf.position(0);
2076                        enc.encode(cbuf, bbuf, true);
2077                        if(bbuf.get(0) == '\\') {
2078                                requiresEscapingEncoder = true;
2079                        } else {
2080                                cbuf.clear();
2081                                bbuf.clear();
2082                               
2083                                cbuf.put("\u20a9");
2084                                cbuf.position(0);
2085                                enc.encode(cbuf, bbuf, true);
2086                                if(bbuf.get(0) == '\\') {
2087                                        requiresEscapingEncoder = true;
2088                                }
2089                        }
2090                } catch(java.nio.charset.UnsupportedCharsetException ucex) {
2091                        // fallback to String API - for Java 1.4
2092                        try {
2093                                byte bbuf[] = StringUtils.getBytes("\u00a5", getEncoding());
2094                                if (bbuf[0] == '\\') {
2095                                        requiresEscapingEncoder = true;
2096                                } else {
2097                                        bbuf = StringUtils.getBytes("\u20a9", getEncoding());
2098                                        if (bbuf[0] == '\\') {
2099                                                requiresEscapingEncoder = true;
2100                                        }
2101                                }
2102                        } catch(UnsupportedEncodingException ueex) {
2103                                throw SQLError.createSQLException("Unable to use encoding: " + getEncoding(),
2104                                                SQLError.SQL_STATE_GENERAL_ERROR, ueex,
2105                                                getExceptionInterceptor());
2106                        }
2107                }
2108
2109                return characterSetAlreadyConfigured;
2110        }
2111
2112        /**
2113         * Configures the client's timezone if required.
2114         *
2115         * @throws SQLException
2116         *             if the timezone the server is configured to use can't be
2117         *             mapped to a Java timezone.
2118         */
2119        private void configureTimezone() throws SQLException {
2120                String configuredTimeZoneOnServer = this.serverVariables.get("timezone");
2121
2122                if (configuredTimeZoneOnServer == null) {
2123                        configuredTimeZoneOnServer = this.serverVariables.get("time_zone");
2124
2125                        if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) {
2126                                configuredTimeZoneOnServer = this.serverVariables.get("system_time_zone");
2127                        }
2128                }
2129
2130                String canoncicalTimezone = getServerTimezone();
2131               
2132                if ((getUseTimezone() || !getUseLegacyDatetimeCode()) && configuredTimeZoneOnServer != null) {
2133                        // user can override this with driver properties, so don't detect if that's the case
2134                        if (canoncicalTimezone == null || StringUtils.isEmptyOrWhitespaceOnly(canoncicalTimezone)) {
2135                                try {
2136                                        canoncicalTimezone = TimeUtil
2137                                                        .getCanoncialTimezone(configuredTimeZoneOnServer, getExceptionInterceptor());
2138
2139                                        if (canoncicalTimezone == null) {
2140                                                throw SQLError.createSQLException("Can't map timezone '"
2141                                                                + configuredTimeZoneOnServer + "' to "
2142                                                                + " canonical timezone.",
2143                                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
2144                                        }
2145                                } catch (IllegalArgumentException iae) {
2146                                        throw SQLError.createSQLException(iae.getMessage(),
2147                                                        SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
2148                                }
2149                        }
2150                } else {
2151                        canoncicalTimezone = getServerTimezone();
2152                }
2153               
2154                if (canoncicalTimezone != null && canoncicalTimezone.length() > 0) {
2155                                this.serverTimezoneTZ = TimeZone.getTimeZone(canoncicalTimezone);
2156
2157                        //
2158                        // The Calendar class has the behavior of mapping
2159                        // unknown timezones to 'GMT' instead of throwing an
2160                        // exception, so we must check for this...
2161                        //
2162                        if (!canoncicalTimezone.equalsIgnoreCase("GMT")
2163                                        && this.serverTimezoneTZ.getID().equals("GMT")) {
2164                                throw SQLError.createSQLException("No timezone mapping entry for '"
2165                                                + canoncicalTimezone + "'",
2166                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
2167                        }
2168
2169                        if ("GMT".equalsIgnoreCase(this.serverTimezoneTZ.getID())) {
2170                                this.isServerTzUTC = true;
2171                        } else {
2172                                this.isServerTzUTC = false;
2173                        }
2174                }
2175        }
2176
2177        private void createInitialHistogram(long[] breakpoints,
2178                        long lowerBound, long upperBound) {
2179
2180                double bucketSize = (((double) upperBound - (double) lowerBound) / HISTOGRAM_BUCKETS) * 1.25;
2181
2182                if (bucketSize < 1) {
2183                        bucketSize = 1;
2184                }
2185
2186                for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
2187                        breakpoints[i] = lowerBound;
2188                        lowerBound += bucketSize;
2189                }
2190        }
2191
2192        /**
2193         * Creates an IO channel to the server
2194         *
2195         * @param isForReconnect
2196         *            is this request for a re-connect
2197         * @return a new MysqlIO instance connected to a server
2198         * @throws SQLException
2199         *             if a database access error occurs
2200         * @throws CommunicationsException
2201         *             DOCUMENT ME!
2202         */
2203        public synchronized void createNewIO(boolean isForReconnect)
2204                        throws SQLException {
2205                // Synchronization Not needed for *new* connections, but defintely for
2206                // connections going through fail-over, since we might get the
2207                // new connection up and running *enough* to start sending
2208                // cached or still-open server-side prepared statements over
2209                // to the backend before we get a chance to re-prepare them...
2210               
2211
2212                Properties mergedProps  = exposeAsProperties(this.props);
2213
2214                if (!getHighAvailability()) {
2215                        connectOneTryOnly(isForReconnect, mergedProps);
2216                       
2217                        return;
2218                } 
2219
2220                connectWithRetries(isForReconnect, mergedProps);
2221               
2222        }
2223
2224        private void connectWithRetries(boolean isForReconnect,
2225                        Properties mergedProps) throws SQLException {
2226                double timeout = getInitialTimeout();
2227                boolean connectionGood = false;
2228
2229                Exception connectionException = null;
2230
2231                for (int attemptCount = 0; (attemptCount < getMaxReconnects())
2232                                && !connectionGood; attemptCount++) {
2233                        try {
2234                                if (this.io != null) {
2235                                        this.io.forceClose();
2236                                }
2237
2238                                coreConnect(mergedProps);
2239                                pingInternal(false, 0);
2240                               
2241                                boolean oldAutoCommit;
2242                                int oldIsolationLevel;
2243                                boolean oldReadOnly;
2244                                String oldCatalog;
2245                               
2246                                synchronized (this) {
2247                                        this.connectionId = this.io.getThreadId();
2248                                        this.isClosed = false;
2249
2250                                        // save state from old connection
2251                                        oldAutoCommit = getAutoCommit();
2252                                        oldIsolationLevel = this.isolationLevel;
2253                                        oldReadOnly = isReadOnly();
2254                                        oldCatalog = getCatalog();
2255       
2256                                        this.io.setStatementInterceptors(this.statementInterceptors);
2257                                }
2258                               
2259                                // Server properties might be different
2260                                // from previous connection, so initialize
2261                                // again...
2262                                initializePropsFromServer();
2263
2264                                if (isForReconnect) {
2265                                        // Restore state from old connection
2266                                        setAutoCommit(oldAutoCommit);
2267
2268                                        if (this.hasIsolationLevels) {
2269                                                setTransactionIsolation(oldIsolationLevel);
2270                                        }
2271
2272                                        setCatalog(oldCatalog);
2273                                        setReadOnly(oldReadOnly);
2274                                }
2275
2276                                connectionGood = true;
2277
2278                                break;
2279                        } catch (Exception EEE) {
2280                                connectionException = EEE;
2281                                connectionGood = false;
2282                        }
2283
2284                                if (connectionGood) {
2285                                        break;
2286                                }
2287
2288                                if (attemptCount > 0) {
2289                                        try {
2290                                                Thread.sleep((long) timeout * 1000);
2291                                        } catch (InterruptedException IE) {
2292                                                // ignore
2293                                        }
2294                                }
2295                        } // end attempts for a single host
2296
2297                if (!connectionGood) {
2298                        // We've really failed!
2299                        SQLException chainedEx = SQLError.createSQLException(
2300                                        Messages.getString("Connection.UnableToConnectWithRetries",
2301                                                        new Object[] {Integer.valueOf(getMaxReconnects())}),
2302                                        SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor());
2303                        chainedEx.initCause(connectionException);
2304                       
2305                        throw chainedEx;
2306                }
2307
2308                if (getParanoid() && !getHighAvailability()) {
2309                        this.password = null;
2310                        this.user = null;
2311                }
2312
2313                if (isForReconnect) {
2314                        //
2315                        // Retrieve any 'lost' prepared statements if re-connecting
2316                        //
2317                        Iterator<Statement> statementIter = this.openStatements.values().iterator();
2318
2319                        //
2320                        // We build a list of these outside the map of open statements,
2321                        // because
2322                        // in the process of re-preparing, we might end up having to
2323                        // close
2324                        // a prepared statement, thus removing it from the map, and
2325                        // generating
2326                        // a ConcurrentModificationException
2327                        //
2328                        Stack<Statement> serverPreparedStatements = null;
2329
2330                        while (statementIter.hasNext()) {
2331                                Statement statementObj = statementIter.next();
2332
2333                                if (statementObj instanceof ServerPreparedStatement) {
2334                                        if (serverPreparedStatements == null) {
2335                                                serverPreparedStatements = new Stack<Statement>();
2336                                        }
2337
2338                                        serverPreparedStatements.add(statementObj);
2339                                }
2340                        }
2341
2342                        if (serverPreparedStatements != null) {
2343                                while (!serverPreparedStatements.isEmpty()) {
2344                                        ((ServerPreparedStatement) serverPreparedStatements
2345                                                        .pop()).rePrepare();
2346                                }
2347                        }
2348                }
2349        }
2350
2351        private void coreConnect(Properties mergedProps) throws SQLException,
2352                        IOException {
2353                int newPort = 3306;
2354                String newHost = "localhost";
2355               
2356                String protocol = mergedProps.getProperty(NonRegisteringDriver.PROTOCOL_PROPERTY_KEY);
2357               
2358                if (protocol != null) {
2359                        // "new" style URL
2360
2361                        if ("tcp".equalsIgnoreCase(protocol)) {
2362                                newHost = normalizeHost(mergedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY));
2363                                newPort = parsePortNumber(mergedProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"));
2364                        } else if ("pipe".equalsIgnoreCase(protocol)) {
2365                                setSocketFactoryClassName(NamedPipeSocketFactory.class.getName());
2366                               
2367                                String path = mergedProps.getProperty(NonRegisteringDriver.PATH_PROPERTY_KEY);
2368                               
2369                                if (path != null) {
2370                                        mergedProps.setProperty(NamedPipeSocketFactory.NAMED_PIPE_PROP_NAME, path);
2371                                }
2372                        } else {
2373                                // normalize for all unknown protocols
2374                                newHost = normalizeHost(mergedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY));
2375                                newPort = parsePortNumber(mergedProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"));
2376                        }
2377                } else {
2378               
2379                        String[] parsedHostPortPair = NonRegisteringDriver
2380                                        .parseHostPortPair(this.hostPortPair);
2381                        newHost = parsedHostPortPair[NonRegisteringDriver.HOST_NAME_INDEX];
2382
2383                        newHost = normalizeHost(newHost);
2384
2385                        if (parsedHostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX] != null) {
2386                                newPort = parsePortNumber(parsedHostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]);
2387                        }
2388                }
2389
2390                this.port = newPort;
2391                this.host = newHost;
2392               
2393                this.io = new MysqlIO(newHost, newPort,
2394                                mergedProps, getSocketFactoryClassName(),
2395                                getProxy(), getSocketTimeout(),
2396                                this.largeRowSizeThreshold.getValueAsInt());
2397                this.io.doHandshake(this.user, this.password,
2398                                this.database);
2399        }
2400
2401        private String normalizeHost(String hostname) {
2402                if (hostname == null || StringUtils.isEmptyOrWhitespaceOnly(hostname)) {
2403                        return "localhost";
2404                }
2405               
2406                return hostname;
2407        }
2408        private int parsePortNumber(String portAsString)
2409                        throws SQLException {
2410                int portNumber = 3306;
2411                try {
2412                        portNumber = Integer
2413                                        .parseInt(portAsString);
2414                } catch (NumberFormatException nfe) {
2415                        throw SQLError.createSQLException(
2416                                        "Illegal connection port value '"
2417                                                        + portAsString
2418                                                        + "'",
2419                                        SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
2420                }
2421                return portNumber;
2422        }
2423
2424        private void connectOneTryOnly(boolean isForReconnect,
2425                        Properties mergedProps) throws SQLException {
2426                Exception connectionNotEstablishedBecause = null;
2427
2428                try {
2429                       
2430                        coreConnect(mergedProps);
2431                        this.connectionId = this.io.getThreadId();
2432                        this.isClosed = false;
2433
2434                        // save state from old connection
2435                        boolean oldAutoCommit = getAutoCommit();
2436                        int oldIsolationLevel = this.isolationLevel;
2437                        boolean oldReadOnly = isReadOnly();
2438                        String oldCatalog = getCatalog();
2439
2440                        this.io.setStatementInterceptors(this.statementInterceptors);
2441                       
2442                        // Server properties might be different
2443                        // from previous connection, so initialize
2444                        // again...
2445                        initializePropsFromServer();
2446
2447                        if (isForReconnect) {
2448                                // Restore state from old connection
2449                                setAutoCommit(oldAutoCommit);
2450
2451                                if (this.hasIsolationLevels) {
2452                                        setTransactionIsolation(oldIsolationLevel);
2453                                }
2454
2455                                setCatalog(oldCatalog);
2456                               
2457                                setReadOnly(oldReadOnly);
2458                        }
2459                        return;
2460                       
2461                } catch (Exception EEE) {
2462                        if (this.io != null) {
2463                                this.io.forceClose();
2464                        }
2465
2466                        connectionNotEstablishedBecause = EEE;
2467
2468                        if (EEE instanceof SQLException) {
2469                                throw (SQLException)EEE;
2470                        }
2471                       
2472                        SQLException chainedEx = SQLError.createSQLException(
2473                                        Messages.getString("Connection.UnableToConnect"),
2474                                        SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor());
2475                        chainedEx.initCause(connectionNotEstablishedBecause);
2476                       
2477                        throw chainedEx;
2478                }
2479        }
2480
2481        private synchronized void createPreparedStatementCaches() throws SQLException {
2482                int cacheSize = getPreparedStatementCacheSize();
2483               
2484                try {
2485                        Class<?> factoryClass;
2486                       
2487                        factoryClass = Class.forName(getParseInfoCacheFactory());
2488                       
2489                        @SuppressWarnings("unchecked")
2490                        CacheAdapterFactory<String, ParseInfo> cacheFactory = ((CacheAdapterFactory<String, ParseInfo>)factoryClass.newInstance());
2491                       
2492                        this.cachedPreparedStatementParams = cacheFactory.getInstance(this, myURL, getPreparedStatementCacheSize(), getPreparedStatementCacheSqlLimit(), props);
2493                       
2494                } catch (ClassNotFoundException e) {
2495                        SQLException sqlEx = SQLError.createSQLException(
2496                                        Messages.getString("Connection.CantFindCacheFactory", new Object[] {getParseInfoCacheFactory(), "parseInfoCacheFactory"}),
2497                                        getExceptionInterceptor());
2498                        sqlEx.initCause(e);
2499                       
2500                        throw sqlEx;
2501                } catch (InstantiationException e) {
2502                        SQLException sqlEx = SQLError.createSQLException(
2503                                        Messages.getString("Connection.CantLoadCacheFactory", new Object[] {getParseInfoCacheFactory(), "parseInfoCacheFactory"}),
2504                                        getExceptionInterceptor());
2505                        sqlEx.initCause(e);
2506                       
2507                        throw sqlEx;
2508                } catch (IllegalAccessException e) {
2509                        SQLException sqlEx = SQLError.createSQLException(
2510                                        Messages.getString("Connection.CantLoadCacheFactory", new Object[] {getParseInfoCacheFactory(), "parseInfoCacheFactory"}),
2511                                        getExceptionInterceptor());
2512                        sqlEx.initCause(e);
2513                       
2514                        throw sqlEx;
2515                }
2516
2517                if (getUseServerPreparedStmts()) {
2518                        this.serverSideStatementCheckCache = new LRUCache(cacheSize);
2519                       
2520                        this.serverSideStatementCache = new LRUCache(cacheSize) {
2521
2522                                private static final long serialVersionUID = 7692318650375988114L;
2523
2524                                protected boolean removeEldestEntry(java.util.Map.Entry<Object, Object> eldest) {
2525                                        if (this.maxElements <= 1) {
2526                                                return false;
2527                                        }
2528                                       
2529                                        boolean removeIt = super.removeEldestEntry(eldest);
2530                                       
2531                                        if (removeIt) {
2532                                                ServerPreparedStatement ps = 
2533                                                        (ServerPreparedStatement)eldest.getValue();
2534                                                ps.isCached = false;
2535                                                ps.setClosed(false);
2536                                               
2537                                                try {
2538                                                        ps.close();
2539                                                } catch (SQLException sqlEx) {
2540                                                        // punt
2541                                                }
2542                                        }
2543                                       
2544                                        return removeIt;
2545                                }
2546                        };
2547                }
2548        }
2549
2550        /**
2551         * SQL statements without parameters are normally executed using Statement
2552         * objects. If the same SQL statement is executed many times, it is more
2553         * efficient to use a PreparedStatement
2554         *
2555         * @return a new Statement object
2556         * @throws SQLException
2557         *             passed through from the constructor
2558         */
2559        public java.sql.Statement createStatement() throws SQLException {
2560                return createStatement(DEFAULT_RESULT_SET_TYPE,
2561                                DEFAULT_RESULT_SET_CONCURRENCY);
2562        }
2563
2564        /**
2565         * JDBC 2.0 Same as createStatement() above, but allows the default result
2566         * set type and result set concurrency type to be overridden.
2567         *
2568         * @param resultSetType
2569         *            a result set type, see ResultSet.TYPE_XXX
2570         * @param resultSetConcurrency
2571         *            a concurrency type, see ResultSet.CONCUR_XXX
2572         * @return a new Statement object
2573         * @exception SQLException
2574         *                if a database-access error occurs.
2575         */
2576        public java.sql.Statement createStatement(int resultSetType,
2577                        int resultSetConcurrency) throws SQLException {
2578                checkClosed();
2579
2580                StatementImpl stmt = new StatementImpl(getLoadBalanceSafeProxy(), this.database);
2581                stmt.setResultSetType(resultSetType);
2582                stmt.setResultSetConcurrency(resultSetConcurrency);
2583
2584                return stmt;
2585        }
2586
2587        /**
2588         * @see Connection#createStatement(int, int, int)
2589         */
2590        public java.sql.Statement createStatement(int resultSetType,
2591                        int resultSetConcurrency, int resultSetHoldability)
2592                        throws SQLException {
2593                if (getPedantic()) {
2594                        if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
2595                                throw SQLError.createSQLException(
2596                                                "HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
2597                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
2598                        }
2599                }
2600
2601                return createStatement(resultSetType, resultSetConcurrency);
2602        }
2603
2604        public void dumpTestcaseQuery(String query) {
2605                System.err.println(query);
2606        }
2607
2608        public Connection duplicate() throws SQLException {
2609                return new ConnectionImpl(      this.origHostToConnectTo, 
2610                                this.origPortToConnectTo,
2611                                this.props,
2612                                this.origDatabaseToConnectTo,
2613                                this.myURL);
2614        }
2615
2616        /**
2617         * Send a query to the server. Returns one of the ResultSet objects. This is
2618         * synchronized, so Statement's queries will be serialized.
2619         *
2620         * @param callingStatement
2621         *            DOCUMENT ME!
2622         * @param sql
2623         *            the SQL statement to be executed
2624         * @param maxRows
2625         *            DOCUMENT ME!
2626         * @param packet
2627         *            DOCUMENT ME!
2628         * @param resultSetType
2629         *            DOCUMENT ME!
2630         * @param resultSetConcurrency
2631         *            DOCUMENT ME!
2632         * @param streamResults
2633         *            DOCUMENT ME!
2634         * @param queryIsSelectOnly
2635         *            DOCUMENT ME!
2636         * @param catalog
2637         *            DOCUMENT ME!
2638         * @param unpackFields
2639         *            DOCUMENT ME!
2640         * @return a ResultSet holding the results
2641         * @exception SQLException
2642         *                if a database error occurs
2643         */
2644
2645        // ResultSet execSQL(Statement callingStatement, String sql,
2646        // int maxRowsToRetreive, String catalog) throws SQLException {
2647        // return execSQL(callingStatement, sql, maxRowsToRetreive, null,
2648        // java.sql.ResultSet.TYPE_FORWARD_ONLY,
2649        // DEFAULT_RESULT_SET_CONCURRENCY, catalog);
2650        // }
2651        // ResultSet execSQL(Statement callingStatement, String sql, int maxRows,
2652        // int resultSetType, int resultSetConcurrency, boolean streamResults,
2653        // boolean queryIsSelectOnly, String catalog, boolean unpackFields) throws
2654        // SQLException {
2655        // return execSQL(callingStatement, sql, maxRows, null, resultSetType,
2656        // resultSetConcurrency, streamResults, queryIsSelectOnly, catalog,
2657        // unpackFields);
2658        // }
2659        public ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows,
2660                        Buffer packet, int resultSetType, int resultSetConcurrency,
2661                        boolean streamResults, String catalog,
2662                        Field[] cachedMetadata) throws SQLException {
2663                return execSQL(callingStatement, sql, maxRows, packet, resultSetType,
2664                                resultSetConcurrency, streamResults,
2665                                catalog, cachedMetadata, false);
2666        }
2667
2668        public synchronized ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows,
2669                        Buffer packet, int resultSetType, int resultSetConcurrency,
2670                        boolean streamResults, String catalog,
2671                        Field[] cachedMetadata,
2672                        boolean isBatch) throws SQLException {
2673                //
2674                // Fall-back if the master is back online if we've
2675                // issued queriesBeforeRetryMaster queries since
2676                // we failed over
2677                //
2678
2679                long queryStartTime = 0;
2680
2681                int endOfQueryPacketPosition = 0;
2682
2683                if (packet != null) {
2684                        endOfQueryPacketPosition = packet.getPosition();
2685                }
2686
2687                if (getGatherPerformanceMetrics()) {
2688                        queryStartTime = System.currentTimeMillis();
2689                }
2690
2691                this.lastQueryFinishedTime = 0; // we're busy!
2692
2693                if ((getHighAvailability())
2694                                && (this.autoCommit || getAutoReconnectForPools())
2695                                && this.needsPing && !isBatch) {
2696                        try {
2697                                pingInternal(false, 0);
2698
2699                                this.needsPing = false;
2700                        } catch (Exception Ex) {
2701                                createNewIO(true);
2702                        }
2703                }
2704
2705                try {
2706                        if (packet == null) {
2707                                String encoding = null;
2708
2709                                if (getUseUnicode()) {
2710                                        encoding = getEncoding();
2711                                }
2712
2713                                return this.io.sqlQueryDirect(callingStatement, sql,
2714                                                encoding, null, maxRows, resultSetType,
2715                                                resultSetConcurrency, streamResults, catalog,
2716                                                cachedMetadata);
2717                        }
2718
2719                        return this.io.sqlQueryDirect(callingStatement, null, null,
2720                                        packet, maxRows, resultSetType,
2721                                        resultSetConcurrency, streamResults, catalog,
2722                                        cachedMetadata);
2723                } catch (java.sql.SQLException sqlE) {
2724                        // don't clobber SQL exceptions
2725
2726                        if (getDumpQueriesOnException()) {
2727                                String extractedSql = extractSqlFromPacket(sql, packet,
2728                                                endOfQueryPacketPosition);
2729                                StringBuffer messageBuf = new StringBuffer(extractedSql
2730                                                .length() + 32);
2731                                messageBuf
2732                                                .append("\n\nQuery being executed when exception was thrown:\n");
2733                                messageBuf.append(extractedSql);
2734                                messageBuf.append("\n\n");
2735
2736                                sqlE = appendMessageToException(sqlE, messageBuf.toString(), getExceptionInterceptor());
2737                        }
2738
2739                        if ((getHighAvailability())) {
2740                                this.needsPing = true;
2741                        } else {
2742                                String sqlState = sqlE.getSQLState();
2743
2744                                if ((sqlState != null)
2745                                                && sqlState
2746                                                                .equals(SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE)) {
2747                                        cleanup(sqlE);
2748                                }
2749                        }
2750
2751                        throw sqlE;
2752                } catch (Exception ex) {
2753                        if (getHighAvailability()) {
2754                                this.needsPing = true;
2755                        } else if (ex instanceof IOException) {
2756                                cleanup(ex);
2757                        }
2758
2759                        SQLException sqlEx = SQLError.createSQLException(
2760                                        Messages.getString("Connection.UnexpectedException"),
2761                                        SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
2762                        sqlEx.initCause(ex);
2763                       
2764                        throw sqlEx;
2765                } finally {
2766                        if (getMaintainTimeStats()) {
2767                                this.lastQueryFinishedTime = System.currentTimeMillis();
2768                        }
2769
2770
2771                        if (getGatherPerformanceMetrics()) {
2772                                long queryTime = System.currentTimeMillis()
2773                                                - queryStartTime;
2774
2775                                registerQueryExecutionTime(queryTime);
2776                        }
2777                }
2778        }
2779
2780        public String extractSqlFromPacket(String possibleSqlQuery,
2781                        Buffer queryPacket, int endOfQueryPacketPosition)
2782                        throws SQLException {
2783
2784                String extractedSql = null;
2785
2786                if (possibleSqlQuery != null) {
2787                        if (possibleSqlQuery.length() > getMaxQuerySizeToLog()) {
2788                                StringBuffer truncatedQueryBuf = new StringBuffer(
2789                                                possibleSqlQuery.substring(0, getMaxQuerySizeToLog()));
2790                                truncatedQueryBuf.append(Messages.getString("MysqlIO.25"));
2791                                extractedSql = truncatedQueryBuf.toString();
2792                        } else {
2793                                extractedSql = possibleSqlQuery;
2794                        }
2795                }
2796
2797                if (extractedSql == null) {
2798                        // This is probably from a client-side prepared
2799                        // statement
2800
2801                        int extractPosition = endOfQueryPacketPosition;
2802
2803                        boolean truncated = false;
2804
2805                        if (endOfQueryPacketPosition > getMaxQuerySizeToLog()) {
2806                                extractPosition = getMaxQuerySizeToLog();
2807                                truncated = true;
2808                        }
2809
2810                        extractedSql = StringUtils.toString(queryPacket.getByteBuffer(), 5,
2811                                        (extractPosition - 5));
2812
2813                        if (truncated) {
2814                                extractedSql += Messages.getString("MysqlIO.25"); //$NON-NLS-1$
2815                        }
2816                }
2817
2818                return extractedSql;
2819
2820        }
2821
2822        public StringBuffer generateConnectionCommentBlock(StringBuffer buf) {
2823                buf.append("/* conn id ");
2824                buf.append(getId());
2825                buf.append(" clock: ");
2826                buf.append(System.currentTimeMillis());
2827                buf.append(" */ ");
2828
2829                return buf;
2830        }
2831
2832        public int getActiveStatementCount() {
2833                // Might not have one of these if
2834                // not tracking open resources
2835                if (this.openStatements != null) {
2836                        synchronized (this.openStatements) {
2837                                return this.openStatements.size();
2838                        }
2839                }
2840
2841                return 0;
2842        }
2843
2844        /**
2845         * Gets the current auto-commit state
2846         *
2847         * @return Current state of auto-commit
2848         * @exception SQLException
2849         *                if an error occurs
2850         * @see setAutoCommit
2851         */
2852        public synchronized boolean getAutoCommit() throws SQLException {
2853                return this.autoCommit;
2854        }
2855
2856        /**
2857         * Optimization to only use one calendar per-session, or calculate it for
2858         * each call, depending on user configuration
2859         */
2860        public Calendar getCalendarInstanceForSessionOrNew() {
2861                if (getDynamicCalendars()) {
2862                        return Calendar.getInstance();
2863                }
2864
2865                return getSessionLockedCalendar();
2866        }
2867
2868        /**
2869         * Return the connections current catalog name, or null if no catalog name
2870         * is set, or we dont support catalogs.
2871         * <p>
2872         * <b>Note:</b> MySQL's notion of catalogs are individual databases.
2873         * </p>
2874         *
2875         * @return the current catalog name or null
2876         * @exception SQLException
2877         *                if a database access error occurs
2878         */
2879        public synchronized String getCatalog() throws SQLException {
2880                return this.database;
2881        }
2882
2883        /**
2884         * @return Returns the characterSetMetadata.
2885         */
2886        public synchronized String getCharacterSetMetadata() {
2887                return this.characterSetMetadata;
2888        }
2889
2890        /**
2891         * Returns the locally mapped instance of a charset converter (to avoid
2892         * overhead of static synchronization).
2893         *
2894         * @param javaEncodingName
2895         *            the encoding name to retrieve
2896         * @return a character converter, or null if one couldn't be mapped.
2897         */
2898        public SingleByteCharsetConverter getCharsetConverter(
2899                        String javaEncodingName) throws SQLException {
2900                if (javaEncodingName == null) {
2901                        return null;
2902                }
2903
2904                if (this.usePlatformCharsetConverters) {
2905                        return null; // we'll use Java's built-in routines for this
2906                                     // they're finally fast enough
2907                }
2908               
2909                SingleByteCharsetConverter converter = null;
2910               
2911                synchronized (this.charsetConverterMap) {
2912                        Object asObject = this.charsetConverterMap
2913                        .get(javaEncodingName);
2914
2915                        if (asObject == CHARSET_CONVERTER_NOT_AVAILABLE_MARKER) {
2916                                return null;
2917                        }
2918                       
2919                        converter = (SingleByteCharsetConverter)asObject;
2920                       
2921                        if (converter == null) {
2922                                try {
2923                                        converter = SingleByteCharsetConverter.getInstance(
2924                                                        javaEncodingName, this);
2925
2926                                        if (converter == null) {
2927                                                this.charsetConverterMap.put(javaEncodingName,
2928                                                                CHARSET_CONVERTER_NOT_AVAILABLE_MARKER);
2929                                        } else {
2930                                                this.charsetConverterMap.put(javaEncodingName, converter);
2931                                        }
2932                                } catch (UnsupportedEncodingException unsupEncEx) {
2933                                        this.charsetConverterMap.put(javaEncodingName,
2934                                                        CHARSET_CONVERTER_NOT_AVAILABLE_MARKER);
2935
2936                                        converter = null;
2937                                }
2938                        }
2939                }
2940
2941                return converter;
2942        }
2943
2944        /**
2945         * Returns the Java character encoding name for the given MySQL server
2946         * charset index
2947         *
2948         * @param charsetIndex
2949         * @return the Java character encoding name for the given MySQL server
2950         *         charset index
2951         * @throws SQLException
2952         *             if the character set index isn't known by the driver
2953         */
2954        public String getCharsetNameForIndex(int charsetIndex)
2955                        throws SQLException {
2956                String charsetName = null;
2957
2958                if (getUseOldUTF8Behavior()) {
2959                        return getEncoding();
2960                }
2961
2962                if (charsetIndex != MysqlDefs.NO_CHARSET_INFO) {
2963                        try {
2964                                charsetName = this.indexToJavaCharset.get(charsetIndex);
2965                                // checking against static maps if no custom charset found
2966                                if (charsetName==null) charsetName = CharsetMapping.INDEX_TO_CHARSET[charsetIndex];
2967
2968                                if ("sjis".equalsIgnoreCase(charsetName) || 
2969                                                "MS932".equalsIgnoreCase(charsetName) /* for JDK6 */) {
2970                                        // Use our encoding so that code pages like Cp932 work
2971                                        if (CharsetMapping.isAliasForSjis(getEncoding())) {
2972                                                charsetName = getEncoding();
2973                                        }
2974                                }
2975                        } catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
2976                                throw SQLError.createSQLException(
2977                                                "Unknown character set index for field '"
2978                                                                + charsetIndex + "' received from server.",
2979                                                SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
2980                        } catch (RuntimeException ex) {
2981                                SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
2982                                sqlEx.initCause(ex);
2983                                throw sqlEx;
2984                        }
2985
2986                        // Punt
2987                        if (charsetName == null) {
2988                                charsetName = getEncoding();
2989                        }
2990                } else {
2991                        charsetName = getEncoding();
2992                }
2993
2994                return charsetName;
2995        }
2996
2997        /**
2998         * DOCUMENT ME!
2999         *
3000         * @return Returns the defaultTimeZone.
3001         */
3002        public TimeZone getDefaultTimeZone() {
3003                return this.defaultTimeZone;
3004        }
3005
3006        public String getErrorMessageEncoding() {
3007                return errorMessageEncoding;
3008        }
3009
3010        /**
3011         * @see Connection#getHoldability()
3012         */
3013        public int getHoldability() throws SQLException {
3014                return java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT;
3015        }
3016
3017        public long getId() {
3018                return this.connectionId;
3019        }
3020
3021        /**
3022         * NOT JDBC-Compliant, but clients can use this method to determine how long
3023         * this connection has been idle. This time (reported in milliseconds) is
3024         * updated once a query has completed.
3025         *
3026         * @return number of ms that this connection has been idle, 0 if the driver
3027         *         is busy retrieving results.
3028         */
3029        public synchronized long getIdleFor() {
3030                if (this.lastQueryFinishedTime == 0) {
3031                        return 0;
3032                }
3033
3034                long now = System.currentTimeMillis();
3035                long idleTime = now - this.lastQueryFinishedTime;
3036
3037                return idleTime;
3038        }
3039
3040        /**
3041         * Returns the IO channel to the server
3042         *
3043         * @return the IO channel to the server
3044         * @throws SQLException
3045         *             if the connection is closed.
3046         */
3047        public MysqlIO getIO() throws SQLException {
3048                if ((this.io == null) || this.isClosed) {
3049                        throw SQLError.createSQLException(
3050                                        "Operation not allowed on closed connection",
3051                                        SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor());
3052                }
3053
3054                return this.io;
3055        }
3056
3057        /**
3058         * Returns the log mechanism that should be used to log information from/for
3059         * this Connection.
3060         *
3061         * @return the Log instance to use for logging messages.
3062         * @throws SQLException
3063         *             if an error occurs
3064         */
3065        public Log getLog() throws SQLException {
3066                return this.log;
3067        }
3068
3069        public int getMaxBytesPerChar(String javaCharsetName) throws SQLException {
3070                return getMaxBytesPerChar(null, javaCharsetName);
3071        }
3072       
3073        public int getMaxBytesPerChar(Integer charsetIndex, String javaCharsetName) throws SQLException {
3074               
3075                String charset = null;
3076               
3077                try {
3078                        // if we can get it by charsetIndex just doing it
3079
3080                        // getting charset name from dynamic maps in connection;
3081                        // we do it before checking against static maps because custom charset on server
3082                        // can be mapped to index from our static map key's diapason
3083                        charset = this.indexToCustomMysqlCharset.get(charsetIndex);
3084                        // checking against static maps if no custom charset found
3085                        if (charset==null) charset = CharsetMapping.STATIC_INDEX_TO_MYSQL_CHARSET_MAP.get(charsetIndex);
3086
3087                        // if we didn't find charset name by index
3088                        if (charset == null) {
3089                                charset = CharsetMapping.getMysqlEncodingForJavaEncoding(javaCharsetName, this);
3090                                if ((this.io.serverCharsetIndex == 33) && (versionMeetsMinimum(5, 5, 3)) && (javaCharsetName.equalsIgnoreCase("UTF-8"))) {
3091                                        //Avoid UTF8mb4
3092                                        charset = "utf8";
3093                                }
3094                        }
3095       
3096                        // checking against dynamic maps in connection
3097                        Integer mblen = this.mysqlCharsetToCustomMblen.get(charset);
3098
3099                        // checking against static maps
3100                        if (mblen == null) mblen = CharsetMapping.STATIC_CHARSET_TO_NUM_BYTES_MAP.get(charset);
3101                        if (mblen == null) mblen = CharsetMapping.STATIC_4_0_CHARSET_TO_NUM_BYTES_MAP.get(charset);
3102       
3103                        if (mblen != null) return mblen.intValue();
3104                } catch (SQLException ex) {
3105                        throw ex;
3106                } catch (RuntimeException ex) {
3107                        SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
3108                        sqlEx.initCause(ex);
3109                        throw sqlEx;
3110                }
3111
3112                return 1; // we don't know
3113        }
3114
3115        /**
3116         * A connection's database is able to provide information describing its
3117         * tables, its supported SQL grammar, its stored procedures, the
3118         * capabilities of this connection, etc. This information is made available
3119         * through a DatabaseMetaData object.
3120         *
3121         * @return a DatabaseMetaData object for this connection
3122         * @exception SQLException
3123         *                if a database access error occurs
3124         */
3125        public java.sql.DatabaseMetaData getMetaData() throws SQLException {
3126                return getMetaData(true, true);
3127        }
3128       
3129        private java.sql.DatabaseMetaData getMetaData(boolean checkClosed, boolean checkForInfoSchema) throws SQLException {
3130                if (checkClosed) {
3131                        checkClosed(); 
3132                }
3133               
3134                return com.mysql.jdbc.DatabaseMetaData.getInstance(getLoadBalanceSafeProxy(), this.database, checkForInfoSchema);
3135        }
3136
3137        public java.sql.Statement getMetadataSafeStatement() throws SQLException {
3138                java.sql.Statement stmt = createStatement();
3139
3140                if (stmt.getMaxRows() != 0) {
3141                        stmt.setMaxRows(0);
3142                }
3143
3144                stmt.setEscapeProcessing(false);
3145               
3146                if (stmt.getFetchSize() != 0) {
3147                        stmt.setFetchSize(0);
3148                }
3149
3150                return stmt;
3151        }
3152
3153        /**
3154         * Returns the packet buffer size the MySQL server reported upon connection
3155         *
3156         * @return DOCUMENT ME!
3157         */
3158        public int getNetBufferLength() {
3159                return this.netBufferLength;
3160        }
3161
3162        /**
3163         * Returns the server's character set
3164         *
3165         * @return the server's character set.
3166         */
3167        public String getServerCharacterEncoding() {
3168                if (this.io.versionMeetsMinimum(4, 1, 0)) {
3169                        String charset = this.indexToCustomMysqlCharset.get(this.io.serverCharsetIndex);
3170                        if (charset == null) charset = CharsetMapping.STATIC_INDEX_TO_MYSQL_CHARSET_MAP.get(this.io.serverCharsetIndex);
3171                        return charset != null ? charset : this.serverVariables.get("character_set_server");
3172                }
3173                return this.serverVariables.get("character_set");
3174        }
3175
3176        public int getServerMajorVersion() {
3177                return this.io.getServerMajorVersion();
3178        }
3179
3180        public int getServerMinorVersion() {
3181                return this.io.getServerMinorVersion();
3182        }
3183
3184        public int getServerSubMinorVersion() {
3185                return this.io.getServerSubMinorVersion();
3186        }
3187
3188        /**
3189         * DOCUMENT ME!
3190         *
3191         * @return DOCUMENT ME!
3192         */
3193        public TimeZone getServerTimezoneTZ() {
3194                return this.serverTimezoneTZ;
3195        }
3196       
3197       
3198        public String getServerVariable(String variableName) {
3199                if (this.serverVariables != null) {
3200                        return this.serverVariables.get(variableName);
3201                }
3202
3203                return null;
3204        }
3205
3206        public String getServerVersion() {
3207                return this.io.getServerVersion();
3208        }
3209
3210        public Calendar getSessionLockedCalendar() {
3211       
3212                return this.sessionCalendar;
3213        }
3214
3215        /**
3216         * Get this Connection's current transaction isolation mode.
3217         *
3218         * @return the current TRANSACTION_ mode value
3219         * @exception SQLException
3220         *                if a database access error occurs
3221         */
3222        public synchronized int getTransactionIsolation() throws SQLException {
3223
3224                if (this.hasIsolationLevels && !getUseLocalSessionState()) {
3225                        java.sql.Statement stmt = null;
3226                        java.sql.ResultSet rs = null;
3227
3228                        try {
3229                                stmt = getMetadataSafeStatement();
3230
3231                                String query = null;
3232
3233                                int offset = 0;
3234                               
3235                                if (versionMeetsMinimum(4, 0, 3)) {
3236                                        query = "SELECT @@session.tx_isolation";
3237                                        offset = 1;
3238                                } else {
3239                                        query = "SHOW VARIABLES LIKE 'transaction_isolation'";
3240                                        offset = 2;
3241                                }
3242
3243                                rs = stmt.executeQuery(query);
3244
3245                                if (rs.next()) {
3246                                        String s = rs.getString(offset);
3247
3248                                        if (s != null) {
3249                                                Integer intTI = mapTransIsolationNameToValue.get(s);
3250
3251                                                if (intTI != null) {
3252                                                        return intTI.intValue();
3253                                                }
3254                                        }
3255
3256                                        throw SQLError.createSQLException(
3257                                                        "Could not map transaction isolation '" + s
3258                                                                        + " to a valid JDBC level.",
3259                                                        SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
3260                                }
3261
3262                                throw SQLError.createSQLException(
3263                                                "Could not retrieve transaction isolation level from server",
3264                                                SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
3265
3266                        } finally {
3267                                if (rs != null) {
3268                                        try {
3269                                                rs.close();
3270                                        } catch (Exception ex) {
3271                                                // ignore
3272                                                ;
3273                                        }
3274
3275                                        rs = null;
3276                                }
3277
3278                                if (stmt != null) {
3279                                        try {
3280                                                stmt.close();
3281                                        } catch (Exception ex) {
3282                                                // ignore
3283                                                ;
3284                                        }
3285
3286                                        stmt = null;
3287                                }
3288                        }
3289                }
3290
3291                return this.isolationLevel;
3292        }
3293
3294        /**
3295         * JDBC 2.0 Get the type-map object associated with this connection. By
3296         * default, the map returned is empty.
3297         *
3298         * @return the type map
3299         * @throws SQLException
3300         *             if a database error occurs
3301         */
3302        public synchronized java.util.Map<String,Class<?>> getTypeMap() throws SQLException {
3303                if (this.typeMap == null) {
3304                        this.typeMap = new HashMap<String,Class<?>>();
3305                }
3306
3307                return this.typeMap;
3308        }
3309
3310        public String getURL() {
3311                return this.myURL;
3312        }
3313
3314        public String getUser() {
3315                return this.user;
3316        }
3317
3318        public Calendar getUtcCalendar() {
3319                return this.utcCalendar;
3320        }
3321       
3322        /**
3323         * The first warning reported by calls on this Connection is returned.
3324         * <B>Note:</B> Sebsequent warnings will be changed to this
3325         * java.sql.SQLWarning
3326         *
3327         * @return the first java.sql.SQLWarning or null
3328         * @exception SQLException
3329         *                if a database access error occurs
3330         */
3331        public SQLWarning getWarnings() throws SQLException {
3332                return null;
3333        }
3334
3335        public boolean hasSameProperties(Connection c) {
3336                return this.props.equals(c.getProperties());
3337        }
3338
3339        public Properties getProperties() {
3340                return this.props;
3341        }
3342       
3343        public boolean hasTriedMaster() {
3344                return this.hasTriedMasterFlag;
3345        }
3346
3347        public void incrementNumberOfPreparedExecutes() {
3348                if (getGatherPerformanceMetrics()) {
3349                        this.numberOfPreparedExecutes++;
3350
3351                        // We need to increment this, because
3352                        // server-side prepared statements bypass
3353                        // any execution by the connection itself...
3354                        this.numberOfQueriesIssued++;
3355                }
3356        }
3357
3358        public void incrementNumberOfPrepares() {
3359                if (getGatherPerformanceMetrics()) {
3360                        this.numberOfPrepares++;
3361                }
3362        }
3363
3364        public void incrementNumberOfResultSetsCreated() {
3365                if (getGatherPerformanceMetrics()) {
3366                        this.numberOfResultSetsCreated++;
3367                }
3368        }
3369
3370        /**
3371         * Initializes driver properties that come from URL or properties passed to
3372         * the driver manager.
3373         *
3374         * @param info
3375         *            DOCUMENT ME!
3376         * @throws SQLException
3377         *             DOCUMENT ME!
3378         */
3379        private void initializeDriverProperties(Properties info)
3380                        throws SQLException {
3381                initializeProperties(info);
3382               
3383                String exceptionInterceptorClasses = getExceptionInterceptors();
3384               
3385                if (exceptionInterceptorClasses != null && !"".equals(exceptionInterceptorClasses)) {
3386                        this.exceptionInterceptor = new ExceptionInterceptorChain(exceptionInterceptorClasses);
3387                        this.exceptionInterceptor.init(this, info);
3388                }
3389               
3390                this.usePlatformCharsetConverters = getUseJvmCharsetConverters();
3391
3392                this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME, getExceptionInterceptor());
3393
3394                if (getProfileSql() || getUseUsageAdvisor()) {
3395                        this.eventSink = ProfilerEventHandlerFactory.getInstance(getLoadBalanceSafeProxy());
3396                }
3397
3398                if (getCachePreparedStatements()) {
3399                        createPreparedStatementCaches();               
3400                }
3401
3402                if (getNoDatetimeStringSync() && getUseTimezone()) {
3403                        throw SQLError.createSQLException(
3404                                        "Can't enable noDatetimeSync and useTimezone configuration "
3405                                                        + "properties at the same time",
3406                                        SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
3407                }
3408               
3409                if (getCacheCallableStatements()) {
3410                        this.parsedCallableStatementCache = new LRUCache(
3411                                        getCallableStatementCacheSize());
3412                }
3413               
3414                if (getAllowMultiQueries()) {
3415                        setCacheResultSetMetadata(false); // we don't handle this yet
3416                }
3417               
3418                if (getCacheResultSetMetadata()) {
3419                        this.resultSetMetadataCache = new LRUCache(
3420                                        getMetadataCacheSize());
3421                }
3422        }
3423
3424        /**
3425         * Sets varying properties that depend on server information. Called once we
3426         * have connected to the server.
3427         *
3428         * @param info
3429         *            DOCUMENT ME!
3430         * @throws SQLException
3431         *             DOCUMENT ME!
3432         */
3433        private void initializePropsFromServer() throws SQLException {
3434                String connectionInterceptorClasses = getConnectionLifecycleInterceptors();
3435               
3436                this.connectionLifecycleInterceptors = null;
3437               
3438                if (connectionInterceptorClasses != null) {
3439                        this.connectionLifecycleInterceptors = Util.loadExtensions(this, this.props, 
3440                                        connectionInterceptorClasses, 
3441                                        "Connection.badLifecycleInterceptor", getExceptionInterceptor());
3442                }
3443               
3444                setSessionVariables();
3445
3446                //
3447                // the "boolean" type didn't come along until MySQL-4.1
3448                //
3449
3450                if (!versionMeetsMinimum(4, 1, 0)) {
3451                        setTransformedBitIsBoolean(false);
3452                }
3453
3454                this.parserKnowsUnicode = versionMeetsMinimum(4, 1, 0);
3455
3456                //
3457                // Users can turn off detection of server-side prepared statements
3458                //
3459                if (getUseServerPreparedStmts() && versionMeetsMinimum(4, 1, 0)) {
3460                        this.useServerPreparedStmts = true;
3461
3462                        if (versionMeetsMinimum(5, 0, 0) && !versionMeetsMinimum(5, 0, 3)) {
3463                                this.useServerPreparedStmts = false; // 4.1.2+ style prepared
3464                                // statements
3465                                // don't work on these versions
3466                        }
3467                }
3468               
3469                //
3470                // If version is greater than 3.21.22 get the server
3471                // variables.
3472                if (versionMeetsMinimum(3, 21, 22)) {
3473                        loadServerVariables();
3474
3475                        if (versionMeetsMinimum(5, 0, 2)) {
3476                                this.autoIncrementIncrement = getServerVariableAsInt("auto_increment_increment", 1);
3477                        } else {
3478                                this.autoIncrementIncrement = 1;
3479                        }
3480                       
3481                        buildCollationMapping();
3482
3483                        LicenseConfiguration.checkLicenseType(this.serverVariables);
3484
3485                        String lowerCaseTables = this.serverVariables.get("lower_case_table_names");
3486
3487                        this.lowerCaseTableNames = "on".equalsIgnoreCase(lowerCaseTables)
3488                                        || "1".equalsIgnoreCase(lowerCaseTables)
3489                                        || "2".equalsIgnoreCase(lowerCaseTables);
3490
3491                        this.storesLowerCaseTableName = "1".equalsIgnoreCase(lowerCaseTables) ||
3492                                        "on".equalsIgnoreCase(lowerCaseTables);
3493
3494                        configureTimezone();
3495
3496                        if (this.serverVariables.containsKey("max_allowed_packet")) {
3497                                int serverMaxAllowedPacket = getServerVariableAsInt("max_allowed_packet", -1);
3498                                // use server value if maxAllowedPacket hasn't been given, or max_allowed_packet is smaller
3499                                if (serverMaxAllowedPacket != -1 && (serverMaxAllowedPacket < getMaxAllowedPacket() ||
3500                                                getMaxAllowedPacket() <= 0))
3501                                        setMaxAllowedPacket(serverMaxAllowedPacket);
3502                                else if (serverMaxAllowedPacket == -1 && getMaxAllowedPacket() == -1)
3503                                        setMaxAllowedPacket(65535);
3504                               
3505                                int preferredBlobSendChunkSize = getBlobSendChunkSize();
3506                               
3507                                int allowedBlobSendChunkSize = Math.min(preferredBlobSendChunkSize, 
3508                                                getMaxAllowedPacket()) - 
3509                                                ServerPreparedStatement.BLOB_STREAM_READ_BUF_SIZE 
3510                                                - 11 /* LONG_DATA and MySQLIO packet header size */;
3511                               
3512                                setBlobSendChunkSize(String.valueOf(allowedBlobSendChunkSize));
3513                        }
3514
3515                        if (this.serverVariables.containsKey("net_buffer_length")) {
3516                                this.netBufferLength = getServerVariableAsInt("net_buffer_length", 16 * 1024);
3517                        }
3518
3519                        checkTransactionIsolationLevel();
3520                       
3521                        if (!versionMeetsMinimum(4, 1, 0)) {
3522                                checkServerEncoding();
3523                        }
3524
3525                        this.io.checkForCharsetMismatch();
3526
3527                        if (this.serverVariables.containsKey("sql_mode")) {
3528                                int sqlMode = 0;
3529
3530                                String sqlModeAsString = this.serverVariables.get("sql_mode");
3531                                try {
3532                                        sqlMode = Integer.parseInt(sqlModeAsString);
3533                                } catch (NumberFormatException nfe) {
3534                                        // newer versions of the server has this as a string-y
3535                                        // list...
3536                                        sqlMode = 0;
3537
3538                                        if (sqlModeAsString != null) {
3539                                                if (sqlModeAsString.indexOf("ANSI_QUOTES") != -1) {
3540                                                        sqlMode |= 4;
3541                                                }
3542
3543                                                if (sqlModeAsString.indexOf("NO_BACKSLASH_ESCAPES") != -1) {
3544                                                        this.noBackslashEscapes = true;
3545                                                }
3546                                        }
3547                                }
3548
3549                                if ((sqlMode & 4) > 0) {
3550                                        this.useAnsiQuotes = true;
3551                                } else {
3552                                        this.useAnsiQuotes = false;
3553                                }
3554                        }
3555                }
3556               
3557                try {
3558                        this.errorMessageEncoding = 
3559                                CharsetMapping.getCharacterEncodingForErrorMessages(this);
3560                } catch (SQLException ex) {
3561                        throw ex;
3562                } catch (RuntimeException ex) {
3563                        SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
3564                        sqlEx.initCause(ex);
3565                        throw sqlEx;
3566                }
3567               
3568               
3569                boolean overrideDefaultAutocommit = isAutoCommitNonDefaultOnServer();
3570       
3571                configureClientCharacterSet(false);
3572
3573                if (versionMeetsMinimum(3, 23, 15)) {
3574                        this.transactionsSupported = true;
3575                       
3576                        if (!overrideDefaultAutocommit) {
3577                                setAutoCommit(true); // to override anything
3578                                // the server is set to...reqd
3579                                // by JDBC spec.
3580                        }
3581                } else {
3582                        this.transactionsSupported = false;
3583                }
3584               
3585
3586                if (versionMeetsMinimum(3, 23, 36)) {
3587                        this.hasIsolationLevels = true;
3588                } else {
3589                        this.hasIsolationLevels = false;
3590                }
3591
3592                this.hasQuotedIdentifiers = versionMeetsMinimum(3, 23, 6);
3593
3594                this.io.resetMaxBuf();
3595
3596                //
3597                // If we're using MySQL 4.1.0 or newer, we need to figure
3598                // out what character set metadata will be returned in,
3599                // and then map that to a Java encoding name.
3600                //
3601                // We've already set it, and it might be different than what
3602                // was originally on the server, which is why we use the
3603                // "special" key to retrieve it
3604                if (this.io.versionMeetsMinimum(4, 1, 0)) {
3605                        String characterSetResultsOnServerMysql = this.serverVariables.get(JDBC_LOCAL_CHARACTER_SET_RESULTS);
3606
3607                        if (characterSetResultsOnServerMysql == null
3608                                        || StringUtils.startsWithIgnoreCaseAndWs(
3609                                                        characterSetResultsOnServerMysql, "NULL")
3610                                        || characterSetResultsOnServerMysql.length() == 0) {
3611                                String defaultMetadataCharsetMysql = this.serverVariables.get("character_set_system");
3612                                String defaultMetadataCharset = null;
3613
3614                                if (defaultMetadataCharsetMysql != null) {
3615                                        defaultMetadataCharset = getJavaEncodingForMysqlEncoding(defaultMetadataCharsetMysql);
3616                                } else {
3617                                        defaultMetadataCharset = "UTF-8";
3618                                }
3619
3620                                this.characterSetMetadata = defaultMetadataCharset;
3621                        } else {
3622                                this.characterSetResultsOnServer = getJavaEncodingForMysqlEncoding(characterSetResultsOnServerMysql);
3623                                this.characterSetMetadata = this.characterSetResultsOnServer;
3624                        }
3625                } else {
3626                        this.characterSetMetadata = getEncoding();
3627                }
3628
3629                //
3630                // Query cache is broken wrt. multi-statements before MySQL-4.1.10
3631                //
3632
3633                if (versionMeetsMinimum(4, 1, 0)
3634                                && !this.versionMeetsMinimum(4, 1, 10)
3635                                && getAllowMultiQueries()) {
3636                        if (isQueryCacheEnabled()) {
3637                                setAllowMultiQueries(false);
3638                        }
3639                }
3640               
3641                if (versionMeetsMinimum(5, 0, 0) && 
3642                                (getUseLocalTransactionState() || getElideSetAutoCommits()) &&
3643                                isQueryCacheEnabled() && !versionMeetsMinimum(6, 0, 10)) {
3644                        // Can't trust the server status flag on the wire if query cache is enabled,
3645                        // due to Bug#36326
3646                        setUseLocalTransactionState(false);
3647                        setElideSetAutoCommits(false);
3648                }
3649               
3650                //
3651                // Server can do this more efficiently for us
3652                //
3653               
3654                setupServerForTruncationChecks();
3655        }
3656
3657        private boolean isQueryCacheEnabled() {
3658                return "ON".equalsIgnoreCase(this.serverVariables.get("query_cache_type"))
3659                                && !"0".equalsIgnoreCase(this.serverVariables.get("query_cache_size"));
3660        }
3661
3662        private int getServerVariableAsInt(String variableName, int fallbackValue)
3663                        throws SQLException {
3664                try {
3665                        return Integer.parseInt(this.serverVariables.get(variableName));
3666                } catch (NumberFormatException nfe) {
3667                        getLog().logWarn(Messages.getString("Connection.BadValueInServerVariables", new Object[] {variableName, 
3668                                        this.serverVariables.get(variableName), Integer.valueOf(fallbackValue)}));
3669                       
3670                        return fallbackValue;
3671                }
3672        }
3673
3674        /**
3675         * Has the default autocommit value of 0 been changed on the server
3676         * via init_connect?
3677         *
3678         * @return true if autocommit is not the default of '0' on the server.
3679         *
3680         * @throws SQLException
3681         */
3682        private boolean isAutoCommitNonDefaultOnServer() throws SQLException {
3683                boolean overrideDefaultAutocommit = false;
3684               
3685                String initConnectValue = this.serverVariables.get("init_connect");
3686
3687                if (versionMeetsMinimum(4, 1, 2) && initConnectValue != null
3688                                && initConnectValue.length() > 0) {
3689                        if (!getElideSetAutoCommits()) {
3690                                // auto-commit might have changed
3691                                java.sql.ResultSet rs = null;
3692                                java.sql.Statement stmt = null;
3693                               
3694                                try {
3695                                        stmt = getMetadataSafeStatement();
3696                                       
3697                                        rs = stmt.executeQuery("SELECT @@session.autocommit");
3698                                       
3699                                        if (rs.next()) {
3700                                                this.autoCommit = rs.getBoolean(1);
3701                                                if (this.autoCommit != true) {
3702                                                        overrideDefaultAutocommit = true;
3703                                                }
3704                                        }
3705                                       
3706                                } finally {
3707                                        if (rs != null) {
3708                                                try {
3709                                                        rs.close();
3710                                                } catch (SQLException sqlEx) {
3711                                                        // do nothing
3712                                                }
3713                                        }
3714                                       
3715                                        if (stmt != null) {
3716                                                try {
3717                                                        stmt.close();
3718                                                } catch (SQLException sqlEx) {
3719                                                        // do nothing
3720                                                }
3721                                        }
3722                                }
3723                        } else {
3724                                if (this.getIO().isSetNeededForAutoCommitMode(true)) {
3725                                        // we're not in standard autocommit=true mode
3726                                        this.autoCommit = false;
3727                                        overrideDefaultAutocommit = true;
3728                                }
3729                        }
3730                }
3731               
3732                return overrideDefaultAutocommit;
3733        }
3734
3735        public boolean isClientTzUTC() {
3736                return this.isClientTzUTC;
3737        }
3738
3739        /**
3740         * DOCUMENT ME!
3741         *
3742         * @return DOCUMENT ME!
3743         */
3744        public boolean isClosed() {
3745                return this.isClosed;
3746        }
3747
3748        public boolean isCursorFetchEnabled() throws SQLException {
3749                return (versionMeetsMinimum(5, 0, 2) && getUseCursorFetch());
3750        }
3751
3752        public boolean isInGlobalTx() {
3753                return this.isInGlobalTx;
3754        }
3755
3756        /**
3757         * Is this connection connected to the first host in the list if
3758         * there is a list of servers in the URL?
3759         *
3760         * @return true if this connection is connected to the first in
3761         * the list.
3762         */
3763        public synchronized boolean isMasterConnection() {
3764                return false; // handled higher up
3765        }
3766
3767        /**
3768         * Is the server in a sql_mode that doesn't allow us to use \\ to escape
3769         * things?
3770         *
3771         * @return Returns the noBackslashEscapes.
3772         */
3773        public boolean isNoBackslashEscapesSet() {
3774                return this.noBackslashEscapes;
3775        }
3776
3777        public boolean isReadInfoMsgEnabled() {
3778                return this.readInfoMsg;
3779        }
3780
3781        /**
3782         * Tests to see if the connection is in Read Only Mode. Note that we cannot
3783         * really put the database in read only mode, but we pretend we can by
3784         * returning the value of the readOnly flag
3785         *
3786         * @return true if the connection is read only
3787         * @exception SQLException
3788         *                if a database access error occurs
3789         */
3790        public boolean isReadOnly() throws SQLException {
3791                return this.readOnly;
3792        }
3793
3794        public boolean isRunningOnJDK13() {
3795                return this.isRunningOnJDK13;
3796        }
3797
3798        public synchronized boolean isSameResource(Connection otherConnection) {
3799                if (otherConnection == null) {
3800                        return false;
3801                }
3802               
3803                boolean directCompare = true;
3804               
3805                String otherHost = ((ConnectionImpl)otherConnection).origHostToConnectTo;
3806                String otherOrigDatabase = ((ConnectionImpl)otherConnection).origDatabaseToConnectTo;
3807                String otherCurrentCatalog = ((ConnectionImpl)otherConnection).database;
3808               
3809                if (!nullSafeCompare(otherHost, this.origHostToConnectTo)) {
3810                        directCompare = false;
3811                } else if (otherHost != null && otherHost.indexOf(',') == -1 && 
3812                                otherHost.indexOf(':') == -1) {
3813                        // need to check port numbers
3814                        directCompare = (((ConnectionImpl)otherConnection).origPortToConnectTo == 
3815                                this.origPortToConnectTo);
3816                }
3817               
3818                if (directCompare) {
3819                        if (!nullSafeCompare(otherOrigDatabase, this.origDatabaseToConnectTo)) {                        directCompare = false;
3820                                directCompare = false;
3821                        } else if (!nullSafeCompare(otherCurrentCatalog, this.database)) {
3822                                directCompare = false;
3823                        }
3824                }
3825
3826                if (directCompare) {
3827                        return true;
3828                }
3829               
3830                // Has the user explicitly set a resourceId?
3831                String otherResourceId = ((ConnectionImpl)otherConnection).getResourceId();
3832                String myResourceId = getResourceId();
3833               
3834                if (otherResourceId != null || myResourceId != null) {
3835                        directCompare = nullSafeCompare(otherResourceId, myResourceId);
3836                       
3837                        if (directCompare) {
3838                                return true;
3839                        }
3840                }
3841               
3842                return false;   
3843        }
3844
3845        public boolean isServerTzUTC() {
3846                return this.isServerTzUTC;
3847        }
3848
3849        private boolean usingCachedConfig = false;
3850
3851        /**
3852         * Loads the result of 'SHOW VARIABLES' into the serverVariables field so
3853         * that the driver can configure itself.
3854         *
3855         * @throws SQLException
3856         *             if the 'SHOW VARIABLES' query fails for any reason.
3857         */
3858        private void loadServerVariables() throws SQLException {
3859
3860                if (getCacheServerConfiguration()) {
3861                        synchronized (serverConfigByUrl) {
3862                                Map<String, String> cachedVariableMap = serverConfigByUrl.get(getURL());
3863
3864                                if (cachedVariableMap != null) {
3865                                        this.serverVariables = cachedVariableMap;
3866                                        this.usingCachedConfig = true;
3867
3868                                        return;
3869                                }
3870                        }
3871                }
3872
3873                java.sql.Statement stmt = null;
3874                java.sql.ResultSet results = null;
3875
3876                try {
3877                        stmt = getMetadataSafeStatement();
3878                       
3879                        String version = this.dbmd.getDriverVersion();
3880                       
3881                        if (version != null && version.indexOf('*') != -1) {
3882                                StringBuffer buf = new StringBuffer(version.length() + 10);
3883
3884                                for (int i = 0; i < version.length(); i++) {
3885                                        char c = version.charAt(i);
3886
3887                                        if (c == '*') {
3888                                                buf.append("[star]");
3889                                        } else {
3890                                                buf.append(c);
3891                                        }
3892                                }
3893
3894                                version = buf.toString();
3895                        }
3896
3897                        String versionComment = (this.getParanoid() || version == null) ? ""
3898                                        : "/* " + version + " */";
3899                       
3900                        String query = versionComment + "SHOW VARIABLES";
3901                       
3902                        if (versionMeetsMinimum(5, 0, 3)) {
3903                                query = versionComment + "SHOW VARIABLES WHERE Variable_name ='language'"
3904                                        + " OR Variable_name = 'net_write_timeout'"
3905                                        + " OR Variable_name = 'interactive_timeout'"
3906                                        + " OR Variable_name = 'wait_timeout'"
3907                                        + " OR Variable_name = 'character_set_client'"
3908                                        + " OR Variable_name = 'character_set_connection'"
3909                                        + " OR Variable_name = 'character_set'"
3910                                        + " OR Variable_name = 'character_set_server'"
3911                                        + " OR Variable_name = 'tx_isolation'"
3912                                        + " OR Variable_name = 'transaction_isolation'"
3913                                        + " OR Variable_name = 'character_set_results'"
3914                                        + " OR Variable_name = 'timezone'"
3915                                        + " OR Variable_name = 'time_zone'"
3916                                        + " OR Variable_name = 'system_time_zone'"
3917                                        + " OR Variable_name = 'lower_case_table_names'"
3918                                        + " OR Variable_name = 'max_allowed_packet'"
3919                                        + " OR Variable_name = 'net_buffer_length'"
3920                                        + " OR Variable_name = 'sql_mode'"
3921                                        + " OR Variable_name = 'query_cache_type'"
3922                                        + " OR Variable_name = 'query_cache_size'"
3923                                        + " OR Variable_name = 'init_connect'";
3924                        }
3925                       
3926                        this.serverVariables = new HashMap<String, String>();
3927
3928                        results = stmt.executeQuery(query);
3929                       
3930                        while (results.next()) {
3931                                this.serverVariables.put(results.getString(1), results
3932                                                .getString(2));
3933                        }
3934
3935                        results.close();
3936                        results = null;
3937                       
3938                        if (versionMeetsMinimum(5, 0, 2)) {
3939                                results = stmt.executeQuery(versionComment + "SELECT @@session.auto_increment_increment");
3940                               
3941                                if (results.next()) {
3942                                        this.serverVariables.put("auto_increment_increment", results.getString(1));
3943                                }
3944                        }
3945                       
3946                        if (getCacheServerConfiguration()) {
3947                                synchronized (serverConfigByUrl) {
3948                                        serverConfigByUrl.put(getURL(), this.serverVariables);
3949                                       
3950                                        this.usingCachedConfig = true;
3951                                }
3952                        }
3953                } catch (SQLException e) {
3954                        throw e;
3955                } finally {
3956                        if (results != null) {
3957                                try {
3958                                        results.close();
3959                                } catch (SQLException sqlE) {
3960                                        ;
3961                                }
3962                        }
3963
3964                        if (stmt != null) {
3965                                try {
3966                                        stmt.close();
3967                                } catch (SQLException sqlE) {
3968                                        ;
3969                                }
3970                        }
3971                }
3972        }
3973
3974        private int autoIncrementIncrement = 0;
3975       
3976        public int getAutoIncrementIncrement() {
3977                return this.autoIncrementIncrement;
3978        }
3979       
3980        /**
3981         * Is the server configured to use lower-case table names only?
3982         *
3983         * @return true if lower_case_table_names is 'on'
3984         */
3985        public boolean lowerCaseTableNames() {
3986                return this.lowerCaseTableNames;
3987        }
3988
3989        /**
3990         * Has the maxRows value changed?
3991         *
3992         * @param stmt
3993         *            DOCUMENT ME!
3994         */
3995        public synchronized void maxRowsChanged(Statement stmt) {
3996                if (this.statementsUsingMaxRows == null) {
3997                        this.statementsUsingMaxRows = new HashMap<Statement, Statement>();
3998                }
3999
4000                this.statementsUsingMaxRows.put(stmt, stmt);
4001
4002                this.maxRowsChanged = true;
4003        }
4004
4005        /**
4006         * A driver may convert the JDBC sql grammar into its system's native SQL
4007         * grammar prior to sending it; nativeSQL returns the native form of the
4008         * statement that the driver would have sent.
4009         *
4010         * @param sql
4011         *            a SQL statement that may contain one or more '?' parameter
4012         *            placeholders
4013         * @return the native form of this statement
4014         * @exception SQLException
4015         *                if a database access error occurs
4016         */
4017        public String nativeSQL(String sql) throws SQLException {
4018                if (sql == null) {
4019                        return null;
4020                }
4021
4022                Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
4023                                                          serverSupportsConvertFn(),
4024                                                          getLoadBalanceSafeProxy());
4025
4026                if (escapedSqlResult instanceof String) {
4027                        return (String) escapedSqlResult;
4028                }
4029
4030                return ((EscapeProcessorResult) escapedSqlResult).escapedSql;
4031        }
4032
4033        private CallableStatement parseCallableStatement(String sql)
4034                        throws SQLException {
4035                Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
4036                                serverSupportsConvertFn(), getLoadBalanceSafeProxy());
4037
4038                boolean isFunctionCall = false;
4039                String parsedSql = null;
4040
4041                if (escapedSqlResult instanceof EscapeProcessorResult) {
4042                        parsedSql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
4043                        isFunctionCall = ((EscapeProcessorResult) escapedSqlResult).callingStoredFunction;
4044                } else {
4045                        parsedSql = (String) escapedSqlResult;
4046                        isFunctionCall = false;
4047                }
4048
4049                return CallableStatement.getInstance(getLoadBalanceSafeProxy(), parsedSql, this.database,
4050                                isFunctionCall);
4051        }
4052
4053        /**
4054         * DOCUMENT ME!
4055         *
4056         * @return DOCUMENT ME!
4057         */
4058        public boolean parserKnowsUnicode() {
4059                return this.parserKnowsUnicode;
4060        }
4061
4062        /**
4063         * Detect if the connection is still good
4064         *
4065         * @throws SQLException
4066         *             if the ping fails
4067         */
4068        public void ping() throws SQLException {
4069                pingInternal(true, 0);
4070        }
4071
4072        public void pingInternal(boolean checkForClosedConnection, int timeoutMillis)
4073                        throws SQLException {
4074                if (checkForClosedConnection) {
4075                        checkClosed();
4076                }
4077
4078                long pingMillisLifetime = getSelfDestructOnPingSecondsLifetime();
4079                int pingMaxOperations = getSelfDestructOnPingMaxOperations();
4080
4081                if ((pingMillisLifetime > 0 && (System.currentTimeMillis() - this.connectionCreationTimeMillis) > pingMillisLifetime)
4082                                || (pingMaxOperations > 0 && pingMaxOperations <= this.io
4083                                                .getCommandCount())) {
4084
4085                        close();
4086
4087                        throw SQLError.createSQLException(Messages
4088                                        .getString("Connection.exceededConnectionLifetime"),
4089                                        SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, getExceptionInterceptor());
4090                }
4091                // Need MySQL-3.22.1, but who uses anything older!?
4092                this.io.sendCommand(MysqlDefs.PING, null, null, false, null, timeoutMillis);
4093        }
4094
4095        /**
4096         * DOCUMENT ME!
4097         *
4098         * @param sql
4099         *            DOCUMENT ME!
4100         * @return DOCUMENT ME!
4101         * @throws SQLException
4102         *             DOCUMENT ME!
4103         */
4104        public java.sql.CallableStatement prepareCall(String sql)
4105                        throws SQLException {
4106
4107                return prepareCall(sql, DEFAULT_RESULT_SET_TYPE,
4108                                DEFAULT_RESULT_SET_CONCURRENCY);
4109        }
4110
4111        /**
4112         * JDBC 2.0 Same as prepareCall() above, but allows the default result set
4113         * type and result set concurrency type to be overridden.
4114         *
4115         * @param sql
4116         *            the SQL representing the callable statement
4117         * @param resultSetType
4118         *            a result set type, see ResultSet.TYPE_XXX
4119         * @param resultSetConcurrency
4120         *            a concurrency type, see ResultSet.CONCUR_XXX
4121         * @return a new CallableStatement object containing the pre-compiled SQL
4122         *         statement
4123         * @exception SQLException
4124         *                if a database-access error occurs.
4125         */
4126        public java.sql.CallableStatement prepareCall(String sql,
4127                        int resultSetType, int resultSetConcurrency) throws SQLException {
4128                if (versionMeetsMinimum(5, 0, 0)) {
4129                        CallableStatement cStmt = null;
4130
4131                        if (!getCacheCallableStatements()) {
4132
4133                                cStmt = parseCallableStatement(sql);
4134                        } else {
4135                                synchronized (this.parsedCallableStatementCache) {
4136                                        CompoundCacheKey key = new CompoundCacheKey(getCatalog(), sql);
4137       
4138                                        CallableStatement.CallableStatementParamInfo cachedParamInfo = (CallableStatement.CallableStatementParamInfo) this.parsedCallableStatementCache
4139                                                        .get(key);
4140       
4141                                        if (cachedParamInfo != null) {
4142                                                cStmt = CallableStatement.getInstance(getLoadBalanceSafeProxy(), cachedParamInfo);
4143                                        } else {
4144                                                cStmt = parseCallableStatement(sql);
4145       
4146                                                synchronized (cStmt) {
4147                                                        cachedParamInfo = cStmt.paramInfo;
4148                                                }
4149       
4150                                                this.parsedCallableStatementCache.put(key, cachedParamInfo);
4151                                        }
4152                                }
4153                        }
4154
4155                        cStmt.setResultSetType(resultSetType);
4156                        cStmt.setResultSetConcurrency(resultSetConcurrency);
4157
4158                        return cStmt;
4159                }
4160
4161                throw SQLError.createSQLException("Callable statements not " + "supported.",
4162                                SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor());
4163        }
4164
4165        /**
4166         * @see Connection#prepareCall(String, int, int, int)
4167         */
4168        public java.sql.CallableStatement prepareCall(String sql,
4169                        int resultSetType, int resultSetConcurrency,
4170                        int resultSetHoldability) throws SQLException {
4171                if (getPedantic()) {
4172                        if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
4173                                throw SQLError.createSQLException(
4174                                                "HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
4175                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
4176                        }
4177                }
4178
4179                CallableStatement cStmt = (com.mysql.jdbc.CallableStatement) prepareCall(
4180                                sql, resultSetType, resultSetConcurrency);
4181
4182                return cStmt;
4183        }
4184
4185        /**
4186         * A SQL statement with or without IN parameters can be pre-compiled and
4187         * stored in a PreparedStatement object. This object can then be used to
4188         * efficiently execute this statement multiple times.
4189         * <p>
4190         * <B>Note:</B> This method is optimized for handling parametric SQL
4191         * statements that benefit from precompilation if the driver supports
4192         * precompilation. In this case, the statement is not sent to the database
4193         * until the PreparedStatement is executed. This has no direct effect on
4194         * users; however it does affect which method throws certain
4195         * java.sql.SQLExceptions
4196         * </p>
4197         * <p>
4198         * MySQL does not support precompilation of statements, so they are handled
4199         * by the driver.
4200         * </p>
4201         *
4202         * @param sql
4203         *            a SQL statement that may contain one or more '?' IN parameter
4204         *            placeholders
4205         * @return a new PreparedStatement object containing the pre-compiled
4206         *         statement.
4207         * @exception SQLException
4208         *                if a database access error occurs.
4209         */
4210        public java.sql.PreparedStatement prepareStatement(String sql)
4211                        throws SQLException {
4212                return prepareStatement(sql, DEFAULT_RESULT_SET_TYPE,
4213                                DEFAULT_RESULT_SET_CONCURRENCY);
4214        }
4215
4216        /**
4217         * @see Connection#prepareStatement(String, int)
4218         */
4219        public java.sql.PreparedStatement prepareStatement(String sql,
4220                        int autoGenKeyIndex) throws SQLException {
4221                java.sql.PreparedStatement pStmt = prepareStatement(sql);
4222
4223                ((com.mysql.jdbc.PreparedStatement) pStmt)
4224                                .setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
4225
4226                return pStmt;
4227        }
4228
4229        /**
4230         * JDBC 2.0 Same as prepareStatement() above, but allows the default result
4231         * set type and result set concurrency type to be overridden.
4232         *
4233         * @param sql
4234         *            the SQL query containing place holders
4235         * @param resultSetType
4236         *            a result set type, see ResultSet.TYPE_XXX
4237         * @param resultSetConcurrency
4238         *            a concurrency type, see ResultSet.CONCUR_XXX
4239         * @return a new PreparedStatement object containing the pre-compiled SQL
4240         *         statement
4241         * @exception SQLException
4242         *                if a database-access error occurs.
4243         */
4244        public synchronized java.sql.PreparedStatement prepareStatement(String sql,
4245                        int resultSetType, int resultSetConcurrency) throws SQLException {
4246                checkClosed();
4247
4248                //
4249                // FIXME: Create warnings if can't create results of the given
4250                // type or concurrency
4251                //
4252                PreparedStatement pStmt = null;
4253               
4254                boolean canServerPrepare = true;
4255               
4256                String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
4257               
4258                if (this.useServerPreparedStmts && getEmulateUnsupportedPstmts()) {
4259                        canServerPrepare = canHandleAsServerPreparedStatement(nativeSql);
4260                }
4261               
4262                if (this.useServerPreparedStmts && canServerPrepare) {
4263                        if (this.getCachePreparedStatements()) {
4264                                synchronized (this.serverSideStatementCache) {
4265                                        pStmt = (com.mysql.jdbc.ServerPreparedStatement)this.serverSideStatementCache.remove(sql);
4266                                       
4267                                        if (pStmt != null) {
4268                                                ((com.mysql.jdbc.ServerPreparedStatement)pStmt).setClosed(false);
4269                                                pStmt.clearParameters();
4270                                        }
4271
4272                                        if (pStmt == null) {
4273                                                try {
4274                                                        pStmt = ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql,
4275                                                                        this.database, resultSetType, resultSetConcurrency);
4276                                                        if (sql.length() < getPreparedStatementCacheSqlLimit()) {
4277                                                                ((com.mysql.jdbc.ServerPreparedStatement)pStmt).isCached = true;
4278                                                        }
4279                                                       
4280                                                        pStmt.setResultSetType(resultSetType);
4281                                                        pStmt.setResultSetConcurrency(resultSetConcurrency);
4282                                                } catch (SQLException sqlEx) {
4283                                                        // Punt, if necessary
4284                                                        if (getEmulateUnsupportedPstmts()) {
4285                                                                pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
4286                                                               
4287                                                                if (sql.length() < getPreparedStatementCacheSqlLimit()) {
4288                                                                        this.serverSideStatementCheckCache.put(sql, Boolean.FALSE);
4289                                                                }
4290                                                        } else {
4291                                                                throw sqlEx;
4292                                                        }
4293                                                }
4294                                        }
4295                                }
4296                        } else {
4297                                try {
4298                                        pStmt = ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql,
4299                                                        this.database, resultSetType, resultSetConcurrency);
4300                                       
4301                                        pStmt.setResultSetType(resultSetType);
4302                                        pStmt.setResultSetConcurrency(resultSetConcurrency);
4303                                } catch (SQLException sqlEx) {
4304                                        // Punt, if necessary
4305                                        if (getEmulateUnsupportedPstmts()) {
4306                                                pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
4307                                        } else {
4308                                                throw sqlEx;
4309                                        }
4310                                }
4311                        }
4312                } else {
4313                        pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
4314                }
4315               
4316                return pStmt;
4317        }
4318
4319        /**
4320         * @see Connection#prepareStatement(String, int, int, int)
4321         */
4322        public java.sql.PreparedStatement prepareStatement(String sql,
4323                        int resultSetType, int resultSetConcurrency,
4324                        int resultSetHoldability) throws SQLException {
4325                if (getPedantic()) {
4326                        if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
4327                                throw SQLError.createSQLException(
4328                                                "HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
4329                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
4330                        }
4331                }
4332
4333                return prepareStatement(sql, resultSetType, resultSetConcurrency);
4334        }
4335
4336        /**
4337         * @see Connection#prepareStatement(String, int[])
4338         */
4339        public java.sql.PreparedStatement prepareStatement(String sql,
4340                        int[] autoGenKeyIndexes) throws SQLException {
4341                java.sql.PreparedStatement pStmt = prepareStatement(sql);
4342
4343                ((com.mysql.jdbc.PreparedStatement) pStmt)
4344                                .setRetrieveGeneratedKeys((autoGenKeyIndexes != null)
4345                                                && (autoGenKeyIndexes.length > 0));
4346
4347                return pStmt;
4348        }
4349
4350        /**
4351         * @see Connection#prepareStatement(String, String[])
4352         */
4353        public java.sql.PreparedStatement prepareStatement(String sql,
4354                        String[] autoGenKeyColNames) throws SQLException {
4355                java.sql.PreparedStatement pStmt = prepareStatement(sql);
4356
4357                ((com.mysql.jdbc.PreparedStatement) pStmt)
4358                                .setRetrieveGeneratedKeys((autoGenKeyColNames != null)
4359                                                && (autoGenKeyColNames.length > 0));
4360
4361                return pStmt;
4362        }
4363
4364        /**
4365         * Closes connection and frees resources.
4366         *
4367         * @param calledExplicitly
4368         *            is this being called from close()
4369         * @param issueRollback
4370         *            should a rollback() be issued?
4371         * @throws SQLException
4372         *             if an error occurs
4373         */
4374        public void realClose(boolean calledExplicitly, boolean issueRollback,
4375                        boolean skipLocalTeardown, Throwable reason) throws SQLException {
4376                SQLException sqlEx = null;
4377
4378                if (this.isClosed()) {
4379                        return;
4380                }
4381               
4382                this.forceClosedReason = reason;
4383               
4384                try {
4385                        if (!skipLocalTeardown) {
4386                                if (!getAutoCommit() && issueRollback) {
4387                                        try {
4388                                                rollback();
4389                                        } catch (SQLException ex) {
4390                                                sqlEx = ex;
4391                                        }
4392                                }
4393
4394                                reportMetrics();
4395
4396                                if (getUseUsageAdvisor()) {
4397                                        if (!calledExplicitly) {
4398                                                String message = "Connection implicitly closed by Driver. You should call Connection.close() from your code to free resources more efficiently and avoid resource leaks.";
4399
4400                                                this.eventSink.consumeEvent(new ProfilerEvent(
4401                                                                ProfilerEvent.TYPE_WARN, "", //$NON-NLS-1$
4402                                                                this.getCatalog(), this.getId(), -1, -1, System
4403                                                                                .currentTimeMillis(), 0, Constants.MILLIS_I18N,
4404                                                                                null,
4405                                                                this.pointOfOrigin, message));
4406                                        }
4407
4408                                        long connectionLifeTime = System.currentTimeMillis()
4409                                                        - this.connectionCreationTimeMillis;
4410
4411                                        if (connectionLifeTime < 500) {
4412                                                String message = "Connection lifetime of < .5 seconds. You might be un-necessarily creating short-lived connections and should investigate connection pooling to be more efficient.";
4413
4414                                                this.eventSink.consumeEvent(new ProfilerEvent(
4415                                                                ProfilerEvent.TYPE_WARN, "", //$NON-NLS-1$
4416                                                                this.getCatalog(), this.getId(), -1, -1, System
4417                                                                                .currentTimeMillis(), 0, Constants.MILLIS_I18N,
4418                                                                                null,
4419                                                                this.pointOfOrigin, message));
4420                                        }
4421                                }
4422
4423                                try {
4424                                        closeAllOpenStatements();
4425                                } catch (SQLException ex) {
4426                                        sqlEx = ex;
4427                                }
4428
4429                                if (this.io != null) {
4430                                        try {
4431                                                this.io.quit();
4432                                        } catch (Exception e) {
4433                                                ;
4434                                        }
4435
4436                                }
4437                        } else {
4438                                this.io.forceClose();
4439                        }
4440                       
4441                if (this.statementInterceptors != null) {
4442                        for (int i = 0; i < this.statementInterceptors.size(); i++) {
4443                                this.statementInterceptors.get(i).destroy();
4444                        }
4445                }
4446               
4447                if (this.exceptionInterceptor != null) {
4448                        this.exceptionInterceptor.destroy();
4449                }
4450                } finally {
4451                        this.openStatements = null;
4452                        this.io = null;
4453                        this.statementInterceptors = null;
4454                        this.exceptionInterceptor = null;
4455                        ProfilerEventHandlerFactory.removeInstance(this);
4456                       
4457                        synchronized (this) {
4458                                if (this.cancelTimer != null) {
4459                                        this.cancelTimer.cancel();
4460                                }
4461                        }
4462                       
4463                        this.isClosed = true;
4464                }
4465
4466                if (sqlEx != null) {
4467                        throw sqlEx;
4468                }
4469
4470        }
4471
4472        public synchronized void recachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException {
4473                if (pstmt.isPoolable()) {
4474                        synchronized (this.serverSideStatementCache) {
4475                                this.serverSideStatementCache.put(pstmt.originalSql, pstmt);
4476                        }
4477                }
4478        }
4479
4480        /**
4481         * DOCUMENT ME!
4482         *
4483         * @param queryTimeMs
4484         */
4485        public void registerQueryExecutionTime(long queryTimeMs) {
4486                if (queryTimeMs > this.longestQueryTimeMs) {
4487                        this.longestQueryTimeMs = queryTimeMs;
4488
4489                        repartitionPerformanceHistogram();
4490                }
4491
4492                addToPerformanceHistogram(queryTimeMs, 1);
4493
4494                if (queryTimeMs < this.shortestQueryTimeMs) {
4495                        this.shortestQueryTimeMs = (queryTimeMs == 0) ? 1 : queryTimeMs;
4496                }
4497
4498                this.numberOfQueriesIssued++;
4499
4500                this.totalQueryTimeMs += queryTimeMs;
4501        }
4502
4503        /**
4504         * Register a Statement instance as open.
4505         *
4506         * @param stmt
4507         *            the Statement instance to remove
4508         */
4509        public void registerStatement(Statement stmt) {
4510                synchronized (this.openStatements) {
4511                        this.openStatements.put(stmt, stmt);
4512                }
4513        }
4514
4515        /**
4516         * @see Connection#releaseSavepoint(Savepoint)
4517         */
4518        public void releaseSavepoint(Savepoint arg0) throws SQLException {
4519                // this is a no-op
4520        }
4521
4522        private void repartitionHistogram(int[] histCounts, long[] histBreakpoints,
4523                        long currentLowerBound, long currentUpperBound) {
4524
4525                if (oldHistCounts == null) {
4526                        oldHistCounts = new int[histCounts.length];
4527                        oldHistBreakpoints = new long[histBreakpoints.length];
4528                }
4529
4530                System.arraycopy(histCounts, 0, oldHistCounts, 0, histCounts.length);
4531               
4532                System.arraycopy(histBreakpoints, 0, oldHistBreakpoints, 0,
4533                                histBreakpoints.length);
4534       
4535                createInitialHistogram(histBreakpoints, currentLowerBound,
4536                                currentUpperBound);
4537
4538                for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
4539                        addToHistogram(histCounts, histBreakpoints, oldHistBreakpoints[i],
4540                                        oldHistCounts[i], currentLowerBound, currentUpperBound);
4541                }
4542        }
4543
4544        private void repartitionPerformanceHistogram() {
4545                checkAndCreatePerformanceHistogram();
4546
4547                repartitionHistogram(this.perfMetricsHistCounts,
4548                                this.perfMetricsHistBreakpoints,
4549                                this.shortestQueryTimeMs == Long.MAX_VALUE ? 0
4550                                                : this.shortestQueryTimeMs, this.longestQueryTimeMs);
4551        }
4552
4553        private void repartitionTablesAccessedHistogram() {
4554                checkAndCreateTablesAccessedHistogram();
4555
4556                repartitionHistogram(this.numTablesMetricsHistCounts,
4557                                this.numTablesMetricsHistBreakpoints,
4558                                this.minimumNumberTablesAccessed == Long.MAX_VALUE ? 0
4559                                                : this.minimumNumberTablesAccessed,
4560                                this.maximumNumberTablesAccessed);
4561        }
4562
4563        private void reportMetrics() {
4564                if (getGatherPerformanceMetrics()) {
4565                        StringBuffer logMessage = new StringBuffer(256);
4566
4567                        logMessage.append("** Performance Metrics Report **\n");
4568                        logMessage.append("\nLongest reported query: "
4569                                        + this.longestQueryTimeMs + " ms");
4570                        logMessage.append("\nShortest reported query: "
4571                                        + this.shortestQueryTimeMs + " ms");
4572                        logMessage
4573                                        .append("\nAverage query execution time: "
4574                                                        + (this.totalQueryTimeMs / this.numberOfQueriesIssued)
4575                                                        + " ms");
4576                        logMessage.append("\nNumber of statements executed: "
4577                                        + this.numberOfQueriesIssued);
4578                        logMessage.append("\nNumber of result sets created: "
4579                                        + this.numberOfResultSetsCreated);
4580                        logMessage.append("\nNumber of statements prepared: "
4581                                        + this.numberOfPrepares);
4582                        logMessage.append("\nNumber of prepared statement executions: "
4583                                        + this.numberOfPreparedExecutes);
4584
4585                        if (this.perfMetricsHistBreakpoints != null) {
4586                                logMessage.append("\n\n\tTiming Histogram:\n");
4587                                int maxNumPoints = 20;
4588                                int highestCount = Integer.MIN_VALUE;
4589
4590                                for (int i = 0; i < (HISTOGRAM_BUCKETS); i++) {
4591                                        if (this.perfMetricsHistCounts[i] > highestCount) {
4592                                                highestCount = this.perfMetricsHistCounts[i];
4593                                        }
4594                                }
4595
4596                                if (highestCount == 0) {
4597                                        highestCount = 1; // avoid DIV/0
4598                                }
4599
4600                                for (int i = 0; i < (HISTOGRAM_BUCKETS - 1); i++) {
4601
4602                                        if (i == 0) {
4603                                                logMessage.append("\n\tless than "
4604                                                                + this.perfMetricsHistBreakpoints[i + 1]
4605                                                                + " ms: \t" + this.perfMetricsHistCounts[i]);
4606                                        } else {
4607                                                logMessage.append("\n\tbetween "
4608                                                                + this.perfMetricsHistBreakpoints[i] + " and "
4609                                                                + this.perfMetricsHistBreakpoints[i + 1]
4610                                                                + " ms: \t" + this.perfMetricsHistCounts[i]);
4611                                        }
4612
4613                                        logMessage.append("\t");
4614
4615                                        int numPointsToGraph = (int) (maxNumPoints * ((double) this.perfMetricsHistCounts[i] / (double) highestCount));
4616
4617                                        for (int j = 0; j < numPointsToGraph; j++) {
4618                                                logMessage.append("*");
4619                                        }
4620
4621                                        if (this.longestQueryTimeMs < this.perfMetricsHistCounts[i + 1]) {
4622                                                break;
4623                                        }
4624                                }
4625
4626                                if (this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2] < this.longestQueryTimeMs) {
4627                                        logMessage.append("\n\tbetween ");
4628                                        logMessage
4629                                                        .append(this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2]);
4630                                        logMessage.append(" and ");
4631                                        logMessage
4632                                                        .append(this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 1]);
4633                                        logMessage.append(" ms: \t");
4634                                        logMessage
4635                                                        .append(this.perfMetricsHistCounts[HISTOGRAM_BUCKETS - 1]);
4636                                }
4637                        }
4638
4639                        if (this.numTablesMetricsHistBreakpoints != null) {
4640                                logMessage.append("\n\n\tTable Join Histogram:\n");
4641                                int maxNumPoints = 20;
4642                                int highestCount = Integer.MIN_VALUE;
4643
4644                                for (int i = 0; i < (HISTOGRAM_BUCKETS); i++) {
4645                                        if (this.numTablesMetricsHistCounts[i] > highestCount) {
4646                                                highestCount = this.numTablesMetricsHistCounts[i];
4647                                        }
4648                                }
4649
4650                                if (highestCount == 0) {
4651                                        highestCount = 1; // avoid DIV/0
4652                                }
4653
4654                                for (int i = 0; i < (HISTOGRAM_BUCKETS - 1); i++) {
4655
4656                                        if (i == 0) {
4657                                                logMessage.append("\n\t"
4658                                                                + this.numTablesMetricsHistBreakpoints[i + 1]
4659                                                                + " tables or less: \t\t"
4660                                                                + this.numTablesMetricsHistCounts[i]);
4661                                        } else {
4662                                                logMessage.append("\n\tbetween "
4663                                                                + this.numTablesMetricsHistBreakpoints[i]
4664                                                                + " and "
4665                                                                + this.numTablesMetricsHistBreakpoints[i + 1]
4666                                                                + " tables: \t"
4667                                                                + this.numTablesMetricsHistCounts[i]);
4668                                        }
4669
4670                                        logMessage.append("\t");
4671
4672                                        int numPointsToGraph = (int) (maxNumPoints * ((double) this.numTablesMetricsHistCounts[i] / (double) highestCount));
4673
4674                                        for (int j = 0; j < numPointsToGraph; j++) {
4675                                                logMessage.append("*");
4676                                        }
4677
4678                                        if (this.maximumNumberTablesAccessed < this.numTablesMetricsHistBreakpoints[i + 1]) {
4679                                                break;
4680                                        }
4681                                }
4682
4683                                if (this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2] < this.maximumNumberTablesAccessed) {
4684                                        logMessage.append("\n\tbetween ");
4685                                        logMessage
4686                                                        .append(this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2]);
4687                                        logMessage.append(" and ");
4688                                        logMessage
4689                                                        .append(this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 1]);
4690                                        logMessage.append(" tables: ");
4691                                        logMessage
4692                                                        .append(this.numTablesMetricsHistCounts[HISTOGRAM_BUCKETS - 1]);
4693                                }
4694                        }
4695
4696                        this.log.logInfo(logMessage);
4697
4698                        this.metricsLastReportedMs = System.currentTimeMillis();
4699                }
4700        }
4701
4702        /**
4703         * Reports currently collected metrics if this feature is enabled and the
4704         * timeout has passed.
4705         */
4706        protected void reportMetricsIfNeeded() {
4707                if (getGatherPerformanceMetrics()) {
4708                        if ((System.currentTimeMillis() - this.metricsLastReportedMs) > getReportMetricsIntervalMillis()) {
4709                                reportMetrics();
4710                        }
4711                }
4712        }
4713
4714        public void reportNumberOfTablesAccessed(int numTablesAccessed) {
4715                if (numTablesAccessed < this.minimumNumberTablesAccessed) {
4716                        this.minimumNumberTablesAccessed = numTablesAccessed;
4717                }
4718
4719                if (numTablesAccessed > this.maximumNumberTablesAccessed) {
4720                        this.maximumNumberTablesAccessed = numTablesAccessed;
4721
4722                        repartitionTablesAccessedHistogram();
4723                }
4724
4725                addToTablesAccessedHistogram(numTablesAccessed, 1);
4726        }
4727
4728        /**
4729         * Resets the server-side state of this connection. Doesn't work for MySQL
4730         * versions older than 4.0.6 or if isParanoid() is set (it will become a
4731         * no-op in these cases). Usually only used from connection pooling code.
4732         *
4733         * @throws SQLException
4734         *             if the operation fails while resetting server state.
4735         */
4736        public void resetServerState() throws SQLException {
4737                if (!getParanoid()
4738                                && ((this.io != null) && versionMeetsMinimum(4, 0, 6))) {
4739                        changeUser(this.user, this.password);
4740                }
4741        }
4742
4743        /**
4744         * The method rollback() drops all changes made since the previous
4745         * commit/rollback and releases any database locks currently held by the
4746         * Connection.
4747         *
4748         * @exception SQLException
4749         *                if a database access error occurs
4750         * @see commit
4751         */
4752        public synchronized void rollback() throws SQLException {
4753                checkClosed();
4754
4755                try {
4756                        if (this.connectionLifecycleInterceptors != null) {
4757                                IterateBlock<Extension> iter = new IterateBlock<Extension>(this.connectionLifecycleInterceptors.iterator()) {
4758
4759                                        void forEach(Extension each) throws SQLException {
4760                                                if (!((ConnectionLifecycleInterceptor)each).rollback()) {
4761                                                        this.stopIterating = true;
4762                                                }
4763                                        }
4764                                };
4765                               
4766                                iter.doForAll();
4767                               
4768                                if (!iter.fullIteration()) {
4769                                        return;
4770                                }
4771                        }
4772                        // no-op if _relaxAutoCommit == true
4773                        if (this.autoCommit && !getRelaxAutoCommit()) {
4774                                throw SQLError.createSQLException(
4775                                                "Can't call rollback when autocommit=true",
4776                                                SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor());
4777                        } else if (this.transactionsSupported) {
4778                                try {
4779                                        rollbackNoChecks();
4780                                } catch (SQLException sqlEx) {
4781                                        // We ignore non-transactional tables if told to do so
4782                                        if (getIgnoreNonTxTables()
4783                                                        && (sqlEx.getErrorCode() == SQLError.ER_WARNING_NOT_COMPLETE_ROLLBACK)) {
4784                                                return;
4785                                        }
4786                                        throw sqlEx;
4787
4788                                }
4789                        }
4790                } catch (SQLException sqlException) {
4791                        if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
4792                                        .equals(sqlException.getSQLState())) {
4793                                throw SQLError.createSQLException(
4794                                                "Communications link failure during rollback(). Transaction resolution unknown.",
4795                                                SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, getExceptionInterceptor());
4796                        }
4797
4798                        throw sqlException;
4799                } finally {
4800                        this.needsPing = this.getReconnectAtTxEnd();
4801                }
4802        }
4803
4804        /**
4805         * @see Connection#rollback(Savepoint)
4806         */
4807        public synchronized void rollback(final Savepoint savepoint) throws SQLException {
4808
4809                if (versionMeetsMinimum(4, 0, 14) || versionMeetsMinimum(4, 1, 1)) {
4810                        checkClosed();
4811       
4812                        try {
4813                                if (this.connectionLifecycleInterceptors != null) {
4814                                        IterateBlock<Extension> iter = new IterateBlock<Extension>(this.connectionLifecycleInterceptors.iterator()) {
4815
4816                                                void forEach(Extension each) throws SQLException {
4817                                                        if (!((ConnectionLifecycleInterceptor)each).rollback(savepoint)) {
4818                                                                this.stopIterating = true;
4819                                                        }
4820                                                }
4821                                        };
4822                                       
4823                                        iter.doForAll();
4824                                       
4825                                        if (!iter.fullIteration()) {
4826                                                return;
4827                                        }
4828                                }
4829                               
4830                                StringBuffer rollbackQuery = new StringBuffer(
4831                                                "ROLLBACK TO SAVEPOINT ");
4832                                rollbackQuery.append('`');
4833                                rollbackQuery.append(savepoint.getSavepointName());
4834                                rollbackQuery.append('`');
4835
4836                                java.sql.Statement stmt = null;
4837
4838                                try {
4839                                        stmt = getMetadataSafeStatement();
4840
4841                                        stmt.executeUpdate(rollbackQuery.toString());
4842                                } catch (SQLException sqlEx) {
4843                                        int errno = sqlEx.getErrorCode();
4844
4845                                        if (errno == 1181) {
4846                                                String msg = sqlEx.getMessage();
4847
4848                                                if (msg != null) {
4849                                                        int indexOfError153 = msg.indexOf("153");
4850
4851                                                        if (indexOfError153 != -1) {
4852                                                                throw SQLError.createSQLException("Savepoint '"
4853                                                                                + savepoint.getSavepointName()
4854                                                                                + "' does not exist",
4855                                                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
4856                                                                                errno, getExceptionInterceptor());
4857                                                        }
4858                                                }
4859                                        }
4860
4861                                        // We ignore non-transactional tables if told to do so
4862                                        if (getIgnoreNonTxTables()
4863                                                        && (sqlEx.getErrorCode() != SQLError.ER_WARNING_NOT_COMPLETE_ROLLBACK)) {
4864                                                throw sqlEx;
4865                                        }
4866
4867                                        if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
4868                                                        .equals(sqlEx.getSQLState())) {
4869                                                throw SQLError.createSQLException(
4870                                                                "Communications link failure during rollback(). Transaction resolution unknown.",
4871                                                                SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, getExceptionInterceptor());
4872                                        }
4873
4874                                        throw sqlEx;
4875                                } finally {
4876                                        closeStatement(stmt);
4877                                }
4878                        } finally {
4879                                this.needsPing = this.getReconnectAtTxEnd();
4880                        }
4881                } else {
4882                        throw SQLError.notImplemented();
4883                }
4884        }
4885
4886        private void rollbackNoChecks() throws SQLException {
4887                if (getUseLocalTransactionState() && versionMeetsMinimum(5, 0, 0)) {
4888                        if (!this.io.inTransactionOnServer()) {
4889                                return; // effectively a no-op
4890                        }
4891                }
4892               
4893                execSQL(null, "rollback", -1, null,
4894                                DEFAULT_RESULT_SET_TYPE,
4895                                DEFAULT_RESULT_SET_CONCURRENCY, false,
4896                                this.database, null, false);
4897        }
4898
4899        /**
4900         * @see java.sql.Connection#prepareStatement(String)
4901         */
4902        public java.sql.PreparedStatement serverPrepareStatement(String sql)
4903                throws SQLException {
4904
4905                String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
4906
4907                return ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql, this.getCatalog(),
4908                                DEFAULT_RESULT_SET_TYPE,
4909                                DEFAULT_RESULT_SET_CONCURRENCY);
4910        }
4911
4912        /**
4913         * @see java.sql.Connection#prepareStatement(String, int)
4914         */
4915        public java.sql.PreparedStatement serverPrepareStatement(String sql,
4916                        int autoGenKeyIndex) throws SQLException {
4917                String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
4918
4919                PreparedStatement pStmt = ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql, this.getCatalog(),
4920                                DEFAULT_RESULT_SET_TYPE,
4921                                DEFAULT_RESULT_SET_CONCURRENCY);
4922               
4923                pStmt.setRetrieveGeneratedKeys(
4924                                autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
4925
4926                return pStmt;
4927        }
4928       
4929        /**
4930         * @see java.sql.Connection#prepareStatement(String, int, int)
4931         */
4932        public java.sql.PreparedStatement serverPrepareStatement(String sql,
4933                        int resultSetType, int resultSetConcurrency) throws SQLException {
4934                String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
4935
4936                return ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql, this.getCatalog(),
4937                                resultSetType,
4938                                resultSetConcurrency);
4939        }
4940       
4941        /**
4942         * @see java.sql.Connection#prepareStatement(String, int, int, int)
4943         */
4944        public java.sql.PreparedStatement serverPrepareStatement(String sql,
4945                        int resultSetType, int resultSetConcurrency,
4946                        int resultSetHoldability) throws SQLException {
4947                if (getPedantic()) {
4948                        if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
4949                                throw SQLError.createSQLException(
4950                                                "HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
4951                                                SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
4952                        }
4953                }
4954
4955                return serverPrepareStatement(sql, resultSetType, resultSetConcurrency);
4956        }
4957       
4958        /**
4959         * @see java.sql.Connection#prepareStatement(String, int[])
4960         */
4961        public java.sql.PreparedStatement serverPrepareStatement(String sql,
4962                        int[] autoGenKeyIndexes) throws SQLException {
4963               
4964                PreparedStatement pStmt = (PreparedStatement) serverPrepareStatement(sql);
4965               
4966                pStmt
4967                                .setRetrieveGeneratedKeys((autoGenKeyIndexes != null)
4968                                                && (autoGenKeyIndexes.length > 0));
4969
4970                return pStmt;
4971        }
4972
4973        /**
4974         * @see java.sql.Connection#prepareStatement(String, String[])
4975         */
4976        public java.sql.PreparedStatement serverPrepareStatement(String sql,
4977                        String[] autoGenKeyColNames) throws SQLException {
4978                PreparedStatement pStmt = (PreparedStatement) serverPrepareStatement(sql);
4979
4980                pStmt
4981                                .setRetrieveGeneratedKeys((autoGenKeyColNames != null)
4982                                                && (autoGenKeyColNames.length > 0));
4983
4984                return pStmt;
4985        }
4986       
4987        public boolean serverSupportsConvertFn() throws SQLException {
4988                return versionMeetsMinimum(4, 0, 2);
4989        }
4990
4991        /**
4992         * If a connection is in auto-commit mode, than all its SQL statements will
4993         * be executed and committed as individual transactions. Otherwise, its SQL
4994         * statements are grouped into transactions that are terminated by either
4995         * commit() or rollback(). By default, new connections are in auto- commit
4996         * mode. The commit occurs when the statement completes or the next execute
4997         * occurs, whichever comes first. In the case of statements returning a
4998         * ResultSet, the statement completes when the last row of the ResultSet has
4999         * been retrieved or the ResultSet has been closed. In advanced cases, a
5000         * single statement may return multiple results as well as output parameter
5001         * values. Here the commit occurs when all results and output param values
5002         * have been retrieved.
5003         * <p>
5004         * <b>Note:</b> MySQL does not support transactions, so this method is a
5005         * no-op.
5006         * </p>
5007         *
5008         * @param autoCommitFlag -
5009         *            true enables auto-commit; false disables it
5010         * @exception SQLException
5011         *                if a database access error occurs
5012         */
5013        public synchronized void setAutoCommit(final boolean autoCommitFlag) throws SQLException {
5014                checkClosed();
5015               
5016                if (this.connectionLifecycleInterceptors != null) {
5017                        IterateBlock<Extension> iter = new IterateBlock<Extension>(this.connectionLifecycleInterceptors.iterator()) {
5018
5019                                void forEach(Extension each) throws SQLException {
5020                                        if (!((ConnectionLifecycleInterceptor)each).setAutoCommit(autoCommitFlag)) {
5021                                                this.stopIterating = true;
5022                                        }
5023                                }
5024                        };
5025                       
5026                        iter.doForAll();
5027                       
5028                        if (!iter.fullIteration()) {
5029                                return;
5030                        }
5031                }
5032
5033                if (getAutoReconnectForPools()) {
5034                        setHighAvailability(true);
5035                }
5036
5037                try {
5038                        if (this.transactionsSupported) {
5039
5040                                boolean needsSetOnServer = true;
5041
5042                                if (this.getUseLocalSessionState()
5043                                                && this.autoCommit == autoCommitFlag) {
5044                                        needsSetOnServer = false;
5045                                } else if (!this.getHighAvailability()) {
5046                                        needsSetOnServer = this.getIO()
5047                                                        .isSetNeededForAutoCommitMode(autoCommitFlag);
5048                                }
5049
5050                                // this internal value must be set first as failover depends on
5051                                // it
5052                                // being set to true to fail over (which is done by most
5053                                // app servers and connection pools at the end of
5054                                // a transaction), and the driver issues an implicit set
5055                                // based on this value when it (re)-connects to a server
5056                                // so the value holds across connections
5057                                this.autoCommit = autoCommitFlag;
5058
5059                                if (needsSetOnServer) {
5060                                        execSQL(null, autoCommitFlag ? "SET autocommit=1"
5061                                                        : "SET autocommit=0", -1, null,
5062                                                        DEFAULT_RESULT_SET_TYPE,
5063                                                        DEFAULT_RESULT_SET_CONCURRENCY, false,
5064                                                        this.database, null, false);
5065                                }
5066
5067                        } else {
5068                                if ((autoCommitFlag == false) && !getRelaxAutoCommit()) {
5069                                        throw SQLError.createSQLException("MySQL Versions Older than 3.23.15 "
5070                                                        + "do not support transactions",
5071                                                        SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor());
5072                                }
5073
5074                                this.autoCommit = autoCommitFlag;
5075                        }
5076                } finally {
5077                        if (this.getAutoReconnectForPools()) {
5078                                setHighAvailability(false);
5079                        }
5080                }
5081               
5082                return;
5083        }
5084
5085        /**
5086         * A sub-space of this Connection's database may be selected by setting a
5087         * catalog name. If the driver does not support catalogs, it will silently
5088         * ignore this request
5089         * <p>
5090         * <b>Note:</b> MySQL's notion of catalogs are individual databases.
5091         * </p>
5092         *
5093         * @param catalog
5094         *            the database for this connection to use
5095         * @throws SQLException
5096         *             if a database access error occurs
5097         */
5098        public synchronized void setCatalog(final String catalog) throws SQLException {
5099                checkClosed();
5100
5101                if (catalog == null) {
5102                        throw SQLError.createSQLException("Catalog can not be null",
5103                                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
5104                }
5105               
5106                if (this.connectionLifecycleInterceptors != null) {
5107                        IterateBlock<Extension> iter = new IterateBlock<Extension>(this.connectionLifecycleInterceptors.iterator()) {
5108
5109                                void forEach(Extension each) throws SQLException {
5110                                        if (!((ConnectionLifecycleInterceptor)each).setCatalog(catalog)) {
5111                                                this.stopIterating = true;
5112                                        }
5113                                }
5114                        };
5115                       
5116                        iter.doForAll();
5117                       
5118                        if (!iter.fullIteration()) {
5119                                return;
5120                        }
5121                }
5122               
5123                if (getUseLocalSessionState()) {
5124                        if (this.lowerCaseTableNames) {
5125                                if (this.database.equalsIgnoreCase(catalog)) {
5126                                        return;
5127                                }
5128                        } else {
5129                                if (this.database.equals(catalog)) {
5130                                        return;
5131                                }
5132                        }
5133                }
5134               
5135                String quotedId = this.dbmd.getIdentifierQuoteString();
5136
5137                if ((quotedId == null) || quotedId.equals(" ")) {
5138                        quotedId = "";
5139                }
5140
5141                StringBuffer query = new StringBuffer("USE ");
5142                query.append(quotedId);
5143                query.append(catalog);
5144                query.append(quotedId);
5145
5146                execSQL(null, query.toString(), -1, null,
5147                                DEFAULT_RESULT_SET_TYPE,
5148                                DEFAULT_RESULT_SET_CONCURRENCY, false,
5149                                this.database, null, false);
5150               
5151                this.database = catalog;
5152        }
5153
5154        /**
5155         * @param failedOver
5156         *            The failedOver to set.
5157         */
5158        public synchronized void setFailedOver(boolean flag) {
5159                // handled higher up
5160        }
5161
5162        /**
5163         * @see Connection#setHoldability(int)
5164         */
5165        public void setHoldability(int arg0) throws SQLException {
5166                // do nothing
5167        }
5168
5169        public void setInGlobalTx(boolean flag) {
5170                this.isInGlobalTx = flag;
5171        }
5172
5173        // exposed for testing
5174        /**
5175         * @param preferSlaveDuringFailover
5176         *            The preferSlaveDuringFailover to set.
5177         */
5178        public void setPreferSlaveDuringFailover(boolean flag) {
5179                // no-op, handled further up in the wrapper
5180        }
5181
5182        public void setReadInfoMsgEnabled(boolean flag) {
5183                this.readInfoMsg = flag;
5184        }
5185
5186        /**
5187         * You can put a connection in read-only mode as a hint to enable database
5188         * optimizations <B>Note:</B> setReadOnly cannot be called while in the
5189         * middle of a transaction
5190         *
5191         * @param readOnlyFlag -
5192         *            true enables read-only mode; false disables it
5193         * @exception SQLException
5194         *                if a database access error occurs
5195         */
5196        public void setReadOnly(boolean readOnlyFlag) throws SQLException {
5197                checkClosed();
5198
5199                setReadOnlyInternal(readOnlyFlag);
5200        }
5201       
5202        public void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException {
5203                this.readOnly = readOnlyFlag;
5204        }
5205       
5206        /**
5207         * @see Connection#setSavepoint()
5208         */
5209        public java.sql.Savepoint setSavepoint() throws SQLException {
5210                MysqlSavepoint savepoint = new MysqlSavepoint(getExceptionInterceptor());
5211
5212                setSavepoint(savepoint);
5213
5214                return savepoint;
5215        }
5216
5217        private synchronized void setSavepoint(MysqlSavepoint savepoint) throws SQLException {
5218
5219                if (versionMeetsMinimum(4, 0, 14) || versionMeetsMinimum(4, 1, 1)) {
5220                        checkClosed();
5221
5222                        StringBuffer savePointQuery = new StringBuffer("SAVEPOINT ");
5223                        savePointQuery.append('`');
5224                        savePointQuery.append(savepoint.getSavepointName());
5225                        savePointQuery.append('`');
5226
5227                        java.sql.Statement stmt = null;
5228
5229                        try {
5230                                stmt = getMetadataSafeStatement();
5231
5232                                stmt.executeUpdate(savePointQuery.toString());
5233                        } finally {
5234                                closeStatement(stmt);
5235                        }
5236                } else {
5237                        throw SQLError.notImplemented();
5238                }
5239        }
5240       
5241        /**
5242         * @see Connection#setSavepoint(String)
5243         */
5244        public synchronized java.sql.Savepoint setSavepoint(String name) throws SQLException {
5245                MysqlSavepoint savepoint = new MysqlSavepoint(name, getExceptionInterceptor());
5246
5247                setSavepoint(savepoint);
5248
5249                return savepoint;
5250        }
5251       
5252        /**
5253         *
5254         */
5255        private void setSessionVariables() throws SQLException {
5256                if (this.versionMeetsMinimum(4, 0, 0) && getSessionVariables() != null) {
5257                        List<String> variablesToSet = StringUtils.split(getSessionVariables(), ",", "\"'", "\"'",
5258                                        false);
5259
5260                        int numVariablesToSet = variablesToSet.size();
5261
5262                        java.sql.Statement stmt = null;
5263
5264                        try {
5265                                stmt = getMetadataSafeStatement();
5266
5267                                for (int i = 0; i < numVariablesToSet; i++) {
5268                                        String variableValuePair = variablesToSet.get(i);
5269
5270                                        if (variableValuePair.startsWith("@")) {
5271                                                stmt.executeUpdate("SET " + variableValuePair);
5272                                        } else {
5273                                                stmt.executeUpdate("SET SESSION " + variableValuePair);
5274                                        }
5275                                }
5276                        } finally {
5277                                if (stmt != null) {
5278                                        stmt.close();
5279                                }
5280                        }
5281                }
5282
5283        }
5284       
5285        /**
5286         * DOCUMENT ME!
5287         *
5288         * @param level
5289         *            DOCUMENT ME!
5290         * @throws SQLException
5291         *             DOCUMENT ME!
5292         */
5293        public synchronized void setTransactionIsolation(int level) throws SQLException {
5294                checkClosed();
5295
5296                if (this.hasIsolationLevels) {
5297                        String sql = null;
5298
5299                        boolean shouldSendSet = false;
5300
5301                        if (getAlwaysSendSetIsolation()) {
5302                                shouldSendSet = true;
5303                        } else {
5304                                if (level != this.isolationLevel) {
5305                                        shouldSendSet = true;
5306                                }
5307                        }
5308
5309                        if (getUseLocalSessionState()) {
5310                                shouldSendSet = this.isolationLevel != level;
5311                        }
5312
5313                        if (shouldSendSet) {
5314                                switch (level) {
5315                                case java.sql.Connection.TRANSACTION_NONE:
5316                                        throw SQLError.createSQLException("Transaction isolation level "
5317                                                        + "NONE not supported by MySQL", getExceptionInterceptor());
5318
5319                                case java.sql.Connection.TRANSACTION_READ_COMMITTED:
5320                                        sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED";
5321
5322                                        break;
5323
5324                                case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED:
5325                                        sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
5326
5327                                        break;
5328
5329                                case java.sql.Connection.TRANSACTION_REPEATABLE_READ:
5330                                        sql = "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";
5331
5332                                        break;
5333
5334                                case java.sql.Connection.TRANSACTION_SERIALIZABLE:
5335                                        sql = "SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE";
5336
5337                                        break;
5338
5339                                default:
5340                                        throw SQLError.createSQLException("Unsupported transaction "
5341                                                        + "isolation level '" + level + "'",
5342                                                        SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor());
5343                                }
5344
5345                                execSQL(null, sql, -1, null,
5346                                                DEFAULT_RESULT_SET_TYPE,
5347                                                DEFAULT_RESULT_SET_CONCURRENCY,false,
5348                                                this.database, null, false);
5349
5350                                this.isolationLevel = level;
5351                        }
5352                } else {
5353                        throw SQLError.createSQLException("Transaction Isolation Levels are "
5354                                        + "not supported on MySQL versions older than 3.23.36.",
5355                                        SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor());
5356                }
5357        }
5358       
5359        /**
5360         * JDBC 2.0 Install a type-map object as the default type-map for this
5361         * connection
5362         *
5363         * @param map
5364         *            the type mapping
5365         * @throws SQLException
5366         *             if a database error occurs.
5367         */
5368        public synchronized void setTypeMap(java.util.Map<String,Class<?>> map) throws SQLException {
5369                this.typeMap = map;
5370        }
5371       
5372        private void setupServerForTruncationChecks() throws SQLException {
5373                if (getJdbcCompliantTruncation()) {
5374                        if (versionMeetsMinimum(5, 0, 2)) {
5375                                String currentSqlMode = 
5376                                        this.serverVariables.get("sql_mode");
5377                               
5378                                boolean strictTransTablesIsSet = StringUtils.indexOfIgnoreCase(currentSqlMode, "STRICT_TRANS_TABLES") != -1;
5379                               
5380                                if (currentSqlMode == null ||
5381                                                currentSqlMode.length() == 0 || !strictTransTablesIsSet) {
5382                                        StringBuffer commandBuf = new StringBuffer("SET sql_mode='");
5383                                       
5384                                        if (currentSqlMode != null && currentSqlMode.length() > 0) {
5385                                                commandBuf.append(currentSqlMode);
5386                                                commandBuf.append(",");
5387                                        }
5388                                       
5389                                        commandBuf.append("STRICT_TRANS_TABLES'");
5390                                       
5391                                        execSQL(null,  commandBuf.toString(), -1, null,
5392                                                        DEFAULT_RESULT_SET_TYPE,
5393                                                        DEFAULT_RESULT_SET_CONCURRENCY, false,
5394                                                        this.database, null, false);
5395                                       
5396                                        setJdbcCompliantTruncation(false); // server's handling this for us now
5397                                } else if (strictTransTablesIsSet) {
5398                                        // We didn't set it, but someone did, so we piggy back on it
5399                                        setJdbcCompliantTruncation(false); // server's handling this for us now
5400                                }
5401                               
5402                        }
5403                }
5404        }
5405       
5406       
5407        /**
5408         * Used by MiniAdmin to shutdown a MySQL server
5409         *
5410         * @throws SQLException
5411         *             if the command can not be issued.
5412         */
5413        public void shutdownServer() throws SQLException {
5414                try {
5415                        this.io.sendCommand(MysqlDefs.SHUTDOWN, null, null, false, null, 0);
5416                } catch (Exception ex) {
5417                        SQLException sqlEx = SQLError.createSQLException(
5418                                        Messages.getString("Connection.UnhandledExceptionDuringShutdown"),
5419                                        SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
5420                       
5421                        sqlEx.initCause(ex);
5422                       
5423                        throw sqlEx;
5424                }
5425        }
5426
5427        /**
5428         * DOCUMENT ME!
5429         *
5430         * @return DOCUMENT ME!
5431         */
5432        public boolean supportsIsolationLevel() {
5433                return this.hasIsolationLevels;
5434        }
5435       
5436        /**
5437         * DOCUMENT ME!
5438         *
5439         * @return DOCUMENT ME!
5440         */
5441        public boolean supportsQuotedIdentifiers() {
5442                return this.hasQuotedIdentifiers;
5443        }
5444
5445        /**
5446         * DOCUMENT ME!
5447         *
5448         * @return DOCUMENT ME!
5449         */
5450        public boolean supportsTransactions() {
5451                return this.transactionsSupported;
5452        }
5453
5454        /**
5455         * Remove the given statement from the list of open statements
5456         *
5457         * @param stmt
5458         *            the Statement instance to remove
5459         */
5460        public void unregisterStatement(Statement stmt) {
5461                if (this.openStatements != null) {
5462                        synchronized (this.openStatements) {
5463                                this.openStatements.remove(stmt);
5464                        }
5465                }
5466        }
5467
5468        /**
5469         * Called by statements on their .close() to let the connection know when it
5470         * is safe to set the connection back to 'default' row limits.
5471         *
5472         * @param stmt
5473         *            the statement releasing it's max-rows requirement
5474         * @throws SQLException
5475         *             if a database error occurs issuing the statement that sets
5476         *             the limit default.
5477         */
5478        public synchronized void unsetMaxRows(Statement stmt) throws SQLException {
5479                if (this.statementsUsingMaxRows != null) {
5480                        Object found = this.statementsUsingMaxRows.remove(stmt);
5481
5482                        if ((found != null)
5483                                        && (this.statementsUsingMaxRows.size() == 0)) {
5484                                execSQL(null, "SET SQL_SELECT_LIMIT=DEFAULT", -1,
5485                                                null, DEFAULT_RESULT_SET_TYPE,
5486                                                DEFAULT_RESULT_SET_CONCURRENCY, false, 
5487                                                this.database, null, false);
5488
5489                                this.maxRowsChanged = false;
5490                        }
5491                }
5492        }
5493       
5494        public synchronized boolean useAnsiQuotedIdentifiers() {
5495                return this.useAnsiQuotes;
5496        }
5497       
5498        /**
5499         * Has maxRows() been set?
5500         *
5501         * @return DOCUMENT ME!
5502         */
5503        public synchronized boolean useMaxRows() {
5504                return this.maxRowsChanged;
5505        }
5506       
5507        public boolean versionMeetsMinimum(int major, int minor, int subminor)
5508                        throws SQLException {
5509                checkClosed();
5510
5511                return this.io.versionMeetsMinimum(major, minor, subminor);
5512        }
5513
5514        /**
5515         * Returns cached metadata (or null if not cached) for the given query,
5516         * which must match _exactly_.   
5517         * 
5518         * This method is synchronized by the caller on getMutex(), so if
5519         * calling this method from internal code in the driver, make sure it's
5520         * synchronized on the mutex that guards communication with the server.
5521         *
5522         * @param sql
5523         *            the query that is the key to the cache
5524         *
5525         * @return metadata cached for the given SQL, or none if it doesn't
5526         *                  exist.
5527         */
5528        public CachedResultSetMetaData getCachedMetaData(String sql) {
5529                if (this.resultSetMetadataCache != null) {
5530                        synchronized (this.resultSetMetadataCache) {
5531                                return (CachedResultSetMetaData) this.resultSetMetadataCache
5532                                                .get(sql);
5533                        }
5534                }
5535
5536                return null; // no cache exists
5537        }
5538
5539        /**
5540         * Caches CachedResultSetMetaData that has been placed in the cache using
5541         * the given SQL as a key.
5542         *
5543         * This method is synchronized by the caller on getMutex(), so if
5544         * calling this method from internal code in the driver, make sure it's
5545         * synchronized on the mutex that guards communication with the server.
5546         *
5547         * @param sql the query that the metadata pertains too.
5548         * @param cachedMetaData metadata (if it exists) to populate the cache.
5549         * @param resultSet the result set to retreive metadata from, or apply to.
5550         *
5551         * @throws SQLException
5552         */
5553        public void initializeResultsMetadataFromCache(String sql,
5554                        CachedResultSetMetaData cachedMetaData, ResultSetInternalMethods resultSet)
5555                        throws SQLException {
5556
5557                if (cachedMetaData == null) {
5558                       
5559                        // read from results
5560                        cachedMetaData = new CachedResultSetMetaData();
5561
5562                        // assume that users will use named-based
5563                        // lookups
5564                        resultSet.buildIndexMapping();
5565                        resultSet.initializeWithMetadata();
5566
5567                        if (resultSet instanceof UpdatableResultSet) {
5568                                ((UpdatableResultSet)resultSet).checkUpdatability();
5569                        }
5570
5571                        resultSet.populateCachedMetaData(cachedMetaData);
5572
5573                        this.resultSetMetadataCache.put(sql, cachedMetaData);
5574                } else {
5575                        resultSet.initializeFromCachedMetaData(cachedMetaData);
5576                        resultSet.initializeWithMetadata();
5577                       
5578                        if (resultSet instanceof UpdatableResultSet) {
5579                                ((UpdatableResultSet)resultSet).checkUpdatability();
5580                        }
5581                }
5582        }
5583       
5584        /**
5585         * Returns the comment that will be prepended to all statements
5586         * sent to the server.
5587         *
5588         * @return the comment that will be prepended to all statements
5589         * sent to the server.
5590         */
5591        public String getStatementComment() {
5592                return this.statementComment;
5593        }
5594
5595        /**
5596         * Sets the comment that will be prepended to all statements
5597         * sent to the server. Do not use slash-star or star-slash tokens
5598         * in the comment as these will be added by the driver itself.
5599         *
5600         * @param comment  the comment that will be prepended to all statements
5601         * sent to the server.
5602         */
5603        public void setStatementComment(String comment) {
5604                this.statementComment = comment;
5605        }
5606       
5607        public synchronized void reportQueryTime(long millisOrNanos) {
5608                this.queryTimeCount++;
5609                this.queryTimeSum += millisOrNanos;
5610                this.queryTimeSumSquares += (millisOrNanos * millisOrNanos);
5611                this.queryTimeMean = ((this.queryTimeMean * (this.queryTimeCount - 1)) + millisOrNanos)
5612                                / this.queryTimeCount;
5613        }
5614       
5615        public synchronized boolean isAbonormallyLongQuery(long millisOrNanos) {
5616                if (this.queryTimeCount < 15) {
5617                        return false; // need a minimum amount for this to make sense
5618                }
5619               
5620                double stddev = Math.sqrt((this.queryTimeSumSquares - ((this.queryTimeSum*this.queryTimeSum) / this.queryTimeCount)) / (this.queryTimeCount - 1));
5621               
5622                return millisOrNanos > (this.queryTimeMean + 5 * stddev);
5623        }
5624
5625        public void initializeExtension(Extension ex) throws SQLException {
5626                ex.init(this, this.props);
5627        }
5628       
5629        public synchronized void transactionBegun() throws SQLException {
5630                if (this.connectionLifecycleInterceptors != null) {
5631                        IterateBlock<Extension> iter = new IterateBlock<Extension>(this.connectionLifecycleInterceptors.iterator()) {
5632
5633                                void forEach(Extension each) throws SQLException {
5634                                        ((ConnectionLifecycleInterceptor)each).transactionBegun();
5635                                }
5636                        };
5637                       
5638                        iter.doForAll();
5639                }
5640        }
5641       
5642        public synchronized void transactionCompleted() throws SQLException {
5643                if (this.connectionLifecycleInterceptors != null) {
5644                        IterateBlock<Extension> iter = new IterateBlock<Extension>(this.connectionLifecycleInterceptors.iterator()) {
5645
5646                                void forEach(Extension each) throws SQLException {
5647                                        ((ConnectionLifecycleInterceptor)each).transactionCompleted();
5648                                }
5649                        };
5650                       
5651                        iter.doForAll();
5652                }
5653        }
5654       
5655        public boolean storesLowerCaseTableName() {
5656                return storesLowerCaseTableName;
5657        }
5658       
5659        private ExceptionInterceptor exceptionInterceptor;
5660       
5661        public ExceptionInterceptor getExceptionInterceptor() {
5662                return this.exceptionInterceptor;
5663        }
5664       
5665        public boolean getRequiresEscapingEncoder() {
5666                return requiresEscapingEncoder;
5667        }
5668       
5669        public synchronized boolean isServerLocal() throws SQLException {
5670                SocketFactory factory = getIO().socketFactory;
5671               
5672                if (factory instanceof SocketMetadata) {
5673                        return ((SocketMetadata)factory).isLocallyConnected(this);
5674                } 
5675                getLog().logWarn(Messages.getString("Connection.NoMetadataOnSocketFactory"));
5676                return false;
5677        }
5678       
5679        // JDBC-4.1
5680        // until we flip catalog/schema, this is a no-op
5681        public synchronized void setSchema(String schema) throws SQLException {
5682                checkClosed();
5683        }
5684       
5685        // JDBC-4.1
5686        public synchronized String getSchema() throws SQLException {
5687                checkClosed();
5688               
5689                return null;
5690        }
5691       
5692        // JDBC-4.1
5693        public void abort(Executor executor) throws SQLException {
5694SecurityManager sec = System.getSecurityManager();
5695               
5696                if (sec != null) {
5697                    sec.checkPermission(ABORT_PERM);
5698                }
5699               
5700                if (executor == null) {
5701                        throw SQLError.createSQLException("Executor can not be null", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
5702                }
5703               
5704                executor.execute(new Runnable() {
5705
5706                        public void run() {
5707                                try {
5708                                        abortInternal();
5709                                } catch (SQLException e) {
5710                                        throw new RuntimeException(e);
5711                                }
5712                        }
5713                });
5714        }
5715       
5716        // JDBC-4.1
5717        public synchronized void setNetworkTimeout(Executor executor, final int milliseconds) throws SQLException {
5718                SecurityManager sec = System.getSecurityManager();
5719               
5720                if (sec != null) {
5721                    sec.checkPermission(SET_NETWORK_TIMEOUT_PERM);
5722                }
5723               
5724                if (executor == null) {
5725                        throw SQLError.createSQLException("Executor can not be null", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
5726                }
5727               
5728                checkClosed();
5729                final MysqlIO mysqlIo = this.io;
5730               
5731                executor.execute(new Runnable() {
5732
5733                        public void run() {
5734                                setSocketTimeout(milliseconds); // for re-connects
5735                                try {
5736                                        mysqlIo.setSocketTimeout(milliseconds);
5737                                } catch (SQLException e) {
5738                                        throw new RuntimeException(e);
5739                                }
5740                        }});
5741        }
5742       
5743        // JDBC-4.1
5744        public synchronized int getNetworkTimeout() throws SQLException {
5745                checkClosed();
5746                return getSocketTimeout();
5747        }
5748}
Note: See TracBrowser for help on using the repository browser.