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

Save This Page
Home » MySQL-JDBC-5.1.11 » com.mysql.jdbc » [javadoc | source]