gt:         double timeout = getInitialTimeout();
context: /*
    Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
  
    The MySQL Connector/J is licensed under the terms of the GPLv2
    <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
    There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
    this software, see the FLOSS License Exception
    <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  
    This program is free software; you can redistribute it and/or modify it under the terms
    of the GNU General Public License as published by the Free Software Foundation; version 2
    of the License.
  
    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
    without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    See the GNU General Public License for more details.
  
    You should have received a copy of the GNU General Public License along with this
    program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
    Floor, Boston, MA 02110-1301  USA
  
   */
  
  package com.mysql.jdbc;
  
  import java.io.IOException;
  import java.io.UnsupportedEncodingException;
  import java.lang.reflect.Array;
  import java.lang.reflect.Constructor;
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Method;
  import java.nio.ByteBuffer;
  import java.nio.CharBuffer;
  import java.nio.charset.Charset;
  import java.nio.charset.CharsetEncoder;
  import java.sql.Blob;
  import java.sql.DatabaseMetaData;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.sql.SQLPermission;
  import java.sql.SQLWarning;
  import java.sql.Savepoint;
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.Collections;
  import java.util.Enumeration;
  import java.util.GregorianCalendar;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Locale;
  import java.util.Map;
  import java.util.Properties;
  import java.util.Random;
  import java.util.Stack;
  import java.util.TimeZone;
  import java.util.Timer;
  import java.util.TreeMap;
  import java.util.concurrent.Executor;
  
  import com.mysql.jdbc.PreparedStatement.ParseInfo;
  import com.mysql.jdbc.log.Log;
  import com.mysql.jdbc.log.LogFactory;
  import com.mysql.jdbc.log.LogUtils;
  import com.mysql.jdbc.log.NullLogger;
  import com.mysql.jdbc.profiler.ProfilerEvent;
  import com.mysql.jdbc.profiler.ProfilerEventHandler;
  import com.mysql.jdbc.util.LRUCache;
  
  /**
   * A Connection represents a session with a specific database. Within the context of a Connection, SQL statements are executed and results are returned.
   * 
   * <P>
   * A Connection's database is able to provide information describing its tables, its supported SQL grammar, its stored procedures, the capabilities of this
   * connection, etc. This information is obtained with the getMetaData method.
   * </p>
   */
  public class ConnectionImpl extends ConnectionPropertiesImpl implements MySQLConnection {
  
      private static final long serialVersionUID = 2877471301981509474L;
  
      private static final SQLPermission SET_NETWORK_TIMEOUT_PERM = new SQLPermission("setNetworkTimeout");
  
      private static final SQLPermission ABORT_PERM = new SQLPermission("abort");
  
      static final String JDBC_LOCAL_CHARACTER_SET_RESULTS = "jdbc.local.character_set_results";
  
      public String getHost() {
          return this.host;
      }
  
      private MySQLConnection proxy = null;
  
      private InvocationHandler realProxy = null;
  
      public boolean isProxySet() {
          return this.proxy != null;
      }
  
      public void setProxy(MySQLConnection proxy) {
          this.proxy = proxy;
      }
  
      public void setRealProxy(InvocationHandler proxy) {
          this.realProxy = proxy;
      }
  
      // We have to proxy ourselves when we're load balanced so that statements get routed to the right physical connection
      // (when load balanced, we're a "logical" connection)
      private MySQLConnection getProxy() {
          return (this.proxy != null) ? this.proxy : (MySQLConnection) this;
      }
  
      public MySQLConnection getLoadBalanceSafeProxy() {
          return this.getProxy();
      }
  
      public Object getConnectionMutex() {
          return (this.realProxy != null) ? this.realProxy : this;
      }
  
      class ExceptionInterceptorChain implements ExceptionInterceptor {
          List<Extension> interceptors;
  
          ExceptionInterceptorChain(String interceptorClasses) throws SQLException {
              this.interceptors = Util.loadExtensions(ConnectionImpl.this, ConnectionImpl.this.props, interceptorClasses, "Connection.BadExceptionInterceptor",
                      this);
          }
  
          void addRingZero(ExceptionInterceptor interceptor) throws SQLException {
              this.interceptors.add(0, interceptor);
          }
  
          public SQLException interceptException(SQLException sqlEx, Connection conn) {
              if (this.interceptors != null) {
                  Iterator<Extension> iter = this.interceptors.iterator();
  
                  while (iter.hasNext()) {
                      sqlEx = ((ExceptionInterceptor) iter.next()).interceptException(sqlEx, ConnectionImpl.this);
                  }
              }
  
              return sqlEx;
          }
  
          public void destroy() {
              if (this.interceptors != null) {
                  Iterator<Extension> iter = this.interceptors.iterator();
  
                  while (iter.hasNext()) {
                      ((ExceptionInterceptor) iter.next()).destroy();
                  }
              }
  
          }
  
          public void init(Connection conn, Properties properties) throws SQLException {
              if (this.interceptors != null) {
                  Iterator<Extension> iter = this.interceptors.iterator();
  
                  while (iter.hasNext()) {
                      ((ExceptionInterceptor) iter.next()).init(conn, properties);
                  }
              }
          }
      }
  
      /**
       * Used as a key for caching callable statements which (may) depend on
       * current catalog...In 5.0.x, they don't (currently), but stored procedure
       * names soon will, so current catalog is a (hidden) component of the name.
       */
      static class CompoundCacheKey {
          String componentOne;
  
          String componentTwo;
  
          int hashCode;
  
          CompoundCacheKey(String partOne, String partTwo) {
              this.componentOne = partOne;
              this.componentTwo = partTwo;
  
              // Handle first component (in most cases, currentCatalog being NULL....
              this.hashCode = (((this.componentOne != null) ? this.componentOne : "") + this.componentTwo).hashCode();
          }
  
          /*
           * (non-Javadoc)
           * 
           * @see java.lang.Object#equals(java.lang.Object)
           */
          @Override
          public boolean equals(Object obj) {
              if (obj instanceof CompoundCacheKey) {
                  CompoundCacheKey another = (CompoundCacheKey) obj;
  
                  boolean firstPartEqual = false;
  
                  if (this.componentOne == null) {
                      firstPartEqual = (another.componentOne == null);
                  } else {
                      firstPartEqual = this.componentOne.equals(another.componentOne);
                  }
  
                  return (firstPartEqual && this.componentTwo.equals(another.componentTwo));
              }
  
              return false;
          }
  
          /*
           * (non-Javadoc)
           * 
           * @see java.lang.Object#hashCode()
           */
          @Override
          public int hashCode() {
              return this.hashCode;
          }
      }
  
      /**
       * Marker for character set converter not being available (not written,
       * multibyte, etc) Used to prevent multiple instantiation requests.
       */
      private static final Object CHARSET_CONVERTER_NOT_AVAILABLE_MARKER = new Object();
  
      /**
       * The mapping between MySQL charset names and Java charset names.
       * Initialized by loadCharacterSetMapping()
       */
      public static Map<?, ?> charsetMap;
  
      /** Default logger class name */
      protected static final String DEFAULT_LOGGER_CLASS = "com.mysql.jdbc.log.StandardLogger";
  
      private final static int HISTOGRAM_BUCKETS = 20;
  
      /** Logger instance name */
      private static final String LOGGER_INSTANCE_NAME = "MySQL";
  
      /**
       * Map mysql transaction isolation level name to
       * java.sql.Connection.TRANSACTION_XXX
       */
      private static Map<String, Integer> mapTransIsolationNameToValue = null;
  
      /** Null logger shared by all connections at startup */
      private static final Log NULL_LOGGER = new NullLogger(LOGGER_INSTANCE_NAME);
  
      protected static Map<?, ?> roundRobinStatsMap;
  
      /**
       * Actual collation index to collation name map for given server URLs.
       */
      private static final Map<String, Map<Long, String>> dynamicIndexToCollationMapByUrl = new HashMap<String, Map<Long, String>>();
  
      /**
       * Actual collation index to mysql charset name map for given server URLs.
       */
      private static final Map<String, Map<Integer, String>> dynamicIndexToCharsetMapByUrl = new HashMap<String, Map<Integer, String>>();
  
      /**
       * Actual collation index to mysql charset name map of user defined charsets for given server URLs.
       */
      private static final Map<String, Map<Integer, String>> customIndexToCharsetMapByUrl = new HashMap<String, Map<Integer, String>>();
  
      /**
       * Actual mysql charset name to mblen map of user defined charsets for given server URLs.
       */
      private static final Map<String, Map<String, Integer>> customCharsetToMblenMapByUrl = new HashMap<String, Map<String, Integer>>();
  
      private CacheAdapter<String, Map<String, String>> serverConfigCache;
  
      private long queryTimeCount;
      private double queryTimeSum;
      private double queryTimeSumSquares;
      private double queryTimeMean;
  
      private transient Timer cancelTimer;
  
      private List<Extension> connectionLifecycleInterceptors;
  
      private static final Constructor<?> JDBC_4_CONNECTION_CTOR;
  
      private static final int DEFAULT_RESULT_SET_TYPE = ResultSet.TYPE_FORWARD_ONLY;
  
      private static final int DEFAULT_RESULT_SET_CONCURRENCY = ResultSet.CONCUR_READ_ONLY;
  
      static {
          mapTransIsolationNameToValue = new HashMap<String, Integer>(8);
          mapTransIsolationNameToValue.put("READ-UNCOMMITED", TRANSACTION_READ_UNCOMMITTED);
          mapTransIsolationNameToValue.put("READ-UNCOMMITTED", TRANSACTION_READ_UNCOMMITTED);
          mapTransIsolationNameToValue.put("READ-COMMITTED", TRANSACTION_READ_COMMITTED);
          mapTransIsolationNameToValue.put("REPEATABLE-READ", TRANSACTION_REPEATABLE_READ);
          mapTransIsolationNameToValue.put("SERIALIZABLE", TRANSACTION_SERIALIZABLE);
  
          if (Util.isJdbc4()) {
              try {
                  JDBC_4_CONNECTION_CTOR = Class.forName("com.mysql.jdbc.JDBC4Connection").getConstructor(
                          new Class[] { String.class, Integer.TYPE, Properties.class, String.class, String.class });
              } catch (SecurityException e) {
                  throw new RuntimeException(e);
              } catch (NoSuchMethodException e) {
                  throw new RuntimeException(e);
              } catch (ClassNotFoundException e) {
                  throw new RuntimeException(e);
              }
          } else {
              JDBC_4_CONNECTION_CTOR = null;
          }
      }
  
      protected static SQLException appendMessageToException(SQLException sqlEx, String messageToAppend, ExceptionInterceptor interceptor) {
          String origMessage = sqlEx.getMessage();
          String sqlState = sqlEx.getSQLState();
          int vendorErrorCode = sqlEx.getErrorCode();
  
          StringBuffer messageBuf = new StringBuffer(origMessage.length() + messageToAppend.length());
          messageBuf.append(origMessage);
          messageBuf.append(messageToAppend);
  
          SQLException sqlExceptionWithNewMessage = SQLError.createSQLException(messageBuf.toString(), sqlState, vendorErrorCode, interceptor);
  
          //
          // Try and maintain the original stack trace, only works on JDK-1.4 and newer
          //
  
          try {
              // Have to do this with reflection, otherwise older JVMs croak
              Method getStackTraceMethod = null;
              Method setStackTraceMethod = null;
              Object theStackTraceAsObject = null;
  
              Class<?> stackTraceElementClass = Class.forName("java.lang.StackTraceElement");
              Class<?> stackTraceElementArrayClass = Array.newInstance(stackTraceElementClass, new int[] { 0 }).getClass();
  
              getStackTraceMethod = Throwable.class.getMethod("getStackTrace", new Class[] {});
  
              setStackTraceMethod = Throwable.class.getMethod("setStackTrace", new Class[] { stackTraceElementArrayClass });
  
              if (getStackTraceMethod != null && setStackTraceMethod != null) {
                  theStackTraceAsObject = getStackTraceMethod.invoke(sqlEx, new Object[0]);
                  setStackTraceMethod.invoke(sqlExceptionWithNewMessage, new Object[] { theStackTraceAsObject });
              }
          } catch (NoClassDefFoundError noClassDefFound) {
  
          } catch (NoSuchMethodException noSuchMethodEx) {
  
          } catch (Throwable catchAll) {
  
          }
  
          return sqlExceptionWithNewMessage;
      }
  
      public Timer getCancelTimer() {
          synchronized (getConnectionMutex()) {
              if (this.cancelTimer == null) {
                  boolean createdNamedTimer = false;
  
                  // Use reflection magic to try this on JDK's 1.5 and newer, fallback to non-named timer on older VMs.
                  try {
                      Constructor<Timer> ctr = Timer.class.getConstructor(new Class[] { String.class, Boolean.TYPE });
  
                      this.cancelTimer = ctr.newInstance(new Object[] { "MySQL Statement Cancellation Timer", Boolean.TRUE });
                      createdNamedTimer = true;
                  } catch (Throwable t) {
                      createdNamedTimer = false;
                  }
  
                  if (!createdNamedTimer) {
                      this.cancelTimer = new Timer(true);
                  }
              }
  
              return this.cancelTimer;
          }
      }
  
      /**
       * Creates a connection instance -- We need to provide factory-style methods
       * so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise
       * the class verifier complains when it tries to load JDBC4-only interface
       * classes that are present in JDBC4 method signatures.
       */
  
      protected static Connection getInstance(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url)
              throws SQLException {
          if (!Util.isJdbc4()) {
              return new ConnectionImpl(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url);
          }
  
          return (Connection) Util.handleNewInstance(JDBC_4_CONNECTION_CTOR, new Object[] { hostToConnectTo, Integer.valueOf(portToConnectTo), info,
                  databaseToConnectTo, url }, null);
      }
  
      private static final Random random = new Random();
  
      /**
       * @param url
       * @param hostList
       */
      protected static synchronized int getNextRoundRobinHostIndex(String url, List<?> hostList) {
          // we really do "random" here, because you don't get even distribution when this is coupled with connection pools
  
          int indexRange = hostList.size();
  
          int index = random.nextInt(indexRange);
  
          return index;
      }
  
      private static boolean nullSafeCompare(String s1, String s2) {
          if (s1 == null && s2 == null) {
              return true;
          }
  
          if (s1 == null && s2 != null) {
              return false;
          }
  
          return s1 != null && s1.equals(s2);
      }
  
      /** Are we in autoCommit mode? */
      private boolean autoCommit = true;
  
      /** A cache of SQL to parsed prepared statement parameters. */
      private CacheAdapter<String, ParseInfo> cachedPreparedStatementParams;
  
      /**
       * For servers > 4.1.0, what character set is the metadata returned in?
       */
      private String characterSetMetadata = null;
  
      /**
       * The character set we want results and result metadata returned in (null ==
       * results in any charset, metadata in UTF-8).
       */
      private String characterSetResultsOnServer = null;
  
      /**
       * Holds cached mappings to charset converters to avoid static
       * synchronization and at the same time save memory (each charset converter
       * takes approx 65K of static data).
       */
      private Map<String, Object> charsetConverterMap = new HashMap<String, Object>(CharsetMapping.getNumberOfCharsetsConfigured());
  
      /** The point in time when this connection was created */
      private long connectionCreationTimeMillis = 0;
  
      /** ID used when profiling */
      private long connectionId;
  
      /** The database we're currently using (called Catalog in JDBC terms). */
      private String database = null;
  
      /** Internal DBMD to use for various database-version specific features */
      private DatabaseMetaData dbmd = null;
  
      private TimeZone defaultTimeZone;
  
      /** The event sink to use for profiling */
      private ProfilerEventHandler eventSink;
  
      /** Why was this connection implicitly closed, if known? (for diagnostics) */
      private Throwable forceClosedReason;
  
      /** Does the server suuport isolation levels? */
      private boolean hasIsolationLevels = false;
  
      /** Does this version of MySQL support quoted identifiers? */
      private boolean hasQuotedIdentifiers = false;
  
      /** The hostname we're connected to */
      private String host = null;
  
      /**
       * We need this 'bootstrapped', because 4.1 and newer will send fields back
       * with this even before we fill this dynamically from the server.
       */
      public Map<Integer, String> indexToMysqlCharset = new HashMap<Integer, String>();
  
      public Map<Integer, String> indexToCustomMysqlCharset = null; //new HashMap<Integer, String>();
  
      private Map<String, Integer> mysqlCharsetToCustomMblen = null; //new HashMap<String, Integer>();
  
      /** The I/O abstraction interface (network conn to MySQL server */
      private transient MysqlIO io = null;
  
      private boolean isClientTzUTC = false;
  
      /** Has this connection been closed? */
      private boolean isClosed = true;
  
      /** Is this connection associated with a global tx? */
      private boolean isInGlobalTx = false;
  
      /** Is this connection running inside a JDK-1.3 VM? */
      private boolean isRunningOnJDK13 = false;
  
      /** isolation level */
      private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED;
  
      private boolean isServerTzUTC = false;
  
      /** When did the last query finish? */
      private long lastQueryFinishedTime = 0;
  
      /** The logger we're going to use */
      private transient Log log = NULL_LOGGER;
  
      /**
       * If gathering metrics, what was the execution time of the longest query so
       * far ?
       */
      private long longestQueryTimeMs = 0;
  
      /** Is the server configured to use lower-case table names only? */
      private boolean lowerCaseTableNames = false;
  
      /** When did the master fail? */
      //	private long masterFailTimeMillis = 0L;
  
      private long maximumNumberTablesAccessed = 0;
  
      /** The max-rows setting for current session */
      private int sessionMaxRows = -1;
  
      /** When was the last time we reported metrics? */
      private long metricsLastReportedMs;
  
      private long minimumNumberTablesAccessed = Long.MAX_VALUE;
  
      /** The JDBC URL we're using */
      private String myURL = null;
  
      /** Does this connection need to be tested? */
      private boolean needsPing = false;
  
      private int netBufferLength = 16384;
  
      private boolean noBackslashEscapes = false;
  
      private long numberOfPreparedExecutes = 0;
  
      private long numberOfPrepares = 0;
  
      private long numberOfQueriesIssued = 0;
  
      private long numberOfResultSetsCreated = 0;
  
      private long[] numTablesMetricsHistBreakpoints;
  
      private int[] numTablesMetricsHistCounts;
  
      private long[] oldHistBreakpoints = null;
  
      private int[] oldHistCounts = null;
  
      /** A map of currently open statements */
      private Map<Statement, Statement> openStatements;
  
      private LRUCache parsedCallableStatementCache;
  
      private boolean parserKnowsUnicode = false;
  
      /** The password we used */
      private String password = null;
  
      private long[] perfMetricsHistBreakpoints;
  
      private int[] perfMetricsHistCounts;
  
      /** Point of origin where this Connection was created */
      private String pointOfOrigin;
  
      /** The port number we're connected to (defaults to 3306) */
      private int port = 3306;
  
      /** Properties for this connection specified by user */
      protected Properties props = null;
  
      /** Should we retrieve 'info' messages from the server? */
      private boolean readInfoMsg = false;
  
      /** Are we in read-only mode? */
      private boolean readOnly = false;
  
      /** Cache of ResultSet metadata */
      protected LRUCache resultSetMetadataCache;
  
      /** The timezone of the server */
      private TimeZone serverTimezoneTZ = null;
  
      /** The map of server variables that we retrieve at connection init. */
      private Map<String, String> serverVariables = null;
  
      private long shortestQueryTimeMs = Long.MAX_VALUE;
  
      private double totalQueryTimeMs = 0;
  
      /** Are transactions supported by the MySQL server we are connected to? */
      private boolean transactionsSupported = false;
  
      /**
       * The type map for UDTs (not implemented, but used by some third-party
       * vendors, most notably IBM WebSphere)
       */
      private Map<String, Class<?>> typeMap;
  
      /** Has ANSI_QUOTES been enabled on the server? */
      private boolean useAnsiQuotes = false;
  
      /** The user we're connected as */
      private String user = null;
  
      /**
       * Should we use server-side prepared statements? (auto-detected, but can be
       * disabled by user)
       */
      private boolean useServerPreparedStmts = false;
  
      private LRUCache serverSideStatementCheckCache;
      private LRUCache serverSideStatementCache;
      private Calendar sessionCalendar;
  
      private Calendar utcCalendar;
  
      private String origHostToConnectTo;
  
      // we don't want to be able to publicly clone this...
  
      private int origPortToConnectTo;
  
      private String origDatabaseToConnectTo;
  
      private String errorMessageEncoding = "Cp1252"; // to begin with, changes after we talk to the server
  
      private boolean usePlatformCharsetConverters;
  
      /*
       * For testing failover scenarios
       */
      private boolean hasTriedMasterFlag = false;
  
      /**
       * The comment (if any) that we'll prepend to all statements
       * sent to the server (to show up in "SHOW PROCESSLIST")
       */
      private String statementComment = null;
  
      private boolean storesLowerCaseTableName;
  
      private List<StatementInterceptorV2> statementInterceptors;
  
      /**
       * If a CharsetEncoder is required for escaping. Needed for SJIS and related
       * problems with \u00A5.
       */
      private boolean requiresEscapingEncoder;
  
      private String hostPortPair;
  
      /**
       * '
       * For the delegate only
       */
      protected ConnectionImpl() {
      }
  
      /**
       * Creates a connection to a MySQL Server.
       * 
       * @param hostToConnectTo
       *            the hostname of the database server
       * @param portToConnectTo
       *            the port number the server is listening on
       * @param info
       *            a Properties[] list holding the user and password
       * @param databaseToConnectTo
       *            the database to connect to
       * @param url
       *            the URL of the connection
       * @param d
       *            the Driver instantation of the connection
       * @exception SQLException
       *                if a database access error occurs
       */
      public ConnectionImpl(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url) throws SQLException {
  
          this.connectionCreationTimeMillis = System.currentTimeMillis();
  
          if (databaseToConnectTo == null) {
              databaseToConnectTo = "";
          }
  
          // Stash away for later, used to clone this connection for Statement.cancel and Statement.setQueryTimeout().
          //
  
          this.origHostToConnectTo = hostToConnectTo;
          this.origPortToConnectTo = portToConnectTo;
          this.origDatabaseToConnectTo = databaseToConnectTo;
  
          try {
              Blob.class.getMethod("truncate", new Class[] { Long.TYPE });
  
              this.isRunningOnJDK13 = false;
          } catch (NoSuchMethodException nsme) {
              this.isRunningOnJDK13 = true;
          }
  
          this.sessionCalendar = new GregorianCalendar();
          this.utcCalendar = new GregorianCalendar();
          this.utcCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
  
          //
          // Normally, this code would be in initializeDriverProperties, but we need to do this as early as possible, so we can start logging to the 'correct'
          // place as early as possible...this.log points to 'NullLogger' for every connection at startup to avoid NPEs and the overhead of checking for NULL at
          // every logging call.
          //
          // We will reset this to the configured logger during properties initialization.
          //
          this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME, getExceptionInterceptor());
  
          // We store this per-connection, due to static synchronization issues in Java's built-in TimeZone class...
          this.defaultTimeZone = Util.getDefaultTimeZone();
  
          this.isClientTzUTC = "GMT".equalsIgnoreCase(this.defaultTimeZone.getID());
  
          this.openStatements = new HashMap<Statement, Statement>();
  
          if (NonRegisteringDriver.isHostPropertiesList(hostToConnectTo)) {
              Properties hostSpecificProps = NonRegisteringDriver.expandHostKeyValues(hostToConnectTo);
  
              Enumeration<?> propertyNames = hostSpecificProps.propertyNames();
  
              while (propertyNames.hasMoreElements()) {
                  String propertyName = propertyNames.nextElement().toString();
                  String propertyValue = hostSpecificProps.getProperty(propertyName);
  
                  info.setProperty(propertyName, propertyValue);
              }
          } else {
  
              if (hostToConnectTo == null) {
                  this.host = "localhost";
                  this.hostPortPair = this.host + ":" + portToConnectTo;
              } else {
                  this.host = hostToConnectTo;
  
                  if (hostToConnectTo.indexOf(":") == -1) {
                      this.hostPortPair = this.host + ":" + portToConnectTo;
                  } else {
                      this.hostPortPair = this.host;
                  }
              }
          }
  
          this.port = portToConnectTo;
  
          this.database = databaseToConnectTo;
          this.myURL = url;
          this.user = info.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY);
          this.password = info.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY);
  
          if ((this.user == null) || this.user.equals("")) {
              this.user = "";
          }
  
          if (this.password == null) {
              this.password = "";
          }
  
          this.props = info;
  
          initializeDriverProperties(info);
  
          if (getUseUsageAdvisor()) {
              this.pointOfOrigin = LogUtils.findCallingClassAndMethod(new Throwable());
          } else {
              this.pointOfOrigin = "";
          }
  
          try {
              this.dbmd = getMetaData(false, false);
              initializeSafeStatementInterceptors();
              createNewIO(false);
              unSafeStatementInterceptors();
          } catch (SQLException ex) {
              cleanup(ex);
  
              // don't clobber SQL exceptions
              throw ex;
          } catch (Exception ex) {
              cleanup(ex);
  
              StringBuffer mesg = new StringBuffer(128);
  
              if (!getParanoid()) {
                  mesg.append("Cannot connect to MySQL server on ");
                  mesg.append(this.host);
                  mesg.append(":");
                  mesg.append(this.port);
                  mesg.append(".\n\n");
                  mesg.append("Make sure that there is a MySQL server ");
                  mesg.append("running on the machine/port you are trying ");
                  mesg.append("to connect to and that the machine this software is running on ");
                  mesg.append("is able to connect to this host/port (i.e. not firewalled). ");
                  mesg.append("Also make sure that the server has not been started with the --skip-networking ");
                  mesg.append("flag.\n\n");
              } else {
                  mesg.append("Unable to connect to database.");
              }
  
              SQLException sqlEx = SQLError.createSQLException(mesg.toString(), SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, getExceptionInterceptor());
  
              sqlEx.initCause(ex);
  
              throw sqlEx;
          }
  
          NonRegisteringDriver.trackConnection(this);
      }
  
      public void unSafeStatementInterceptors() throws SQLException {
  
          ArrayList<StatementInterceptorV2> unSafedStatementInterceptors = new ArrayList<StatementInterceptorV2>(this.statementInterceptors.size());
  
          for (int i = 0; i < this.statementInterceptors.size(); i++) {
              NoSubInterceptorWrapper wrappedInterceptor = (NoSubInterceptorWrapper) this.statementInterceptors.get(i);
  
              unSafedStatementInterceptors.add(wrappedInterceptor.getUnderlyingInterceptor());
          }
  
          this.statementInterceptors = unSafedStatementInterceptors;
  
          if (this.io != null) {
              this.io.setStatementInterceptors(this.statementInterceptors);
          }
      }
  
      public void initializeSafeStatementInterceptors() throws SQLException {
          this.isClosed = false;
  
          List<Extension> unwrappedInterceptors = Util.loadExtensions(this, this.props, getStatementInterceptors(), "MysqlIo.BadStatementInterceptor",
                  getExceptionInterceptor());
  
          this.statementInterceptors = new ArrayList<StatementInterceptorV2>(unwrappedInterceptors.size());
  
          for (int i = 0; i < unwrappedInterceptors.size(); i++) {
              Extension interceptor = unwrappedInterceptors.get(i);
  
              // adapt older versions of statement interceptors, handle the case where something wants v2 functionality but wants to run with an older driver
              if (interceptor instanceof StatementInterceptor) {
                  if (ReflectiveStatementInterceptorAdapter.getV2PostProcessMethod(interceptor.getClass()) != null) {
                      this.statementInterceptors.add(new NoSubInterceptorWrapper(new ReflectiveStatementInterceptorAdapter((StatementInterceptor) interceptor)));
                  } else {
                      this.statementInterceptors.add(new NoSubInterceptorWrapper(new V1toV2StatementInterceptorAdapter((StatementInterceptor) interceptor)));
                  }
              } else {
                  this.statementInterceptors.add(new NoSubInterceptorWrapper((StatementInterceptorV2) interceptor));
              }
          }
  
      }
  
      public List<StatementInterceptorV2> getStatementInterceptorsInstances() {
          return this.statementInterceptors;
      }
  
      private void addToHistogram(int[] histogramCounts, long[] histogramBreakpoints, long value, int numberOfTimes, long currentLowerBound,
              long currentUpperBound) {
          if (histogramCounts == null) {
              createInitialHistogram(histogramBreakpoints, currentLowerBound, currentUpperBound);
          } else {
              for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
                  if (histogramBreakpoints[i] >= value) {
                      histogramCounts[i] += numberOfTimes;
  
                      break;
                  }
              }
          }
      }
  
      private void addToPerformanceHistogram(long value, int numberOfTimes) {
          checkAndCreatePerformanceHistogram();
  
          addToHistogram(this.perfMetricsHistCounts, this.perfMetricsHistBreakpoints, value, numberOfTimes, this.shortestQueryTimeMs == Long.MAX_VALUE ? 0
                  : this.shortestQueryTimeMs, this.longestQueryTimeMs);
      }
  
      private void addToTablesAccessedHistogram(long value, int numberOfTimes) {
          checkAndCreateTablesAccessedHistogram();
  
          addToHistogram(this.numTablesMetricsHistCounts, this.numTablesMetricsHistBreakpoints, value, numberOfTimes,
                  this.minimumNumberTablesAccessed == Long.MAX_VALUE ? 0 : this.minimumNumberTablesAccessed, this.maximumNumberTablesAccessed);
      }
  
      /**
       * Builds the map needed for 4.1.0 and newer servers that maps field-level
       * charset/collation info to a java character encoding name.
       * 
       * @throws SQLException
       */
      private void buildCollationMapping() throws SQLException {
  
          Map<Integer, String> indexToCharset = null;
          Map<Long, String> sortedCollationMap = null;
          Map<Integer, String> customCharset = null;
          Map<String, Integer> customMblen = null;
  
          if (getCacheServerConfiguration()) {
              synchronized (dynamicIndexToCharsetMapByUrl) {
                  indexToCharset = dynamicIndexToCharsetMapByUrl.get(getURL());
                  sortedCollationMap = dynamicIndexToCollationMapByUrl.get(getURL());
                  customCharset = customIndexToCharsetMapByUrl.get(getURL());
                  customMblen = customCharsetToMblenMapByUrl.get(getURL());
              }
          }
  
          if (indexToCharset == null) {
              indexToCharset = new HashMap<Integer, String>();
  
              if (versionMeetsMinimum(4, 1, 0) && getDetectCustomCollations()) {
  
                  java.sql.Statement stmt = null;
                  java.sql.ResultSet results = null;
  
                  try {
                      sortedCollationMap = new TreeMap<Long, String>();
                      customCharset = new HashMap<Integer, String>();
                      customMblen = new HashMap<String, Integer>();
  
                      stmt = getMetadataSafeStatement();
  
                      try {
                          results = stmt.executeQuery("SHOW COLLATION");
                          if (versionMeetsMinimum(5, 0, 0)) {
                              Util.resultSetToMap(sortedCollationMap, results, 3, 2);
                          } else {
                              while (results.next()) {
                                  sortedCollationMap.put(results.getLong(3), results.getString(2));
                              }
                          }
                      } catch (SQLException ex) {
                          if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) {
                              throw ex;
                          }
                      }
  
                      for (Iterator<Map.Entry<Long, String>> indexIter = sortedCollationMap.entrySet().iterator(); indexIter.hasNext();) {
                          Map.Entry<Long, String> indexEntry = indexIter.next();
  
                          int collationIndex = indexEntry.getKey().intValue();
                          String charsetName = indexEntry.getValue();
  
                          indexToCharset.put(collationIndex, charsetName);
  
                          // if no static map for charsetIndex or server has a different mapping then our static map, adding it to custom map 
                          if (collationIndex >= CharsetMapping.MAP_SIZE
                                  || !charsetName.equals(CharsetMapping.getMysqlCharsetNameForCollationIndex(collationIndex))) {
                              customCharset.put(collationIndex, charsetName);
                          }
  
                          // if no static map for charsetName adding to custom map
                          if (!CharsetMapping.CHARSET_NAME_TO_CHARSET.containsKey(charsetName)) {
                              customMblen.put(charsetName, null);
                          }
                      }
  
                      // if there is a number of custom charsets we should execute SHOW CHARACTER SET to know theirs mblen
                      if (customMblen.size() > 0) {
                          try {
                              results = stmt.executeQuery("SHOW CHARACTER SET");
                              while (results.next()) {
                                  String charsetName = results.getString("Charset");
                                  if (customMblen.containsKey(charsetName)) {
                                      customMblen.put(charsetName, results.getInt("Maxlen"));
                                  }
                              }
                          } catch (SQLException ex) {
                              if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) {
                                  throw ex;
                              }
                          }
                      }
  
                      if (getCacheServerConfiguration()) {
                          synchronized (dynamicIndexToCharsetMapByUrl) {
                              dynamicIndexToCharsetMapByUrl.put(getURL(), indexToCharset);
                              dynamicIndexToCollationMapByUrl.put(getURL(), sortedCollationMap);
                              customIndexToCharsetMapByUrl.put(getURL(), customCharset);
                              customCharsetToMblenMapByUrl.put(getURL(), customMblen);
                          }
                      }
  
                  } catch (SQLException ex) {
                      throw ex;
                  } catch (RuntimeException ex) {
                      SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
                      sqlEx.initCause(ex);
                      throw sqlEx;
                  } finally {
                      if (results != null) {
                          try {
                              results.close();
                          } catch (java.sql.SQLException sqlE) {
                              // ignore
                          }
                      }
  
                      if (stmt != null) {
                          try {
                              stmt.close();
                          } catch (java.sql.SQLException sqlE) {
                              // ignore
                          }
                      }
                  }
              } else {
                  for (int i = 1; i < CharsetMapping.MAP_SIZE; i++) {
                      indexToCharset.put(i, CharsetMapping.getMysqlCharsetNameForCollationIndex(i));
                  }
                  if (getCacheServerConfiguration()) {
                      synchronized (dynamicIndexToCharsetMapByUrl) {
                          dynamicIndexToCharsetMapByUrl.put(getURL(), indexToCharset);
                      }
                  }
              }
  
          }
  
          this.indexToMysqlCharset = Collections.unmodifiableMap(indexToCharset);
          if (customCharset != null) {
              this.indexToCustomMysqlCharset = Collections.unmodifiableMap(customCharset);
          }
          if (customMblen != null) {
              this.mysqlCharsetToCustomMblen = Collections.unmodifiableMap(customMblen);
          }
      }
  
      private boolean canHandleAsServerPreparedStatement(String sql) throws SQLException {
          if (sql == null || sql.length() == 0) {
              return true;
          }
  
          if (!this.useServerPreparedStmts) {
              return false;
          }
  
          if (getCachePreparedStatements()) {
              synchronized (this.serverSideStatementCheckCache) {
                  Boolean flag = (Boolean) this.serverSideStatementCheckCache.get(sql);
  
                  if (flag != null) {
                      return flag.booleanValue();
                  }
  
                  boolean canHandle = canHandleAsServerPreparedStatementNoCache(sql);
  
                  if (sql.length() < getPreparedStatementCacheSqlLimit()) {
                      this.serverSideStatementCheckCache.put(sql, canHandle ? Boolean.TRUE : Boolean.FALSE);
                  }
  
                  return canHandle;
              }
          }
  
          return canHandleAsServerPreparedStatementNoCache(sql);
      }
  
      private boolean canHandleAsServerPreparedStatementNoCache(String sql) throws SQLException {
  
          // Can't use server-side prepare for CALL
          if (StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "CALL")) {
              return false;
          }
  
          boolean canHandleAsStatement = true;
  
          if (!versionMeetsMinimum(5, 0, 7)
                  && (StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "SELECT") || StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "DELETE")
                          || StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "INSERT")
                          || StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "UPDATE") || StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
                          "REPLACE"))) {
  
              // check for limit ?[,?]
  
              /*
               * The grammar for this (from the server) is: ULONG_NUM | ULONG_NUM ',' ULONG_NUM | ULONG_NUM OFFSET_SYM ULONG_NUM
               */
  
              int currentPos = 0;
              int statementLength = sql.length();
              int lastPosToLook = statementLength - 7; // "LIMIT ".length()
              boolean allowBackslashEscapes = !this.noBackslashEscapes;
              String quoteChar = this.useAnsiQuotes ? "\"" : "'";
              boolean foundLimitWithPlaceholder = false;
  
              while (currentPos < lastPosToLook) {
                  int limitStart = StringUtils.indexOfIgnoreCase(currentPos, sql, "LIMIT ", quoteChar, quoteChar,
                          allowBackslashEscapes ? StringUtils.SEARCH_MODE__ALL : StringUtils.SEARCH_MODE__MRK_COM_WS);
  
                  if (limitStart == -1) {
                      break;
                  }
  
                  currentPos = limitStart + 7;
  
                  while (currentPos < statementLength) {
                      char c = sql.charAt(currentPos);
  
                      //
                      // Have we reached the end of what can be in a LIMIT clause?
                      //
  
                      if (!Character.isDigit(c) && !Character.isWhitespace(c) && c != ',' && c != '?') {
                          break;
                      }
  
                      if (c == '?') {
                          foundLimitWithPlaceholder = true;
                          break;
                      }
  
                      currentPos++;
                  }
              }
  
              canHandleAsStatement = !foundLimitWithPlaceholder;
          } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "XA ")) {
              canHandleAsStatement = false;
          } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "CREATE TABLE")) {
              canHandleAsStatement = false;
          } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "DO")) {
              canHandleAsStatement = false;
          } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "SET")) {
              canHandleAsStatement = false;
          }
  
          return canHandleAsStatement;
      }
  
      /**
       * Changes the user on this connection by performing a re-authentication. If
       * authentication fails, the connection will remain under the context of the
       * current user.
       * 
       * @param userName
       *            the username to authenticate with
       * @param newPassword
       *            the password to authenticate with
       * @throws SQLException
       *             if authentication fails, or some other error occurs while
       *             performing the command.
       */
      public void changeUser(String userName, String newPassword) throws SQLException {
          synchronized (getConnectionMutex()) {
              checkClosed();
  
              if ((userName == null) || userName.equals("")) {
                  userName = "";
              }
  
              if (newPassword == null) {
                  newPassword = "";
              }
  
              // reset maxRows to default value
              this.sessionMaxRows = -1;
  
              try {
                  this.io.changeUser(userName, newPassword, this.database);
              } catch (SQLException ex) {
                  if (versionMeetsMinimum(5, 6, 13) && "28000".equals(ex.getSQLState())) {
                      cleanup(ex);
                  }
                  throw ex;
              }
              this.user = userName;
              this.password = newPassword;
  
              if (versionMeetsMinimum(4, 1, 0)) {
                  configureClientCharacterSet(true);
              }
  
              setSessionVariables();
  
              setupServerForTruncationChecks();
          }
      }
  
      private boolean characterSetNamesMatches(String mysqlEncodingName) {
          // set names is equivalent to character_set_client ..._results and ..._connection, but we set _results later, so don't check it here.
          return (mysqlEncodingName != null && mysqlEncodingName.equalsIgnoreCase(this.serverVariables.get("character_set_client")) && mysqlEncodingName
                  .equalsIgnoreCase(this.serverVariables.get("character_set_connection")));
      }
  
      private void checkAndCreatePerformanceHistogram() {
          if (this.perfMetricsHistCounts == null) {
              this.perfMetricsHistCounts = new int[HISTOGRAM_BUCKETS];
          }
  
          if (this.perfMetricsHistBreakpoints == null) {
              this.perfMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS];
          }
      }
  
      private void checkAndCreateTablesAccessedHistogram() {
          if (this.numTablesMetricsHistCounts == null) {
              this.numTablesMetricsHistCounts = new int[HISTOGRAM_BUCKETS];
          }
  
          if (this.numTablesMetricsHistBreakpoints == null) {
              this.numTablesMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS];
          }
      }
  
      public void checkClosed() throws SQLException {
          if (this.isClosed) {
              throwConnectionClosedException();
          }
      }
  
      public void throwConnectionClosedException() throws SQLException {
          StringBuffer messageBuf = new StringBuffer("No operations allowed after connection closed.");
  
          SQLException ex = SQLError.createSQLException(messageBuf.toString(), SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor());
  
          if (this.forceClosedReason != null) {
              ex.initCause(this.forceClosedReason);
          }
  
          throw ex;
      }
  
      /**
       * If useUnicode flag is set and explicit client character encoding isn't
       * specified then assign encoding from server if any.
       * 
       * @throws SQLException
       */
      private void checkServerEncoding() throws SQLException {
          if (getUseUnicode() && (getEncoding() != null)) {
              // spec'd by client, don't map
              return;
          }
  
          String serverCharset = this.serverVariables.get("character_set");
  
          if (serverCharset == null) {
              // must be 4.1.1 or newer?
              serverCharset = this.serverVariables.get("character_set_server");
          }
  
          String mappedServerEncoding = null;
  
          if (serverCharset != null) {
              try {
                  mappedServerEncoding = CharsetMapping.getJavaEncodingForMysqlCharset(serverCharset);
              } catch (RuntimeException ex) {
                  SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
                  sqlEx.initCause(ex);
                  throw sqlEx;
              }
          }
  
          //
          // First check if we can do the encoding ourselves
          //
          if (!getUseUnicode() && (mappedServerEncoding != null)) {
              SingleByteCharsetConverter converter = getCharsetConverter(mappedServerEncoding);
  
              if (converter != null) { // we know how to convert this ourselves
                  setUseUnicode(true); // force the issue
                  setEncoding(mappedServerEncoding);
  
                  return;
              }
          }
  
          //
          // Now, try and find a Java I/O converter that can do the encoding for us
          //
          if (serverCharset != null) {
              if (mappedServerEncoding == null) {
                  // We don't have a mapping for it, so try and canonicalize the name....
                  if (Character.isLowerCase(serverCharset.charAt(0))) {
                      char[] ach = serverCharset.toCharArray();
                      ach[0] = Character.toUpperCase(serverCharset.charAt(0));
                      setEncoding(new String(ach));
                  }
              }
  
              if (mappedServerEncoding == null) {
                  throw SQLError.createSQLException("Unknown character encoding on server '" + serverCharset + "', use 'characterEncoding=' property "
                          + " to provide correct mapping", SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
              }
  
              //
              // Attempt to use the encoding, and bail out if it can't be used
              //
              try {
                  StringUtils.getBytes("abc", mappedServerEncoding);
                  setEncoding(mappedServerEncoding);
                  setUseUnicode(true);
              } catch (UnsupportedEncodingException UE) {
                  throw SQLError.createSQLException("The driver can not map the character encoding '" + getEncoding() + "' that your server is using "
                          + "to a character encoding your JVM understands. You can specify this mapping manually by adding \"useUnicode=true\" "
                          + "as well as \"characterEncoding=[an_encoding_your_jvm_understands]\" to your JDBC URL.", "0S100", getExceptionInterceptor());
              }
          }
      }
  
      /**
       * Set transaction isolation level to the value received from server if any.
       * Is called by connectionInit(...)
       * 
       * @throws SQLException
       */
      private void checkTransactionIsolationLevel() throws SQLException {
          String txIsolationName = null;
  
          if (versionMeetsMinimum(4, 0, 3)) {
              txIsolationName = "tx_isolation";
          } else {
              txIsolationName = "transaction_isolation";
          }
  
          String s = this.serverVariables.get(txIsolationName);
  
          if (s != null) {
              Integer intTI = mapTransIsolationNameToValue.get(s);
  
              if (intTI != null) {
                  this.isolationLevel = intTI.intValue();
              }
          }
      }
  
      /**
       * Clobbers the physical network connection and marks
       * this connection as closed.
       * 
       * @throws SQLException
       */
      public void abortInternal() throws SQLException {
          if (this.io != null) {
              try {
                  this.io.forceClose();
              } catch (Throwable t) {
                  // can't do anything about it, and we're forcibly aborting
              }
              this.io.releaseResources();
              this.io = null;
          }
  
          this.isClosed = true;
      }
  
      /**
       * Destroys this connection and any underlying resources
       * 
       * @param fromWhere
       * @param whyCleanedUp
       */
      private void cleanup(Throwable whyCleanedUp) {
          try {
              if (this.io != null) {
                  if (isClosed()) {
                      this.io.forceClose();
                  } else {
                      realClose(false, false, false, whyCleanedUp);
                  }
              }
          } catch (SQLException sqlEx) {
              // ignore, we're going away.
          }
  
          this.isClosed = true;
      }
  
      public void clearHasTriedMaster() {
          this.hasTriedMasterFlag = false;
      }
  
      /**
       * After this call, getWarnings returns null until a new warning is reported
       * for this connection.
       * 
       * @exception SQLException
       *                if a database access error occurs
       */
      public void clearWarnings() throws SQLException {
          // firstWarning = null;
      }
  
      /**
       * @param sql
       * @throws SQLException
       */
      public java.sql.PreparedStatement clientPrepareStatement(String sql) throws SQLException {
          return clientPrepareStatement(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY);
      }
  
      /**
       * @see Connection#prepareStatement(String, int)
       */
      public java.sql.PreparedStatement clientPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException {
          java.sql.PreparedStatement pStmt = clientPrepareStatement(sql);
  
          ((com.mysql.jdbc.PreparedStatement) pStmt).setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
  
          return pStmt;
      }
  
      /**
       * @param sql
       * @param resultSetType
       * @param resultSetConcurrency
       * @throws SQLException
       */
      public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
          return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true);
      }
  
      public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, boolean processEscapeCodesIfNeeded)
              throws SQLException {
          checkClosed();
  
          String nativeSql = processEscapeCodesIfNeeded && getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql) : sql;
  
          PreparedStatement pStmt = null;
  
          if (getCachePreparedStatements()) {
              PreparedStatement.ParseInfo pStmtInfo = this.cachedPreparedStatementParams.get(nativeSql);
  
              if (pStmtInfo == null) {
                  pStmt = com.mysql.jdbc.PreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql, this.database);
  
                  this.cachedPreparedStatementParams.put(nativeSql, pStmt.getParseInfo());
              } else {
                  pStmt = new com.mysql.jdbc.PreparedStatement(getLoadBalanceSafeProxy(), nativeSql, this.database, pStmtInfo);
              }
          } else {
              pStmt = com.mysql.jdbc.PreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql, this.database);
          }
  
          pStmt.setResultSetType(resultSetType);
          pStmt.setResultSetConcurrency(resultSetConcurrency);
  
          return pStmt;
      }
  
      /**
       * @see java.sql.Connection#prepareStatement(String, int[])
       */
      public java.sql.PreparedStatement clientPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException {
  
          PreparedStatement pStmt = (PreparedStatement) clientPrepareStatement(sql);
  
          pStmt.setRetrieveGeneratedKeys((autoGenKeyIndexes != null) && (autoGenKeyIndexes.length > 0));
  
          return pStmt;
      }
  
      /**
       * @see java.sql.Connection#prepareStatement(String, String[])
       */
      public java.sql.PreparedStatement clientPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException {
          PreparedStatement pStmt = (PreparedStatement) clientPrepareStatement(sql);
  
          pStmt.setRetrieveGeneratedKeys((autoGenKeyColNames != null) && (autoGenKeyColNames.length > 0));
  
          return pStmt;
      }
  
      public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
              throws SQLException {
          return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true);
      }
  
      // --------------------------JDBC 2.0-----------------------------
  
      /**
       * In some cases, it is desirable to immediately release a Connection's
       * database and JDBC resources instead of waiting for them to be
       * automatically released (cant think why off the top of my head) <B>Note:</B>
       * A Connection is automatically closed when it is garbage collected.
       * Certain fatal errors also result in a closed connection.
       * 
       * @exception SQLException
       *                if a database access error occurs
       */
      public void close() throws SQLException {
          synchronized (getConnectionMutex()) {
              if (this.connectionLifecycleInterceptors != null) {
                  new IterateBlock<Extension>(this.connectionLifecycleInterceptors.iterator()) {
                      @Override
                      void forEach(Extension each) throws SQLException {
                          ((ConnectionLifecycleInterceptor) each).close();
                      }
                  }.doForAll();
              }
  
              realClose(true, true, false, null);
          }
      }
  
      /**
       * Closes all currently open statements.
       * 
       * @throws SQLException
       */
      private void closeAllOpenStatements() throws SQLException {
          SQLException postponedException = null;
  
          if (this.openStatements != null) {
              List<Statement> currentlyOpenStatements = new ArrayList<Statement>(); // we need this to
              // avoid ConcurrentModificationEx
  
              for (Iterator<Statement> iter = this.openStatements.keySet().iterator(); iter.hasNext();) {
                  currentlyOpenStatements.add(iter.next());
              }
  
              int numStmts = currentlyOpenStatements.size();
  
              for (int i = 0; i < numStmts; i++) {
                  StatementImpl stmt = (StatementImpl) currentlyOpenStatements.get(i);
  
                  try {
                      stmt.realClose(false, true);
                  } catch (SQLException sqlEx) {
                      postponedException = sqlEx; // throw it later, cleanup all
                      // statements first
                  }
              }
  
              if (postponedException != null) {
                  throw postponedException;
              }
          }
      }
  
      private void closeStatement(java.sql.Statement stmt) {
          if (stmt != null) {
              try {
                  stmt.close();
              } catch (SQLException sqlEx) {
                  // ignore
              }
  
              stmt = null;
          }
      }
  
      /**
       * The method commit() makes all changes made since the previous
       * commit/rollback permanent and releases any database locks currently held
       * by the Connection. This method should only be used when auto-commit has
       * been disabled.
       * <p>
       * <b>Note:</b> MySQL does not support transactions, so this method is a no-op.
       * </p>
       * 
       * @exception SQLException
       *                if a database access error occurs
       * @see setAutoCommit
       */
      public void commit() throws SQLException {
          synchronized (getConnectionMutex()) {
              checkClosed();
  
              try {
                  if (this.connectionLifecycleInterceptors != null) {
                      IterateBlock<Extension> iter = new IterateBlock<Extension>(this.connectionLifecycleInterceptors.iterator()) {
  
                          @Override
                          void forEach(Extension each) throws SQLException {
                              if (!((ConnectionLifecycleInterceptor) each).commit()) {
                                  this.stopIterating = true;
                              }
                          }
                      };
  
                      iter.doForAll();
  
                      if (!iter.fullIteration()) {
                          return;
                      }
                  }
  
                  // no-op if _relaxAutoCommit == true
                  if (this.autoCommit && !getRelaxAutoCommit()) {
                      throw SQLError.createSQLException("Can't call commit when autocommit=true", getExceptionInterceptor());
                  } else if (this.transactionsSupported) {
                      if (getUseLocalTransactionState() && versionMeetsMinimum(5, 0, 0)) {
                          if (!this.io.inTransactionOnServer()) {
                              return; // effectively a no-op
                          }
                      }
  
                      execSQL(null, "commit", -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false);
                  }
              } catch (SQLException sqlException) {
                  if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlException.getSQLState())) {
                      throw SQLError.createSQLException("Communications link failure during commit(). Transaction resolution unknown.",
                              SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, getExceptionInterceptor());
                  }
  
                  throw sqlException;
              } finally {
                  this.needsPing = this.getReconnectAtTxEnd();
              }
          }
          return;
      }
  
      /**
       * Configures client-side properties for character set information.
       * 
       * @throws SQLException
       *             if unable to configure the specified character set.
       */
      private void configureCharsetProperties() throws SQLException {
          if (getEncoding() != null) {
              // Attempt to use the encoding, and bail out if it can't be used
              try {
                  String testString = "abc";
                  StringUtils.getBytes(testString, getEncoding());
              } catch (UnsupportedEncodingException UE) {
                  // Try the MySQL character encoding, then....
                  String oldEncoding = getEncoding();
  
                  try {
                      setEncoding(CharsetMapping.getJavaEncodingForMysqlCharset(oldEncoding));
                  } catch (RuntimeException ex) {
                      SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
                      sqlEx.initCause(ex);
                      throw sqlEx;
                  }
  
                  if (getEncoding() == null) {
                      throw SQLError.createSQLException("Java does not support the MySQL character encoding '" + oldEncoding + "'.",
                              SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
                  }
  
                  try {
                      String testString = "abc";
                      StringUtils.getBytes(testString, getEncoding());
                  } catch (UnsupportedEncodingException encodingEx) {
                      throw SQLError.createSQLException("Unsupported character encoding '" + getEncoding() + "'.",
                              SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
                  }
              }
          }
      }
  
      /**
       * Sets up client character set for MySQL-4.1 and newer if the user This
       * must be done before any further communication with the server!
       * 
       * @return true if this routine actually configured the client character
       *         set, or false if the driver needs to use 'older' methods to
       *         detect the character set, as it is connected to a MySQL server
       *         older than 4.1.0
       * @throws SQLException
       *             if an exception happens while sending 'SET NAMES' to the
       *             server, or the server sends character set information that
       *             the client doesn't know about.
       */
      private boolean configureClientCharacterSet(boolean dontCheckServerMatch) throws SQLException {
          String realJavaEncoding = getEncoding();
          boolean characterSetAlreadyConfigured = false;
  
          try {
              if (versionMeetsMinimum(4, 1, 0)) {
                  characterSetAlreadyConfigured = true;
  
                  setUseUnicode(true);
  
                  configureCharsetProperties();
                  realJavaEncoding = getEncoding(); // we need to do this again to grab this for versions > 4.1.0
  
                  try {
  
                      // Fault injection for testing server character set indices
  
                      if (this.props != null && this.props.getProperty("com.mysql.jdbc.faultInjection.serverCharsetIndex") != null) {
                          this.io.serverCharsetIndex = Integer.parseInt(this.props.getProperty("com.mysql.jdbc.faultInjection.serverCharsetIndex"));
                      }
  
                      String serverEncodingToSet = CharsetMapping.getJavaEncodingForCollationIndex(this.io.serverCharsetIndex);
  
                      if (serverEncodingToSet == null || serverEncodingToSet.length() == 0) {
                          if (realJavaEncoding != null) {
                              // user knows best, try it
                              setEncoding(realJavaEncoding);
                          } else {
                              throw SQLError.createSQLException("Unknown initial character set index '" + this.io.serverCharsetIndex
                                      + "' received from server. Initial client character set can be forced via the 'characterEncoding' property.",
                                      SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
                          }
                      }
  
                      // "latin1" on MySQL-4.1.0+ is actually CP1252, not ISO8859_1
                      if (versionMeetsMinimum(4, 1, 0) && "ISO8859_1".equalsIgnoreCase(serverEncodingToSet)) {
                          serverEncodingToSet = "Cp1252";
                      }
                      if ("UnicodeBig".equalsIgnoreCase(serverEncodingToSet) || "UTF-16".equalsIgnoreCase(serverEncodingToSet)
                              || "UTF-16LE".equalsIgnoreCase(serverEncodingToSet) || "UTF-32".equalsIgnoreCase(serverEncodingToSet)) {
                          serverEncodingToSet = "UTF-8";
                      }
  
                      setEncoding(serverEncodingToSet);
  
                  } catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
                      if (realJavaEncoding != null) {
                          // user knows best, try it
                          setEncoding(realJavaEncoding);
                      } else {
                          throw SQLError.createSQLException("Unknown initial character set index '" + this.io.serverCharsetIndex
                                  + "' received from server. Initial client character set can be forced via the 'characterEncoding' property.",
                                  SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
                      }
                  } catch (SQLException ex) {
                      throw ex;
                  } catch (RuntimeException ex) {
                      SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
                      sqlEx.initCause(ex);
                      throw sqlEx;
                  }
  
                  if (getEncoding() == null) {
                      // punt?
                      setEncoding("ISO8859_1");
                  }
  
                  //
                  // Has the user has 'forced' the character encoding via driver properties?
                  //
                  if (getUseUnicode()) {
                      if (realJavaEncoding != null) {
  
                          //
                          // Now, inform the server what character set we will be using from now-on...
                          //
                          if (realJavaEncoding.equalsIgnoreCase("UTF-8") || realJavaEncoding.equalsIgnoreCase("UTF8")) {
                              // charset names are case-sensitive
  
                              boolean utf8mb4Supported = versionMeetsMinimum(5, 5, 2);
                              boolean useutf8mb4 = utf8mb4Supported && (CharsetMapping.UTF8MB4_INDEXES.contains(this.io.serverCharsetIndex));
  
                              if (!getUseOldUTF8Behavior()) {
                                  if (dontCheckServerMatch || !characterSetNamesMatches("utf8") || (utf8mb4Supported && !characterSetNamesMatches("utf8mb4"))) {
                                      execSQL(null, "SET NAMES " + (useutf8mb4 ? "utf8mb4" : "utf8"), -1, null, DEFAULT_RESULT_SET_TYPE,
                                              DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false);
                                  }
                              } else {
                                  execSQL(null, "SET NAMES latin1", -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database,
                                          null, false);
                              }
  
                              setEncoding(realJavaEncoding);
                          } /* not utf-8 */else {
                              String mysqlCharsetName = CharsetMapping.getMysqlCharsetForJavaEncoding(realJavaEncoding.toUpperCase(Locale.ENGLISH), this);
  
                              /*
                               * if ("koi8_ru".equals(mysqlEncodingName)) { //
                               * This has a _different_ name in 4.1...
                               * mysqlEncodingName = "ko18r"; } else if
                               * ("euc_kr".equals(mysqlEncodingName)) { //
                               * Different name in 4.1 mysqlEncodingName =
                               * "euckr"; }
                               */
  
                              if (mysqlCharsetName != null) {
  
                                  if (dontCheckServerMatch || !characterSetNamesMatches(mysqlCharsetName)) {
                                      execSQL(null, "SET NAMES " + mysqlCharsetName, -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false,
                                              this.database, null, false);
                                  }
                              }
  
                              // Switch driver's encoding now, since the server knows what we're sending...
                              //
                              setEncoding(realJavaEncoding);
                          }
                      } else if (getEncoding() != null) {
                          // Tell the server we'll use the server default charset to send our queries from now on....
                          String mysqlCharsetName = getServerCharset();
  
                          if (getUseOldUTF8Behavior()) {
                              mysqlCharsetName = "latin1";
                          }
  
                          boolean ucs2 = false;
                          if ("ucs2".equalsIgnoreCase(mysqlCharsetName) || "utf16".equalsIgnoreCase(mysqlCharsetName)
                                  || "utf16le".equalsIgnoreCase(mysqlCharsetName) || "utf32".equalsIgnoreCase(mysqlCharsetName)) {
                              mysqlCharsetName = "utf8";
                              ucs2 = true;
                              if (getCharacterSetResults() == null) {
                                  setCharacterSetResults("UTF-8");
                              }
                          }
  
                          if (dontCheckServerMatch || !characterSetNamesMatches(mysqlCharsetName) || ucs2) {
                              try {
                                  execSQL(null, "SET NAMES " + mysqlCharsetName, -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false,
                                          this.database, null, false);
                              } catch (SQLException ex) {
                                  if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) {
                                      throw ex;
                                  }
                              }
                          }
  
                          realJavaEncoding = getEncoding();
                      }
  
                  }
  
                  //
                  // We know how to deal with any charset coming back from the database, so tell the server not to do conversion if the user hasn't 'forced' a
                  // result-set character set
                  //
  
                  String onServer = null;
                  boolean isNullOnServer = false;
  
                  if (this.serverVariables != null) {
                      onServer = this.serverVariables.get("character_set_results");
  
                      isNullOnServer = onServer == null || "NULL".equalsIgnoreCase(onServer) || onServer.length() == 0;
                  }
  
                  if (getCharacterSetResults() == null) {
  
                      //
                      // Only send if needed, if we're caching server variables we -have- to send, because we don't know what it was before we cached them.
                      //
                      if (!isNullOnServer) {
                          try {
                              execSQL(null, "SET character_set_results = NULL", -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false,
                                      this.database, null, false);
                          } catch (SQLException ex) {
                              if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) {
                                  throw ex;
                              }
                          }
                          this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, null);
                      } else {
                          this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer);
                      }
                  } else {
  
                      if (getUseOldUTF8Behavior()) {
                          try {
                              execSQL(null, "SET NAMES latin1", -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null,
                                      false);
                          } catch (SQLException ex) {
                              if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) {
                                  throw ex;
                              }
                          }
                      }
                      String charsetResults = getCharacterSetResults();
                      String mysqlEncodingName = null;
  
                      if ("UTF-8".equalsIgnoreCase(charsetResults) || "UTF8".equalsIgnoreCase(charsetResults)) {
                          mysqlEncodingName = "utf8";
                      } else if ("null".equalsIgnoreCase(charsetResults)) {
                          mysqlEncodingName = "NULL";
                      } else {
                          mysqlEncodingName = CharsetMapping.getMysqlCharsetForJavaEncoding(charsetResults.toUpperCase(Locale.ENGLISH), this);
                      }
  
                      //
                      // Only change the value if needed
                      //
  
                      if (mysqlEncodingName == null) {
                          throw SQLError.createSQLException("Can't map " + charsetResults + " given for characterSetResults to a supported MySQL encoding.",
                                  SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
                      }
  
                      if (!mysqlEncodingName.equalsIgnoreCase(this.serverVariables.get("character_set_results"))) {
                          StringBuffer setBuf = new StringBuffer("SET character_set_results = ".length() + mysqlEncodingName.length());
                          setBuf.append("SET character_set_results = ").append(mysqlEncodingName);
  
                          try {
                              execSQL(null, setBuf.toString(), -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null,
                                      false);
                          } catch (SQLException ex) {
                              if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) {
                                  throw ex;
                              }
                          }
  
                          this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, mysqlEncodingName);
  
                          // We have to set errorMessageEncoding according to new value of charsetResults for server version 5.5 and higher
                          if (versionMeetsMinimum(5, 5, 0)) {
                              this.errorMessageEncoding = charsetResults;
                          }
  
                      } else {
                          this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer);
                      }
                  }
  
                  if (getConnectionCollation() != null) {
                      StringBuffer setBuf = new StringBuffer("SET collation_connection = ".length() + getConnectionCollation().length());
                      setBuf.append("SET collation_connection = ").append(getConnectionCollation());
  
                      try {
                          execSQL(null, setBuf.toString(), -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false);
                      } catch (SQLException ex) {
                          if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) {
                              throw ex;
                          }
                      }
                  }
              } else {
                  // Use what the server has specified
                  realJavaEncoding = getEncoding(); // so we don't get
                  // swapped out in the finally block....
              }
          } finally {
              // Failsafe, make sure that the driver's notion of character encoding matches what the user has specified.
              //
              setEncoding(realJavaEncoding);
          }
  
          /**
           * Check if we need a CharsetEncoder for escaping codepoints that are
           * transformed to backslash (0x5c) in the connection encoding.
           */
          try {
              CharsetEncoder enc = Charset.forName(getEncoding()).newEncoder();
              CharBuffer cbuf = CharBuffer.allocate(1);
              ByteBuffer bbuf = ByteBuffer.allocate(1);
  
              cbuf.put("\u00a5");
              cbuf.position(0);
              enc.encode(cbuf, bbuf, true);
              if (bbuf.get(0) == '\\') {
                  this.requiresEscapingEncoder = true;
              } else {
                  cbuf.clear();
                  bbuf.clear();
  
                  cbuf.put("\u20a9");
                  cbuf.position(0);
                  enc.encode(cbuf, bbuf, true);
                  if (bbuf.get(0) == '\\') {
                      this.requiresEscapingEncoder = true;
                  }
              }
          } catch (java.nio.charset.UnsupportedCharsetException ucex) {
              // fallback to String API - for Java 1.4
              try {
                  byte bbuf[] = StringUtils.getBytes("\u00a5", getEncoding());
                  if (bbuf[0] == '\\') {
                      this.requiresEscapingEncoder = true;
                  } else {
                      bbuf = StringUtils.getBytes("\u20a9", getEncoding());
                      if (bbuf[0] == '\\') {
                          this.requiresEscapingEncoder = true;
                      }
                  }
              } catch (UnsupportedEncodingException ueex) {
                  throw SQLError
                          .createSQLException("Unable to use encoding: " + getEncoding(), SQLError.SQL_STATE_GENERAL_ERROR, ueex, getExceptionInterceptor());
              }
          }
  
          return characterSetAlreadyConfigured;
      }
  
      /**
       * Configures the client's timezone if required.
       * 
       * @throws SQLException
       *             if the timezone the server is configured to use can't be
       *             mapped to a Java timezone.
       */
      private void configureTimezone() throws SQLException {
          String configuredTimeZoneOnServer = this.serverVariables.get("timezone");
  
          if (configuredTimeZoneOnServer == null) {
              configuredTimeZoneOnServer = this.serverVariables.get("time_zone");
  
              if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) {
                  configuredTimeZoneOnServer = this.serverVariables.get("system_time_zone");
              }
          }
  
          String canonicalTimezone = getServerTimezone();
  
          if ((getUseTimezone() || !getUseLegacyDatetimeCode()) && configuredTimeZoneOnServer != null) {
              // user can override this with driver properties, so don't detect if that's the case
              if (canonicalTimezone == null || StringUtils.isEmptyOrWhitespaceOnly(canonicalTimezone)) {
                  try {
                      canonicalTimezone = TimeUtil.getCanonicalTimezone(configuredTimeZoneOnServer, getExceptionInterceptor());
                  } catch (IllegalArgumentException iae) {
                      throw SQLError.createSQLException(iae.getMessage(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
                  }
              }
          }
  
          if (canonicalTimezone != null && canonicalTimezone.length() > 0) {
              this.serverTimezoneTZ = TimeZone.getTimeZone(canonicalTimezone);
  
              //
              // The Calendar class has the behavior of mapping unknown timezones to 'GMT' instead of throwing an exception, so we must check for this...
              //
              if (!canonicalTimezone.equalsIgnoreCase("GMT") && this.serverTimezoneTZ.getID().equals("GMT")) {
                  throw SQLError.createSQLException("No timezone mapping entry for '" + canonicalTimezone + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
                          getExceptionInterceptor());
              }
  
              this.isServerTzUTC = "GMT".equalsIgnoreCase(this.serverTimezoneTZ.getID());
          }
      }
  
      private void createInitialHistogram(long[] breakpoints, long lowerBound, long upperBound) {
  
          double bucketSize = (((double) upperBound - (double) lowerBound) / HISTOGRAM_BUCKETS) * 1.25;
  
          if (bucketSize < 1) {
              bucketSize = 1;
          }
  
          for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
              breakpoints[i] = lowerBound;
              lowerBound += bucketSize;
          }
      }
  
      /**
       * Creates an IO channel to the server
       * 
       * @param isForReconnect
       *            is this request for a re-connect
       * @return a new MysqlIO instance connected to a server
       * @throws SQLException
       *             if a database access error occurs
       * @throws CommunicationsException
       */
      public void createNewIO(boolean isForReconnect) throws SQLException {
          synchronized (getConnectionMutex()) {
              // Synchronization Not needed for *new* connections, but defintely for connections going through fail-over, since we might get the new connection up
              // and running *enough* to start sending cached or still-open server-side prepared statements over to the backend before we get a chance to
              // re-prepare them...
  
              Properties mergedProps = exposeAsProperties(this.props);
  
              if (!getHighAvailability()) {
                  connectOneTryOnly(isForReconnect, mergedProps);
  
                  return;
              }
  
              connectWithRetries(isForReconnect, mergedProps);
          }
      }
  
      private void connectWithRetries(boolean isForReconnect, Properties mergedProps) throws SQLException {
  