public abstract class BaseTestCase extends TestCase {
	private final static String ADMIN_CONNECTION_PROPERTY_NAME = "com.mysql.jdbc.testsuite.admin-url";

	private final static String NO_MULTI_HOST_PROPERTY_NAME = "com.mysql.jdbc.testsuite.no-multi-hosts-tests";

	/**
	 * JDBC URL, initialized from com.mysql.jdbc.testsuite.url system property,
	 * or defaults to jdbc:mysql:///test
	 */
	protected static String dbUrl = "jdbc:mysql:///test";

	/** Instance counter */
	private static int instanceCount = 1;

	/** Connection to server, initialized in setUp() Cleaned up in tearDown(). */
	protected Connection conn = null;

	/** list of schema objects to be dropped in tearDown */
	private List createdObjects;

	/** The driver to use */
	protected String dbClass = "com.mysql.jdbc.Driver";

	/** My instance number */
	private int myInstanceNumber = 0;

	/**
	 * PreparedStatement to be used in tests, not initialized. Cleaned up in
	 * tearDown().
	 */
	protected PreparedStatement pstmt = null;

	/**
	 * ResultSet to be used in tests, not initialized. Cleaned up in tearDown().
	 */
	protected ResultSet rs = null;

	/**
	 * Statement to be used in tests, initialized in setUp(). Cleaned up in
	 * tearDown().
	 */
	protected Statement stmt = null;

	private boolean runningOnJdk131 = false;

	private boolean isOnCSFS = true;
	
	/**
	 * Creates a new BaseTestCase object.
	 * 
	 * @param name
	 *            The name of the JUnit test case
	 */
	public BaseTestCase(String name) {
		super(name);
		this.myInstanceNumber = instanceCount++;

		String newDbUrl = System.getProperty("com.mysql.jdbc.testsuite.url");

		if ((newDbUrl != null) && (newDbUrl.trim().length() != 0)) {
			dbUrl = newDbUrl;
		} else {
			String defaultDbUrl = System
					.getProperty("com.mysql.jdbc.testsuite.url.default");

			if ((defaultDbUrl != null) && (defaultDbUrl.trim().length() != 0)) {
				dbUrl = defaultDbUrl;
			}
		}

		String newDriver = System
				.getProperty("com.mysql.jdbc.testsuite.driver");

		if ((newDriver != null) && (newDriver.trim().length() != 0)) {
			this.dbClass = newDriver;
		}

		try {
			Blob.class.getMethod("truncate", new Class[] { Long.TYPE });
			this.runningOnJdk131 = false;
		} catch (NoSuchMethodException nsme) {
			this.runningOnJdk131 = true;
		}
	}

	protected void createSchemaObject(String objectType, String objectName,
			String columnsAndOtherStuff) throws SQLException {
		this.createdObjects.add(new String[] {objectType, objectName});
		dropSchemaObject(objectType, objectName);
		
		StringBuffer createSql = new StringBuffer(objectName.length()
				+ objectType.length() + columnsAndOtherStuff.length() + 10);
		createSql.append("CREATE  ");
		createSql.append(objectType);
		createSql.append(" ");
		createSql.append(objectName);
		createSql.append(" ");
		createSql.append(columnsAndOtherStuff);
		
		try {
			this.stmt.executeUpdate(createSql.toString());
		} catch (SQLException sqlEx) {
			if ("42S01".equals(sqlEx.getSQLState())) {
				System.err.println("WARN: Stale mysqld table cache preventing table creation - flushing tables and trying again");
				this.stmt.executeUpdate("FLUSH TABLES"); // some bug in 5.1 on the mac causes tables to not disappear from the cache
				this.stmt.executeUpdate(createSql.toString());
			} else {
				throw sqlEx;
			}
		}
	}

	protected void createFunction(String functionName, String functionDefn)
			throws SQLException {
		createSchemaObject("FUNCTION", functionName, functionDefn);
	}
	
	protected void dropFunction(String functionName) throws SQLException {
		dropSchemaObject("FUNCTION", functionName);
	}
	
	protected void createProcedure(String procedureName, String procedureDefn)
			throws SQLException {
		createSchemaObject("PROCEDURE", procedureName, procedureDefn);
	}

	protected void dropProcedure(String procedureName) throws SQLException {
		dropSchemaObject("PROCEDURE", procedureName);
	}

	protected void createTable(String tableName, String columnsAndOtherStuff)
			throws SQLException {
		createSchemaObject("TABLE", tableName, columnsAndOtherStuff);
	}
	
	protected void createTable(String tableName, String columnsAndOtherStuff,
			String engine) throws SQLException {
		createSchemaObject("TABLE", tableName,
				columnsAndOtherStuff + " " + getTableTypeDecl() + " = " + engine);
	}

	protected void dropTable(String tableName) throws SQLException {
		dropSchemaObject("TABLE", tableName);
	}

	protected void createDatabase(String databaseName) throws SQLException {
		createSchemaObject("DATABASE", databaseName, "");
	}

	protected void dropDatabase(String databaseName) throws SQLException {
		dropSchemaObject("DATABASE", databaseName);
	}

	protected void dropSchemaObject(String objectType, String objectName)
			throws SQLException {
		this.stmt.executeUpdate("DROP " + objectType + " IF EXISTS "
				+ objectName);
	}

	protected Connection getAdminConnection() throws SQLException {
		return getAdminConnectionWithProps(new Properties());
	}

	protected Connection getAdminConnectionWithProps(Properties props)
			throws SQLException {
		String adminUrl = System.getProperty(ADMIN_CONNECTION_PROPERTY_NAME);

		if (adminUrl != null) {
			return DriverManager.getConnection(adminUrl, props);
		} else {
			return null;
		}
	}

	protected Connection getConnectionWithProps(String propsList) throws SQLException {
		return getConnectionWithProps(dbUrl, propsList);
	}

	protected Connection getConnectionWithProps(String url, String propsList) throws SQLException {
		Properties props = new Properties();
		
		if (propsList != null) {
			List keyValuePairs = StringUtils.split(propsList, ",", false);
			
			Iterator iter = keyValuePairs.iterator();
			
			while (iter.hasNext()) {
				String kvp = (String)iter.next();
				List splitUp = StringUtils.split(kvp, "=", false);
				StringBuffer value = new StringBuffer();
				
				for (int i = 1; i < splitUp.size(); i++) {
					if (i != 1) {
						value.append("=");
					}
					
					value.append(splitUp.get(i));
					
				}
				
				props.setProperty(splitUp.get(0).toString().trim(), value.toString());
			}
		}
		
		return getConnectionWithProps(url, props);
	}
	
	/**
	 * Returns a new connection with the given properties
	 * 
	 * @param props
	 *            the properties to use (the URL will come from the standard for
	 *            this testcase).
	 * 
	 * @return a new connection using the given properties.
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	protected Connection getConnectionWithProps(Properties props)
			throws SQLException {
		return DriverManager.getConnection(dbUrl, props);
	}

	protected Connection getConnectionWithProps(String url, Properties props)
			throws SQLException {
		return DriverManager.getConnection(url, props);
	}

	/**
	 * Returns the per-instance counter (for messages when multi-threading
	 * stress tests)
	 * 
	 * @return int the instance number
	 */
	protected int getInstanceNumber() {
		return this.myInstanceNumber;
	}

	protected String getMysqlVariable(Connection c, String variableName)
			throws SQLException {
		Object value = getSingleIndexedValueWithQuery(c, 2,
				"SHOW VARIABLES LIKE '" + variableName + "'");

		if (value != null) {
			if (value instanceof byte[]) {
				// workaround for bad 4.1.x bugfix
				return new String((byte[]) value);
			}

			return value.toString();
		}

		return null;

	}

	/**
	 * Returns the named MySQL variable from the currently connected server.
	 * 
	 * @param variableName
	 *            the name of the variable to return
	 * 
	 * @return the value of the given variable, or NULL if it doesn't exist
	 * 
	 * @throws SQLException
	 *             if an error occurs
	 */
	protected String getMysqlVariable(String variableName) throws SQLException {
		return getMysqlVariable(this.conn, variableName);
	}

	/**
	 * Returns the properties that represent the default URL used for
	 * connections for all testcases.
	 * 
	 * @return properties parsed from com.mysql.jdbc.testsuite.url
	 * 
	 * @throws SQLException
	 *             if parsing fails
	 */
	protected Properties getPropertiesFromTestsuiteUrl() throws SQLException {
		Properties props = new NonRegisteringDriver().parseURL(dbUrl, null);

		String hostname = props
				.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);

		if (hostname == null) {
			props.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY,
					"localhost");
		} else if (hostname.startsWith(":")) {
			props.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY,
					"localhost");
			props.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, hostname
					.substring(1));
		}

		String portNumber = props
				.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY);

		if (portNumber == null) {
			props.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306");
		}

		return props;
	}

	protected int getRowCount(String tableName) throws SQLException {
		ResultSet countRs = null;

		try {
			countRs = this.stmt.executeQuery("SELECT COUNT(*) FROM "
					+ tableName);

			countRs.next();

			return countRs.getInt(1);
		} finally {
			if (countRs != null) {
				countRs.close();
			}
		}
	}

	protected Object getSingleIndexedValueWithQuery(Connection c,
			int columnIndex, String query) throws SQLException {
		ResultSet valueRs = null;

		Statement svStmt = null;

		try {
			svStmt = c.createStatement();

			valueRs = svStmt.executeQuery(query);

			if (!valueRs.next()) {
				return null;
			}

			return valueRs.getObject(columnIndex);
		} finally {
			if (valueRs != null) {
				valueRs.close();
			}

			if (svStmt != null) {
				svStmt.close();
			}
		}
	}

	protected Object getSingleIndexedValueWithQuery(int columnIndex,
			String query) throws SQLException {
		return getSingleIndexedValueWithQuery(this.conn, columnIndex, query);
	}

	protected Object getSingleValue(String tableName, String columnName,
			String whereClause) throws SQLException {
		return getSingleValueWithQuery("SELECT " + columnName + " FROM "
				+ tableName + ((whereClause == null) ? "" : " " + whereClause));
	}

	protected Object getSingleValueWithQuery(String query) throws SQLException {
		return getSingleIndexedValueWithQuery(1, query);
	}
	
	protected String getTableTypeDecl() throws SQLException {
		if (versionMeetsMinimum(5, 0)) {
			return "ENGINE";
		}
		return "TYPE";
	}

	protected boolean isAdminConnectionConfigured() {
		return System.getProperty(ADMIN_CONNECTION_PROPERTY_NAME) != null;
	}

	protected boolean isServerRunningOnWindows() throws SQLException {
		return (getMysqlVariable("datadir").indexOf('\\') != -1);
	}

	public void logDebug(String message) {
		if (System.getProperty("com.mysql.jdbc.testsuite.noDebugOutput") == null) {
			System.err.println(message);
		}
	}

	protected File newTempBinaryFile(String name, long size) throws IOException {
		File tempFile = File.createTempFile(name, "tmp");
		tempFile.deleteOnExit();
		
		cleanupTempFiles(tempFile, name);
		
		FileOutputStream fos = new FileOutputStream(tempFile);
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		for (long i = 0; i < size; i++) {
			bos.write((byte) i);
		}
		bos.close();
		assertTrue(tempFile.exists());
		assertEquals(size, tempFile.length());
		return tempFile;
	}

	protected final boolean runLongTests() {
		return runTestIfSysPropDefined("com.mysql.jdbc.testsuite.runLongTests");
	}

	/**
	 * Checks whether a certain system property is defined, in order to
	 * run/not-run certain tests
	 * 
	 * @param propName
	 *            the property name to check for
	 * 
	 * @return true if the property is defined.
	 */
	protected boolean runTestIfSysPropDefined(String propName) {
		String prop = System.getProperty(propName);

		return (prop != null) && (prop.length() > 0);
	}

	protected boolean runMultiHostTests() {
		return !runTestIfSysPropDefined(NO_MULTI_HOST_PROPERTY_NAME);
	}

	/**
	 * Creates resources used by all tests.
	 * 
	 * @throws Exception
	 *             if an error occurs.
	 */
	public void setUp() throws Exception {
		System.out.println("Loading JDBC driver '" + this.dbClass + "'");
		Class.forName(this.dbClass).newInstance();
		System.out.println("Done.\n");
		this.createdObjects = new ArrayList();


		if (this.dbClass.equals("gwe.sql.gweMysqlDriver")) {
			try {
				this.conn = DriverManager.getConnection(dbUrl, "", "");
			} catch (Exception ex) {
				ex.printStackTrace();
				fail();
			}
		} else {
			try {
				this.conn = DriverManager.getConnection(dbUrl);
			} catch (Exception ex) {
				ex.printStackTrace();
				fail();
			}
		}

		System.out.println("Done.\n");
		
		this.stmt = this.conn.createStatement();

		try {
			if (dbUrl.indexOf("mysql") != -1) {
				this.rs = this.stmt.executeQuery("SELECT VERSION()");
				this.rs.next();
				logDebug("Connected to " + this.rs.getString(1));
				this.rs.close();
				this.rs = null;
			} else {
				logDebug("Connected to "
						+ this.conn.getMetaData().getDatabaseProductName()
						+ " / "
						+ this.conn.getMetaData().getDatabaseProductVersion());
			}
		} finally {
			if (this.rs != null) {
				this.rs.close();
			}
		}

		this.isOnCSFS = !this.conn.getMetaData().storesLowerCaseIdentifiers();
	}

	/**
	 * Destroys resources created during the test case.
	 * 
	 * @throws Exception
	 *             DOCUMENT ME!
	 */
	public void tearDown() throws Exception {
		if (this.rs != null) {
			try {
				this.rs.close();
			} catch (SQLException SQLE) {
				;
			}
		}

		if (System.getProperty("com.mysql.jdbc.testsuite.retainArtifacts") == null) {
			for (int i = 0; i < this.createdObjects.size(); i++) {
				try {
					String[] objectInfo = (String[])this.createdObjects.get(i);
					
					dropSchemaObject(objectInfo[0], objectInfo[1]);
				} catch (SQLException SQLE) {
					;
				}
			}
		}

		if (this.stmt != null) {
			try {
				this.stmt.close();
			} catch (SQLException SQLE) {
				;
			}
		}

		if (this.pstmt != null) {
			try {
				this.pstmt.close();
			} catch (SQLException SQLE) {
				;
			}
		}

		if (this.conn != null) {
			try {
				this.conn.close();
			} catch (SQLException SQLE) {
				;
			}
		}
	}

	/**
	 * Checks whether the database we're connected to meets the given version
	 * minimum
	 * 
	 * @param major
	 *            the major version to meet
	 * @param minor
	 *            the minor version to meet
	 * 
	 * @return boolean if the major/minor is met
	 * 
	 * @throws SQLException
	 *             if an error occurs.
	 */
	protected boolean versionMeetsMinimum(int major, int minor)
			throws SQLException {
		return versionMeetsMinimum(major, minor, 0);
	}

	/**
	 * Checks whether the database we're connected to meets the given version
	 * minimum
	 * 
	 * @param major
	 *            the major version to meet
	 * @param minor
	 *            the minor version to meet
	 * 
	 * @return boolean if the major/minor is met
	 * 
	 * @throws SQLException
	 *             if an error occurs.
	 */
	protected boolean versionMeetsMinimum(int major, int minor, int subminor)
			throws SQLException {
		return (((com.mysql.jdbc.Connection) this.conn).versionMeetsMinimum(
				major, minor, subminor));
	}

	protected boolean isRunningOnJdk131() {
		return this.runningOnJdk131;
	}

	protected boolean isClassAvailable(String classname) {
		try {
			Class.forName(classname);
			return true;
		} catch (ClassNotFoundException e) {
			return false;
		}
	}

	protected boolean isRunningOnJRockit() {
		String vmVendor = System.getProperty("java.vm.vendor");

		return (vmVendor != null && vmVendor.toUpperCase(Locale.US).startsWith(
				"BEA"));
	}

	protected String randomString() {
		int length = (int)(Math.random() * 32);
		
		StringBuffer buf = new StringBuffer(length);
		
		for (int i = 0; i < length; i++) {
			buf.append((char)((Math.random() * 26) + 'a'));
		}
		
		return buf.toString();
	}

	protected void cleanupTempFiles(final File exampleTempFile, final String tempfilePrefix) {
	
		File tempfilePath = exampleTempFile.getParentFile();
		
		File[] possibleFiles = tempfilePath.listFiles(new FilenameFilter() {
	
			public boolean accept(File dir, String name) {
				return (name.indexOf(tempfilePrefix) != -1 
						&& !exampleTempFile.getName().equals(name));
			}});
		
		for (int i = 0; i < possibleFiles.length; i++) {
			try {
				possibleFiles[i].delete();
			} catch (Throwable t) {
				// ignore, we're only making a best effort cleanup attempt here
			}
		}
	}
	
	protected void assertResultSetLength(ResultSet rs, int len) throws Exception {
		assertTrue("Result set is scrollable", rs.getType() != ResultSet.TYPE_FORWARD_ONLY);
		int oldRowPos = rs.getRow();
		rs.last();
		assertEquals("Result set length", len, rs.getRow());
		if (oldRowPos > 0)
			rs.absolute(oldRowPos);
		else
			rs.beforeFirst();
	}

	protected void assertResultSetsEqual(ResultSet control, ResultSet test)
			throws Exception {
				int controlNumCols = control.getMetaData().getColumnCount();
				int testNumCols = test.getMetaData().getColumnCount();
				assertEquals(controlNumCols, testNumCols);
				
				StringBuffer rsAsString = new StringBuffer();
				
				while (control.next()) {
					test.next();
					rsAsString.append("\n");
					for (int i = 0; i < controlNumCols; i++) {
						Object controlObj = control.getObject(i + 1);
						Object testObj = test.getObject(i + 1);
						
						rsAsString.append("" + controlObj);
						rsAsString.append("\t = \t");
						rsAsString.append("" + testObj);
						rsAsString.append(", ");
						
						if (controlObj == null) {
							assertNull("Expected null, see last row: \n" + rsAsString.toString(), testObj);
						} else {
							assertNotNull("Expected non-null, see last row: \n" + rsAsString.toString(), testObj);
						}
						
						if (controlObj instanceof Float) {
							assertEquals("Float comparison failed, see last row: \n" + rsAsString.toString(), ((Float)controlObj).floatValue(),
									((Float)testObj).floatValue(), 0.1);
						} else if (controlObj instanceof Double) {
							assertEquals("Double comparison failed, see last row: \n" + rsAsString.toString(), ((Double)controlObj).doubleValue(),
									((Double)testObj).doubleValue(), 0.1);
						} else {
							assertEquals("Value comparison failed, see last row: \n" + rsAsString.toString(), controlObj, testObj);
						}
					}
				}
					
				int howMuchMore = 0;
				
				while (test.next()) {
					rsAsString.append("\n");
					howMuchMore++;
					for (int i = 0; i < controlNumCols; i++) {
						rsAsString.append("\t = \t");
						rsAsString.append("" + test.getObject(i + 1));
						rsAsString.append(", ");
					}
				}
				
				assertTrue("Found " + howMuchMore + " extra rows in result set to be compared: ", howMuchMore == 0);
			}

	/*
	 * Set default values for primitives.
	 * (prevents NPE in Java 1.4 when calling via reflection)
	 */
	protected void fillPrimitiveDefaults(Class types[], Object vals[], int count) {
		for (int i = 0; i < count; ++i) {
			if (vals[i] != null)
				continue;
			String type = types[i].toString();
			if (type.equals("short")) {
				vals[i] = new Short((short)0);
			} else if (type.equals("int")) {
				vals[i] = new Integer(0);
			} else if (type.equals("long")) {
				vals[i] = new Long(0);
			} else if (type.equals("boolean")) {
				vals[i] = new Boolean(false);
			} else if (type.equals("byte")) {
				vals[i] = new Byte((byte)0);
			} else if (type.equals("double")) {
				vals[i] = new Double(0.0);
			} else if (type.equals("float")) {
				vals[i] = new Float(0.0);
			}
		}
	}

	/**
	 * Retrieve the current system time in milliseconds, using the nanosecond
	 * time if possible.
	 */
	protected long currentTimeMillis() {
		try {
			Method mNanoTime = System.class.getDeclaredMethod("nanoTime", (Class[])null);
			return ((Long)mNanoTime.invoke(null, (Object[])null)).longValue() / 1000000;
		} catch(Exception ex) {
			return System.currentTimeMillis();
		}
	}

	protected Connection getMasterSlaveReplicationConnection() throws SQLException {
	
		Connection replConn = new ReplicationDriver().connect(
				getMasterSlaveUrl(), getMasterSlaveProps());
	
		return replConn;
	}

	protected String getMasterSlaveUrl() throws SQLException {
		
		Properties defaultProps = getPropertiesFromTestsuiteUrl();
		String hostname = defaultProps
				.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);
	
		if (NonRegisteringDriver.isHostPropertiesList(hostname)) {
			String url = String.format("jdbc:mysql://%s,%s/", hostname, hostname);
			
			return url;
		}
		
		StringBuffer urlBuf = new StringBuffer("jdbc:mysql://");
		
		String portNumber = defaultProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306");
	
		hostname = (hostname == null ? "localhost" : hostname);
	
		for (int i = 0; i < 2; i++) {
			urlBuf.append(hostname);
			urlBuf.append(":");
			urlBuf.append(portNumber);
	
			if (i == 0) {
				urlBuf.append(",");
			}
		}
		urlBuf.append("/");
	
		return urlBuf.toString();
	}

	protected Properties getMasterSlaveProps() throws SQLException {
		Properties props = getPropertiesFromTestsuiteUrl();
	
		removeHostRelatedProps(props);
	
		return props;
	}
	
	protected void removeHostRelatedProps(Properties props) {
		props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY);
		props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY);
		
		int numHosts = Integer.parseInt(props.getProperty(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY));
		
		for (int i = 1; i <= numHosts; i++) {
			props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + "." + i);
			props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + "." + i);
		}
		
		props.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY);
	}

	protected Connection getLoadBalancedConnection(int badHostLocation, String badHost,
			Properties props) throws SQLException {
		Properties parsedProps = new NonRegisteringDriver().parseURL(dbUrl, null);
		
		String firstHost = parsedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);
		
		if (!NonRegisteringDriver.isHostPropertiesList(firstHost)) {
			String port = parsedProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306");
			
			if (firstHost == null) {
				firstHost = "localhost";
			}
			
			
			firstHost = firstHost + ":" + port;
		}

		if (badHost != null) {
			badHost = badHost + ",";
		}
		
		String hostsString = null;
		
		switch (badHostLocation) {
		case 1:
			hostsString = badHost + firstHost;
			break;
		case 2:
			hostsString = firstHost + "," + badHost + firstHost;
			break;
		case 3:
			hostsString = firstHost + "," + badHost;
			break;
		default:
				throw new IllegalArgumentException();
		}
		
		if (props != null) {
			parsedProps.putAll(props);
		}

		Connection lbConn = DriverManager.getConnection("jdbc:mysql:loadbalance://" + hostsString, parsedProps);
		
		return lbConn;
	}

	protected Connection getLoadBalancedConnection() throws SQLException {
		return getLoadBalancedConnection(1, "", null);
	}

	protected Connection getLoadBalancedConnection(Properties props)
			throws SQLException {
				return getLoadBalancedConnection(1, "", props);
			}

	protected void copyBasePropertiesIntoProps(Properties props, NonRegisteringDriver d)
			throws SQLException {
				Properties testCaseProps = d.parseURL(BaseTestCase.dbUrl, null);
				String user = testCaseProps.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY);
				
				if (user != null) {
					props.setProperty(NonRegisteringDriver.USER_PROPERTY_KEY, user);
				}
				
				String password =  testCaseProps.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY);
				
				if (password != null) {
					props.setProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY, password);
				}
				
				String port = testCaseProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY);
				
				if (port != null) {
					props.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, port);
				} else {
					String host = testCaseProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);
					
					if (host != null) {
						String[] hostPort = host.split(":");
						
						if (hostPort.length > 1) {
							props.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, hostPort[1]);
						}
					}
				}
			}

	protected String getPortFreeHostname(Properties props, NonRegisteringDriver d)
			throws SQLException {
				String host = d.parseURL(BaseTestCase.dbUrl, props).getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);
				
				if(host == null){
					host = "localhost";
				}
				
				host = host.split(":")[0];
				return host;
			}

	protected Connection getUnreliableLoadBalancedConnection(String[] hostNames,
			Properties props) throws Exception {
		return getUnreliableLoadBalancedConnection(hostNames,
				props, new HashSet());
	}
	protected Connection getUnreliableLoadBalancedConnection(String[] hostNames,
			Properties props, Set downedHosts) throws Exception {
				if(props == null){
					props = new Properties();
				}
				NonRegisteringDriver d = new NonRegisteringDriver();
				this.copyBasePropertiesIntoProps(props, d);
				props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory");
				Properties parsed = d.parseURL(BaseTestCase.dbUrl, props);
				String db = parsed.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY);
				String port = parsed.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY);
				String host = getPortFreeHostname(props, d);
				UnreliableSocketFactory.flushAllHostLists();
				StringBuffer hostString = new StringBuffer();
				String glue = "";
				for(int i = 0; i < hostNames.length; i++){
					UnreliableSocketFactory.mapHost(hostNames[i], host);
					hostString.append(glue);
					glue = ",";
					hostString.append(hostNames[i] + ":" + (port == null ? "3306" : port));
					
					if (downedHosts.contains(hostNames[i])) {
						UnreliableSocketFactory.downHost(hostNames[i]);
					}
				}
				
				props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY);
					
				return getConnectionWithProps("jdbc:mysql:loadbalance://" + hostString.toString() +"/" + db, props);
				
			}
	protected Connection getUnreliableReplicationConnection(String[] hostNames,
			Properties props) throws Exception {
		return getUnreliableReplicationConnection(hostNames,
				props, new HashSet());
	}
	protected Connection getUnreliableReplicationConnection(String[] hostNames,
			Properties props, Set downedHosts) throws Exception {
				if(props == null){
					props = new Properties();
				}
				NonRegisteringDriver d = new NonRegisteringDriver();
				this.copyBasePropertiesIntoProps(props, d);
				props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory");
				Properties parsed = d.parseURL(BaseTestCase.dbUrl, props);
				String db = parsed.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY);
				String port = parsed.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY);
				String host = getPortFreeHostname(props, d);
				UnreliableSocketFactory.flushAllHostLists();
				StringBuffer hostString = new StringBuffer();
				String glue = "";
				for(int i = 0; i < hostNames.length; i++){
					UnreliableSocketFactory.mapHost(hostNames[i], host);
					hostString.append(glue);
					glue = ",";
					hostString.append(hostNames[i] + ":" + (port == null ? "3306" : port));
					
					if (downedHosts.contains(hostNames[i])) {
						UnreliableSocketFactory.downHost(hostNames[i]);
					}
				}
				
				props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY);
					
				return getConnectionWithProps("jdbc:mysql:replication://" + hostString.toString() +"/" + db, props);
				
			}
	
	protected boolean assertEqualsFSAware(String matchStr, String inStr) throws Exception {
		if (this.isOnCSFS)
			return matchStr.equals(inStr);
		else
			return matchStr.equalsIgnoreCase(inStr);
	}
} public class UnreliableSocketFactory extends StandardSocketFactory {
	public static final long DEFAULT_TIMEOUT_MILLIS = 10 * 60 * 1000; // ugh

	private static final Map MAPPED_HOSTS = new HashMap();
	
	static final Set HUNG_READ_HOSTS = new HashSet();
	
	static final Set HUNG_WRITE_HOSTS = new HashSet();
	
	static final Set HUNG_CONNECT_HOSTS = new HashSet();
	
	static final Set IMMEDIATELY_DOWNED_HOSTS = new HashSet();
	
	private String hostname;
	private int portNumber;
	private Properties props;
	
	public static void flushAllHostLists(){
		IMMEDIATELY_DOWNED_HOSTS.clear();
		HUNG_CONNECT_HOSTS.clear();
		HUNG_READ_HOSTS.clear();
		HUNG_WRITE_HOSTS.clear();
	}
	
	
	public static void mapHost(String alias, String orig) {
		MAPPED_HOSTS.put(alias, orig);
	}
	
	public static void hangOnRead(String hostname) {
		HUNG_READ_HOSTS.add(hostname);
	}
	
	public static void dontHangOnRead(String hostname) {
		HUNG_READ_HOSTS.remove(hostname);
	}
	
	public static void hangOnWrite(String hostname) {
		HUNG_WRITE_HOSTS.add(hostname);
	}
	
	public static void dontHangOnWrite (String hostname) {
		HUNG_WRITE_HOSTS.remove(hostname);
	}
	
	public static void hangOnConnect(String hostname) {
		HUNG_CONNECT_HOSTS.add(hostname);
	}
	
	public static void dontHangOnConnect(String hostname) {
		HUNG_CONNECT_HOSTS.remove(hostname);
	}
	
	public static void downHost(String hostname) {
		IMMEDIATELY_DOWNED_HOSTS.add(hostname);
		
	}
	
	public static void dontDownHost(String hostname) {
		IMMEDIATELY_DOWNED_HOSTS.remove(hostname);
	}
	
	public Socket connect(String hostname, int portNumber, Properties props)
			throws SocketException, IOException {
		this.hostname = hostname;
		this.portNumber = portNumber;
		this.props = props;
		return getNewSocket();
	}
	
	private Socket getNewSocket() throws SocketException, IOException {
		if (IMMEDIATELY_DOWNED_HOSTS.contains(hostname)) {

			sleepMillisForProperty(props, "connectTimeout");

			throw new SocketTimeoutException();
		}
		
		String hostnameToConnectTo = (String) MAPPED_HOSTS.get(hostname);
		
		if (hostnameToConnectTo == null) {
			hostnameToConnectTo = hostname;
		}
		
		if (NonRegisteringDriver.isHostPropertiesList(hostnameToConnectTo)) {
			Properties hostSpecificProps = NonRegisteringDriver.expandHostKeyValues(hostnameToConnectTo);
			
			String protocol = hostSpecificProps.getProperty(NonRegisteringDriver.PROTOCOL_PROPERTY_KEY);
			
			if ("unix".equalsIgnoreCase(protocol)) {
				SocketFactory factory;
				try {
					factory = (SocketFactory) Class
							.forName(
									"org.newsclub.net.mysql.AFUNIXDatabaseSocketFactory")
							.newInstance();
				} catch (InstantiationException e) {
					throw new SocketException(e.getMessage());
				} catch (IllegalAccessException e) {
					throw new SocketException(e.getMessage());
				} catch (ClassNotFoundException e) {
					throw new SocketException(e.getMessage());
				}

				String path = hostSpecificProps
						.getProperty(NonRegisteringDriver.PATH_PROPERTY_KEY);

				if (path != null) {
					hostSpecificProps.setProperty("junixsocket.file", path);
				}

				return new HangingSocket(factory.connect(hostnameToConnectTo,
						portNumber, hostSpecificProps), props, hostname);
			}

		}
		
		return new HangingSocket(super.connect(hostnameToConnectTo, portNumber, props), props, hostname);
	}
	
	

	public Socket afterHandshake() throws SocketException, IOException {
		return getNewSocket();
	}

	public Socket beforeHandshake() throws SocketException, IOException {
		return getNewSocket();
	}

	static void sleepMillisForProperty(Properties props, String name) {
		try {
			Thread.sleep(Long.parseLong(props.getProperty(name, String
					.valueOf(DEFAULT_TIMEOUT_MILLIS))));
		} catch (NumberFormatException e) {
			throw new RuntimeException(e);
		} catch (InterruptedException e) {
			// ignore
		}
	}

	class HangingSocket extends Socket {
		public void bind(SocketAddress bindpoint) throws IOException {

			underlyingSocket.bind(bindpoint);
		}

		public synchronized void close() throws IOException {

			underlyingSocket.close();
		}

		public SocketChannel getChannel() {

			return underlyingSocket.getChannel();
		}

		public InetAddress getInetAddress() {

			return underlyingSocket.getInetAddress();
		}

		public InputStream getInputStream() throws IOException {

			return new HangingInputStream(underlyingSocket.getInputStream(), props, aliasedHostname);
		}

		public boolean getKeepAlive() throws SocketException {

			return underlyingSocket.getKeepAlive();
		}

		public InetAddress getLocalAddress() {

			return underlyingSocket.getLocalAddress();
		}

		public int getLocalPort() {

			return underlyingSocket.getLocalPort();
		}

		public SocketAddress getLocalSocketAddress() {

			return underlyingSocket.getLocalSocketAddress();
		}

		public boolean getOOBInline() throws SocketException {

			return underlyingSocket.getOOBInline();
		}

		public OutputStream getOutputStream() throws IOException {
			return new HangingOutputStream(underlyingSocket.getOutputStream(), props, aliasedHostname);
		}

		public int getPort() {

			return underlyingSocket.getPort();
		}

		public synchronized int getReceiveBufferSize() throws SocketException {

			return underlyingSocket.getReceiveBufferSize();
		}

		public SocketAddress getRemoteSocketAddress() {

			return underlyingSocket.getRemoteSocketAddress();
		}

		public boolean getReuseAddress() throws SocketException {

			return underlyingSocket.getReuseAddress();
		}

		public synchronized int getSendBufferSize() throws SocketException {

			return underlyingSocket.getSendBufferSize();
		}

		public int getSoLinger() throws SocketException {

			return underlyingSocket.getSoLinger();
		}

		public synchronized int getSoTimeout() throws SocketException {

			return underlyingSocket.getSoTimeout();
		}

		public boolean getTcpNoDelay() throws SocketException {
			return underlyingSocket.getTcpNoDelay();
		}

		public int getTrafficClass() throws SocketException {
			return underlyingSocket.getTrafficClass();
		}

		public boolean isBound() {
			return underlyingSocket.isBound();
		}

		public boolean isClosed() {
			return underlyingSocket.isClosed();
		}

		public boolean isConnected() {
			return underlyingSocket.isConnected();
		}

		public boolean isInputShutdown() {
			return underlyingSocket.isInputShutdown();
		}

		public boolean isOutputShutdown() {
			return underlyingSocket.isOutputShutdown();
		}

		public void sendUrgentData(int data) throws IOException {
			underlyingSocket.sendUrgentData(data);
		}

		public void setKeepAlive(boolean on) throws SocketException {
			underlyingSocket.setKeepAlive(on);
		}

		public void setOOBInline(boolean on) throws SocketException {
			underlyingSocket.setOOBInline(on);
		}

		public synchronized void setReceiveBufferSize(int size)
				throws SocketException {
			underlyingSocket.setReceiveBufferSize(size);
		}

		public void setReuseAddress(boolean on) throws SocketException {
			underlyingSocket.setReuseAddress(on);
		}

		public synchronized void setSendBufferSize(int size)
				throws SocketException {
			underlyingSocket.setSendBufferSize(size);
		}

		public void setSoLinger(boolean on, int linger) throws SocketException {
			underlyingSocket.setSoLinger(on, linger);
		}

		public synchronized void setSoTimeout(int timeout)
				throws SocketException {
			underlyingSocket.setSoTimeout(timeout);
		}

		public void setTcpNoDelay(boolean on) throws SocketException {
			underlyingSocket.setTcpNoDelay(on);
		}

		public void setTrafficClass(int tc) throws SocketException {
			underlyingSocket.setTrafficClass(tc);
		}

		public void shutdownInput() throws IOException {
			underlyingSocket.shutdownInput();
		}

		public void shutdownOutput() throws IOException {
			underlyingSocket.shutdownOutput();
		}

		public String toString() {
			return underlyingSocket.toString();
		}

		final Socket underlyingSocket;
		final Properties props;
		final String aliasedHostname;
		
		HangingSocket(Socket realSocket, Properties props, String aliasedHostname) {
			underlyingSocket = realSocket;
			this.props = props;
			this.aliasedHostname = aliasedHostname;
		}

	}

	static class HangingInputStream extends InputStream {
		final InputStream underlyingInputStream;
		final Properties props;
		final String aliasedHostname;
		
		HangingInputStream(InputStream realInputStream, Properties props, String aliasedHostname) {
			underlyingInputStream = realInputStream;
			this.props = props;
			this.aliasedHostname = aliasedHostname;
		}

		public int available() throws IOException {
			return underlyingInputStream.available();
		}

		public void close() throws IOException {
			underlyingInputStream.close();
		}

		public synchronized void mark(int readlimit) {
			underlyingInputStream.mark(readlimit);
		}

		public boolean markSupported() {
			return underlyingInputStream.markSupported();
		}

		public int read(byte[] b, int off, int len) throws IOException {
			failIfRequired();

			return underlyingInputStream.read(b, off, len);
		}

		public int read(byte[] b) throws IOException {
			failIfRequired();

			return underlyingInputStream.read(b);
		}

		public synchronized void reset() throws IOException {
			underlyingInputStream.reset();
		}

		public long skip(long n) throws IOException {
			failIfRequired();

			return underlyingInputStream.skip(n);
		}

		private void failIfRequired() throws SocketTimeoutException {
			if (HUNG_READ_HOSTS.contains(aliasedHostname) || IMMEDIATELY_DOWNED_HOSTS.contains(aliasedHostname)) {
				sleepMillisForProperty(props, "socketTimeout");

				throw new SocketTimeoutException();
			}
		}

		public int read() throws IOException {
			failIfRequired();

			return underlyingInputStream.read();
		}
	}
	
	static class HangingOutputStream extends OutputStream {

			final Properties props;
			final String aliasedHostname;
			final OutputStream underlyingOutputStream;
			
			HangingOutputStream(OutputStream realOutputStream, Properties props, String aliasedHostname) {
				underlyingOutputStream = realOutputStream;
				this.props = props;
				this.aliasedHostname = aliasedHostname;
			}

			public void close() throws IOException {
				failIfRequired();
				underlyingOutputStream.close();
			}

			public void flush() throws IOException {
				underlyingOutputStream.flush();
			}

			public void write(byte[] b, int off, int len) throws IOException {
				failIfRequired();
				underlyingOutputStream.write(b, off, len);
			}

			public void write(byte[] b) throws IOException {
				failIfRequired();
				underlyingOutputStream.write(b);
			}

			public void write(int b) throws IOException {
				failIfRequired();
				underlyingOutputStream.write(b);
			}
			
			private void failIfRequired() throws SocketTimeoutException {
				if (HUNG_WRITE_HOSTS.contains(aliasedHostname) || IMMEDIATELY_DOWNED_HOSTS.contains(aliasedHostname)) {
					sleepMillisForProperty(props, "socketTimeout");

					throw new SocketTimeoutException();
				}
			}
		
	}
} public class ConnectionImpl extends ConnectionPropertiesImpl implements
		MySQLConnection {
	private static final String JDBC_LOCAL_CHARACTER_SET_RESULTS = "jdbc.local.character_set_results";

   public String getHost() {
      return host;
   }

   private MySQLConnection proxy = null;
   
   public boolean isProxySet(){
	   return this.proxy != null;
   }

   public void setProxy(MySQLConnection proxy) {
      this.proxy = proxy;
   }

   // We have to proxy ourselves when we're load balanced so that
   // statements get routed to the right physical connection
   // (when load balanced, we're a "logical" connection)
   private MySQLConnection getProxy() {
      return (proxy != null) ? proxy : (MySQLConnection) this;
   }
   
   public MySQLConnection getLoadBalanceSafeProxy() {
	   return this.getProxy();
   }

  @Override
  public <T> T unwrap(Class<T> tClass) throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public boolean isWrapperFor(Class<?> aClass) throws SQLException {
    return false;  //To change body of implemented methods use File | Settings | File Templates.
  }


  class ExceptionInterceptorChain implements ExceptionInterceptor {
		List interceptors;
		
		ExceptionInterceptorChain(String interceptorClasses) throws SQLException {
			interceptors = Util.loadExtensions(ConnectionImpl.this, props, interceptorClasses, "Connection.BadExceptionInterceptor",  this);
		}
		
		public SQLException interceptException(SQLException sqlEx, Connection conn) {
			if (interceptors != null) {
				Iterator iter = interceptors.iterator();
				
				while (iter.hasNext()) {
					sqlEx = ((ExceptionInterceptor)iter.next()).interceptException(sqlEx, ConnectionImpl.this);
				}
			}
			
			return sqlEx;
		}

		public void destroy() {
			if (interceptors != null) {
				Iterator iter = interceptors.iterator();
				
				while (iter.hasNext()) {
					((ExceptionInterceptor)iter.next()).destroy();
				}
			}
			
		}

		public void init(Connection conn, Properties props) throws SQLException {
			if (interceptors != null) {
				Iterator iter = interceptors.iterator();
				
				while (iter.hasNext()) {
					((ExceptionInterceptor)iter.next()).init(conn, props);
				}
			}
		}
	}
	
	/**
	 * Used as a key for caching callable statements which (may) depend on
	 * current catalog...In 5.0.x, they don't (currently), but stored procedure
	 * names soon will, so current catalog is a (hidden) component of the name.
	 */
	static class CompoundCacheKey {
		String componentOne;

		String componentTwo;

		int hashCode;

		CompoundCacheKey(String partOne, String partTwo) {
			this.componentOne = partOne;
			this.componentTwo = partTwo;

			// Handle first component (in most cases, currentCatalog)
			// being NULL....
			this.hashCode = (((this.componentOne != null) ? this.componentOne
					: "") + this.componentTwo).hashCode();
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#equals(java.lang.Object)
		 */
		public boolean equals(Object obj) {
			if (obj instanceof CompoundCacheKey) {
				CompoundCacheKey another = (CompoundCacheKey) obj;

				boolean firstPartEqual = false;

				if (this.componentOne == null) {
					firstPartEqual = (another.componentOne == null);
				} else {
					firstPartEqual = this.componentOne
							.equals(another.componentOne);
				}

				return (firstPartEqual && this.componentTwo
						.equals(another.componentTwo));
			}

			return false;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#hashCode()
		 */
		public int hashCode() {
			return this.hashCode;
		}
	}

	/**
	 * Marker for character set converter not being available (not written,
	 * multibyte, etc) Used to prevent multiple instantiation requests.
	 */
	private static final Object CHARSET_CONVERTER_NOT_AVAILABLE_MARKER = new Object();

	/**
	 * The mapping between MySQL charset names and Java charset names.
	 * Initialized by loadCharacterSetMapping()
	 */
	public static Map charsetMap;

	/** Default logger class name */
	protected static final String DEFAULT_LOGGER_CLASS = "com.mysql.jdbc.log.StandardLogger";

	private final static int HISTOGRAM_BUCKETS = 20;

	/** Logger instance name */
	private static final String LOGGER_INSTANCE_NAME = "MySQL";

	/**
	 * Map mysql transaction isolation level name to
	 * java.sql.Connection.TRANSACTION_XXX
	 */
	private static Map mapTransIsolationNameToValue = null;

	/** Null logger shared by all connections at startup */
	private static final Log NULL_LOGGER = new NullLogger(LOGGER_INSTANCE_NAME);

	private static Map roundRobinStatsMap;

	private static final Map serverCollationByUrl = new HashMap();

	private static final Map serverConfigByUrl = new HashMap();

	private long queryTimeCount;
	private double queryTimeSum;
	private double queryTimeSumSquares;
	private double queryTimeMean;
	
	private transient Timer cancelTimer;
	
	private List connectionLifecycleInterceptors;
	
	private static final Constructor JDBC_4_CONNECTION_CTOR;
	
	private static final int DEFAULT_RESULT_SET_TYPE = ResultSet.TYPE_FORWARD_ONLY;
	
	private static final int DEFAULT_RESULT_SET_CONCURRENCY = ResultSet.CONCUR_READ_ONLY;
	
	static {
		mapTransIsolationNameToValue = new HashMap(8);
		mapTransIsolationNameToValue.put("READ-UNCOMMITED", Integer.valueOf(
				TRANSACTION_READ_UNCOMMITTED));
		mapTransIsolationNameToValue.put("READ-UNCOMMITTED", Integer.valueOf(
				TRANSACTION_READ_UNCOMMITTED));
		mapTransIsolationNameToValue.put("READ-COMMITTED", Integer.valueOf(
				TRANSACTION_READ_COMMITTED));
		mapTransIsolationNameToValue.put("REPEATABLE-READ", Integer.valueOf(
				TRANSACTION_REPEATABLE_READ));
		mapTransIsolationNameToValue.put("SERIALIZABLE", Integer.valueOf(
				TRANSACTION_SERIALIZABLE));

		if (Util.isJdbc4()) {
			try {
				JDBC_4_CONNECTION_CTOR = Class.forName(
						"com.mysql.jdbc.JDBC4Connection").getConstructor(
						new Class[] { String.class, Integer.TYPE,
								Properties.class, String.class, String.class });
			} catch (SecurityException e) {
				throw new RuntimeException(e);
			} catch (NoSuchMethodException e) {
				throw new RuntimeException(e);
			} catch (ClassNotFoundException e) {
				throw new RuntimeException(e);
			}
		} else {
			JDBC_4_CONNECTION_CTOR = null;
		}
	}

	protected static SQLException appendMessageToException(SQLException sqlEx,
			String messageToAppend, ExceptionInterceptor interceptor) {
		String origMessage = sqlEx.getMessage();
		String sqlState = sqlEx.getSQLState();
		int vendorErrorCode = sqlEx.getErrorCode();

		StringBuffer messageBuf = new StringBuffer(origMessage.length()
				+ messageToAppend.length());
		messageBuf.append(origMessage);
		messageBuf.append(messageToAppend);

		SQLException sqlExceptionWithNewMessage = SQLError.createSQLException(messageBuf
				.toString(), sqlState, vendorErrorCode, interceptor);

		//
		// Try and maintain the original stack trace,
		// only works on JDK-1.4 and newer
		//

		try {
			// Have to do this with reflection, otherwise older JVMs croak
			Method getStackTraceMethod = null;
			Method setStackTraceMethod = null;
			Object theStackTraceAsObject = null;

			Class stackTraceElementClass = Class
					.forName("java.lang.StackTraceElement");
			Class stackTraceElementArrayClass = Array.newInstance(
					stackTraceElementClass, new int[] { 0 }).getClass();

			getStackTraceMethod = Throwable.class.getMethod("getStackTrace",
					new Class[] {});

			setStackTraceMethod = Throwable.class.getMethod("setStackTrace",
					new Class[] { stackTraceElementArrayClass });

			if (getStackTraceMethod != null && setStackTraceMethod != null) {
				theStackTraceAsObject = getStackTraceMethod.invoke(sqlEx,
						new Object[0]);
				setStackTraceMethod.invoke(sqlExceptionWithNewMessage,
						new Object[] { theStackTraceAsObject });
			}
		} catch (NoClassDefFoundError noClassDefFound) {

		} catch (NoSuchMethodException noSuchMethodEx) {

		} catch (Throwable catchAll) {

		}

		return sqlExceptionWithNewMessage;
	}

	public synchronized Timer getCancelTimer() {
		if (cancelTimer == null) {
			boolean createdNamedTimer = false;
			
			// Use reflection magic to try this on JDK's 1.5 and newer, fallback to non-named
			// timer on older VMs.
			try {
				Constructor ctr = Timer.class.getConstructor(new Class[] {String.class, Boolean.TYPE});
				
				cancelTimer = (Timer)ctr.newInstance(new Object[] { "MySQL Statement Cancellation Timer", Boolean.TRUE});
				createdNamedTimer = true;
			} catch (Throwable t) {
				createdNamedTimer = false;
			}
			
			if (!createdNamedTimer) {
				cancelTimer = new Timer(true);
			}
		}
		
		return cancelTimer;
	}

	
	/**
	 * Creates a connection instance -- We need to provide factory-style methods
	 * so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise
	 * the class verifier complains when it tries to load JDBC4-only interface
	 * classes that are present in JDBC4 method signatures.
	 */

	protected static Connection getInstance(String hostToConnectTo,
			int portToConnectTo, Properties info, String databaseToConnectTo,
			String url) throws SQLException {
		if (!Util.isJdbc4()) {
			return new ConnectionImpl(hostToConnectTo, portToConnectTo, info,
					databaseToConnectTo, url);
		}

		return (Connection) Util.handleNewInstance(JDBC_4_CONNECTION_CTOR,
				new Object[] {
							hostToConnectTo, Integer.valueOf(portToConnectTo), info,
							databaseToConnectTo, url }, null);
	}

	private static final Random random = new Random();
	
	private static synchronized int getNextRoundRobinHostIndex(String url,
			List hostList) {
		// we really do "random" here, because you don't get even
		// distribution when this is coupled with connection pools
		
		int indexRange = hostList.size();
		
		int index = random.nextInt(indexRange);
		
		return index;
	}

	private static boolean nullSafeCompare(String s1, String s2) {
		if (s1 == null && s2 == null) {
			return true;
		}

		if (s1 == null && s2 != null) {
			return false;
		}

		return s1 != null && s1.equals(s2);
	}

	/** Are we in autoCommit mode? */
	private boolean autoCommit = true;

	/** A map of SQL to parsed prepared statement parameters. */
	private Map cachedPreparedStatementParams;

	/**
	 * For servers > 4.1.0, what character set is the metadata returned in?
	 */
	private String characterSetMetadata = null;

	/**
	 * The character set we want results and result metadata returned in (null ==
	 * results in any charset, metadata in UTF-8).
	 */
	private String characterSetResultsOnServer = null;

	/**
	 * Holds cached mappings to charset converters to avoid static
	 * synchronization and at the same time save memory (each charset converter
	 * takes approx 65K of static data).
	 */
	private Map charsetConverterMap = new HashMap(CharsetMapping
			.getNumberOfCharsetsConfigured());

	/**
	 * The mapping between MySQL charset names and the max number of chars in
	 * them. Lazily instantiated via getMaxBytesPerChar().
	 */
	private Map charsetToNumBytesMap;

	/** The point in time when this connection was created */
	private long connectionCreationTimeMillis = 0;

	/** ID used when profiling */
	private long connectionId;

	/** The database we're currently using (called Catalog in JDBC terms). */
	private String database = null;

	/** Internal DBMD to use for various database-version specific features */
	private DatabaseMetaData dbmd = null;

	private TimeZone defaultTimeZone;

	/** The event sink to use for profiling */
	private ProfilerEventHandler eventSink;

	/** Why was this connection implicitly closed, if known? (for diagnostics) */
	private Throwable forceClosedReason;

	/** Does the server suuport isolation levels? */
	private boolean hasIsolationLevels = false;

	/** Does this version of MySQL support quoted identifiers? */
	private boolean hasQuotedIdentifiers = false;

	/** The hostname we're connected to */
	private String host = null;
	
	/**
	 * We need this 'bootstrapped', because 4.1 and newer will send fields back
	 * with this even before we fill this dynamically from the server.
	 */
	private String[] indexToCharsetMapping = CharsetMapping.INDEX_TO_CHARSET;
	
	/** The I/O abstraction interface (network conn to MySQL server */
	private transient MysqlIO io = null;
	
	private boolean isClientTzUTC = false;

	/** Has this connection been closed? */
	private boolean isClosed = true;

	/** Is this connection associated with a global tx? */
	private boolean isInGlobalTx = false;

	/** Is this connection running inside a JDK-1.3 VM? */
	private boolean isRunningOnJDK13 = false;

	/** isolation level */
	private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED;

	private boolean isServerTzUTC = false;

	/** When did the last query finish? */
	private long lastQueryFinishedTime = 0;

	/** The logger we're going to use */
	private transient Log log = NULL_LOGGER;

	/**
	 * If gathering metrics, what was the execution time of the longest query so
	 * far ?
	 */
	private long longestQueryTimeMs = 0;

	/** Is the server configured to use lower-case table names only? */
	private boolean lowerCaseTableNames = false;

	/** When did the master fail? */
	private long masterFailTimeMillis = 0L;

	private long maximumNumberTablesAccessed = 0;

	/** Has the max-rows setting been changed from the default? */
	private boolean maxRowsChanged = false;

	/** When was the last time we reported metrics? */
	private long metricsLastReportedMs;

	private long minimumNumberTablesAccessed = Long.MAX_VALUE;

	/** The JDBC URL we're using */
	private String myURL = null;

	/** Does this connection need to be tested? */
	private boolean needsPing = false;

	private int netBufferLength = 16384;

	private boolean noBackslashEscapes = false;

	private long numberOfPreparedExecutes = 0;

	private long numberOfPrepares = 0;

	private long numberOfQueriesIssued = 0;

	private long numberOfResultSetsCreated = 0;

	private long[] numTablesMetricsHistBreakpoints;

	private int[] numTablesMetricsHistCounts;

	private long[] oldHistBreakpoints = null;

	private int[] oldHistCounts = null;

	/** A map of currently open statements */
	private Map openStatements;

	private LRUCache parsedCallableStatementCache;

	private boolean parserKnowsUnicode = false;

	/** The password we used */
	private String password = null;

	private long[] perfMetricsHistBreakpoints;

	private int[] perfMetricsHistCounts;

	/** Point of origin where this Connection was created */
	private Throwable pointOfOrigin;

	/** The port number we're connected to (defaults to 3306) */
	private int port = 3306;

	/** Properties for this connection specified by user */
	protected Properties props = null;

	/** Should we retrieve 'info' messages from the server? */
	private boolean readInfoMsg = false;

	/** Are we in read-only mode? */
	private boolean readOnly = false;

	/** Cache of ResultSet metadata */
	protected LRUCache resultSetMetadataCache;
	
	/** The timezone of the server */
	private TimeZone serverTimezoneTZ = null;

	/** The map of server variables that we retrieve at connection init. */
	private Map serverVariables = null;

	private long shortestQueryTimeMs = Long.MAX_VALUE;

	/** A map of statements that have had setMaxRows() called on them */
	private Map statementsUsingMaxRows;

	private double totalQueryTimeMs = 0;

	/** Are transactions supported by the MySQL server we are connected to? */
	private boolean transactionsSupported = false;

	/**
	 * The type map for UDTs (not implemented, but used by some third-party
	 * vendors, most notably IBM WebSphere)
	 */
	private Map typeMap;

	/** Has ANSI_QUOTES been enabled on the server? */
	private boolean useAnsiQuotes = false;

	/** The user we're connected as */
	private String user = null;
	
	/**
	 * Should we use server-side prepared statements? (auto-detected, but can be
	 * disabled by user)
	 */
	private boolean useServerPreparedStmts = false;

	private LRUCache serverSideStatementCheckCache;
	private LRUCache serverSideStatementCache;
	private Calendar sessionCalendar;
	
	private Calendar utcCalendar;
	
	private String origHostToConnectTo;

	// we don't want to be able to publicly clone this...
	
	private int origPortToConnectTo;

	private String origDatabaseToConnectTo;

	private String errorMessageEncoding = "Cp1252"; // to begin with, changes after we talk to the server
	
	private boolean usePlatformCharsetConverters;
	
	/*
	 * For testing failover scenarios
	 */
	private boolean hasTriedMasterFlag = false;

	/**
	 * The comment (if any) that we'll prepend to all statements
	 * sent to the server (to show up in "SHOW PROCESSLIST")
	 */
	private String statementComment = null;

	private boolean storesLowerCaseTableName;

	private List statementInterceptors;
	
	/**
	 * If a CharsetEncoder is required for escaping. Needed for SJIS and related
	 * problems with \u00A5.
	 */
	private boolean requiresEscapingEncoder;

	private String hostPortPair;
	
	/**'
	 * For the delegate only
	 */
	protected ConnectionImpl() {	
	}
	
	/**
	 * Creates a connection to a MySQL Server.
	 * 
	 * @param hostToConnectTo
	 *            the hostname of the database server
	 * @param portToConnectTo
	 *            the port number the server is listening on
	 * @param info
	 *            a Properties[] list holding the user and password
	 * @param databaseToConnectTo
	 *            the database to connect to
	 * @param url
	 *            the URL of the connection
	 * @param d
	 *            the Driver instantation of the connection
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	protected ConnectionImpl(String hostToConnectTo, int portToConnectTo, Properties info,
			String databaseToConnectTo, String url)
			throws SQLException {
		this.charsetToNumBytesMap = new HashMap();
	
		this.connectionCreationTimeMillis = System.currentTimeMillis();
		this.pointOfOrigin = new Throwable();

      if (databaseToConnectTo == null) {
			databaseToConnectTo = "";
		}

		// Stash away for later, used to clone this connection for Statement.cancel
		// and Statement.setQueryTimeout().
		//
		
		this.origHostToConnectTo = hostToConnectTo;
		this.origPortToConnectTo = portToConnectTo;
		this.origDatabaseToConnectTo = databaseToConnectTo;

		try {
			Blob.class.getMethod("truncate", new Class[] {Long.TYPE});
			
			this.isRunningOnJDK13 = false;
		} catch (NoSuchMethodException nsme) {
			this.isRunningOnJDK13 = true;
		}
		
		this.sessionCalendar = new GregorianCalendar();
		this.utcCalendar = new GregorianCalendar();
		this.utcCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
		
		//
		// Normally, this code would be in initializeDriverProperties,
		// but we need to do this as early as possible, so we can start
		// logging to the 'correct' place as early as possible...this.log
		// points to 'NullLogger' for every connection at startup to avoid
		// NPEs and the overhead of checking for NULL at every logging call.
		//
		// We will reset this to the configured logger during properties
		// initialization.
		//
		this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME, getExceptionInterceptor());

		// We store this per-connection, due to static synchronization
		// issues in Java's built-in TimeZone class...
		this.defaultTimeZone = Util.getDefaultTimeZone();
		
		if ("GMT".equalsIgnoreCase(this.defaultTimeZone.getID())) {
			this.isClientTzUTC = true;
		} else {
			this.isClientTzUTC = false;
		}

		this.openStatements = new HashMap();
		
		if (NonRegisteringDriver.isHostPropertiesList(hostToConnectTo)) {
			Properties hostSpecificProps = NonRegisteringDriver.expandHostKeyValues(hostToConnectTo);
			
			Enumeration<?> propertyNames = hostSpecificProps.propertyNames();
			
			while (propertyNames.hasMoreElements()) {
				String propertyName = propertyNames.nextElement().toString();
				String propertyValue = hostSpecificProps.getProperty(propertyName);
				
				info.setProperty(propertyName, propertyValue);
			}
		} else {
		
			if (hostToConnectTo == null) {
				this.host = "localhost";
				this.hostPortPair = this.host + ":" + portToConnectTo;
			} else {
				this.host = hostToConnectTo;
				
				if (hostToConnectTo.indexOf(":") == -1) {
					this.hostPortPair = this.host + ":" + portToConnectTo;
				} else {
					this.hostPortPair = this.host;
				}
			}
		}

		this.port = portToConnectTo;

		this.database = databaseToConnectTo;
		this.myURL = url;
		this.user = info.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY);
		this.password = info
				.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY);

		if ((this.user == null) || this.user.equals("")) {
			this.user = "";
		}

		if (this.password == null) {
			this.password = "";
		}

		this.props = info;
		
		
		
		initializeDriverProperties(info);
		
		
		try {
			this.dbmd = getMetaData(false, false);
			initializeSafeStatementInterceptors();
			createNewIO(false);
			unSafeStatementInterceptors();
		} catch (SQLException ex) {
			cleanup(ex);

			// don't clobber SQL exceptions
			throw ex;
		} catch (Exception ex) {
			cleanup(ex);

			StringBuffer mesg = new StringBuffer(128);

			if (!getParanoid()) {
				mesg.append("Cannot connect to MySQL server on ");
				mesg.append(this.host);
				mesg.append(":");
				mesg.append(this.port);
				mesg.append(".\n\n");
				mesg.append("Make sure that there is a MySQL server ");
				mesg.append("running on the machine/port you are trying ");
				mesg
						.append("to connect to and that the machine this software is "
								+ "running on ");
				mesg.append("is able to connect to this host/port "
						+ "(i.e. not firewalled). ");
				mesg
						.append("Also make sure that the server has not been started "
								+ "with the --skip-networking ");
				mesg.append("flag.\n\n");
			} else {
				mesg.append("Unable to connect to database.");
			}

			SQLException sqlEx = SQLError.createSQLException(mesg.toString(),
					SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, getExceptionInterceptor());
			
			sqlEx.initCause(ex);
			
			throw sqlEx;
		}
	}

    public void unSafeStatementInterceptors() throws SQLException {
    	
    	ArrayList unSafedStatementInterceptors = new ArrayList(this.statementInterceptors.size());

    	for (int i = 0; i < this.statementInterceptors.size(); i++) {
    		NoSubInterceptorWrapper wrappedInterceptor = (NoSubInterceptorWrapper) this.statementInterceptors.get(i);
    		
    		unSafedStatementInterceptors.add(wrappedInterceptor.getUnderlyingInterceptor());
    	}
    	
    	this.statementInterceptors = unSafedStatementInterceptors;
    	
    	if (this.io != null) {
    		this.io.setStatementInterceptors(this.statementInterceptors);
    	}
	}
    
    public void initializeSafeStatementInterceptors() throws SQLException {
    	this.isClosed = false;
    	
    	List unwrappedInterceptors = Util.loadExtensions(this, this.props, 
				getStatementInterceptors(),
				"MysqlIo.BadStatementInterceptor", getExceptionInterceptor());
    	
    	this.statementInterceptors = new ArrayList(unwrappedInterceptors.size());

    	for (int i = 0; i < unwrappedInterceptors.size(); i++) {
    		Object interceptor = unwrappedInterceptors.get(i);
    		
    		// adapt older versions of statement interceptors, handle the case where something wants v2
    		// functionality but wants to run with an older driver
    		if (interceptor instanceof StatementInterceptor) {
    			if (ReflectiveStatementInterceptorAdapter.getV2PostProcessMethod(interceptor.getClass()) != null) {
    				this.statementInterceptors.add(new NoSubInterceptorWrapper(new ReflectiveStatementInterceptorAdapter((StatementInterceptor) interceptor)));
    			} else {
    				this.statementInterceptors.add(new NoSubInterceptorWrapper(new V1toV2StatementInterceptorAdapter((StatementInterceptor) interceptor)));
    			}
    		} else {
    			this.statementInterceptors.add(new NoSubInterceptorWrapper((StatementInterceptorV2)interceptor));
    		}
    	}
    	
    	
    }
    
    public List getStatementInterceptorsInstances() {
    	return this.statementInterceptors;
    }
    
	private void addToHistogram(int[] histogramCounts,
			long[] histogramBreakpoints, long value, int numberOfTimes,
			long currentLowerBound, long currentUpperBound) {
		if (histogramCounts == null) {
			createInitialHistogram(histogramBreakpoints,
					currentLowerBound, currentUpperBound);
		} else {
			for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
				if (histogramBreakpoints[i] >= value) {
					histogramCounts[i] += numberOfTimes;
	
					break;
				}
			}
		}
	}

	private void addToPerformanceHistogram(long value, int numberOfTimes) {
		checkAndCreatePerformanceHistogram();

		addToHistogram(this.perfMetricsHistCounts,
				this.perfMetricsHistBreakpoints, value, numberOfTimes,
				this.shortestQueryTimeMs == Long.MAX_VALUE ? 0
						: this.shortestQueryTimeMs, this.longestQueryTimeMs);
	}

	private void addToTablesAccessedHistogram(long value, int numberOfTimes) {
		checkAndCreateTablesAccessedHistogram();

		addToHistogram(this.numTablesMetricsHistCounts,
				this.numTablesMetricsHistBreakpoints, value, numberOfTimes,
				this.minimumNumberTablesAccessed == Long.MAX_VALUE ? 0
						: this.minimumNumberTablesAccessed,
				this.maximumNumberTablesAccessed);
	}
	
	/**
	 * Builds the map needed for 4.1.0 and newer servers that maps field-level
	 * charset/collation info to a java character encoding name.
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	private void buildCollationMapping() throws SQLException {
		if (versionMeetsMinimum(4, 1, 0)) {

			TreeMap sortedCollationMap = null;

			if (getCacheServerConfiguration()) {
				synchronized (serverConfigByUrl) {
					sortedCollationMap = (TreeMap) serverCollationByUrl
							.get(getURL());
				}
			}

			java.sql.Statement stmt = null;
			java.sql.ResultSet results = null;

			try {
				if (sortedCollationMap == null) {
					sortedCollationMap = new TreeMap();

					stmt = getMetadataSafeStatement();

					results = stmt
							.executeQuery("SHOW COLLATION");

					while (results.next()) {
						String charsetName = results.getString(2);
						Integer charsetIndex = Integer.valueOf(results.getInt(3));

						sortedCollationMap.put(charsetIndex, charsetName);
					}

					if (getCacheServerConfiguration()) {
						synchronized (serverConfigByUrl) {
							serverCollationByUrl.put(getURL(),
									sortedCollationMap);
						}
					}

				}

				// Now, merge with what we already know
				int highestIndex = ((Integer) sortedCollationMap.lastKey())
						.intValue();

				if (CharsetMapping.INDEX_TO_CHARSET.length > highestIndex) {
					highestIndex = CharsetMapping.INDEX_TO_CHARSET.length;
				}

				this.indexToCharsetMapping = new String[highestIndex + 1];

				for (int i = 0; i < CharsetMapping.INDEX_TO_CHARSET.length; i++) {
					this.indexToCharsetMapping[i] = CharsetMapping.INDEX_TO_CHARSET[i];
				}

				for (Iterator indexIter = sortedCollationMap.entrySet()
						.iterator(); indexIter.hasNext();) {
					Map.Entry indexEntry = (Map.Entry) indexIter.next();

					String mysqlCharsetName = (String) indexEntry.getValue();

					this.indexToCharsetMapping[((Integer) indexEntry.getKey())
							.intValue()] = CharsetMapping
							.getJavaEncodingForMysqlEncoding(mysqlCharsetName,
									this);
				}
			} catch (java.sql.SQLException e) {
				throw e;
			} finally {
				if (results != null) {
					try {
						results.close();
					} catch (java.sql.SQLException sqlE) {
						// ignore
					}
				}

				if (stmt != null) {
					try {
						stmt.close();
					} catch (java.sql.SQLException sqlE) {
						// ignore
					}
				}
			}
		} else {
			// Safety, we already do this as an initializer, but this makes
			// the intent more clear
			this.indexToCharsetMapping = CharsetMapping.INDEX_TO_CHARSET;
		}
	}

	private boolean canHandleAsServerPreparedStatement(String sql) 
		throws SQLException {
		if (sql == null || sql.length() == 0) {
			return true;
		}

		if (!this.useServerPreparedStmts) {
			return false;
		}
		
		if (getCachePreparedStatements()) {
			synchronized (this.serverSideStatementCheckCache) {
				Boolean flag = (Boolean)this.serverSideStatementCheckCache.get(sql);
				
				if (flag != null) {
					return flag.booleanValue();
				}
					
				boolean canHandle = canHandleAsServerPreparedStatementNoCache(sql);
				
				if (sql.length() < getPreparedStatementCacheSqlLimit()) {
					this.serverSideStatementCheckCache.put(sql, 
							canHandle ? Boolean.TRUE : Boolean.FALSE);
				}
					
				return canHandle;
			}
		}
		
		return canHandleAsServerPreparedStatementNoCache(sql);
	}

	private boolean canHandleAsServerPreparedStatementNoCache(String sql) 
		throws SQLException {
		
		// Can't use server-side prepare for CALL
		if (StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "CALL")) {
			return false;
		}
		
		boolean canHandleAsStatement = true;
		
		if (!versionMeetsMinimum(5, 0, 7) && 
				(StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "SELECT")
				|| StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
						"DELETE")
				|| StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
						"INSERT")
				|| StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
						"UPDATE")
				|| StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
						"REPLACE"))) {

			// check for limit ?[,?]

			/*
			 * The grammar for this (from the server) is: ULONG_NUM | ULONG_NUM
			 * ',' ULONG_NUM | ULONG_NUM OFFSET_SYM ULONG_NUM
			 */

			int currentPos = 0;
			int statementLength = sql.length();
			int lastPosToLook = statementLength - 7; // "LIMIT ".length()
			boolean allowBackslashEscapes = !this.noBackslashEscapes;
			char quoteChar = this.useAnsiQuotes ? '"' : '\'';
			boolean foundLimitWithPlaceholder = false;

			while (currentPos < lastPosToLook) {
				int limitStart = StringUtils.indexOfIgnoreCaseRespectQuotes(
						currentPos, sql, "LIMIT ", quoteChar,
						allowBackslashEscapes);

				if (limitStart == -1) {
					break;
				}

				currentPos = limitStart + 7;

				while (currentPos < statementLength) {
					char c = sql.charAt(currentPos);

					//
					// Have we reached the end
					// of what can be in a LIMIT clause?
					//

					if (!Character.isDigit(c) && !Character.isWhitespace(c)
							&& c != ',' && c != '?') {
						break;
					}

					if (c == '?') {
						foundLimitWithPlaceholder = true;
						break;
					}

					currentPos++;
				}
			}

			canHandleAsStatement = !foundLimitWithPlaceholder;
		} else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "CREATE TABLE")) {
			canHandleAsStatement = false;
		} else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "DO")) {
			canHandleAsStatement = false;
		} else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "SET")) {
			canHandleAsStatement = false;
		}

		
		
		return canHandleAsStatement;
	}

	/**
	 * Changes the user on this connection by performing a re-authentication. If
	 * authentication fails, the connection will remain under the context of the
	 * current user.
	 * 
	 * @param userName
	 *            the username to authenticate with
	 * @param newPassword
	 *            the password to authenticate with
	 * @throws SQLException
	 *             if authentication fails, or some other error occurs while
	 *             performing the command.
	 */
	public void changeUser(String userName, String newPassword)
			throws SQLException {
		if ((userName == null) || userName.equals("")) {
			userName = "";
		}

		if (newPassword == null) {
			newPassword = "";
		}

		this.io.changeUser(userName, newPassword, this.database);
		this.user = userName;
		this.password = newPassword;

		if (versionMeetsMinimum(4, 1, 0)) {
			configureClientCharacterSet(true);
		}
		
		setSessionVariables();
		
		setupServerForTruncationChecks();
	}

	private boolean characterSetNamesMatches(String mysqlEncodingName) {
		// set names is equivalent to character_set_client ..._results and ..._connection,
		// but we set _results later, so don't check it here.
		return (mysqlEncodingName != null && 
				mysqlEncodingName.equalsIgnoreCase((String)this.serverVariables.get("character_set_client")) &&
				mysqlEncodingName.equalsIgnoreCase((String)this.serverVariables.get("character_set_connection")));
	}

	private void checkAndCreatePerformanceHistogram() {
		if (this.perfMetricsHistCounts == null) {
			this.perfMetricsHistCounts = new int[HISTOGRAM_BUCKETS];
		}

		if (this.perfMetricsHistBreakpoints == null) {
			this.perfMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS];
		}
	}

	private void checkAndCreateTablesAccessedHistogram() {
		if (this.numTablesMetricsHistCounts == null) {
			this.numTablesMetricsHistCounts = new int[HISTOGRAM_BUCKETS];
		}

		if (this.numTablesMetricsHistBreakpoints == null) {
			this.numTablesMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS];
		}
	}

	public void checkClosed() throws SQLException {
		if (this.isClosed) {
			throwConnectionClosedException();
		}
	}

	public void throwConnectionClosedException() throws SQLException {
		StringBuffer messageBuf = new StringBuffer(
				"No operations allowed after connection closed.");

		SQLException ex = SQLError.createSQLException(messageBuf.toString(),
				SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor());
		
		if (this.forceClosedReason != null) {
			ex.initCause(this.forceClosedReason);
		}

		throw ex;
	}

	/**
	 * If useUnicode flag is set and explicit client character encoding isn't
	 * specified then assign encoding from server if any.
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	private void checkServerEncoding() throws SQLException {
		if (getUseUnicode() && (getEncoding() != null)) {
			// spec'd by client, don't map
			return;
		}

		String serverEncoding = (String) this.serverVariables
				.get("character_set");

		if (serverEncoding == null) {
			// must be 4.1.1 or newer?
			serverEncoding = (String) this.serverVariables
					.get("character_set_server");
		}

		String mappedServerEncoding = null;

		if (serverEncoding != null) {
			mappedServerEncoding = CharsetMapping
					.getJavaEncodingForMysqlEncoding(serverEncoding
							.toUpperCase(Locale.ENGLISH), this);
		}

		//
		// First check if we can do the encoding ourselves
		//
		if (!getUseUnicode() && (mappedServerEncoding != null)) {
			SingleByteCharsetConverter converter = getCharsetConverter(mappedServerEncoding);

			if (converter != null) { // we know how to convert this ourselves
				setUseUnicode(true); // force the issue
				setEncoding(mappedServerEncoding);

				return;
			}
		}

		//
		// Now, try and find a Java I/O converter that can do
		// the encoding for us
		//
		if (serverEncoding != null) {
			if (mappedServerEncoding == null) {
				// We don't have a mapping for it, so try
				// and canonicalize the name....
				if (Character.isLowerCase(serverEncoding.charAt(0))) {
					char[] ach = serverEncoding.toCharArray();
					ach[0] = Character.toUpperCase(serverEncoding.charAt(0));
					setEncoding(new String(ach));
				}
			}

			if (mappedServerEncoding == null) {
				throw SQLError.createSQLException("Unknown character encoding on server '"
						+ serverEncoding
						+ "', use 'characterEncoding=' property "
						+ " to provide correct mapping",
						SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
			}

			//
			// Attempt to use the encoding, and bail out if it
			// can't be used
			//
			try {
				StringUtils.getBytes("abc",mappedServerEncoding);
				setEncoding(mappedServerEncoding);
				setUseUnicode(true);
			} catch (UnsupportedEncodingException UE) {
				throw SQLError.createSQLException(
						"The driver can not map the character encoding '"
								+ getEncoding()
								+ "' that your server is using "
								+ "to a character encoding your JVM understands. You "
								+ "can specify this mapping manually by adding \"useUnicode=true\" "
								+ "as well as \"characterEncoding=[an_encoding_your_jvm_understands]\" "
								+ "to your JDBC URL.", "0S100", getExceptionInterceptor());
			}
		}
	}

	/**
	 * Set transaction isolation level to the value received from server if any.
	 * Is called by connectionInit(...)
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	private void checkTransactionIsolationLevel() throws SQLException {
		String txIsolationName = null;

		if (versionMeetsMinimum(4, 0, 3)) {
			txIsolationName = "tx_isolation";
		} else {
			txIsolationName = "transaction_isolation";
		}

		String s = (String) this.serverVariables.get(txIsolationName);

		if (s != null) {
			Integer intTI = (Integer) mapTransIsolationNameToValue.get(s);

			if (intTI != null) {
				this.isolationLevel = intTI.intValue();
			}
		}
	}

	/**
	 * Clobbers the physical network connection and marks
	 * this connection as closed.
	 * 
	 * @throws SQLException
	 */
	public void abortInternal() throws SQLException {
		if (this.io != null) {
			try {
				this.io.forceClose();
			} catch (Throwable t) {
				// can't do anything about it, and we're forcibly aborting
			}
			this.io = null;
		}
		
		this.isClosed = true;
	}
	
	/**
	 * Destroys this connection and any underlying resources
	 * 
	 * @param fromWhere
	 *            DOCUMENT ME!
	 * @param whyCleanedUp
	 *            DOCUMENT ME!
	 */
	private void cleanup(Throwable whyCleanedUp) {
		try {
			if ((this.io != null) && !isClosed()) {
				realClose(false, false, false, whyCleanedUp);
			} else if (this.io != null) {
				this.io.forceClose();
			}
		} catch (SQLException sqlEx) {
			// ignore, we're going away.
			;
		}

		this.isClosed = true;
	}

	public void clearHasTriedMaster() {
		this.hasTriedMasterFlag = false;
	}
	
	/**
	 * After this call, getWarnings returns null until a new warning is reported
	 * for this connection.
	 * 
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	public void clearWarnings() throws SQLException {
		// firstWarning = null;
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param sql
	 *            DOCUMENT ME!
	 * @return DOCUMENT ME!
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public java.sql.PreparedStatement clientPrepareStatement(String sql)
			throws SQLException {
		return clientPrepareStatement(sql,
				DEFAULT_RESULT_SET_TYPE,
				DEFAULT_RESULT_SET_CONCURRENCY);
	}

	/**
	 * @see Connection#prepareStatement(String, int)
	 */
	public java.sql.PreparedStatement clientPrepareStatement(String sql,
			int autoGenKeyIndex) throws SQLException {
		java.sql.PreparedStatement pStmt = clientPrepareStatement(sql);

		((com.mysql.jdbc.PreparedStatement) pStmt)
				.setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);

		return pStmt;
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param sql
	 *            DOCUMENT ME!
	 * @param resultSetType
	 *            DOCUMENT ME!
	 * @param resultSetConcurrency
	 *            DOCUMENT ME!
	 * @return DOCUMENT ME!
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public java.sql.PreparedStatement clientPrepareStatement(String sql,
			int resultSetType, int resultSetConcurrency) throws SQLException {
		return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true);
	}


	
	public java.sql.PreparedStatement clientPrepareStatement(String sql,
			int resultSetType, int resultSetConcurrency, 
			boolean processEscapeCodesIfNeeded) throws SQLException {
		checkClosed();

		String nativeSql = processEscapeCodesIfNeeded && getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
		
		PreparedStatement pStmt = null;

		if (getCachePreparedStatements()) {
			synchronized (this.cachedPreparedStatementParams) {
				PreparedStatement.ParseInfo pStmtInfo = (PreparedStatement.ParseInfo) this.cachedPreparedStatementParams
						.get(nativeSql);
	
				if (pStmtInfo == null) {
					pStmt = com.mysql.jdbc.PreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql,
							this.database);
	
					PreparedStatement.ParseInfo parseInfo = pStmt.getParseInfo();
	
					if (parseInfo.statementLength < getPreparedStatementCacheSqlLimit()) {
						if (this.cachedPreparedStatementParams.size() >= getPreparedStatementCacheSize()) {
							Iterator oldestIter = this.cachedPreparedStatementParams
									.keySet().iterator();
							long lruTime = Long.MAX_VALUE;
							String oldestSql = null;
	
							while (oldestIter.hasNext()) {
								String sqlKey = (String) oldestIter.next();
								PreparedStatement.ParseInfo lruInfo = (PreparedStatement.ParseInfo) this.cachedPreparedStatementParams
										.get(sqlKey);
	
								if (lruInfo.lastUsed < lruTime) {
									lruTime = lruInfo.lastUsed;
									oldestSql = sqlKey;
								}
							}
	
							if (oldestSql != null) {
								this.cachedPreparedStatementParams
										.remove(oldestSql);
							}
						}
	
						this.cachedPreparedStatementParams.put(nativeSql, pStmt
								.getParseInfo());
					}
				} else {
					pStmtInfo.lastUsed = System.currentTimeMillis();
					pStmt = new com.mysql.jdbc.PreparedStatement(getLoadBalanceSafeProxy(), nativeSql,
							this.database, pStmtInfo);
				}
			}
		} else {
			pStmt = com.mysql.jdbc.PreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql,
					this.database);
		}

		pStmt.setResultSetType(resultSetType);
		pStmt.setResultSetConcurrency(resultSetConcurrency);

		return pStmt;
	}
	
	/**
	 * @see java.sql.Connection#prepareStatement(String, int[])
	 */
	public java.sql.PreparedStatement clientPrepareStatement(String sql,
			int[] autoGenKeyIndexes) throws SQLException {
		
		PreparedStatement pStmt = (PreparedStatement) clientPrepareStatement(sql);
		
		pStmt
				.setRetrieveGeneratedKeys((autoGenKeyIndexes != null)
						&& (autoGenKeyIndexes.length > 0));

		return pStmt;
	}

	/**
	 * @see java.sql.Connection#prepareStatement(String, String[])
	 */
	public java.sql.PreparedStatement clientPrepareStatement(String sql,
			String[] autoGenKeyColNames) throws SQLException {
		PreparedStatement pStmt = (PreparedStatement) clientPrepareStatement(sql);

		pStmt
				.setRetrieveGeneratedKeys((autoGenKeyColNames != null)
						&& (autoGenKeyColNames.length > 0));

		return pStmt;
	}

	public java.sql.PreparedStatement clientPrepareStatement(String sql,
			int resultSetType, int resultSetConcurrency,
			int resultSetHoldability) throws SQLException {
		return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true);
	}
	
	// --------------------------JDBC 2.0-----------------------------

	/**
	 * In some cases, it is desirable to immediately release a Connection's
	 * database and JDBC resources instead of waiting for them to be
	 * automatically released (cant think why off the top of my head) <B>Note:</B>
	 * A Connection is automatically closed when it is garbage collected.
	 * Certain fatal errors also result in a closed connection.
	 * 
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	public synchronized void close() throws SQLException {
		if (this.connectionLifecycleInterceptors != null) {
			new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {
				void forEach(Object each) throws SQLException {
					((ConnectionLifecycleInterceptor)each).close();
				}
			}.doForAll();
		}
	
		realClose(true, true, false, null);
	}

	/**
	 * Closes all currently open statements.
	 * 
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	private void closeAllOpenStatements() throws SQLException {
		SQLException postponedException = null;

		if (this.openStatements != null) {
			List currentlyOpenStatements = new ArrayList(); // we need this to
			// avoid
			// ConcurrentModificationEx

			for (Iterator iter = this.openStatements.keySet().iterator(); iter
					.hasNext();) {
				currentlyOpenStatements.add(iter.next());
			}

			int numStmts = currentlyOpenStatements.size();

			for (int i = 0; i < numStmts; i++) {
				StatementImpl stmt = (StatementImpl) currentlyOpenStatements.get(i);

				try {
					stmt.realClose(false, true);
				} catch (SQLException sqlEx) {
					postponedException = sqlEx; // throw it later, cleanup all
					// statements first
				}
			}

			if (postponedException != null) {
				throw postponedException;
			}
		}
	}

	private void closeStatement(java.sql.Statement stmt) {
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException sqlEx) {
				; // ignore
			}

			stmt = null;
		}
	}

	/**
	 * The method commit() makes all changes made since the previous
	 * commit/rollback permanent and releases any database locks currently held
	 * by the Connection. This method should only be used when auto-commit has
	 * been disabled.
	 * <p>
	 * <b>Note:</b> MySQL does not support transactions, so this method is a
	 * no-op.
	 * </p>
	 * 
	 * @exception SQLException
	 *                if a database access error occurs
	 * @see setAutoCommit
	 */
	public synchronized void commit() throws SQLException {
		checkClosed();

		try {
			if (this.connectionLifecycleInterceptors != null) {
				IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {

					void forEach(Object each) throws SQLException {
						if (!((ConnectionLifecycleInterceptor)each).commit()) {
							this.stopIterating = true;
						}
					}
				};
				
				iter.doForAll();
				
				if (!iter.fullIteration()) {
					return;
				}
			}
			
			// no-op if _relaxAutoCommit == true
			if (this.autoCommit && !getRelaxAutoCommit()) {
				throw SQLError.createSQLException("Can't call commit when autocommit=true", getExceptionInterceptor());
			} else if (this.transactionsSupported) {
				if (getUseLocalTransactionState() && versionMeetsMinimum(5, 0, 0)) {
					if (!this.io.inTransactionOnServer()) {
						return; // effectively a no-op
					}
				}

				execSQL(null, "commit", -1, null,
						DEFAULT_RESULT_SET_TYPE,
						DEFAULT_RESULT_SET_CONCURRENCY, false,
						this.database, null,
						false);
			}
		} catch (SQLException sqlException) {
			if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
					.equals(sqlException.getSQLState())) {
				throw SQLError
						.createSQLException(
								"Communications link failure during commit(). Transaction resolution unknown.",
								SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN,
								getExceptionInterceptor());
			}

			throw sqlException;
		} finally {
			this.needsPing = this.getReconnectAtTxEnd();
		}

		return;
	}
	
	/**
	 * Configures client-side properties for character set information.
	 * 
	 * @throws SQLException
	 *             if unable to configure the specified character set.
	 */
	private void configureCharsetProperties() throws SQLException {
		if (getEncoding() != null) {
			// Attempt to use the encoding, and bail out if it
			// can't be used
			try {
				String testString = "abc";
				StringUtils.getBytes(testString, getEncoding());
			} catch (UnsupportedEncodingException UE) {
				// Try the MySQL character encoding, then....
				String oldEncoding = getEncoding();

				setEncoding(CharsetMapping.getJavaEncodingForMysqlEncoding(
						oldEncoding, this));

				if (getEncoding() == null) {
					throw SQLError.createSQLException(
							"Java does not support the MySQL character encoding "
									+ " " + "encoding '" + oldEncoding + "'.",
							SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
				}

				try {
					String testString = "abc";
					StringUtils.getBytes(testString, getEncoding());
				} catch (UnsupportedEncodingException encodingEx) {
					throw SQLError.createSQLException("Unsupported character "
							+ "encoding '" + getEncoding() + "'.",
							SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
				}
			}
		}
	}

	/**
	 * Sets up client character set for MySQL-4.1 and newer if the user This
	 * must be done before any further communication with the server!
	 * 
	 * @return true if this routine actually configured the client character
	 *         set, or false if the driver needs to use 'older' methods to
	 *         detect the character set, as it is connected to a MySQL server
	 *         older than 4.1.0
	 * @throws SQLException
	 *             if an exception happens while sending 'SET NAMES' to the
	 *             server, or the server sends character set information that
	 *             the client doesn't know about.
	 */
	private boolean configureClientCharacterSet(boolean dontCheckServerMatch) throws SQLException {
		String realJavaEncoding = getEncoding();
		boolean characterSetAlreadyConfigured = false;

		try {
			if (versionMeetsMinimum(4, 1, 0)) {
				characterSetAlreadyConfigured = true;

				setUseUnicode(true);

				configureCharsetProperties();
				realJavaEncoding = getEncoding(); // we need to do this again
				// to grab this for
				// versions > 4.1.0

				try {

					// Fault injection for testing server character set indices
		            
		            if (props != null && props.getProperty("com.mysql.jdbc.faultInjection.serverCharsetIndex") != null) {
		            	this.io.serverCharsetIndex = Integer.parseInt(
		            			props.getProperty(
		            					"com.mysql.jdbc.faultInjection.serverCharsetIndex"));	
		            }
		            
					String serverEncodingToSet = 
						CharsetMapping.INDEX_TO_CHARSET[this.io.serverCharsetIndex];
					
					if (serverEncodingToSet == null || serverEncodingToSet.length() == 0) {
						if (realJavaEncoding != null) {
							// user knows best, try it
							setEncoding(realJavaEncoding);
						} else {
							throw SQLError.createSQLException(
									"Unknown initial character set index '"
											+ this.io.serverCharsetIndex
											+ "' received from server. Initial client character set can be forced via the 'characterEncoding' property.",
									SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
						}
					}
					
					// "latin1" on MySQL-4.1.0+ is actually CP1252, not ISO8859_1
					if (versionMeetsMinimum(4, 1, 0) && 
							"ISO8859_1".equalsIgnoreCase(serverEncodingToSet)) {
						serverEncodingToSet = "Cp1252";
					}
					
					setEncoding(serverEncodingToSet);
				
				} catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
					if (realJavaEncoding != null) {
						// user knows best, try it
						setEncoding(realJavaEncoding);
					} else {
						throw SQLError.createSQLException(
								"Unknown initial character set index '"
										+ this.io.serverCharsetIndex
										+ "' received from server. Initial client character set can be forced via the 'characterEncoding' property.",
								SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
					}
				}

				if (getEncoding() == null) {
					// punt?
					setEncoding("ISO8859_1");
				}

				//
				// Has the user has 'forced' the character encoding via
				// driver properties?
				//
				if (getUseUnicode()) {
					if (realJavaEncoding != null) {

						//
						// Now, inform the server what character set we
						// will be using from now-on...
						//
						if (realJavaEncoding.equalsIgnoreCase("UTF-8")
								|| realJavaEncoding.equalsIgnoreCase("UTF8")) {
							// charset names are case-sensitive

							boolean utf8mb4Supported = versionMeetsMinimum(5, 5, 2);
							boolean useutf8mb4 = false;
							
							if (utf8mb4Supported) {
								useutf8mb4 = (this.io.serverCharsetIndex == 45);
							}
							
							if (!getUseOldUTF8Behavior()) {
								if (dontCheckServerMatch || !characterSetNamesMatches("utf8") 
										|| (utf8mb4Supported && !characterSetNamesMatches("utf8mb4"))) {
									execSQL(null, "SET NAMES " + (useutf8mb4 ? "utf8mb4" : "utf8"), -1, null,
											DEFAULT_RESULT_SET_TYPE,
											DEFAULT_RESULT_SET_CONCURRENCY,
											false, this.database, null, false);
								}
							} else {
								execSQL(null, "SET NAMES latin1", -1, null,
										DEFAULT_RESULT_SET_TYPE,
										DEFAULT_RESULT_SET_CONCURRENCY,
										false, this.database, null, false);
							}

							setEncoding(realJavaEncoding);
						} /* not utf-8 */else {
							String mysqlEncodingName = CharsetMapping
									.getMysqlEncodingForJavaEncoding(
											realJavaEncoding
													.toUpperCase(Locale.ENGLISH),
											this);

							/*
							 * if ("koi8_ru".equals(mysqlEncodingName)) { //
							 * This has a _different_ name in 4.1...
							 * mysqlEncodingName = "ko18r"; } else if
							 * ("euc_kr".equals(mysqlEncodingName)) { //
							 * Different name in 4.1 mysqlEncodingName =
							 * "euckr"; }
							 */

							if (mysqlEncodingName != null) {
								
								if (dontCheckServerMatch || !characterSetNamesMatches(mysqlEncodingName)) {
									execSQL(null, "SET NAMES " + mysqlEncodingName,
										-1, null,
										DEFAULT_RESULT_SET_TYPE,
										DEFAULT_RESULT_SET_CONCURRENCY,
										false, this.database, null, false);
								}
							}

							// Switch driver's encoding now, since the server
							// knows what we're sending...
							//
							setEncoding(realJavaEncoding);
						}
					} else if (getEncoding() != null) {
						// Tell the server we'll use the server default charset
						// to send our
						// queries from now on....
						String mysqlEncodingName = CharsetMapping
								.getMysqlEncodingForJavaEncoding(getEncoding()
										.toUpperCase(Locale.ENGLISH), this);
						
						if(getUseOldUTF8Behavior()){
							mysqlEncodingName = "latin1";
						}

						if (dontCheckServerMatch || !characterSetNamesMatches(mysqlEncodingName)) {
							execSQL(null, "SET NAMES " + mysqlEncodingName, -1,
								null, DEFAULT_RESULT_SET_TYPE,
								DEFAULT_RESULT_SET_CONCURRENCY, false,
								this.database, null, false);
						}

						realJavaEncoding = getEncoding();
					}

				}

				//
				// We know how to deal with any charset coming back from
				// the database, so tell the server not to do conversion
				// if the user hasn't 'forced' a result-set character set
				//

				String onServer = null;
				boolean isNullOnServer = false;
				
				if (this.serverVariables != null) {
					onServer = (String)this.serverVariables.get("character_set_results");
					
					isNullOnServer = onServer == null || "NULL".equalsIgnoreCase(onServer) || onServer.length() == 0;
				}
				
				if (getCharacterSetResults() == null) {
					
					//
					// Only send if needed, if we're caching server variables
					// we -have- to send, because we don't know what it was
					// before we cached them.
					//
					if (!isNullOnServer) {
						execSQL(null, "SET character_set_results = NULL", -1, null,
								DEFAULT_RESULT_SET_TYPE,
								DEFAULT_RESULT_SET_CONCURRENCY, false,
								this.database, null, 
								false);
						if (!this.usingCachedConfig) {
							this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, null);
						}
					} else {
						if (!this.usingCachedConfig) {
							this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer);
						}
					}
				} else {

					if(getUseOldUTF8Behavior()){
						execSQL(null, "SET NAMES " + "latin1", -1,
							null, DEFAULT_RESULT_SET_TYPE,
							DEFAULT_RESULT_SET_CONCURRENCY, false,
							this.database, null, false);
					}
					String charsetResults = getCharacterSetResults();
					String mysqlEncodingName = null;

					if ("UTF-8".equalsIgnoreCase(charsetResults)
							|| "UTF8".equalsIgnoreCase(charsetResults)) {
						mysqlEncodingName = "utf8";
					} else {
						mysqlEncodingName = CharsetMapping
								.getMysqlEncodingForJavaEncoding(charsetResults
										.toUpperCase(Locale.ENGLISH), this);
					}

					//
					// Only change the value if needed
					//
					
					if (!mysqlEncodingName.equalsIgnoreCase(
							(String)this.serverVariables.get("character_set_results"))) {
						StringBuffer setBuf = new StringBuffer(
								"SET character_set_results = ".length()
										+ mysqlEncodingName.length());
						setBuf.append("SET character_set_results = ").append(
								mysqlEncodingName);
	
						execSQL(null, setBuf.toString(), -1, null,
								DEFAULT_RESULT_SET_TYPE,
								DEFAULT_RESULT_SET_CONCURRENCY, false,
								this.database, null, false);
						
						if (!this.usingCachedConfig) {
							this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, 
								mysqlEncodingName);
						}
					} else {
						if (!this.usingCachedConfig) {
							this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer);
						}
					}
				}

				if (getConnectionCollation() != null) {
					StringBuffer setBuf = new StringBuffer(
							"SET collation_connection = ".length()
									+ getConnectionCollation().length());
					setBuf.append("SET collation_connection = ").append(
							getConnectionCollation());

					execSQL(null, setBuf.toString(), -1, null,
							DEFAULT_RESULT_SET_TYPE,
							DEFAULT_RESULT_SET_CONCURRENCY, false,
							this.database, null, false);
				}
			} else {
				// Use what the server has specified
				realJavaEncoding = getEncoding(); // so we don't get
				// swapped out in the finally
				// block....
			}
		} finally {
			// Failsafe, make sure that the driver's notion of character
			// encoding matches what the user has specified.
			//
			setEncoding(realJavaEncoding);
		}
		
		/**
		 * Check if we need a CharsetEncoder for escaping codepoints that are
		 * transformed to backslash (0x5c) in the connection encoding.
		 */
		try {
			CharsetEncoder enc = Charset.forName(getEncoding()).newEncoder();
			CharBuffer cbuf = CharBuffer.allocate(1);
			ByteBuffer bbuf = ByteBuffer.allocate(1);

			cbuf.put("\u00a5");
			cbuf.position(0);
			enc.encode(cbuf, bbuf, true);
			if(bbuf.get(0) == '\\') {
				requiresEscapingEncoder = true;
			} else {
				cbuf.clear();
				bbuf.clear();
				
				cbuf.put("\u20a9");
				cbuf.position(0);
				enc.encode(cbuf, bbuf, true);
				if(bbuf.get(0) == '\\') {
					requiresEscapingEncoder = true;
				}
			}
		} catch(java.nio.charset.UnsupportedCharsetException ucex) {
			// fallback to String API - for Java 1.4
			try {
				byte bbuf[] = StringUtils.getBytes("\u00a5", getEncoding());
				if (bbuf[0] == '\\') {
					requiresEscapingEncoder = true;
				} else {
					bbuf = StringUtils.getBytes("\u20a9", getEncoding());
					if (bbuf[0] == '\\') {
						requiresEscapingEncoder = true;
					}
				}
			} catch(UnsupportedEncodingException ueex) {
				throw SQLError.createSQLException("Unable to use encoding: " + getEncoding(),
						SQLError.SQL_STATE_GENERAL_ERROR, ueex,
						getExceptionInterceptor());
			}
		}

		return characterSetAlreadyConfigured;
	}

	/**
	 * Configures the client's timezone if required.
	 * 
	 * @throws SQLException
	 *             if the timezone the server is configured to use can't be
	 *             mapped to a Java timezone.
	 */
	private void configureTimezone() throws SQLException {
		String configuredTimeZoneOnServer = (String) this.serverVariables
				.get("timezone");

		if (configuredTimeZoneOnServer == null) {
			configuredTimeZoneOnServer = (String) this.serverVariables
					.get("time_zone");

			if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) {
				configuredTimeZoneOnServer = (String) this.serverVariables
						.get("system_time_zone");
			}
		}

		String canoncicalTimezone = getServerTimezone();
		
		if ((getUseTimezone() || !getUseLegacyDatetimeCode()) && configuredTimeZoneOnServer != null) {
			// user can override this with driver properties, so don't detect if that's the case
			if (canoncicalTimezone == null || StringUtils.isEmptyOrWhitespaceOnly(canoncicalTimezone)) {
				try {
					canoncicalTimezone = TimeUtil
							.getCanoncialTimezone(configuredTimeZoneOnServer, getExceptionInterceptor());

					if (canoncicalTimezone == null) {
						throw SQLError.createSQLException("Can't map timezone '"
								+ configuredTimeZoneOnServer + "' to "
								+ " canonical timezone.",
								SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
					}
				} catch (IllegalArgumentException iae) {
					throw SQLError.createSQLException(iae.getMessage(),
							SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
				}
			}
		} else {
			canoncicalTimezone = getServerTimezone();
		}
		
		if (canoncicalTimezone != null && canoncicalTimezone.length() > 0) {
				this.serverTimezoneTZ = TimeZone.getTimeZone(canoncicalTimezone);

			//
			// The Calendar class has the behavior of mapping
			// unknown timezones to 'GMT' instead of throwing an
			// exception, so we must check for this...
			//
			if (!canoncicalTimezone.equalsIgnoreCase("GMT")
					&& this.serverTimezoneTZ.getID().equals("GMT")) {
				throw SQLError.createSQLException("No timezone mapping entry for '"
						+ canoncicalTimezone + "'",
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
			}

			if ("GMT".equalsIgnoreCase(this.serverTimezoneTZ.getID())) {
				this.isServerTzUTC = true;
			} else {
				this.isServerTzUTC = false;
			}
		}
	}

	private void createInitialHistogram(long[] breakpoints,
			long lowerBound, long upperBound) {

		double bucketSize = (((double) upperBound - (double) lowerBound) / HISTOGRAM_BUCKETS) * 1.25;

		if (bucketSize < 1) {
			bucketSize = 1;
		}

		for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
			breakpoints[i] = lowerBound;
			lowerBound += bucketSize;
		}
	}

	/**
	 * Creates an IO channel to the server
	 * 
	 * @param isForReconnect
	 *            is this request for a re-connect
	 * @return a new MysqlIO instance connected to a server
	 * @throws SQLException
	 *             if a database access error occurs
	 * @throws CommunicationsException
	 *             DOCUMENT ME!
	 */
	public synchronized void createNewIO(boolean isForReconnect)
			throws SQLException {
		// Synchronization Not needed for *new* connections, but defintely for
		// connections going through fail-over, since we might get the
		// new connection up and running *enough* to start sending
		// cached or still-open server-side prepared statements over
		// to the backend before we get a chance to re-prepare them...
		

		Properties mergedProps  = exposeAsProperties(this.props);

		if (!getHighAvailability()) {
			connectOneTryOnly(isForReconnect, mergedProps);
			
			return;
		} 

		connectWithRetries(isForReconnect, mergedProps);
		
	}

	private void connectWithRetries(boolean isForReconnect,
			Properties mergedProps) throws SQLException {
		double timeout = getInitialTimeout();
		boolean connectionGood = false;

		Exception connectionException = null;

		for (int attemptCount = 0; (attemptCount < getMaxReconnects())
				&& !connectionGood; attemptCount++) {
			try {
				if (this.io != null) {
					this.io.forceClose();
				}

				coreConnect(mergedProps);
				pingInternal(false, 0);
				
				boolean oldAutoCommit;
				int oldIsolationLevel;
				boolean oldReadOnly;
				String oldCatalog;
				
				synchronized (this) {
					this.connectionId = this.io.getThreadId();
					this.isClosed = false;

					// save state from old connection
					oldAutoCommit = getAutoCommit();
					oldIsolationLevel = this.isolationLevel;
					oldReadOnly = isReadOnly();
					oldCatalog = getCatalog();
	
					this.io.setStatementInterceptors(this.statementInterceptors);
				}
				
				// Server properties might be different
				// from previous connection, so initialize
				// again...
				initializePropsFromServer();

				if (isForReconnect) {
					// Restore state from old connection
					setAutoCommit(oldAutoCommit);

					if (this.hasIsolationLevels) {
						setTransactionIsolation(oldIsolationLevel);
					}

					setCatalog(oldCatalog);
					setReadOnly(oldReadOnly);
				}

				connectionGood = true;

				break;
			} catch (Exception EEE) {
				connectionException = EEE;
				connectionGood = false;
			}

				if (connectionGood) {
					break;
				}

				if (attemptCount > 0) {
					try {
						Thread.sleep((long) timeout * 1000);
					} catch (InterruptedException IE) {
						// ignore
					}
				}
			} // end attempts for a single host

		if (!connectionGood) {
			// We've really failed!
			SQLException chainedEx = SQLError.createSQLException(
					Messages.getString("Connection.UnableToConnectWithRetries",
							new Object[] {Integer.valueOf(getMaxReconnects())}),
					SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor());
			chainedEx.initCause(connectionException);
			
			throw chainedEx;
		}

		if (getParanoid() && !getHighAvailability()) {
			this.password = null;
			this.user = null;
		}

		if (isForReconnect) {
			//
			// Retrieve any 'lost' prepared statements if re-connecting
			//
			Iterator statementIter = this.openStatements.values()
					.iterator();

			//
			// We build a list of these outside the map of open statements,
			// because
			// in the process of re-preparing, we might end up having to
			// close
			// a prepared statement, thus removing it from the map, and
			// generating
			// a ConcurrentModificationException
			//
			Stack serverPreparedStatements = null;

			while (statementIter.hasNext()) {
				Object statementObj = statementIter.next();

				if (statementObj instanceof ServerPreparedStatement) {
					if (serverPreparedStatements == null) {
						serverPreparedStatements = new Stack();
					}

					serverPreparedStatements.add(statementObj);
				}
			}

			if (serverPreparedStatements != null) {
				while (!serverPreparedStatements.isEmpty()) {
					((ServerPreparedStatement) serverPreparedStatements
							.pop()).rePrepare();
				}
			}
		}
	}

	private void coreConnect(Properties mergedProps) throws SQLException,
			IOException {
		int newPort = 3306;
		String newHost = "localhost";
		
		String protocol = mergedProps.getProperty(NonRegisteringDriver.PROTOCOL_PROPERTY_KEY);
		
		if (protocol != null) {
			// "new" style URL

			if ("tcp".equalsIgnoreCase(protocol)) {
				newHost = normalizeHost(mergedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY));
				newPort = parsePortNumber(mergedProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"));
			} else if ("pipe".equalsIgnoreCase(protocol)) {
				setSocketFactoryClassName(NamedPipeSocketFactory.class.getName());
				
				String path = mergedProps.getProperty(NonRegisteringDriver.PATH_PROPERTY_KEY);
				
				if (path != null) {
					mergedProps.setProperty(NamedPipeSocketFactory.NAMED_PIPE_PROP_NAME, path);
				}
			} else {
				// normalize for all unknown protocols
				newHost = normalizeHost(mergedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY));
				newPort = parsePortNumber(mergedProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"));
			}
		} else {
		
			String[] parsedHostPortPair = NonRegisteringDriver
					.parseHostPortPair(this.hostPortPair);
			newHost = parsedHostPortPair[NonRegisteringDriver.HOST_NAME_INDEX];

			newHost = normalizeHost(newHost);

			if (parsedHostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX] != null) {
				newPort = parsePortNumber(parsedHostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]);
			}
		}

		this.port = newPort;
		this.host = newHost;
		
		this.io = new MysqlIO(newHost, newPort,
				mergedProps, getSocketFactoryClassName(),
				getProxy(), getSocketTimeout(),
				this.largeRowSizeThreshold.getValueAsInt());
		this.io.doHandshake(this.user, this.password,
				this.database);
	}

	private String normalizeHost(String host) {
		if (host == null || StringUtils.isEmptyOrWhitespaceOnly(host)) {
			return "localhost";
		}
		
		return host;
	}
	private int parsePortNumber(String portAsString)
			throws SQLException {
		int portNumber = 3306;
		try {
			portNumber = Integer
					.parseInt(portAsString);
		} catch (NumberFormatException nfe) {
			throw SQLError.createSQLException(
					"Illegal connection port value '"
							+ portAsString
							+ "'",
					SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
		}
		return portNumber;
	}

	private void connectOneTryOnly(boolean isForReconnect,
			Properties mergedProps) throws SQLException {
		Exception connectionNotEstablishedBecause = null;

		try {
			
			coreConnect(mergedProps);
			this.connectionId = this.io.getThreadId();
			this.isClosed = false;

			// save state from old connection
			boolean oldAutoCommit = getAutoCommit();
			int oldIsolationLevel = this.isolationLevel;
			boolean oldReadOnly = isReadOnly();
			String oldCatalog = getCatalog();

			this.io.setStatementInterceptors(this.statementInterceptors);
			
			// Server properties might be different
			// from previous connection, so initialize
			// again...
			initializePropsFromServer();

			if (isForReconnect) {
				// Restore state from old connection
				setAutoCommit(oldAutoCommit);

				if (this.hasIsolationLevels) {
					setTransactionIsolation(oldIsolationLevel);
				}

				setCatalog(oldCatalog);
				
				setReadOnly(oldReadOnly);
			}
			return;
			
		} catch (Exception EEE) {
			if (this.io != null) {
				this.io.forceClose();
			}

			connectionNotEstablishedBecause = EEE;

			if (EEE instanceof SQLException) {
				throw (SQLException)EEE;
			}
			
			SQLException chainedEx = SQLError.createSQLException(
					Messages.getString("Connection.UnableToConnect"),
					SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor());
			chainedEx.initCause(connectionNotEstablishedBecause);
			
			throw chainedEx;
		}
	}

	private synchronized void createPreparedStatementCaches() {
		int cacheSize = getPreparedStatementCacheSize();
		
		this.cachedPreparedStatementParams = new HashMap(cacheSize);
		
		if (getUseServerPreparedStmts()) {
			this.serverSideStatementCheckCache = new LRUCache(cacheSize);
			
			this.serverSideStatementCache = new LRUCache(cacheSize) {
				protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
					if (this.maxElements <= 1) {
						return false;
					}
					
					boolean removeIt = super.removeEldestEntry(eldest);
					
					if (removeIt) {
						ServerPreparedStatement ps = 
							(ServerPreparedStatement)eldest.getValue();
						ps.isCached = false;
						ps.setClosed(false);
						
						try {
							ps.close();
						} catch (SQLException sqlEx) {
							// punt
						}
					}
					
					return removeIt;
				}
			};
		}
	}

	/**
	 * SQL statements without parameters are normally executed using Statement
	 * objects. If the same SQL statement is executed many times, it is more
	 * efficient to use a PreparedStatement
	 * 
	 * @return a new Statement object
	 * @throws SQLException
	 *             passed through from the constructor
	 */
	public java.sql.Statement createStatement() throws SQLException {
		return createStatement(DEFAULT_RESULT_SET_TYPE,
				DEFAULT_RESULT_SET_CONCURRENCY);
	}

	/**
	 * JDBC 2.0 Same as createStatement() above, but allows the default result
	 * set type and result set concurrency type to be overridden.
	 * 
	 * @param resultSetType
	 *            a result set type, see ResultSet.TYPE_XXX
	 * @param resultSetConcurrency
	 *            a concurrency type, see ResultSet.CONCUR_XXX
	 * @return a new Statement object
	 * @exception SQLException
	 *                if a database-access error occurs.
	 */
	public java.sql.Statement createStatement(int resultSetType,
			int resultSetConcurrency) throws SQLException {
		checkClosed();

		StatementImpl stmt = new StatementImpl(getLoadBalanceSafeProxy(), this.database);
		stmt.setResultSetType(resultSetType);
		stmt.setResultSetConcurrency(resultSetConcurrency);

		return stmt;
	}

	/**
	 * @see Connection#createStatement(int, int, int)
	 */
	public java.sql.Statement createStatement(int resultSetType,
			int resultSetConcurrency, int resultSetHoldability)
			throws SQLException {
		if (getPedantic()) {
			if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
				throw SQLError.createSQLException(
						"HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
			}
		}

		return createStatement(resultSetType, resultSetConcurrency);
	}

	public void dumpTestcaseQuery(String query) {
		System.err.println(query);
	}

	public Connection duplicate() throws SQLException {
		return new ConnectionImpl(	this.origHostToConnectTo, 
				this.origPortToConnectTo,
				this.props,
				this.origDatabaseToConnectTo,
				this.myURL);
	}

	/**
	 * Send a query to the server. Returns one of the ResultSet objects. This is
	 * synchronized, so Statement's queries will be serialized.
	 * 
	 * @param callingStatement
	 *            DOCUMENT ME!
	 * @param sql
	 *            the SQL statement to be executed
	 * @param maxRows
	 *            DOCUMENT ME!
	 * @param packet
	 *            DOCUMENT ME!
	 * @param resultSetType
	 *            DOCUMENT ME!
	 * @param resultSetConcurrency
	 *            DOCUMENT ME!
	 * @param streamResults
	 *            DOCUMENT ME!
	 * @param queryIsSelectOnly
	 *            DOCUMENT ME!
	 * @param catalog
	 *            DOCUMENT ME!
	 * @param unpackFields
	 *            DOCUMENT ME!
	 * @return a ResultSet holding the results
	 * @exception SQLException
	 *                if a database error occurs
	 */

	// ResultSet execSQL(Statement callingStatement, String sql,
	// int maxRowsToRetreive, String catalog) throws SQLException {
	// return execSQL(callingStatement, sql, maxRowsToRetreive, null,
	// java.sql.ResultSet.TYPE_FORWARD_ONLY,
	// DEFAULT_RESULT_SET_CONCURRENCY, catalog);
	// }
	// ResultSet execSQL(Statement callingStatement, String sql, int maxRows,
	// int resultSetType, int resultSetConcurrency, boolean streamResults,
	// boolean queryIsSelectOnly, String catalog, boolean unpackFields) throws
	// SQLException {
	// return execSQL(callingStatement, sql, maxRows, null, resultSetType,
	// resultSetConcurrency, streamResults, queryIsSelectOnly, catalog,
	// unpackFields);
	// }
	public ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows,
			Buffer packet, int resultSetType, int resultSetConcurrency,
			boolean streamResults, String catalog,
			Field[] cachedMetadata) throws SQLException {
		return execSQL(callingStatement, sql, maxRows, packet, resultSetType,
				resultSetConcurrency, streamResults,
				catalog, cachedMetadata, false);
	}

	public synchronized ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows,
			Buffer packet, int resultSetType, int resultSetConcurrency,
			boolean streamResults, String catalog,
			Field[] cachedMetadata,
			boolean isBatch) throws SQLException {
		//
		// Fall-back if the master is back online if we've
		// issued queriesBeforeRetryMaster queries since
		// we failed over
		//

		long queryStartTime = 0;

		int endOfQueryPacketPosition = 0;

		if (packet != null) {
			endOfQueryPacketPosition = packet.getPosition();
		}

		if (getGatherPerformanceMetrics()) {
			queryStartTime = System.currentTimeMillis();
		}

		this.lastQueryFinishedTime = 0; // we're busy!

		if ((getHighAvailability())
				&& (this.autoCommit || getAutoReconnectForPools())
				&& this.needsPing && !isBatch) {
			try {
				pingInternal(false, 0);

				this.needsPing = false;
			} catch (Exception Ex) {
				createNewIO(true);
			}
		}

		try {
			if (packet == null) {
				String encoding = null;

				if (getUseUnicode()) {
					encoding = getEncoding();
				}

				return this.io.sqlQueryDirect(callingStatement, sql,
						encoding, null, maxRows, resultSetType,
						resultSetConcurrency, streamResults, catalog,
						cachedMetadata);
			}

			return this.io.sqlQueryDirect(callingStatement, null, null,
					packet, maxRows, resultSetType,
					resultSetConcurrency, streamResults, catalog,
					cachedMetadata);
		} catch (java.sql.SQLException sqlE) {
			// don't clobber SQL exceptions

			if (getDumpQueriesOnException()) {
				String extractedSql = extractSqlFromPacket(sql, packet,
						endOfQueryPacketPosition);
				StringBuffer messageBuf = new StringBuffer(extractedSql
						.length() + 32);
				messageBuf
						.append("\n\nQuery being executed when exception was thrown:\n");
				messageBuf.append(extractedSql);
				messageBuf.append("\n\n");

				sqlE = appendMessageToException(sqlE, messageBuf.toString(), getExceptionInterceptor());
			}

			if ((getHighAvailability())) {
				this.needsPing = true;
			} else {
				String sqlState = sqlE.getSQLState();

				if ((sqlState != null)
						&& sqlState
								.equals(SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE)) {
					cleanup(sqlE);
				}
			}

			throw sqlE;
		} catch (Exception ex) {
			if (getHighAvailability()) {
				this.needsPing = true;
			} else if (ex instanceof IOException) {
				cleanup(ex);
			}

			SQLException sqlEx = SQLError.createSQLException(
					Messages.getString("Connection.UnexpectedException"),
					SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
			sqlEx.initCause(ex);
			
			throw sqlEx;
		} finally {
			if (getMaintainTimeStats()) {
				this.lastQueryFinishedTime = System.currentTimeMillis();
			}


			if (getGatherPerformanceMetrics()) {
				long queryTime = System.currentTimeMillis()
						- queryStartTime;

				registerQueryExecutionTime(queryTime);
			}
		}
	}

	public String extractSqlFromPacket(String possibleSqlQuery,
			Buffer queryPacket, int endOfQueryPacketPosition)
			throws SQLException {

		String extractedSql = null;

		if (possibleSqlQuery != null) {
			if (possibleSqlQuery.length() > getMaxQuerySizeToLog()) {
				StringBuffer truncatedQueryBuf = new StringBuffer(
						possibleSqlQuery.substring(0, getMaxQuerySizeToLog()));
				truncatedQueryBuf.append(Messages.getString("MysqlIO.25"));
				extractedSql = truncatedQueryBuf.toString();
			} else {
				extractedSql = possibleSqlQuery;
			}
		}

		if (extractedSql == null) {
			// This is probably from a client-side prepared
			// statement

			int extractPosition = endOfQueryPacketPosition;

			boolean truncated = false;

			if (endOfQueryPacketPosition > getMaxQuerySizeToLog()) {
				extractPosition = getMaxQuerySizeToLog();
				truncated = true;
			}

			extractedSql = StringUtils.toString(queryPacket.getByteBuffer(), 5,
					(extractPosition - 5));

			if (truncated) {
				extractedSql += Messages.getString("MysqlIO.25"); //$NON-NLS-1$
			}
		}

		return extractedSql;

	}

	protected void finalize() throws Throwable {
		cleanup(null);
		
		super.finalize();
	}

	public StringBuffer generateConnectionCommentBlock(StringBuffer buf) {
		buf.append("/* conn id ");
		buf.append(getId());
		buf.append(" clock: ");
		buf.append(System.currentTimeMillis());
		buf.append(" */ ");

		return buf;
	}

	public int getActiveStatementCount() {
		// Might not have one of these if
		// not tracking open resources
		if (this.openStatements != null) {
			synchronized (this.openStatements) {
				return this.openStatements.size();
			}
		}

		return 0;
	}

	/**
	 * Gets the current auto-commit state
	 * 
	 * @return Current state of auto-commit
	 * @exception SQLException
	 *                if an error occurs
	 * @see setAutoCommit
	 */
	public synchronized boolean getAutoCommit() throws SQLException {
		return this.autoCommit;
	}

	/**
	 * Optimization to only use one calendar per-session, or calculate it for
	 * each call, depending on user configuration
	 */
	public Calendar getCalendarInstanceForSessionOrNew() {
		if (getDynamicCalendars()) {
			return Calendar.getInstance();
		}

		return getSessionLockedCalendar();
	}

	/**
	 * Return the connections current catalog name, or null if no catalog name
	 * is set, or we dont support catalogs.
	 * <p>
	 * <b>Note:</b> MySQL's notion of catalogs are individual databases.
	 * </p>
	 * 
	 * @return the current catalog name or null
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	public synchronized String getCatalog() throws SQLException {
		return this.database;
	}

	/**
	 * @return Returns the characterSetMetadata.
	 */
	public synchronized String getCharacterSetMetadata() {
		return this.characterSetMetadata;
	}

	/**
	 * Returns the locally mapped instance of a charset converter (to avoid
	 * overhead of static synchronization).
	 * 
	 * @param javaEncodingName
	 *            the encoding name to retrieve
	 * @return a character converter, or null if one couldn't be mapped.
	 */
	public SingleByteCharsetConverter getCharsetConverter(
			String javaEncodingName) throws SQLException {
		if (javaEncodingName == null) {
			return null;
		}

		if (this.usePlatformCharsetConverters) {
			return null; // we'll use Java's built-in routines for this
			             // they're finally fast enough
		}
		
		SingleByteCharsetConverter converter = null;
		
		synchronized (this.charsetConverterMap) {
			Object asObject = this.charsetConverterMap
			.get(javaEncodingName);

			if (asObject == CHARSET_CONVERTER_NOT_AVAILABLE_MARKER) {
				return null;
			}
			
			converter = (SingleByteCharsetConverter)asObject;
			
			if (converter == null) {
				try {
					converter = SingleByteCharsetConverter.getInstance(
							javaEncodingName, this);

					if (converter == null) {
						this.charsetConverterMap.put(javaEncodingName,
								CHARSET_CONVERTER_NOT_AVAILABLE_MARKER);
					} else {
						this.charsetConverterMap.put(javaEncodingName, converter);
					}
				} catch (UnsupportedEncodingException unsupEncEx) {
					this.charsetConverterMap.put(javaEncodingName,
							CHARSET_CONVERTER_NOT_AVAILABLE_MARKER);

					converter = null;
				}
			}
		}

		return converter;
	}

	/**
	 * Returns the Java character encoding name for the given MySQL server
	 * charset index
	 * 
	 * @param charsetIndex
	 * @return the Java character encoding name for the given MySQL server
	 *         charset index
	 * @throws SQLException
	 *             if the character set index isn't known by the driver
	 */
	public String getCharsetNameForIndex(int charsetIndex)
			throws SQLException {
		String charsetName = null;

		if (getUseOldUTF8Behavior()) {
			return getEncoding();
		}

		if (charsetIndex != MysqlDefs.NO_CHARSET_INFO) {
			try {
				charsetName = this.indexToCharsetMapping[charsetIndex];

				if ("sjis".equalsIgnoreCase(charsetName) || 
						"MS932".equalsIgnoreCase(charsetName) /* for JDK6 */) {
					// Use our encoding so that code pages like Cp932 work
					if (CharsetMapping.isAliasForSjis(getEncoding())) {
						charsetName = getEncoding();
					}
				}
			} catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
				throw SQLError.createSQLException(
						"Unknown character set index for field '"
								+ charsetIndex + "' received from server.",
						SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
			}

			// Punt
			if (charsetName == null) {
				charsetName = getEncoding();
			}
		} else {
			charsetName = getEncoding();
		}

		return charsetName;
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @return Returns the defaultTimeZone.
	 */
	public TimeZone getDefaultTimeZone() {
		return this.defaultTimeZone;
	}

	public String getErrorMessageEncoding() {
		return errorMessageEncoding;
	}

	/**
	 * @see Connection#getHoldability()
	 */
	public int getHoldability() throws SQLException {
		return java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT;
	}

	public long getId() {
		return this.connectionId;
	}

	/**
	 * NOT JDBC-Compliant, but clients can use this method to determine how long
	 * this connection has been idle. This time (reported in milliseconds) is
	 * updated once a query has completed.
	 * 
	 * @return number of ms that this connection has been idle, 0 if the driver
	 *         is busy retrieving results.
	 */
	public synchronized long getIdleFor() {
		if (this.lastQueryFinishedTime == 0) {
			return 0;
		}

		long now = System.currentTimeMillis();
		long idleTime = now - this.lastQueryFinishedTime;

		return idleTime;
	}

	/**
	 * Returns the IO channel to the server
	 * 
	 * @return the IO channel to the server
	 * @throws SQLException
	 *             if the connection is closed.
	 */
	public MysqlIO getIO() throws SQLException {
		if ((this.io == null) || this.isClosed) {
			throw SQLError.createSQLException(
					"Operation not allowed on closed connection",
					SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor());
		}

		return this.io;
	}

	/**
	 * Returns the log mechanism that should be used to log information from/for
	 * this Connection.
	 * 
	 * @return the Log instance to use for logging messages.
	 * @throws SQLException
	 *             if an error occurs
	 */
	public Log getLog() throws SQLException {
		return this.log;
	}

	public int getMaxBytesPerChar(String javaCharsetName)
	throws SQLException {
		// TODO: Check if we can actually run this query at this point in time
		String charset = CharsetMapping.getMysqlEncodingForJavaEncoding(
				javaCharsetName, this);
		
		if ((this.io.serverCharsetIndex == 33) && (versionMeetsMinimum(5, 5, 3)) && (javaCharsetName.equalsIgnoreCase("UTF-8"))) {
			//Avoid UTF8mb4
			charset = "utf8";
		}

		if (versionMeetsMinimum(4, 1, 0)) {
			Map mapToCheck = null;
			
			if (!getUseDynamicCharsetInfo()) {
				mapToCheck = CharsetMapping.STATIC_CHARSET_TO_NUM_BYTES_MAP;
			} else {
				mapToCheck = this.charsetToNumBytesMap;
			
				synchronized (this.charsetToNumBytesMap) {
					if (this.charsetToNumBytesMap.isEmpty()) {
						
						java.sql.Statement stmt = null;
						java.sql.ResultSet rs = null;
		
						try {
							stmt = getMetadataSafeStatement();
		
							rs = stmt.executeQuery("SHOW CHARACTER SET");
		
							while (rs.next()) {
								this.charsetToNumBytesMap.put(rs.getString("Charset"),
										Integer.valueOf(rs.getInt("Maxlen")));
							}
		
							rs.close();
							rs = null;
		
							stmt.close();
		
							stmt = null;
						} finally {
							if (rs != null) {
								rs.close();
								rs = null;
							}
		
							if (stmt != null) {
								stmt.close();
								stmt = null;
							}
						}
					}
				}
			}
		
			//Pin down the case when user with 5.5.3 server actually uses 3 byte UTF8
			//We have already overridden users preference by mapping UTF8 to UTF8mb4
			
			Integer mbPerChar = (Integer) mapToCheck.get(charset);
		
			if (mbPerChar != null) {
				return mbPerChar.intValue();
			}
		
			return 1; // we don't know
		}
		
		return 1; // we don't know
	}

	/**
	 * A connection's database is able to provide information describing its
	 * tables, its supported SQL grammar, its stored procedures, the
	 * capabilities of this connection, etc. This information is made available
	 * through a DatabaseMetaData object.
	 * 
	 * @return a DatabaseMetaData object for this connection
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	public java.sql.DatabaseMetaData getMetaData() throws SQLException {
		return getMetaData(true, true);
	}
	
	private java.sql.DatabaseMetaData getMetaData(boolean checkClosed, boolean checkForInfoSchema) throws SQLException {
		if (checkClosed) {
			checkClosed();	
		}
		
		return com.mysql.jdbc.DatabaseMetaData.getInstance(getLoadBalanceSafeProxy(), this.database, checkForInfoSchema);
	}

	public java.sql.Statement getMetadataSafeStatement() throws SQLException {
		java.sql.Statement stmt = createStatement();

		if (stmt.getMaxRows() != 0) {
			stmt.setMaxRows(0);
		}

		stmt.setEscapeProcessing(false);
		
		if (stmt.getFetchSize() != 0) {
			stmt.setFetchSize(0);
		}

		return stmt;
	}

	/**
	 * Returns the packet buffer size the MySQL server reported upon connection
	 * 
	 * @return DOCUMENT ME!
	 */
	public int getNetBufferLength() {
		return this.netBufferLength;
	}

	/**
	 * Returns the server's character set
	 * 
	 * @return the server's character set.
	 */
	public String getServerCharacterEncoding() {
		if (this.io.versionMeetsMinimum(4, 1, 0)) {
			if (this.io.serverCharsetIndex < 65535) {
				return CharsetMapping.MYSQL_INDEX_TO_MYSQL_CHARSET[this.io.serverCharsetIndex];
			} else {
				return (String) this.serverVariables.get("character_set_server"); 				
			}
		} else {
			return (String) this.serverVariables.get("character_set");
		}
	}

	public int getServerMajorVersion() {
		return this.io.getServerMajorVersion();
	}

	public int getServerMinorVersion() {
		return this.io.getServerMinorVersion();
	}

	public int getServerSubMinorVersion() {
		return this.io.getServerSubMinorVersion();
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	public TimeZone getServerTimezoneTZ() {
		return this.serverTimezoneTZ;
	}
	
	
	public String getServerVariable(String variableName) {
		if (this.serverVariables != null) {
			return (String) this.serverVariables.get(variableName);
		}

		return null;
	}

	public String getServerVersion() {
		return this.io.getServerVersion();
	}

	public Calendar getSessionLockedCalendar() {
	
		return this.sessionCalendar;
	}

	/**
	 * Get this Connection's current transaction isolation mode.
	 * 
	 * @return the current TRANSACTION_ mode value
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	public synchronized int getTransactionIsolation() throws SQLException {

		if (this.hasIsolationLevels && !getUseLocalSessionState()) {
			java.sql.Statement stmt = null;
			java.sql.ResultSet rs = null;

			try {
				stmt = getMetadataSafeStatement();

				String query = null;

				int offset = 0;
				
				if (versionMeetsMinimum(4, 0, 3)) {
					query = "SELECT @@session.tx_isolation";
					offset = 1;
				} else {
					query = "SHOW VARIABLES LIKE 'transaction_isolation'";
					offset = 2;
				}

				rs = stmt.executeQuery(query);

				if (rs.next()) {
					String s = rs.getString(offset);

					if (s != null) {
						Integer intTI = (Integer) mapTransIsolationNameToValue
								.get(s);

						if (intTI != null) {
							return intTI.intValue();
						}
					}

					throw SQLError.createSQLException(
							"Could not map transaction isolation '" + s
									+ " to a valid JDBC level.",
							SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
				}

				throw SQLError.createSQLException(
						"Could not retrieve transaction isolation level from server",
						SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());

			} finally {
				if (rs != null) {
					try {
						rs.close();
					} catch (Exception ex) {
						// ignore
						;
					}

					rs = null;
				}

				if (stmt != null) {
					try {
						stmt.close();
					} catch (Exception ex) {
						// ignore
						;
					}

					stmt = null;
				}
			}
		}

		return this.isolationLevel;
	}

	/**
	 * JDBC 2.0 Get the type-map object associated with this connection. By
	 * default, the map returned is empty.
	 * 
	 * @return the type map
	 * @throws SQLException
	 *             if a database error occurs
	 */
	public synchronized java.util.Map getTypeMap() throws SQLException {
		if (this.typeMap == null) {
			this.typeMap = new HashMap();
		}

		return this.typeMap;
	}

	public String getURL() {
		return this.myURL;
	}

	public String getUser() {
		return this.user;
	}

	public Calendar getUtcCalendar() {
		return this.utcCalendar;
	}
	
	/**
	 * The first warning reported by calls on this Connection is returned.
	 * <B>Note:</B> Sebsequent warnings will be changed to this
	 * java.sql.SQLWarning
	 * 
	 * @return the first java.sql.SQLWarning or null
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	public SQLWarning getWarnings() throws SQLException {
		return null;
	}

	public boolean hasSameProperties(Connection c) {
		return this.props.equals(c.getProperties());
	}

	public Properties getProperties() {
		return this.props;
	}
	
	public boolean hasTriedMaster() {
		return this.hasTriedMasterFlag;
	}

	public void incrementNumberOfPreparedExecutes() {
		if (getGatherPerformanceMetrics()) {
			this.numberOfPreparedExecutes++;

			// We need to increment this, because
			// server-side prepared statements bypass
			// any execution by the connection itself...
			this.numberOfQueriesIssued++;
		}
	}

	public void incrementNumberOfPrepares() {
		if (getGatherPerformanceMetrics()) {
			this.numberOfPrepares++;
		}
	}

	public void incrementNumberOfResultSetsCreated() {
		if (getGatherPerformanceMetrics()) {
			this.numberOfResultSetsCreated++;
		}
	}

	/**
	 * Initializes driver properties that come from URL or properties passed to
	 * the driver manager.
	 * 
	 * @param info
	 *            DOCUMENT ME!
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	private void initializeDriverProperties(Properties info)
			throws SQLException {
		initializeProperties(info);
		
		String exceptionInterceptorClasses = getExceptionInterceptors();
		
		if (exceptionInterceptorClasses != null && !"".equals(exceptionInterceptorClasses)) {
			this.exceptionInterceptor = new ExceptionInterceptorChain(exceptionInterceptorClasses);
			this.exceptionInterceptor.init(this, info);
		}
		
		this.usePlatformCharsetConverters = getUseJvmCharsetConverters();

		this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME, getExceptionInterceptor());

		if (getProfileSql() || getUseUsageAdvisor()) {
			this.eventSink = ProfilerEventHandlerFactory.getInstance(getLoadBalanceSafeProxy());
		}

		if (getCachePreparedStatements()) {
			createPreparedStatementCaches();		
		}

		if (getNoDatetimeStringSync() && getUseTimezone()) {
			throw SQLError.createSQLException(
					"Can't enable noDatetimeSync and useTimezone configuration "
							+ "properties at the same time",
					SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor());
		}
		
		if (getCacheCallableStatements()) {
			this.parsedCallableStatementCache = new LRUCache(
					getCallableStatementCacheSize());
		}
		
		if (getAllowMultiQueries()) {
			setCacheResultSetMetadata(false); // we don't handle this yet
		}
		
		if (getCacheResultSetMetadata()) {
			this.resultSetMetadataCache = new LRUCache(
					getMetadataCacheSize());
		}
	}

	/**
	 * Sets varying properties that depend on server information. Called once we
	 * have connected to the server.
	 * 
	 * @param info
	 *            DOCUMENT ME!
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	private void initializePropsFromServer() throws SQLException {
		String connectionInterceptorClasses = getConnectionLifecycleInterceptors();
		
		this.connectionLifecycleInterceptors = null;
		
		if (connectionInterceptorClasses != null) {
			this.connectionLifecycleInterceptors = Util.loadExtensions(this, this.props, 
					connectionInterceptorClasses, 
					"Connection.badLifecycleInterceptor", getExceptionInterceptor());
		}
		
		setSessionVariables();

		//
		// the "boolean" type didn't come along until MySQL-4.1
		//

		if (!versionMeetsMinimum(4, 1, 0)) {
			setTransformedBitIsBoolean(false);
		}

		this.parserKnowsUnicode = versionMeetsMinimum(4, 1, 0);

		//
		// Users can turn off detection of server-side prepared statements
		//
		if (getUseServerPreparedStmts() && versionMeetsMinimum(4, 1, 0)) {
			this.useServerPreparedStmts = true;

			if (versionMeetsMinimum(5, 0, 0) && !versionMeetsMinimum(5, 0, 3)) {
				this.useServerPreparedStmts = false; // 4.1.2+ style prepared
				// statements
				// don't work on these versions
			}
		}
		
		//
		// If version is greater than 3.21.22 get the server
		// variables.
		if (versionMeetsMinimum(3, 21, 22)) {
			loadServerVariables();

			if (versionMeetsMinimum(5, 0, 2)) {
				this.autoIncrementIncrement = getServerVariableAsInt("auto_increment_increment", 1);
			} else {
				this.autoIncrementIncrement = 1;
			}
			
			buildCollationMapping();

			LicenseConfiguration.checkLicenseType(this.serverVariables);

			String lowerCaseTables = (String) this.serverVariables
					.get("lower_case_table_names");

			this.lowerCaseTableNames = "on".equalsIgnoreCase(lowerCaseTables)
					|| "1".equalsIgnoreCase(lowerCaseTables)
					|| "2".equalsIgnoreCase(lowerCaseTables);

			this.storesLowerCaseTableName = "1".equalsIgnoreCase(lowerCaseTables) ||
					"on".equalsIgnoreCase(lowerCaseTables);

			configureTimezone();

			if (this.serverVariables.containsKey("max_allowed_packet")) {
				int serverMaxAllowedPacket = getServerVariableAsInt("max_allowed_packet", -1);
				// use server value if maxAllowedPacket hasn't been given, or max_allowed_packet is smaller
				if (serverMaxAllowedPacket != -1 && (serverMaxAllowedPacket < getMaxAllowedPacket() ||
						getMaxAllowedPacket() <= 0))
					setMaxAllowedPacket(serverMaxAllowedPacket);
				else if (serverMaxAllowedPacket == -1 && getMaxAllowedPacket() == -1)
					setMaxAllowedPacket(65535);
				
				int preferredBlobSendChunkSize = getBlobSendChunkSize();
				
				int allowedBlobSendChunkSize = Math.min(preferredBlobSendChunkSize, 
						getMaxAllowedPacket()) - 
						ServerPreparedStatement.BLOB_STREAM_READ_BUF_SIZE 
						- 11 /* LONG_DATA and MySQLIO packet header size */;
				
				setBlobSendChunkSize(String.valueOf(allowedBlobSendChunkSize));
			}

			if (this.serverVariables.containsKey("net_buffer_length")) {
				this.netBufferLength = getServerVariableAsInt("net_buffer_length", 16 * 1024);
			}

			checkTransactionIsolationLevel();
			
			if (!versionMeetsMinimum(4, 1, 0)) {
				checkServerEncoding();
			}

			this.io.checkForCharsetMismatch();

			if (this.serverVariables.containsKey("sql_mode")) {
				int sqlMode = 0;

				String sqlModeAsString = (String) this.serverVariables
						.get("sql_mode");
				try {
					sqlMode = Integer.parseInt(sqlModeAsString);
				} catch (NumberFormatException nfe) {
					// newer versions of the server has this as a string-y
					// list...
					sqlMode = 0;

					if (sqlModeAsString != null) {
						if (sqlModeAsString.indexOf("ANSI_QUOTES") != -1) {
							sqlMode |= 4;
						}

						if (sqlModeAsString.indexOf("NO_BACKSLASH_ESCAPES") != -1) {
							this.noBackslashEscapes = true;
						}
					}
				}

				if ((sqlMode & 4) > 0) {
					this.useAnsiQuotes = true;
				} else {
					this.useAnsiQuotes = false;
				}
			}
		}
		
		this.errorMessageEncoding = 
			CharsetMapping.getCharacterEncodingForErrorMessages(this);
		
		
		boolean overrideDefaultAutocommit = isAutoCommitNonDefaultOnServer();
	
		configureClientCharacterSet(false);

		if (versionMeetsMinimum(3, 23, 15)) {
			this.transactionsSupported = true;
			
			if (!overrideDefaultAutocommit) {
				setAutoCommit(true); // to override anything
				// the server is set to...reqd
				// by JDBC spec.
			}
		} else {
			this.transactionsSupported = false;
		}
		

		if (versionMeetsMinimum(3, 23, 36)) {
			this.hasIsolationLevels = true;
		} else {
			this.hasIsolationLevels = false;
		}

		this.hasQuotedIdentifiers = versionMeetsMinimum(3, 23, 6);

		this.io.resetMaxBuf();

		//
		// If we're using MySQL 4.1.0 or newer, we need to figure
		// out what character set metadata will be returned in,
		// and then map that to a Java encoding name.
		//
		// We've already set it, and it might be different than what
		// was originally on the server, which is why we use the
		// "special" key to retrieve it
		if (this.io.versionMeetsMinimum(4, 1, 0)) {
			String characterSetResultsOnServerMysql = (String) this.serverVariables
					.get(JDBC_LOCAL_CHARACTER_SET_RESULTS);

			if (characterSetResultsOnServerMysql == null
					|| StringUtils.startsWithIgnoreCaseAndWs(
							characterSetResultsOnServerMysql, "NULL")
					|| characterSetResultsOnServerMysql.length() == 0) {
				String defaultMetadataCharsetMysql = (String) this.serverVariables
						.get("character_set_system");
				String defaultMetadataCharset = null;

				if (defaultMetadataCharsetMysql != null) {
					defaultMetadataCharset = CharsetMapping
							.getJavaEncodingForMysqlEncoding(
									defaultMetadataCharsetMysql, this);
				} else {
					defaultMetadataCharset = "UTF-8";
				}

				this.characterSetMetadata = defaultMetadataCharset;
			} else {
				this.characterSetResultsOnServer = CharsetMapping
						.getJavaEncodingForMysqlEncoding(
								characterSetResultsOnServerMysql, this);
				this.characterSetMetadata = this.characterSetResultsOnServer;
			}
		} else {
			this.characterSetMetadata = getEncoding();
		}

		//
		// Query cache is broken wrt. multi-statements before MySQL-4.1.10
		//

		if (versionMeetsMinimum(4, 1, 0)
				&& !this.versionMeetsMinimum(4, 1, 10)
				&& getAllowMultiQueries()) {
			if (isQueryCacheEnabled()) {
				setAllowMultiQueries(false);
			}
		}
		
		if (versionMeetsMinimum(5, 0, 0) && 
				(getUseLocalTransactionState() || getElideSetAutoCommits()) &&
				isQueryCacheEnabled() && !versionMeetsMinimum(6, 0, 10)) {
			// Can't trust the server status flag on the wire if query cache is enabled,
			// due to Bug#36326
			setUseLocalTransactionState(false);
			setElideSetAutoCommits(false);
		}
		
		//
		// Server can do this more efficiently for us
		//
		
		setupServerForTruncationChecks();
	}

	private boolean isQueryCacheEnabled() {
		return "ON".equalsIgnoreCase((String) this.serverVariables
				.get("query_cache_type"))
				&& !"0".equalsIgnoreCase((String) this.serverVariables
						.get("query_cache_size"));
	}

	private int getServerVariableAsInt(String variableName, int fallbackValue)
			throws SQLException {
		try {
			return Integer.parseInt((String) this.serverVariables
					.get(variableName));
		} catch (NumberFormatException nfe) {
			getLog().logWarn(Messages.getString("Connection.BadValueInServerVariables", new Object[] {variableName, 
					this.serverVariables.get(variableName), Integer.valueOf(fallbackValue)}));
			
			return fallbackValue;
		}
	}

	/**
	 * Has the default autocommit value of 0 been changed on the server
	 * via init_connect?
	 * 
	 * @return true if autocommit is not the default of '0' on the server.
	 * 
	 * @throws SQLException
	 */
	private boolean isAutoCommitNonDefaultOnServer() throws SQLException {
		boolean overrideDefaultAutocommit = false;
		
		String initConnectValue = (String) this.serverVariables
		.get("init_connect");

		if (versionMeetsMinimum(4, 1, 2) && initConnectValue != null
				&& initConnectValue.length() > 0) {
			if (!getElideSetAutoCommits()) {
				// auto-commit might have changed
				java.sql.ResultSet rs = null;
				java.sql.Statement stmt = null;
				
				try {
					stmt = getMetadataSafeStatement();
					
					rs = stmt.executeQuery("SELECT @@session.autocommit");
					
					if (rs.next()) {
						this.autoCommit = rs.getBoolean(1);
						if (this.autoCommit != true) {
							overrideDefaultAutocommit = true;
						}
					}
					
				} finally {
					if (rs != null) {
						try {
							rs.close();
						} catch (SQLException sqlEx) {
							// do nothing
						}
					}
					
					if (stmt != null) {
						try {
							stmt.close();
						} catch (SQLException sqlEx) {
							// do nothing
						}
					}
				}
			} else {
				if (this.getIO().isSetNeededForAutoCommitMode(true)) {
					// we're not in standard autocommit=true mode
					this.autoCommit = false;
					overrideDefaultAutocommit = true;
				}
			}
		}
		
		return overrideDefaultAutocommit;
	}

	public boolean isClientTzUTC() {
		return this.isClientTzUTC;
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	public boolean isClosed() {
		return this.isClosed;
	}

	public boolean isCursorFetchEnabled() throws SQLException {
		return (versionMeetsMinimum(5, 0, 2) && getUseCursorFetch());
	}

	public boolean isInGlobalTx() {
		return this.isInGlobalTx;
	}

	/**
	 * Is this connection connected to the first host in the list if
	 * there is a list of servers in the URL?
	 * 
	 * @return true if this connection is connected to the first in 
	 * the list.
	 */
	public synchronized boolean isMasterConnection() {
		return false; // handled higher up
	}

	/**
	 * Is the server in a sql_mode that doesn't allow us to use \\ to escape
	 * things?
	 * 
	 * @return Returns the noBackslashEscapes.
	 */
	public boolean isNoBackslashEscapesSet() {
		return this.noBackslashEscapes;
	}

	public boolean isReadInfoMsgEnabled() {
		return this.readInfoMsg;
	}

	/**
	 * Tests to see if the connection is in Read Only Mode. Note that we cannot
	 * really put the database in read only mode, but we pretend we can by
	 * returning the value of the readOnly flag
	 * 
	 * @return true if the connection is read only
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	public boolean isReadOnly() throws SQLException {
		return this.readOnly;
	}

	public boolean isRunningOnJDK13() {
		return this.isRunningOnJDK13;
	}

	public synchronized boolean isSameResource(Connection otherConnection) {
		if (otherConnection == null) {
			return false;
		}
		
		boolean directCompare = true;
		
		String otherHost = ((ConnectionImpl)otherConnection).origHostToConnectTo;
		String otherOrigDatabase = ((ConnectionImpl)otherConnection).origDatabaseToConnectTo;
		String otherCurrentCatalog = ((ConnectionImpl)otherConnection).database;
		
		if (!nullSafeCompare(otherHost, this.origHostToConnectTo)) {
			directCompare = false;
		} else if (otherHost != null && otherHost.indexOf(',') == -1 && 
				otherHost.indexOf(':') == -1) {
			// need to check port numbers
			directCompare = (((ConnectionImpl)otherConnection).origPortToConnectTo == 
				this.origPortToConnectTo);
		}
		
		if (directCompare) {
			if (!nullSafeCompare(otherOrigDatabase, this.origDatabaseToConnectTo)) {			directCompare = false;
				directCompare = false;
			} else if (!nullSafeCompare(otherCurrentCatalog, this.database)) {
				directCompare = false;
			}
		}

		if (directCompare) {
			return true;
		}
		
		// Has the user explicitly set a resourceId?
		String otherResourceId = ((ConnectionImpl)otherConnection).getResourceId();
		String myResourceId = getResourceId();
		
		if (otherResourceId != null || myResourceId != null) {
			directCompare = nullSafeCompare(otherResourceId, myResourceId);
			
			if (directCompare) {
				return true;
			}
		}
		
		return false;	
	}

	public boolean isServerTzUTC() {
		return this.isServerTzUTC;
	}

	private boolean usingCachedConfig = false;

	/**
	 * Loads the result of 'SHOW VARIABLES' into the serverVariables field so
	 * that the driver can configure itself.
	 * 
	 * @throws SQLException
	 *             if the 'SHOW VARIABLES' query fails for any reason.
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void loadServerVariables() throws SQLException {

		if (getCacheServerConfiguration()) {
			synchronized (serverConfigByUrl) {
				@SuppressWarnings("rawtypes")
				Map cachedVariableMap = (Map) serverConfigByUrl.get(getURL());

				if (cachedVariableMap != null) {
					this.serverVariables = cachedVariableMap;
					this.usingCachedConfig = true;

					return;
				}
			}
		}

		java.sql.Statement stmt = null;
		java.sql.ResultSet results = null;

		try {
			stmt = getMetadataSafeStatement();
			
			String version = this.dbmd.getDriverVersion();
			
			if (version != null && version.indexOf('*') != -1) {
				StringBuffer buf = new StringBuffer(version.length() + 10);

				for (int i = 0; i < version.length(); i++) {
					char c = version.charAt(i);

					if (c == '*') {
						buf.append("[star]");
					} else {
						buf.append(c);
					}
				}

				version = buf.toString();
			}

			String versionComment = (this.getParanoid() || version == null) ? ""
					: "/* " + version + " */";
			
			String query = versionComment + "SHOW VARIABLES";
			
			if (versionMeetsMinimum(5, 0, 3)) {
				query = versionComment + "SHOW VARIABLES WHERE Variable_name ='language'"
					+ " OR Variable_name = 'net_write_timeout'"
					+ " OR Variable_name = 'interactive_timeout'"
					+ " OR Variable_name = 'wait_timeout'"
					+ " OR Variable_name = 'character_set_client'"
					+ " OR Variable_name = 'character_set_connection'"
					+ " OR Variable_name = 'character_set'"
					+ " OR Variable_name = 'character_set_server'"
					+ " OR Variable_name = 'tx_isolation'"
					+ " OR Variable_name = 'transaction_isolation'"
					+ " OR Variable_name = 'character_set_results'"
					+ " OR Variable_name = 'timezone'"
					+ " OR Variable_name = 'time_zone'"
					+ " OR Variable_name = 'system_time_zone'"
					+ " OR Variable_name = 'lower_case_table_names'"
					+ " OR Variable_name = 'max_allowed_packet'"
					+ " OR Variable_name = 'net_buffer_length'"
					+ " OR Variable_name = 'sql_mode'"
					+ " OR Variable_name = 'query_cache_type'"
					+ " OR Variable_name = 'query_cache_size'"
					+ " OR Variable_name = 'init_connect'";
			}
			
			this.serverVariables = new HashMap();

			results = stmt.executeQuery(query);
			
			while (results.next()) {
				this.serverVariables.put(results.getString(1), results
						.getString(2));
			}

			results.close();
			results = null;
			
			if (versionMeetsMinimum(5, 0, 2)) {
				results = stmt.executeQuery(versionComment + "SELECT @@session.auto_increment_increment");
				
				if (results.next()) {
					this.serverVariables.put("auto_increment_increment", results.getString(1));
				}
			}
			
			if (getCacheServerConfiguration()) {
				synchronized (serverConfigByUrl) {
					serverConfigByUrl.put(getURL(), this.serverVariables);
					
					this.usingCachedConfig = true;
				}
			}
		} catch (SQLException e) {
			throw e;
		} finally {
			if (results != null) {
				try {
					results.close();
				} catch (SQLException sqlE) {
					;
				}
			}

			if (stmt != null) {
				try {
					stmt.close();
				} catch (SQLException sqlE) {
					;
				}
			}
		}
	}

	private int autoIncrementIncrement = 0;
	
	public int getAutoIncrementIncrement() {
		return this.autoIncrementIncrement;
	}
	
	/**
	 * Is the server configured to use lower-case table names only?
	 * 
	 * @return true if lower_case_table_names is 'on'
	 */
	public boolean lowerCaseTableNames() {
		return this.lowerCaseTableNames;
	}

	/**
	 * Has the maxRows value changed?
	 * 
	 * @param stmt
	 *            DOCUMENT ME!
	 */
	public synchronized void maxRowsChanged(Statement stmt) {
		if (this.statementsUsingMaxRows == null) {
			this.statementsUsingMaxRows = new HashMap();
		}

		this.statementsUsingMaxRows.put(stmt, stmt);

		this.maxRowsChanged = true;
	}

	/**
	 * A driver may convert the JDBC sql grammar into its system's native SQL
	 * grammar prior to sending it; nativeSQL returns the native form of the
	 * statement that the driver would have sent.
	 * 
	 * @param sql
	 *            a SQL statement that may contain one or more '?' parameter
	 *            placeholders
	 * @return the native form of this statement
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	public String nativeSQL(String sql) throws SQLException {
		if (sql == null) {
			return null;
		}

		Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
                                                          serverSupportsConvertFn(),
                                                          getLoadBalanceSafeProxy());

		if (escapedSqlResult instanceof String) {
			return (String) escapedSqlResult;
		}

		return ((EscapeProcessorResult) escapedSqlResult).escapedSql;
	}

	private CallableStatement parseCallableStatement(String sql)
			throws SQLException {
		Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
				serverSupportsConvertFn(), getLoadBalanceSafeProxy());

		boolean isFunctionCall = false;
		String parsedSql = null;

		if (escapedSqlResult instanceof EscapeProcessorResult) {
			parsedSql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
			isFunctionCall = ((EscapeProcessorResult) escapedSqlResult).callingStoredFunction;
		} else {
			parsedSql = (String) escapedSqlResult;
			isFunctionCall = false;
		}

		return CallableStatement.getInstance(getLoadBalanceSafeProxy(), parsedSql, this.database,
				isFunctionCall);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	public boolean parserKnowsUnicode() {
		return this.parserKnowsUnicode;
	}

	/**
	 * Detect if the connection is still good
	 * 
	 * @throws SQLException
	 *             if the ping fails
	 */
	public void ping() throws SQLException {
		pingInternal(true, 0);
	}

	public void pingInternal(boolean checkForClosedConnection, int timeoutMillis)
			throws SQLException {
		if (checkForClosedConnection) {
			checkClosed();
		}

		long pingMillisLifetime = getSelfDestructOnPingSecondsLifetime();
		int pingMaxOperations = getSelfDestructOnPingMaxOperations();

		if ((pingMillisLifetime > 0 && (System.currentTimeMillis() - this.connectionCreationTimeMillis) > pingMillisLifetime)
				|| (pingMaxOperations > 0 && pingMaxOperations <= this.io
						.getCommandCount())) {

			close();

			throw SQLError.createSQLException(Messages
					.getString("Connection.exceededConnectionLifetime"),
					SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, getExceptionInterceptor());
		}
		// Need MySQL-3.22.1, but who uses anything older!?
		this.io.sendCommand(MysqlDefs.PING, null, null, false, null, timeoutMillis);
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param sql
	 *            DOCUMENT ME!
	 * @return DOCUMENT ME!
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public java.sql.CallableStatement prepareCall(String sql)
			throws SQLException {

		return prepareCall(sql, DEFAULT_RESULT_SET_TYPE,
				DEFAULT_RESULT_SET_CONCURRENCY);
	}

	/**
	 * JDBC 2.0 Same as prepareCall() above, but allows the default result set
	 * type and result set concurrency type to be overridden.
	 * 
	 * @param sql
	 *            the SQL representing the callable statement
	 * @param resultSetType
	 *            a result set type, see ResultSet.TYPE_XXX
	 * @param resultSetConcurrency
	 *            a concurrency type, see ResultSet.CONCUR_XXX
	 * @return a new CallableStatement object containing the pre-compiled SQL
	 *         statement
	 * @exception SQLException
	 *                if a database-access error occurs.
	 */
	public java.sql.CallableStatement prepareCall(String sql,
			int resultSetType, int resultSetConcurrency) throws SQLException {
		if (versionMeetsMinimum(5, 0, 0)) {
			CallableStatement cStmt = null;

			if (!getCacheCallableStatements()) {

				cStmt = parseCallableStatement(sql);
			} else {
				synchronized (this.parsedCallableStatementCache) {
					CompoundCacheKey key = new CompoundCacheKey(getCatalog(), sql);
	
					CallableStatement.CallableStatementParamInfo cachedParamInfo = (CallableStatement.CallableStatementParamInfo) this.parsedCallableStatementCache
							.get(key);
	
					if (cachedParamInfo != null) {
						cStmt = CallableStatement.getInstance(getLoadBalanceSafeProxy(), cachedParamInfo);
					} else {
						cStmt = parseCallableStatement(sql);
	
						synchronized (cStmt) {
							cachedParamInfo = cStmt.paramInfo;
						}
	
						this.parsedCallableStatementCache.put(key, cachedParamInfo);
					}
				}
			}

			cStmt.setResultSetType(resultSetType);
			cStmt.setResultSetConcurrency(resultSetConcurrency);

			return cStmt;
		}

		throw SQLError.createSQLException("Callable statements not " + "supported.",
				SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor());
	}

	/**
	 * @see Connection#prepareCall(String, int, int, int)
	 */
	public java.sql.CallableStatement prepareCall(String sql,
			int resultSetType, int resultSetConcurrency,
			int resultSetHoldability) throws SQLException {
		if (getPedantic()) {
			if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
				throw SQLError.createSQLException(
						"HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
			}
		}

		CallableStatement cStmt = (com.mysql.jdbc.CallableStatement) prepareCall(
				sql, resultSetType, resultSetConcurrency);

		return cStmt;
	}

	/**
	 * A SQL statement with or without IN parameters can be pre-compiled and
	 * stored in a PreparedStatement object. This object can then be used to
	 * efficiently execute this statement multiple times.
	 * <p>
	 * <B>Note:</B> This method is optimized for handling parametric SQL
	 * statements that benefit from precompilation if the driver supports
	 * precompilation. In this case, the statement is not sent to the database
	 * until the PreparedStatement is executed. This has no direct effect on
	 * users; however it does affect which method throws certain
	 * java.sql.SQLExceptions
	 * </p>
	 * <p>
	 * MySQL does not support precompilation of statements, so they are handled
	 * by the driver.
	 * </p>
	 * 
	 * @param sql
	 *            a SQL statement that may contain one or more '?' IN parameter
	 *            placeholders
	 * @return a new PreparedStatement object containing the pre-compiled
	 *         statement.
	 * @exception SQLException
	 *                if a database access error occurs.
	 */
	public java.sql.PreparedStatement prepareStatement(String sql)
			throws SQLException {
		return prepareStatement(sql, DEFAULT_RESULT_SET_TYPE,
				DEFAULT_RESULT_SET_CONCURRENCY);
	}

	/**
	 * @see Connection#prepareStatement(String, int)
	 */
	public java.sql.PreparedStatement prepareStatement(String sql,
			int autoGenKeyIndex) throws SQLException {
		java.sql.PreparedStatement pStmt = prepareStatement(sql);

		((com.mysql.jdbc.PreparedStatement) pStmt)
				.setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);

		return pStmt;
	}

	/**
	 * JDBC 2.0 Same as prepareStatement() above, but allows the default result
	 * set type and result set concurrency type to be overridden.
	 * 
	 * @param sql
	 *            the SQL query containing place holders
	 * @param resultSetType
	 *            a result set type, see ResultSet.TYPE_XXX
	 * @param resultSetConcurrency
	 *            a concurrency type, see ResultSet.CONCUR_XXX
	 * @return a new PreparedStatement object containing the pre-compiled SQL
	 *         statement
	 * @exception SQLException
	 *                if a database-access error occurs.
	 */
	public synchronized java.sql.PreparedStatement prepareStatement(String sql,
			int resultSetType, int resultSetConcurrency) throws SQLException {
		checkClosed();

		//
		// FIXME: Create warnings if can't create results of the given
		// type or concurrency
		//
		PreparedStatement pStmt = null;
		
		boolean canServerPrepare = true;
		
		String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
		
		if (this.useServerPreparedStmts && getEmulateUnsupportedPstmts()) {
			canServerPrepare = canHandleAsServerPreparedStatement(nativeSql);
		}
		
		if (this.useServerPreparedStmts && canServerPrepare) {
			if (this.getCachePreparedStatements()) {
				synchronized (this.serverSideStatementCache) {
					pStmt = (com.mysql.jdbc.ServerPreparedStatement)this.serverSideStatementCache.remove(sql);
					
					if (pStmt != null) {
						((com.mysql.jdbc.ServerPreparedStatement)pStmt).setClosed(false);
						pStmt.clearParameters();
					}

					if (pStmt == null) {
						try {
							pStmt = ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql,
									this.database, resultSetType, resultSetConcurrency);
							if (sql.length() < getPreparedStatementCacheSqlLimit()) {
								((com.mysql.jdbc.ServerPreparedStatement)pStmt).isCached = true;
							}
							
							pStmt.setResultSetType(resultSetType);
							pStmt.setResultSetConcurrency(resultSetConcurrency);
						} catch (SQLException sqlEx) {
							// Punt, if necessary
							if (getEmulateUnsupportedPstmts()) {
								pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
								
								if (sql.length() < getPreparedStatementCacheSqlLimit()) {
									this.serverSideStatementCheckCache.put(sql, Boolean.FALSE);
								}
							} else {
								throw sqlEx;
							}
						}
					}
				}
			} else {
				try {
					pStmt = ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql,
							this.database, resultSetType, resultSetConcurrency);
					
					pStmt.setResultSetType(resultSetType);
					pStmt.setResultSetConcurrency(resultSetConcurrency);
				} catch (SQLException sqlEx) {
					// Punt, if necessary
					if (getEmulateUnsupportedPstmts()) {
						pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
					} else {
						throw sqlEx;
					}
				}
			}
		} else {
			pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
		}
		
		return pStmt;
	}

	/**
	 * @see Connection#prepareStatement(String, int, int, int)
	 */
	public java.sql.PreparedStatement prepareStatement(String sql,
			int resultSetType, int resultSetConcurrency,
			int resultSetHoldability) throws SQLException {
		if (getPedantic()) {
			if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
				throw SQLError.createSQLException(
						"HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
			}
		}

		return prepareStatement(sql, resultSetType, resultSetConcurrency);
	}

	/**
	 * @see Connection#prepareStatement(String, int[])
	 */
	public java.sql.PreparedStatement prepareStatement(String sql,
			int[] autoGenKeyIndexes) throws SQLException {
		java.sql.PreparedStatement pStmt = prepareStatement(sql);

		((com.mysql.jdbc.PreparedStatement) pStmt)
				.setRetrieveGeneratedKeys((autoGenKeyIndexes != null)
						&& (autoGenKeyIndexes.length > 0));

		return pStmt;
	}

	/**
	 * @see Connection#prepareStatement(String, String[])
	 */
	public java.sql.PreparedStatement prepareStatement(String sql,
			String[] autoGenKeyColNames) throws SQLException {
		java.sql.PreparedStatement pStmt = prepareStatement(sql);

		((com.mysql.jdbc.PreparedStatement) pStmt)
				.setRetrieveGeneratedKeys((autoGenKeyColNames != null)
						&& (autoGenKeyColNames.length > 0));

		return pStmt;
	}

  @Override
  public java.sql.Clob createClob() throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public java.sql.Blob createBlob() throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public NClob createNClob() throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public SQLXML createSQLXML() throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public boolean isValid(int i) throws SQLException {
    return false;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public void setClientInfo(String s, String s1) throws SQLClientInfoException {
    //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public void setClientInfo(Properties properties) throws SQLClientInfoException {
    //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public String getClientInfo(String s) throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public Properties getClientInfo() throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public java.sql.Array createArrayOf(String s, Object[] objects) throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public Struct createStruct(String s, Object[] objects) throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  /**
	 * Closes connection and frees resources.
	 * 
	 * @param calledExplicitly
	 *            is this being called from close()
	 * @param issueRollback
	 *            should a rollback() be issued?
	 * @throws SQLException
	 *             if an error occurs
	 */
	public void realClose(boolean calledExplicitly, boolean issueRollback,
			boolean skipLocalTeardown, Throwable reason) throws SQLException {
		SQLException sqlEx = null;

		if (this.isClosed()) {
			return;
		}
		
		this.forceClosedReason = reason;
		
		try {
			if (!skipLocalTeardown) {
				if (!getAutoCommit() && issueRollback) {
					try {
						rollback();
					} catch (SQLException ex) {
						sqlEx = ex;
					}
				}

				reportMetrics();

				if (getUseUsageAdvisor()) {
					if (!calledExplicitly) {
						String message = "Connection implicitly closed by Driver. You should call Connection.close() from your code to free resources more efficiently and avoid resource leaks.";

						this.eventSink.consumeEvent(new ProfilerEvent(
								ProfilerEvent.TYPE_WARN, "", //$NON-NLS-1$
								this.getCatalog(), this.getId(), -1, -1, System
										.currentTimeMillis(), 0, Constants.MILLIS_I18N,
										null,
								this.pointOfOrigin, message));
					}

					long connectionLifeTime = System.currentTimeMillis()
							- this.connectionCreationTimeMillis;

					if (connectionLifeTime < 500) {
						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.";

						this.eventSink.consumeEvent(new ProfilerEvent(
								ProfilerEvent.TYPE_WARN, "", //$NON-NLS-1$
								this.getCatalog(), this.getId(), -1, -1, System
										.currentTimeMillis(), 0, Constants.MILLIS_I18N,
										null,
								this.pointOfOrigin, message));
					}
				}

				try {
					closeAllOpenStatements();
				} catch (SQLException ex) {
					sqlEx = ex;
				}

				if (this.io != null) {
					try {
						this.io.quit();
					} catch (Exception e) {
						;
					}

				}
			} else {
				this.io.forceClose();
			}
			
	    	if (this.statementInterceptors != null) {
	    		for (int i = 0; i < this.statementInterceptors.size(); i++) {
	    			((StatementInterceptorV2)this.statementInterceptors.get(i)).destroy();
	    		}
	    	}
	    	
	    	if (this.exceptionInterceptor != null) {
	    		this.exceptionInterceptor.destroy();
	    	}
		} finally {
			this.openStatements = null;
			this.io = null;
			this.statementInterceptors = null;
			this.exceptionInterceptor = null;
			ProfilerEventHandlerFactory.removeInstance(this);
			
			synchronized (this) {
				if (this.cancelTimer != null) {
					this.cancelTimer.cancel();
				}
			}
			
			this.isClosed = true;
		}

		if (sqlEx != null) {
			throw sqlEx;
		}

	}

	public synchronized void recachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException {
		if (pstmt.isPoolable()) {
			synchronized (this.serverSideStatementCache) {
				this.serverSideStatementCache.put(pstmt.originalSql, pstmt);
			}
		}
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @param queryTimeMs
	 */
	public void registerQueryExecutionTime(long queryTimeMs) {
		if (queryTimeMs > this.longestQueryTimeMs) {
			this.longestQueryTimeMs = queryTimeMs;

			repartitionPerformanceHistogram();
		}

		addToPerformanceHistogram(queryTimeMs, 1);

		if (queryTimeMs < this.shortestQueryTimeMs) {
			this.shortestQueryTimeMs = (queryTimeMs == 0) ? 1 : queryTimeMs;
		}

		this.numberOfQueriesIssued++;

		this.totalQueryTimeMs += queryTimeMs;
	}

	/**
	 * Register a Statement instance as open.
	 * 
	 * @param stmt
	 *            the Statement instance to remove
	 */
	public void registerStatement(Statement stmt) {
		synchronized (this.openStatements) {
			this.openStatements.put(stmt, stmt);
		}
	}

	/**
	 * @see Connection#releaseSavepoint(Savepoint)
	 */
	public void releaseSavepoint(Savepoint arg0) throws SQLException {
		// this is a no-op
	}

	private void repartitionHistogram(int[] histCounts, long[] histBreakpoints,
			long currentLowerBound, long currentUpperBound) {

		if (oldHistCounts == null) {
			oldHistCounts = new int[histCounts.length];
			oldHistBreakpoints = new long[histBreakpoints.length];
		}

		System.arraycopy(histCounts, 0, oldHistCounts, 0, histCounts.length);
		
		System.arraycopy(histBreakpoints, 0, oldHistBreakpoints, 0,
				histBreakpoints.length);
	
		createInitialHistogram(histBreakpoints, currentLowerBound,
				currentUpperBound);

		for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
			addToHistogram(histCounts, histBreakpoints, oldHistBreakpoints[i],
					oldHistCounts[i], currentLowerBound, currentUpperBound);
		}
	}

	private void repartitionPerformanceHistogram() {
		checkAndCreatePerformanceHistogram();

		repartitionHistogram(this.perfMetricsHistCounts,
				this.perfMetricsHistBreakpoints,
				this.shortestQueryTimeMs == Long.MAX_VALUE ? 0
						: this.shortestQueryTimeMs, this.longestQueryTimeMs);
	}

	private void repartitionTablesAccessedHistogram() {
		checkAndCreateTablesAccessedHistogram();

		repartitionHistogram(this.numTablesMetricsHistCounts,
				this.numTablesMetricsHistBreakpoints,
				this.minimumNumberTablesAccessed == Long.MAX_VALUE ? 0
						: this.minimumNumberTablesAccessed,
				this.maximumNumberTablesAccessed);
	}

	private void reportMetrics() {
		if (getGatherPerformanceMetrics()) {
			StringBuffer logMessage = new StringBuffer(256);

			logMessage.append("** Performance Metrics Report **\n");
			logMessage.append("\nLongest reported query: "
					+ this.longestQueryTimeMs + " ms");
			logMessage.append("\nShortest reported query: "
					+ this.shortestQueryTimeMs + " ms");
			logMessage
					.append("\nAverage query execution time: "
							+ (this.totalQueryTimeMs / this.numberOfQueriesIssued)
							+ " ms");
			logMessage.append("\nNumber of statements executed: "
					+ this.numberOfQueriesIssued);
			logMessage.append("\nNumber of result sets created: "
					+ this.numberOfResultSetsCreated);
			logMessage.append("\nNumber of statements prepared: "
					+ this.numberOfPrepares);
			logMessage.append("\nNumber of prepared statement executions: "
					+ this.numberOfPreparedExecutes);

			if (this.perfMetricsHistBreakpoints != null) {
				logMessage.append("\n\n\tTiming Histogram:\n");
				int maxNumPoints = 20;
				int highestCount = Integer.MIN_VALUE;

				for (int i = 0; i < (HISTOGRAM_BUCKETS); i++) {
					if (this.perfMetricsHistCounts[i] > highestCount) {
						highestCount = this.perfMetricsHistCounts[i];
					}
				}

				if (highestCount == 0) {
					highestCount = 1; // avoid DIV/0
				}

				for (int i = 0; i < (HISTOGRAM_BUCKETS - 1); i++) {

					if (i == 0) {
						logMessage.append("\n\tless than "
								+ this.perfMetricsHistBreakpoints[i + 1]
								+ " ms: \t" + this.perfMetricsHistCounts[i]);
					} else {
						logMessage.append("\n\tbetween "
								+ this.perfMetricsHistBreakpoints[i] + " and "
								+ this.perfMetricsHistBreakpoints[i + 1]
								+ " ms: \t" + this.perfMetricsHistCounts[i]);
					}

					logMessage.append("\t");

					int numPointsToGraph = (int) (maxNumPoints * ((double) this.perfMetricsHistCounts[i] / (double) highestCount));

					for (int j = 0; j < numPointsToGraph; j++) {
						logMessage.append("*");
					}

					if (this.longestQueryTimeMs < this.perfMetricsHistCounts[i + 1]) {
						break;
					}
				}

				if (this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2] < this.longestQueryTimeMs) {
					logMessage.append("\n\tbetween ");
					logMessage
							.append(this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2]);
					logMessage.append(" and ");
					logMessage
							.append(this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 1]);
					logMessage.append(" ms: \t");
					logMessage
							.append(this.perfMetricsHistCounts[HISTOGRAM_BUCKETS - 1]);
				}
			}

			if (this.numTablesMetricsHistBreakpoints != null) {
				logMessage.append("\n\n\tTable Join Histogram:\n");
				int maxNumPoints = 20;
				int highestCount = Integer.MIN_VALUE;

				for (int i = 0; i < (HISTOGRAM_BUCKETS); i++) {
					if (this.numTablesMetricsHistCounts[i] > highestCount) {
						highestCount = this.numTablesMetricsHistCounts[i];
					}
				}

				if (highestCount == 0) {
					highestCount = 1; // avoid DIV/0
				}

				for (int i = 0; i < (HISTOGRAM_BUCKETS - 1); i++) {

					if (i == 0) {
						logMessage.append("\n\t"
								+ this.numTablesMetricsHistBreakpoints[i + 1]
								+ " tables or less: \t\t"
								+ this.numTablesMetricsHistCounts[i]);
					} else {
						logMessage.append("\n\tbetween "
								+ this.numTablesMetricsHistBreakpoints[i]
								+ " and "
								+ this.numTablesMetricsHistBreakpoints[i + 1]
								+ " tables: \t"
								+ this.numTablesMetricsHistCounts[i]);
					}

					logMessage.append("\t");

					int numPointsToGraph = (int) (maxNumPoints * ((double) this.numTablesMetricsHistCounts[i] / (double) highestCount));

					for (int j = 0; j < numPointsToGraph; j++) {
						logMessage.append("*");
					}

					if (this.maximumNumberTablesAccessed < this.numTablesMetricsHistBreakpoints[i + 1]) {
						break;
					}
				}

				if (this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2] < this.maximumNumberTablesAccessed) {
					logMessage.append("\n\tbetween ");
					logMessage
							.append(this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2]);
					logMessage.append(" and ");
					logMessage
							.append(this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 1]);
					logMessage.append(" tables: ");
					logMessage
							.append(this.numTablesMetricsHistCounts[HISTOGRAM_BUCKETS - 1]);
				}
			}

			this.log.logInfo(logMessage);

			this.metricsLastReportedMs = System.currentTimeMillis();
		}
	}

	/**
	 * Reports currently collected metrics if this feature is enabled and the
	 * timeout has passed.
	 */
	private void reportMetricsIfNeeded() {
		if (getGatherPerformanceMetrics()) {
			if ((System.currentTimeMillis() - this.metricsLastReportedMs) > getReportMetricsIntervalMillis()) {
				reportMetrics();
			}
		}
	}

	public void reportNumberOfTablesAccessed(int numTablesAccessed) {
		if (numTablesAccessed < this.minimumNumberTablesAccessed) {
			this.minimumNumberTablesAccessed = numTablesAccessed;
		}

		if (numTablesAccessed > this.maximumNumberTablesAccessed) {
			this.maximumNumberTablesAccessed = numTablesAccessed;

			repartitionTablesAccessedHistogram();
		}

		addToTablesAccessedHistogram(numTablesAccessed, 1);
	}

	/**
	 * Resets the server-side state of this connection. Doesn't work for MySQL
	 * versions older than 4.0.6 or if isParanoid() is set (it will become a
	 * no-op in these cases). Usually only used from connection pooling code.
	 * 
	 * @throws SQLException
	 *             if the operation fails while resetting server state.
	 */
	public void resetServerState() throws SQLException {
		if (!getParanoid()
				&& ((this.io != null) && versionMeetsMinimum(4, 0, 6))) {
			changeUser(this.user, this.password);
		}
	}

	/**
	 * The method rollback() drops all changes made since the previous
	 * commit/rollback and releases any database locks currently held by the
	 * Connection.
	 * 
	 * @exception SQLException
	 *                if a database access error occurs
	 * @see commit
	 */
	public synchronized void rollback() throws SQLException {
		checkClosed();

		try {
			if (this.connectionLifecycleInterceptors != null) {
				IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {

					void forEach(Object each) throws SQLException {
						if (!((ConnectionLifecycleInterceptor)each).rollback()) {
							this.stopIterating = true;
						}
					}
				};
				
				iter.doForAll();
				
				if (!iter.fullIteration()) {
					return;
				}
			}
			// no-op if _relaxAutoCommit == true
			if (this.autoCommit && !getRelaxAutoCommit()) {
				throw SQLError.createSQLException(
						"Can't call rollback when autocommit=true",
						SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor());
			} else if (this.transactionsSupported) {
				try {
					rollbackNoChecks();
				} catch (SQLException sqlEx) {
					// We ignore non-transactional tables if told to do so
					if (getIgnoreNonTxTables()
							&& (sqlEx.getErrorCode() == SQLError.ER_WARNING_NOT_COMPLETE_ROLLBACK)) {
						return;
					}
					throw sqlEx;

				}
			}
		} catch (SQLException sqlException) {
			if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
					.equals(sqlException.getSQLState())) {
				throw SQLError.createSQLException(
						"Communications link failure during rollback(). Transaction resolution unknown.",
						SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, getExceptionInterceptor());
			}

			throw sqlException;
		} finally {
			this.needsPing = this.getReconnectAtTxEnd();
		}
	}

	/**
	 * @see Connection#rollback(Savepoint)
	 */
	public synchronized void rollback(final Savepoint savepoint) throws SQLException {

		if (versionMeetsMinimum(4, 0, 14) || versionMeetsMinimum(4, 1, 1)) {
			checkClosed();
	
			try {
				if (this.connectionLifecycleInterceptors != null) {
					IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {

						void forEach(Object each) throws SQLException {
							if (!((ConnectionLifecycleInterceptor)each).rollback(savepoint)) {
								this.stopIterating = true;
							}
						}
					};
					
					iter.doForAll();
					
					if (!iter.fullIteration()) {
						return;
					}
				}
				
				StringBuffer rollbackQuery = new StringBuffer(
						"ROLLBACK TO SAVEPOINT ");
				rollbackQuery.append('`');
				rollbackQuery.append(savepoint.getSavepointName());
				rollbackQuery.append('`');

				java.sql.Statement stmt = null;

				try {
					stmt = getMetadataSafeStatement();

					stmt.executeUpdate(rollbackQuery.toString());
				} catch (SQLException sqlEx) {
					int errno = sqlEx.getErrorCode();

					if (errno == 1181) {
						String msg = sqlEx.getMessage();

						if (msg != null) {
							int indexOfError153 = msg.indexOf("153");

							if (indexOfError153 != -1) {
								throw SQLError.createSQLException("Savepoint '"
										+ savepoint.getSavepointName()
										+ "' does not exist",
										SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
										errno, getExceptionInterceptor());
							}
						}
					}

					// We ignore non-transactional tables if told to do so
					if (getIgnoreNonTxTables()
							&& (sqlEx.getErrorCode() != SQLError.ER_WARNING_NOT_COMPLETE_ROLLBACK)) {
						throw sqlEx;
					}

					if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
							.equals(sqlEx.getSQLState())) {
						throw SQLError.createSQLException(
								"Communications link failure during rollback(). Transaction resolution unknown.",
								SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, getExceptionInterceptor());
					}

					throw sqlEx;
				} finally {
					closeStatement(stmt);
				}
			} finally {
				this.needsPing = this.getReconnectAtTxEnd();
			}
		} else {
			throw SQLError.notImplemented();
		}
	}

	private void rollbackNoChecks() throws SQLException {
		if (getUseLocalTransactionState() && versionMeetsMinimum(5, 0, 0)) {
			if (!this.io.inTransactionOnServer()) {
				return; // effectively a no-op
			}
		}
		
		execSQL(null, "rollback", -1, null,
				DEFAULT_RESULT_SET_TYPE,
				DEFAULT_RESULT_SET_CONCURRENCY, false,
				this.database, null, false);
	}

	/**
	 * @see java.sql.Connection#prepareStatement(String)
	 */
	public java.sql.PreparedStatement serverPrepareStatement(String sql)
		throws SQLException {

		String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;

		return ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql, this.getCatalog(),
				DEFAULT_RESULT_SET_TYPE,
				DEFAULT_RESULT_SET_CONCURRENCY);
	}

	/**
	 * @see java.sql.Connection#prepareStatement(String, int)
	 */
	public java.sql.PreparedStatement serverPrepareStatement(String sql,
			int autoGenKeyIndex) throws SQLException {
		String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;

		PreparedStatement pStmt = ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql, this.getCatalog(),
				DEFAULT_RESULT_SET_TYPE,
				DEFAULT_RESULT_SET_CONCURRENCY);
		
		pStmt.setRetrieveGeneratedKeys(
				autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);

		return pStmt;
	}
	
	/**
	 * @see java.sql.Connection#prepareStatement(String, int, int)
	 */
	public java.sql.PreparedStatement serverPrepareStatement(String sql,
			int resultSetType, int resultSetConcurrency) throws SQLException {
		String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;

		return ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql, this.getCatalog(),
				resultSetType,
				resultSetConcurrency);
	}
	
	/**
	 * @see java.sql.Connection#prepareStatement(String, int, int, int)
	 */
	public java.sql.PreparedStatement serverPrepareStatement(String sql,
			int resultSetType, int resultSetConcurrency,
			int resultSetHoldability) throws SQLException {
		if (getPedantic()) {
			if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
				throw SQLError.createSQLException(
						"HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
			}
		}

		return serverPrepareStatement(sql, resultSetType, resultSetConcurrency);
	}
	
	/**
	 * @see java.sql.Connection#prepareStatement(String, int[])
	 */
	public java.sql.PreparedStatement serverPrepareStatement(String sql,
			int[] autoGenKeyIndexes) throws SQLException {
		
		PreparedStatement pStmt = (PreparedStatement) serverPrepareStatement(sql);
		
		pStmt
				.setRetrieveGeneratedKeys((autoGenKeyIndexes != null)
						&& (autoGenKeyIndexes.length > 0));

		return pStmt;
	}

	/**
	 * @see java.sql.Connection#prepareStatement(String, String[])
	 */
	public java.sql.PreparedStatement serverPrepareStatement(String sql,
			String[] autoGenKeyColNames) throws SQLException {
		PreparedStatement pStmt = (PreparedStatement) serverPrepareStatement(sql);

		pStmt
				.setRetrieveGeneratedKeys((autoGenKeyColNames != null)
						&& (autoGenKeyColNames.length > 0));

		return pStmt;
	}
	
	public boolean serverSupportsConvertFn() throws SQLException {
		return versionMeetsMinimum(4, 0, 2);
	}

	/**
	 * If a connection is in auto-commit mode, than all its SQL statements will
	 * be executed and committed as individual transactions. Otherwise, its SQL
	 * statements are grouped into transactions that are terminated by either
	 * commit() or rollback(). By default, new connections are in auto- commit
	 * mode. The commit occurs when the statement completes or the next execute
	 * occurs, whichever comes first. In the case of statements returning a
	 * ResultSet, the statement completes when the last row of the ResultSet has
	 * been retrieved or the ResultSet has been closed. In advanced cases, a
	 * single statement may return multiple results as well as output parameter
	 * values. Here the commit occurs when all results and output param values
	 * have been retrieved.
	 * <p>
	 * <b>Note:</b> MySQL does not support transactions, so this method is a
	 * no-op.
	 * </p>
	 * 
	 * @param autoCommitFlag -
	 *            true enables auto-commit; false disables it
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	public synchronized void setAutoCommit(final boolean autoCommitFlag) throws SQLException {
		checkClosed();
		
		if (this.connectionLifecycleInterceptors != null) {
			IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {

				void forEach(Object each) throws SQLException {
					if (!((ConnectionLifecycleInterceptor)each).setAutoCommit(autoCommitFlag)) {
						this.stopIterating = true;
					}
				}
			};
			
			iter.doForAll();
			
			if (!iter.fullIteration()) {
				return;
			}
		}

		if (getAutoReconnectForPools()) {
			setHighAvailability(true);
		}

		try {
			if (this.transactionsSupported) {

				boolean needsSetOnServer = true;

				if (this.getUseLocalSessionState()
						&& this.autoCommit == autoCommitFlag) {
					needsSetOnServer = false;
				} else if (!this.getHighAvailability()) {
					needsSetOnServer = this.getIO()
							.isSetNeededForAutoCommitMode(autoCommitFlag);
				}

				// this internal value must be set first as failover depends on
				// it
				// being set to true to fail over (which is done by most
				// app servers and connection pools at the end of
				// a transaction), and the driver issues an implicit set
				// based on this value when it (re)-connects to a server
				// so the value holds across connections
				this.autoCommit = autoCommitFlag;

				if (needsSetOnServer) {
					execSQL(null, autoCommitFlag ? "SET autocommit=1"
							: "SET autocommit=0", -1, null,
							DEFAULT_RESULT_SET_TYPE,
							DEFAULT_RESULT_SET_CONCURRENCY, false,
							this.database, null, false);
				}

			} else {
				if ((autoCommitFlag == false) && !getRelaxAutoCommit()) {
					throw SQLError.createSQLException("MySQL Versions Older than 3.23.15 "
							+ "do not support transactions",
							SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor());
				}

				this.autoCommit = autoCommitFlag;
			}
		} finally {
			if (this.getAutoReconnectForPools()) {
				setHighAvailability(false);
			}
		}
		
		return;
	}

	/**
	 * A sub-space of this Connection's database may be selected by setting a
	 * catalog name. If the driver does not support catalogs, it will silently
	 * ignore this request
	 * <p>
	 * <b>Note:</b> MySQL's notion of catalogs are individual databases.
	 * </p>
	 * 
	 * @param catalog
	 *            the database for this connection to use
	 * @throws SQLException
	 *             if a database access error occurs
	 */
	public synchronized void setCatalog(final String catalog) throws SQLException {
		checkClosed();

		if (catalog == null) {
			throw SQLError.createSQLException("Catalog can not be null",
					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
		}
		
		if (this.connectionLifecycleInterceptors != null) {
			IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {

				void forEach(Object each) throws SQLException {
					if (!((ConnectionLifecycleInterceptor)each).setCatalog(catalog)) {
						this.stopIterating = true;
					}
				}
			};
			
			iter.doForAll();
			
			if (!iter.fullIteration()) {
				return;
			}
		}
		
		if (getUseLocalSessionState()) {
			if (this.lowerCaseTableNames) {
				if (this.database.equalsIgnoreCase(catalog)) {
					return;
				}
			} else {
				if (this.database.equals(catalog)) {
					return;
				}
			}
		}
		
		String quotedId = this.dbmd.getIdentifierQuoteString();

		if ((quotedId == null) || quotedId.equals(" ")) {
			quotedId = "";
		}

		StringBuffer query = new StringBuffer("USE ");
		query.append(quotedId);
		query.append(catalog);
		query.append(quotedId);

		execSQL(null, query.toString(), -1, null,
				DEFAULT_RESULT_SET_TYPE,
				DEFAULT_RESULT_SET_CONCURRENCY, false,
				this.database, null, false);
		
		this.database = catalog;
	}

	/**
	 * @param failedOver
	 *            The failedOver to set.
	 */
	public synchronized void setFailedOver(boolean flag) {
		// handled higher up
	}

	/**
	 * @see Connection#setHoldability(int)
	 */
	public void setHoldability(int arg0) throws SQLException {
		// do nothing
	}

	public void setInGlobalTx(boolean flag) {
		this.isInGlobalTx = flag;
	}

	// exposed for testing
	/**
	 * @param preferSlaveDuringFailover
	 *            The preferSlaveDuringFailover to set.
	 */
	public void setPreferSlaveDuringFailover(boolean flag) {
		// no-op, handled further up in the wrapper
	}

	public void setReadInfoMsgEnabled(boolean flag) {
		this.readInfoMsg = flag;
	}

	/**
	 * You can put a connection in read-only mode as a hint to enable database
	 * optimizations <B>Note:</B> setReadOnly cannot be called while in the
	 * middle of a transaction
	 * 
	 * @param readOnlyFlag -
	 *            true enables read-only mode; false disables it
	 * @exception SQLException
	 *                if a database access error occurs
	 */
	public void setReadOnly(boolean readOnlyFlag) throws SQLException {
		checkClosed();

		setReadOnlyInternal(readOnlyFlag);
	}
	
	public void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException {
		this.readOnly = readOnlyFlag;
	}
	
	/**
	 * @see Connection#setSavepoint()
	 */
	public java.sql.Savepoint setSavepoint() throws SQLException {
		MysqlSavepoint savepoint = new MysqlSavepoint(getExceptionInterceptor());

		setSavepoint(savepoint);

		return savepoint;
	}

	private synchronized void setSavepoint(MysqlSavepoint savepoint) throws SQLException {

		if (versionMeetsMinimum(4, 0, 14) || versionMeetsMinimum(4, 1, 1)) {
			checkClosed();

			StringBuffer savePointQuery = new StringBuffer("SAVEPOINT ");
			savePointQuery.append('`');
			savePointQuery.append(savepoint.getSavepointName());
			savePointQuery.append('`');

			java.sql.Statement stmt = null;

			try {
				stmt = getMetadataSafeStatement();

				stmt.executeUpdate(savePointQuery.toString());
			} finally {
				closeStatement(stmt);
			}
		} else {
			throw SQLError.notImplemented();
		}
	}
	
	/**
	 * @see Connection#setSavepoint(String)
	 */
	public synchronized java.sql.Savepoint setSavepoint(String name) throws SQLException {
		MysqlSavepoint savepoint = new MysqlSavepoint(name, getExceptionInterceptor());

		setSavepoint(savepoint);

		return savepoint;
	}
	
	/**
	 * 
	 */
	private void setSessionVariables() throws SQLException {
		if (this.versionMeetsMinimum(4, 0, 0) && getSessionVariables() != null) {
			List variablesToSet = StringUtils.split(getSessionVariables(), ",", "\"'", "\"'",
					false);

			int numVariablesToSet = variablesToSet.size();

			java.sql.Statement stmt = null;

			try {
				stmt = getMetadataSafeStatement();

				for (int i = 0; i < numVariablesToSet; i++) {
					String variableValuePair = (String) variablesToSet.get(i);

					if (variableValuePair.startsWith("@")) {
						stmt.executeUpdate("SET " + variableValuePair);
					} else {
						stmt.executeUpdate("SET SESSION " + variableValuePair);
					}
				}
			} finally {
				if (stmt != null) {
					stmt.close();
				}
			}
		}

	}
	
	/**
	 * DOCUMENT ME!
	 * 
	 * @param level
	 *            DOCUMENT ME!
	 * @throws SQLException
	 *             DOCUMENT ME!
	 */
	public synchronized void setTransactionIsolation(int level) throws SQLException {
		checkClosed();

		if (this.hasIsolationLevels) {
			String sql = null;

			boolean shouldSendSet = false;

			if (getAlwaysSendSetIsolation()) {
				shouldSendSet = true;
			} else {
				if (level != this.isolationLevel) {
					shouldSendSet = true;
				}
			}

			if (getUseLocalSessionState()) {
				shouldSendSet = this.isolationLevel != level;
			}

			if (shouldSendSet) {
				switch (level) {
				case java.sql.Connection.TRANSACTION_NONE:
					throw SQLError.createSQLException("Transaction isolation level "
							+ "NONE not supported by MySQL", getExceptionInterceptor());

				case java.sql.Connection.TRANSACTION_READ_COMMITTED:
					sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED";

					break;

				case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED:
					sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";

					break;

				case java.sql.Connection.TRANSACTION_REPEATABLE_READ:
					sql = "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";

					break;

				case java.sql.Connection.TRANSACTION_SERIALIZABLE:
					sql = "SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE";

					break;

				default:
					throw SQLError.createSQLException("Unsupported transaction "
							+ "isolation level '" + level + "'",
							SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor());
				}

				execSQL(null, sql, -1, null,
						DEFAULT_RESULT_SET_TYPE,
						DEFAULT_RESULT_SET_CONCURRENCY,false,
						this.database, null, false);

				this.isolationLevel = level;
			}
		} else {
			throw SQLError.createSQLException("Transaction Isolation Levels are "
					+ "not supported on MySQL versions older than 3.23.36.",
					SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor());
		}
	}
	
	/**
	 * JDBC 2.0 Install a type-map object as the default type-map for this
	 * connection
	 * 
	 * @param map
	 *            the type mapping
	 * @throws SQLException
	 *             if a database error occurs.
	 */
	public synchronized void setTypeMap(java.util.Map map) throws SQLException {
		this.typeMap = map;
	}
	
	private void setupServerForTruncationChecks() throws SQLException {
		if (getJdbcCompliantTruncation()) {
			if (versionMeetsMinimum(5, 0, 2)) {
				String currentSqlMode = 
					(String)this.serverVariables.get("sql_mode");
				
				boolean strictTransTablesIsSet = StringUtils.indexOfIgnoreCase(currentSqlMode, "STRICT_TRANS_TABLES") != -1;
				
				if (currentSqlMode == null ||
						currentSqlMode.length() == 0 || !strictTransTablesIsSet) {
					StringBuffer commandBuf = new StringBuffer("SET sql_mode='");
					
					if (currentSqlMode != null && currentSqlMode.length() > 0) {
						commandBuf.append(currentSqlMode);
						commandBuf.append(",");
					}
					
					commandBuf.append("STRICT_TRANS_TABLES'");
					
					execSQL(null,  commandBuf.toString(), -1, null,
							DEFAULT_RESULT_SET_TYPE,
							DEFAULT_RESULT_SET_CONCURRENCY, false,
							this.database, null, false);
					
					setJdbcCompliantTruncation(false); // server's handling this for us now
				} else if (strictTransTablesIsSet) {
					// We didn't set it, but someone did, so we piggy back on it
					setJdbcCompliantTruncation(false); // server's handling this for us now
				}
				
			}
		}
	}
	
	
	/**
	 * Used by MiniAdmin to shutdown a MySQL server
	 * 
	 * @throws SQLException
	 *             if the command can not be issued.
	 */
	public void shutdownServer() throws SQLException {
		try {
			this.io.sendCommand(MysqlDefs.SHUTDOWN, null, null, false, null, 0);
		} catch (Exception ex) {
			SQLException sqlEx = SQLError.createSQLException(
					Messages.getString("Connection.UnhandledExceptionDuringShutdown"),
					SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
			
			sqlEx.initCause(ex);
			
			throw sqlEx;
		}
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	public boolean supportsIsolationLevel() {
		return this.hasIsolationLevels;
	}
	
	/**
	 * DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	public boolean supportsQuotedIdentifiers() {
		return this.hasQuotedIdentifiers;
	}

	/**
	 * DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	public boolean supportsTransactions() {
		return this.transactionsSupported;
	}

	/**
	 * Remove the given statement from the list of open statements
	 * 
	 * @param stmt
	 *            the Statement instance to remove
	 */
	public void unregisterStatement(Statement stmt) {
		if (this.openStatements != null) {
			synchronized (this.openStatements) {
				this.openStatements.remove(stmt);
			}
		}
	}

	/**
	 * Called by statements on their .close() to let the connection know when it
	 * is safe to set the connection back to 'default' row limits.
	 * 
	 * @param stmt
	 *            the statement releasing it's max-rows requirement
	 * @throws SQLException
	 *             if a database error occurs issuing the statement that sets
	 *             the limit default.
	 */
	public synchronized void unsetMaxRows(Statement stmt) throws SQLException {
		if (this.statementsUsingMaxRows != null) {
			Object found = this.statementsUsingMaxRows.remove(stmt);

			if ((found != null)
					&& (this.statementsUsingMaxRows.size() == 0)) {
				execSQL(null, "SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1,
						null, DEFAULT_RESULT_SET_TYPE,
						DEFAULT_RESULT_SET_CONCURRENCY, false, 
						this.database, null, false);

				this.maxRowsChanged = false;
			}
		}
	}
	
	public synchronized boolean useAnsiQuotedIdentifiers() {
		return this.useAnsiQuotes;
	}
	
	/**
	 * Has maxRows() been set?
	 * 
	 * @return DOCUMENT ME!
	 */
	public synchronized boolean useMaxRows() {
		return this.maxRowsChanged;
	}
	
	public boolean versionMeetsMinimum(int major, int minor, int subminor)
			throws SQLException {
		checkClosed();

		return this.io.versionMeetsMinimum(major, minor, subminor);
	}

	/**
	 * Returns cached metadata (or null if not cached) for the given query,
	 * which must match _exactly_.	 
	 *  
	 * This method is synchronized by the caller on getMutex(), so if
	 * calling this method from internal code in the driver, make sure it's
	 * synchronized on the mutex that guards communication with the server.
	 * 
	 * @param sql
	 *            the query that is the key to the cache
	 * 
	 * @return metadata cached for the given SQL, or none if it doesn't
	 *                  exist.
	 */
	public CachedResultSetMetaData getCachedMetaData(String sql) {
		if (this.resultSetMetadataCache != null) {
			synchronized (this.resultSetMetadataCache) {
				return (CachedResultSetMetaData) this.resultSetMetadataCache
						.get(sql);
			}
		}

		return null; // no cache exists
	}

	/**
	 * Caches CachedResultSetMetaData that has been placed in the cache using
	 * the given SQL as a key.
	 * 
	 * This method is synchronized by the caller on getMutex(), so if
	 * calling this method from internal code in the driver, make sure it's
	 * synchronized on the mutex that guards communication with the server.
	 * 
	 * @param sql the query that the metadata pertains too.
	 * @param cachedMetaData metadata (if it exists) to populate the cache.
	 * @param resultSet the result set to retreive metadata from, or apply to.
	 *
	 * @throws SQLException
	 */
	public void initializeResultsMetadataFromCache(String sql,
			CachedResultSetMetaData cachedMetaData, ResultSetInternalMethods resultSet)
			throws SQLException {

		if (cachedMetaData == null) {
			
			// read from results
			cachedMetaData = new CachedResultSetMetaData();

			// assume that users will use named-based
			// lookups
			resultSet.buildIndexMapping();
			resultSet.initializeWithMetadata();

			if (resultSet instanceof UpdatableResultSet) {
				((UpdatableResultSet)resultSet).checkUpdatability();
			}

			resultSet.populateCachedMetaData(cachedMetaData);

			this.resultSetMetadataCache.put(sql, cachedMetaData);
		} else {
			resultSet.initializeFromCachedMetaData(cachedMetaData);
			resultSet.initializeWithMetadata();
			
			if (resultSet instanceof UpdatableResultSet) {
				((UpdatableResultSet)resultSet).checkUpdatability();
			}
		}
	}
	
	/**
	 * Returns the comment that will be prepended to all statements
	 * sent to the server.
	 * 
	 * @return the comment that will be prepended to all statements
	 * sent to the server.
	 */
	public String getStatementComment() {
		return this.statementComment;
	}

	/**
	 * Sets the comment that will be prepended to all statements
	 * sent to the server. Do not use slash-star or star-slash tokens 
	 * in the comment as these will be added by the driver itself.
	 * 
	 * @param comment  the comment that will be prepended to all statements
	 * sent to the server.
	 */
	public void setStatementComment(String comment) {
		this.statementComment = comment;
	}
	
	public synchronized void reportQueryTime(long millisOrNanos) {
		this.queryTimeCount++;
		this.queryTimeSum += millisOrNanos;
		this.queryTimeSumSquares += (millisOrNanos * millisOrNanos);
		this.queryTimeMean = ((this.queryTimeMean * (this.queryTimeCount - 1)) + millisOrNanos)
				/ this.queryTimeCount;
	}
	
	public synchronized boolean isAbonormallyLongQuery(long millisOrNanos) {
		if (this.queryTimeCount < 15) {
			return false; // need a minimum amount for this to make sense
		}
		
		double stddev = Math.sqrt((this.queryTimeSumSquares - ((this.queryTimeSum*this.queryTimeSum) / this.queryTimeCount)) / (this.queryTimeCount - 1));
		
		return millisOrNanos > (this.queryTimeMean + 5 * stddev);
	}

	public void initializeExtension(Extension ex) throws SQLException {
		ex.init(this, this.props);
	}
	
	public synchronized void transactionBegun() throws SQLException {
		if (this.connectionLifecycleInterceptors != null) {
			IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {

				void forEach(Object each) throws SQLException {
					((ConnectionLifecycleInterceptor)each).transactionBegun();
				}
			};
			
			iter.doForAll();
		}
	}
	
	public synchronized void transactionCompleted() throws SQLException {
		if (this.connectionLifecycleInterceptors != null) {
			IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {

				void forEach(Object each) throws SQLException {
					((ConnectionLifecycleInterceptor)each).transactionCompleted();
				}
			};
			
			iter.doForAll();
		}
	}
	
	public boolean storesLowerCaseTableName() {
		return storesLowerCaseTableName;
	}
	
	private ExceptionInterceptor exceptionInterceptor;
	
	public ExceptionInterceptor getExceptionInterceptor() {
		return this.exceptionInterceptor;
	}
	
	public boolean getRequiresEscapingEncoder() {
		return requiresEscapingEncoder;
	}
	
	public synchronized boolean isServerLocal() throws SQLException {
		SocketFactory factory = getIO().socketFactory;
		
		if (factory instanceof SocketMetadata) {
			return ((SocketMetadata)factory).isLocallyConnected(this);
		} else {
			getLog().logWarn(Messages.getString("Connection.NoMetadataOnSocketFactory"));
			
			return false;
		}
	}
} public class LoadBalancingConnectionProxy implements InvocationHandler,
		PingTarget {

	private static Method getLocalTimeMethod;
	
	private long totalPhysicalConnections = 0;
	private long activePhysicalConnections = 0;
	private String hostToRemove = null;
	private long lastUsed = 0;
	private long transactionCount = 0;
	private ConnectionGroup connectionGroup = null;
	private String closedReason = null;

	public static final String BLACKLIST_TIMEOUT_PROPERTY_KEY = "loadBalanceBlacklistTimeout";

	static {
		try {
			getLocalTimeMethod = System.class.getMethod("nanoTime",
					new Class[0]);
		} catch (SecurityException e) {
			// ignore
		} catch (NoSuchMethodException e) {
			// ignore
		}
	}

	// Lifted from C/J 5.1's JDBC-2.0 connection pool classes, let's merge this
	// if/when this gets into 5.1
	protected class ConnectionErrorFiringInvocationHandler implements
			InvocationHandler {
		Object invokeOn = null;

		public ConnectionErrorFiringInvocationHandler(Object toInvokeOn) {
			invokeOn = toInvokeOn;
		}

		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			Object result = null;

			try {
				result = method.invoke(invokeOn, args);

				if (result != null) {
					result = proxyIfInterfaceIsJdbc(result, result.getClass());
				}
			} catch (InvocationTargetException e) {
				dealWithInvocationException(e);
			}

			return result;
		}
	}

	protected MySQLConnection currentConn;

	protected List<String> hostList;

	protected Map<String, ConnectionImpl> liveConnections;

	private Map<ConnectionImpl, String> connectionsToHostsMap;

	private long[] responseTimes;

	private Map<String, Integer> hostsToListIndexMap;

	private boolean inTransaction = false;

	private long transactionStartTime = 0;

	private Properties localProps;

	private boolean isClosed = false;

	private BalanceStrategy balancer;

	private int retriesAllDown;

	private static Map<String, Long> globalBlacklist = new HashMap<String, Long>();

	private int globalBlacklistTimeout = 0;
	
	private long connectionGroupProxyID = 0;

	private LoadBalanceExceptionChecker exceptionChecker;

	private Map<Class, Boolean> jdbcInterfacesForProxyCache = new HashMap<Class, Boolean>();
	
	private MySQLConnection thisAsConnection = null;

	private int autoCommitSwapThreshold = 0;
	
	private static Constructor JDBC_4_LB_CONNECTION_CTOR;
	
	static {
		if(Util.isJdbc4()){
			try {
				JDBC_4_LB_CONNECTION_CTOR =  Class.forName(
						"com.mysql.jdbc.JDBC4LoadBalancedMySQLConnection").getConstructor(
						new Class[] { LoadBalancingConnectionProxy.class});
			} catch (SecurityException e) {
				throw new RuntimeException(e);
			} catch (NoSuchMethodException e) {
				throw new RuntimeException(e);
			} catch (ClassNotFoundException e) {
				throw new RuntimeException(e);
			}
		}
	}
	
	
	




	/**
	 * Creates a proxy for java.sql.Connection that routes requests between the
	 * given list of host:port and uses the given properties when creating
	 * connections.
	 * 
	 * @param hosts
	 * @param props
	 * @throws SQLException
	 */
	LoadBalancingConnectionProxy(List<String> hosts, Properties props)
			throws SQLException {
		String group = props.getProperty("loadBalanceConnectionGroup",
				null);
		boolean enableJMX = false;
		String enableJMXAsString = props.getProperty("loadBalanceEnableJMX",
				"false");
		try{
			enableJMX = Boolean.parseBoolean(enableJMXAsString);
		} catch (Exception e){
			throw SQLError.createSQLException(Messages.getString(
					"LoadBalancingConnectionProxy.badValueForLoadBalanceEnableJMX",
					new Object[] { enableJMXAsString }),
					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);			
		}
		
		if(group != null){
			this.connectionGroup = ConnectionGroupManager.getConnectionGroupInstance(group);
			if(enableJMX){
				ConnectionGroupManager.registerJmx();
			}
			this.connectionGroupProxyID = this.connectionGroup.registerConnectionProxy(this, hosts);
			hosts = new ArrayList<String>(this.connectionGroup.getInitialHosts());
		}
		
		this.hostList = hosts;

		int numHosts = this.hostList.size();

		this.liveConnections = new HashMap<String, ConnectionImpl>(numHosts);
		this.connectionsToHostsMap = new HashMap<ConnectionImpl, String>(numHosts);
		this.responseTimes = new long[numHosts];
		this.hostsToListIndexMap = new HashMap<String, Integer>(numHosts);

		this.localProps = (Properties) props.clone();
		this.localProps.remove(NonRegisteringDriver.HOST_PROPERTY_KEY);
		this.localProps.remove(NonRegisteringDriver.PORT_PROPERTY_KEY);

		for (int i = 0; i < numHosts; i++) {
			this.hostsToListIndexMap.put(this.hostList.get(i), Integer.valueOf(i));
			this.localProps.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + "."
					+ (i + 1));
			this.localProps.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + "."
					+ (i + 1));
		}

		this.localProps.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY);
		this.localProps.setProperty("useLocalSessionState", "true");

		String strategy = this.localProps.getProperty("loadBalanceStrategy",
				"random");
		
		String lbExceptionChecker = this.localProps.getProperty("loadBalanceExceptionChecker",
		"com.mysql.jdbc.StandardLoadBalanceExceptionChecker");


		String retriesAllDownAsString = this.localProps.getProperty(
				"retriesAllDown", "120");

		try {
			this.retriesAllDown = Integer.parseInt(retriesAllDownAsString);
		} catch (NumberFormatException nfe) {
			throw SQLError.createSQLException(Messages.getString(
					"LoadBalancingConnectionProxy.badValueForRetriesAllDown",
					new Object[] { retriesAllDownAsString }),
					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
		}
		String blacklistTimeoutAsString = this.localProps.getProperty(
				BLACKLIST_TIMEOUT_PROPERTY_KEY, "0");

		try {
			this.globalBlacklistTimeout = Integer
					.parseInt(blacklistTimeoutAsString);
		} catch (NumberFormatException nfe) {
			throw SQLError
					.createSQLException(
							Messages
									.getString(
											"LoadBalancingConnectionProxy.badValueForLoadBalanceBlacklistTimeout",
											new Object[] { retriesAllDownAsString }),
							SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
		}

		if ("random".equals(strategy)) {
			this.balancer = (BalanceStrategy) Util.loadExtensions(null, props,
					"com.mysql.jdbc.RandomBalanceStrategy",
					"InvalidLoadBalanceStrategy", null).get(0);
		} else if ("bestResponseTime".equals(strategy)) {
			this.balancer = (BalanceStrategy) Util.loadExtensions(null, props,
					"com.mysql.jdbc.BestResponseTimeBalanceStrategy",
					"InvalidLoadBalanceStrategy", null).get(0);
		} else {
			this.balancer = (BalanceStrategy) Util.loadExtensions(null, props,
					strategy, "InvalidLoadBalanceStrategy", null).get(0);
		}

		String autoCommitSwapThresholdAsString = props.getProperty("loadBalanceAutoCommitStatementThreshold",
		"0");
		try {
			this.autoCommitSwapThreshold  = Integer.parseInt(autoCommitSwapThresholdAsString);
		} catch (NumberFormatException nfe) {
			throw SQLError.createSQLException(Messages.getString(
					"LoadBalancingConnectionProxy.badValueForLoadBalanceAutoCommitStatementThreshold",
					new Object[] { autoCommitSwapThresholdAsString }),
					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
		}
		
		String autoCommitSwapRegex = props.getProperty("loadBalanceAutoCommitStatementRegex","");
		if(!("".equals(autoCommitSwapRegex))){
			try{
				"".matches(autoCommitSwapRegex);
			} catch (Exception e){
				throw SQLError.createSQLException(Messages.getString(
						"LoadBalancingConnectionProxy.badValueForLoadBalanceAutoCommitStatementRegex",
						new Object[] { autoCommitSwapRegex }),
						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
			}
		}
		
		if(this.autoCommitSwapThreshold > 0){
			String statementInterceptors = this.localProps.getProperty("statementInterceptors");
			if(statementInterceptors == null){
				this.localProps.setProperty("statementInterceptors", "com.mysql.jdbc.LoadBalancedAutoCommitInterceptor");
			} else if(statementInterceptors.length() > 0){
				this.localProps.setProperty("statementInterceptors", statementInterceptors + ",com.mysql.jdbc.LoadBalancedAutoCommitInterceptor");
			}
			props.setProperty("statementInterceptors", this.localProps.getProperty("statementInterceptors"));
			
		}
		this.balancer.init(null, props);
		

		this.exceptionChecker = (LoadBalanceExceptionChecker) Util.loadExtensions(null, props,
				lbExceptionChecker, "InvalidLoadBalanceExceptionChecker", null).get(0);
		this.exceptionChecker.init(null, props);

		if(Util.isJdbc4()  || JDBC_4_LB_CONNECTION_CTOR != null){
			thisAsConnection =  (MySQLConnection) Util.handleNewInstance(JDBC_4_LB_CONNECTION_CTOR,
					new Object[] {this}, null);
		}else{
			thisAsConnection = new LoadBalancedMySQLConnection(this);
		}
		pickNewConnection();
		
		
	}

	/**
	 * Creates a new physical connection for the given host:port and updates
	 * required internal mappings and statistics for that connection.
	 * 
	 * @param hostPortSpec
	 * @return
	 * @throws SQLException
	 */
	public synchronized ConnectionImpl createConnectionForHost(
			String hostPortSpec) throws SQLException {
		Properties connProps = (Properties) this.localProps.clone();

		String[] hostPortPair = NonRegisteringDriver
				.parseHostPortPair(hostPortSpec);
		String hostName = hostPortPair[NonRegisteringDriver.HOST_NAME_INDEX];
		String portNumber = hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX];
		String dbName = connProps
				.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY);

		if (hostName == null) {
			throw new SQLException(
					"Could not find a hostname to start a connection to");
		}
		if (portNumber == null) {
			portNumber = "3306";// use default
		}

		connProps.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, hostName);
		connProps.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY,
				portNumber);
		connProps.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1",
				hostName);
		connProps.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1",
				portNumber);
		connProps.setProperty(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY, "1");
		connProps.setProperty("roundRobinLoadBalance", "false"); // make sure we
																	// don't
																	// pickup
																	// the
																	// default
																	// value

		ConnectionImpl conn = (ConnectionImpl) ConnectionImpl.getInstance(
				hostName, Integer.parseInt(portNumber), connProps, dbName,
				"jdbc:mysql://" + hostName + ":" + portNumber + "/");
		
		

		this.liveConnections.put(hostPortSpec, conn);
		this.connectionsToHostsMap.put(conn, hostPortSpec);
		

		this.activePhysicalConnections++;
		this.totalPhysicalConnections++;

		conn.setProxy(this.thisAsConnection);

		return conn;
	}

	/**
	 * @param e
	 * @throws SQLException
	 * @throws Throwable
	 * @throws InvocationTargetException
	 */
	void dealWithInvocationException(InvocationTargetException e)
			throws SQLException, Throwable, InvocationTargetException {
		Throwable t = e.getTargetException();

		if (t != null) {
			if (t instanceof SQLException && shouldExceptionTriggerFailover((SQLException) t )) {
				invalidateCurrentConnection();
				pickNewConnection();
			}

			throw t;
		}

		throw e;
	}

	/**
	 * Closes current connection and removes it from required mappings.
	 * 
	 * @throws SQLException
	 */
	synchronized void invalidateCurrentConnection() throws SQLException {
		try {
			if (!this.currentConn.isClosed()) {
				this.currentConn.close();
			}

		} finally {
			// add host to the global blacklist, if enabled
			if (this.isGlobalBlacklistEnabled()) {
				this.addToGlobalBlacklist(this.connectionsToHostsMap
						.get(this.currentConn));

			}
			// remove from liveConnections
			this.liveConnections.remove(this.connectionsToHostsMap
					.get(this.currentConn));
			Object mappedHost = this.connectionsToHostsMap
					.remove(this.currentConn);
			if (mappedHost != null
					&& this.hostsToListIndexMap.containsKey(mappedHost)) {
				int hostIndex = (this.hostsToListIndexMap
						.get(mappedHost)).intValue();
				// reset the statistics for the host
				synchronized (this.responseTimes) {
					this.responseTimes[hostIndex] = 0;
				}
			}
		}
	}

	private void closeAllConnections() {
		synchronized (this) {
			// close all underlying connections
			Iterator<ConnectionImpl> allConnections = this.liveConnections.values().iterator();

			while (allConnections.hasNext()) {
				try {
					this.activePhysicalConnections--;
					allConnections.next().close();
				} catch (SQLException e) {
				}
			}

			if (!this.isClosed) {
				this.balancer.destroy();
				if(this.connectionGroup != null){
					this.connectionGroup.closeConnectionProxy(this);
				}
			}

			this.liveConnections.clear();
			this.connectionsToHostsMap.clear();
		}

	}
	
		
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		return this.invoke(proxy, method, args, true);
	}

	/**
	 * Proxies method invocation on the java.sql.Connection interface, trapping
	 * "close", "isClosed" and "commit/rollback" (to switch connections for load
	 * balancing).
	 */
	public synchronized Object invoke(Object proxy, Method method, Object[] args, boolean swapAtTransactionBoundary)
			throws Throwable {

		String methodName = method.getName();
		
		if("getLoadBalanceSafeProxy".equals(methodName)){
			return this.currentConn;
		}

		if ("equals".equals(methodName) && args.length == 1) {
			if (args[0] instanceof Proxy) {
				return Boolean.valueOf((((Proxy) args[0]).equals(this)));
			}
			return Boolean.valueOf(this.equals(args[0]));
		}

		if ("hashCode".equals(methodName)) {
			return Integer.valueOf(this.hashCode());
		}

		if ("close".equals(methodName)) {
			closeAllConnections();

			this.isClosed = true;
			this.closedReason = "Connection explicitly closed.";

			return null;
		}

		if ("isClosed".equals(methodName)) {
			return Boolean.valueOf(this.isClosed);
		}

		if (this.isClosed) {
			String reason = "No operations allowed after connection closed.";
			if(this.closedReason != null){
				reason += ("  " + this.closedReason);
			}
			throw SQLError.createSQLException(
					reason,
					SQLError.SQL_STATE_CONNECTION_NOT_OPEN, null /*
																 * no access to
																 * a interceptor
																 * here...
																 */);
		}

		if (!inTransaction) {
			this.inTransaction = true;
			this.transactionStartTime = getLocalTimeBestResolution();
			this.transactionCount++;
		}

		Object result = null;

		try {
			this.lastUsed = System.currentTimeMillis();
			result = method.invoke(thisAsConnection, args);

			if (result != null) {
				if (result instanceof com.mysql.jdbc.Statement) {
					((com.mysql.jdbc.Statement) result).setPingTarget(this);
				}

				result = proxyIfInterfaceIsJdbc(result, result.getClass());
			}
		} catch (InvocationTargetException e) {
			dealWithInvocationException(e);
		} finally {
			if (swapAtTransactionBoundary && ("commit".equals(methodName) || "rollback".equals(methodName))) {
				this.inTransaction = false;

				// Update stats
				String host = this.connectionsToHostsMap.get(this.currentConn);
				// avoid NPE if the connection has already been removed from
				// connectionsToHostsMap
				// in invalidateCurrenctConnection()
				if (host != null) {
					synchronized (this.responseTimes) {
						int hostIndex = (this.hostsToListIndexMap
								.get(host)).intValue();

						if(hostIndex < this.responseTimes.length){
							this.responseTimes[hostIndex] = getLocalTimeBestResolution()
									- this.transactionStartTime;
						}
					}
				}
				pickNewConnection();
			}
		}

		return result;
	}

	/**
	 * Picks the "best" connection to use for the next transaction based on the
	 * BalanceStrategy in use.
	 * 
	 * @throws SQLException
	 */
	protected synchronized void pickNewConnection() throws SQLException {
		if (this.currentConn == null) { // startup
			this.currentConn = this.balancer.pickConnection(this, Collections
					.unmodifiableList(this.hostList), Collections
					.unmodifiableMap(this.liveConnections),
					this.responseTimes.clone(), this.retriesAllDown);
			return;
		}
		
		if(this.currentConn.isClosed()){
			invalidateCurrentConnection();
		}

		int pingTimeout = this.currentConn.getLoadBalancePingTimeout();
		boolean pingBeforeReturn = this.currentConn.getLoadBalanceValidateConnectionOnSwapServer();
		
		for(int hostsTried = 0, hostsToTry = this.hostList.size(); hostsTried <= hostsToTry; hostsTried++){
			try{
				ConnectionImpl newConn = this.balancer.pickConnection(

					this, Collections.unmodifiableList(this.hostList), Collections
							.unmodifiableMap(this.liveConnections),
					this.responseTimes.clone(), this.retriesAllDown);
		
				if (this.currentConn != null) {
					if(pingBeforeReturn){
						if(pingTimeout == 0){
							newConn.ping();
						} else {
							newConn.pingInternal(true, pingTimeout);
						}
					}
					
					syncSessionState(this.currentConn, newConn);
				}
				
				this.currentConn = newConn;
				return;
			} catch (SQLException e){
				
				if (shouldExceptionTriggerFailover(e)) {
					// connection error, close up shop on current
					// connection
					invalidateCurrentConnection();
				}
			}

		}
		// no hosts available to swap connection to, close up.
		this.isClosed = true;
		this.closedReason = "Connection closed after inability to pick valid new connection during fail-over.";

	}

	/**
	 * Recursively checks for interfaces on the given object to determine if it
	 * implements a java.sql interface, and if so, proxies the instance so that
	 * we can catch and fire SQL errors.
	 * 
	 * @param toProxy
	 * @param clazz
	 * @return
	 */
	Object proxyIfInterfaceIsJdbc(Object toProxy, Class clazz) {
		
		if(isInterfaceJdbc(clazz)){
			
		Class[] interfacesToProxy = getAllInterfacesToProxy(clazz);
		
		return Proxy.newProxyInstance(toProxy.getClass()
				.getClassLoader(), interfacesToProxy,
				createConnectionProxy(toProxy));
		}

		return toProxy;
	}

	private Map<Class, Class[]> allInterfacesToProxy = new HashMap<Class, Class[]>();
	
	private Class[] getAllInterfacesToProxy(Class clazz) {
		Class[] interfacesToProxy = (Class[]) this.allInterfacesToProxy.get(clazz);
		
		if (interfacesToProxy != null) {
			return interfacesToProxy;
		}
		
		List<Class> interfaces = new LinkedList<Class>();
		
		Class superClass = clazz;
		
		while (!(superClass.equals(Object.class))) {
			Class[] declared = superClass.getInterfaces();
			
			for (int i = 0; i < declared.length; i++) {
				interfaces.add(declared[i]);
			}
			
			superClass = superClass.getSuperclass();
		}
		
		interfacesToProxy = new Class[interfaces.size()];
		interfaces.toArray(interfacesToProxy);
		
		this.allInterfacesToProxy.put(clazz, interfacesToProxy);
		
		return interfacesToProxy;
	}
	
	
	private boolean isInterfaceJdbc(Class clazz){
		if(this.jdbcInterfacesForProxyCache.containsKey(clazz)){
			return (this.jdbcInterfacesForProxyCache.get(clazz)).booleanValue();
		}
		
		Class[] interfaces = clazz.getInterfaces();

		for (int i = 0; i < interfaces.length; i++) {
			String packageName = interfaces[i].getPackage().getName();

			if ("java.sql".equals(packageName)
					|| "javax.sql".equals(packageName)
					|| "com.mysql.jdbc".equals(packageName)) {
				this.jdbcInterfacesForProxyCache.put(clazz, Boolean.valueOf(true));
				
				return true;
			}

			if(isInterfaceJdbc(interfaces[i])){
				this.jdbcInterfacesForProxyCache.put(clazz, Boolean.valueOf(true));
				
				return true;
			}
		}

		this.jdbcInterfacesForProxyCache.put(clazz, Boolean.valueOf(false));
		return false;
		
	}

	protected ConnectionErrorFiringInvocationHandler createConnectionProxy(
			Object toProxy) {
		return new ConnectionErrorFiringInvocationHandler(toProxy);
	}

	/**
	 * Returns best-resolution representation of local time, using nanoTime() if
	 * available, otherwise defaulting to currentTimeMillis().
	 */
	private static long getLocalTimeBestResolution() {
		if (getLocalTimeMethod != null) {
			try {
				return ((Long) getLocalTimeMethod.invoke(null, (Object[])null))
						.longValue();
			} catch (IllegalArgumentException e) {
				// ignore - we fall through to currentTimeMillis()
			} catch (IllegalAccessException e) {
				// ignore - we fall through to currentTimeMillis()
			} catch (InvocationTargetException e) {
				// ignore - we fall through to currentTimeMillis()
			}
		}

		return System.currentTimeMillis();
	}

	public synchronized void doPing() throws SQLException {
		SQLException se = null;
		boolean foundHost = false;
		int pingTimeout = this.currentConn.getLoadBalancePingTimeout();
		synchronized (this) {
			for (Iterator<String> i = this.hostList.iterator(); i.hasNext();) {
				String host = i.next();
				ConnectionImpl conn = this.liveConnections.get(host);
				if (conn == null) {
					continue;
				}
				try {
					if(pingTimeout == 0){
						conn.ping();
					} else {
						conn.pingInternal(true, pingTimeout);
					}
					foundHost = true;
				} catch (SQLException e) {
					this.activePhysicalConnections--;
					// give up if it is the current connection, otherwise NPE
					// faking resultset later.
					if (host.equals(this.connectionsToHostsMap
							.get(this.currentConn))) {
						// clean up underlying connections, since connection
						// pool won't do it
						closeAllConnections();
						this.isClosed = true;
						this.closedReason = "Connection closed because ping of current connection failed.";
						throw e;
					}

					// if the Exception is caused by ping connection lifetime
					// checks, don't add to blacklist
					if (e
							.getMessage()
							.equals(
									Messages
											.getString("Connection.exceededConnectionLifetime"))) {
						// only set the return Exception if it's null
						if (se == null) {
							se = e;
						}
					} else {
						// overwrite the return Exception no matter what
						se = e;
						if (this.isGlobalBlacklistEnabled()) {
							this.addToGlobalBlacklist(host);
						}
					}
					// take the connection out of the liveConnections Map
					this.liveConnections.remove(this.connectionsToHostsMap
							.get(conn));
				}
			}
		}
		// if there were no successful pings
		if (!foundHost) {
			closeAllConnections();
			this.isClosed = true;
			this.closedReason = "Connection closed due to inability to ping any active connections.";
			// throw the stored Exception, if exists
			if (se != null) {
				throw se;
			}
			// or create a new SQLException and throw it, must be no
			// liveConnections
			((ConnectionImpl) this.currentConn)
					.throwConnectionClosedException();
		}
	}

	public void addToGlobalBlacklist(String host, long timeout) {
		if (this.isGlobalBlacklistEnabled()) {
			synchronized (globalBlacklist) {
				globalBlacklist.put(host, Long.valueOf(timeout));
			}
		}
	}
	
	public void addToGlobalBlacklist(String host){
		addToGlobalBlacklist(host, System.currentTimeMillis()
						+ this.globalBlacklistTimeout);
		
	}

	public boolean isGlobalBlacklistEnabled() {
		return (this.globalBlacklistTimeout > 0);
	}

	public synchronized Map<String, Long> getGlobalBlacklist() {
		if (!this.isGlobalBlacklistEnabled()) {
			String localHostToRemove = this.hostToRemove;
			
			if(hostToRemove != null){
				HashMap<String, Long> fakedBlacklist = new HashMap<String, Long>();
				fakedBlacklist.put(localHostToRemove, Long.valueOf(System.currentTimeMillis() + 5000));
				return fakedBlacklist;
			}
			
			return new HashMap<String, Long>(1);
		}

		// Make a local copy of the blacklist
		Map<String, Long> blacklistClone = new HashMap<String, Long>(globalBlacklist.size());
		// Copy everything from synchronized global blacklist to local copy for
		// manipulation
		synchronized (globalBlacklist) {
			blacklistClone.putAll(globalBlacklist);
		}
		Set<String> keys = blacklistClone.keySet();

		// we're only interested in blacklisted hosts that are in the hostList
		keys.retainAll(this.hostList);
		if (keys.size() == this.hostList.size()) {
			// return an empty blacklist, let the BalanceStrategy
			// implementations try to connect to everything
			// since it appears that all hosts are unavailable - we don't want
			// to wait for
			// loadBalanceBlacklistTimeout to expire.
			return new HashMap<String, Long>(1);
		}

		// Don't need to synchronize here as we using a local copy
		for (Iterator<String> i = keys.iterator(); i.hasNext();) {
			String host = i.next();
			// OK if null is returned because another thread already purged Map
			// entry.
			Long timeout = globalBlacklist.get(host);
			if (timeout != null
					&& timeout.longValue() < System.currentTimeMillis()) {
				// Timeout has expired, remove from blacklist
				synchronized (globalBlacklist) {
					globalBlacklist.remove(host);
				}
				i.remove();
			}

		}
		
		return blacklistClone;
	}

	public boolean shouldExceptionTriggerFailover(SQLException ex){
		return this.exceptionChecker.shouldExceptionTriggerFailover(ex);
		
	}
	
	public void removeHostWhenNotInUse(String host)
			throws SQLException {
		int timeBetweenChecks = 1000;
		long timeBeforeHardFail = 15000;
		
		synchronized (this) {
			addToGlobalBlacklist(host, timeBeforeHardFail + 1000);
			
			long cur = System.currentTimeMillis();
			
			while (System.currentTimeMillis() - timeBeforeHardFail < cur) {
	
				this.hostToRemove = host;
				
				if (!host.equals(this.currentConn.getHost())) {
					removeHost(host);
					return;
				}
			}
		}
		
		try {
			Thread.sleep(timeBetweenChecks);
		} catch (InterruptedException e) {
			// better to swallow this and retry.
		}

		removeHost(host);
	}
	
	public synchronized void removeHost(String host) throws SQLException {

		if (this.connectionGroup != null) {
			if (this.connectionGroup.getInitialHosts().size() == 1
					&& this.connectionGroup.getInitialHosts().contains(host)) {
				throw SQLError.createSQLException(
						"Cannot remove only configured host.", null);
			}

			this.hostToRemove = host;
			
			if (host.equals(this.currentConn.getHost())) {
				closeAllConnections();
			} else {
				this.connectionsToHostsMap.remove(this.liveConnections
						.remove(host));
				Integer idx = this.hostsToListIndexMap.remove(host);
				long[] newResponseTimes = new long[this.responseTimes.length - 1];
				int newIdx = 0;
				for (Iterator<String> i = this.hostList.iterator(); i.hasNext(); newIdx++) {
					String copyHost = i.next();
					if (idx != null
							&& idx.intValue() < this.responseTimes.length) {
						newResponseTimes[newIdx] = this.responseTimes[idx
								.intValue()];
						this.hostsToListIndexMap.put(copyHost,
								Integer.valueOf(newIdx));
					}
				}
				this.responseTimes = newResponseTimes;
			}
		}

	}

	public synchronized boolean addHost(String host) {

		if (this.hostsToListIndexMap.containsKey(host)) {
			return false;
		}
		
		long[] newResponseTimes = new long[this.responseTimes.length + 1];
		
		for (int i = 0; i < this.responseTimes.length; i++) {
			newResponseTimes[i] = this.responseTimes[i];
		}
		
		this.responseTimes = newResponseTimes;
		this.hostList.add(host);
		this.hostsToListIndexMap.put(host,
				Integer.valueOf(this.responseTimes.length - 1));
		
		return true;
	}
	
	public synchronized long getLastUsed(){
		return this.lastUsed;
	}
	
	public synchronized boolean inTransaction(){
		return this.inTransaction;
	}
	
	public synchronized long getTransactionCount(){
		return this.transactionCount;
	}
	
	public synchronized long getActivePhysicalConnectionCount(){
		return this.activePhysicalConnections;
	}
	
	public synchronized long getTotalPhysicalConnectionCount(){
		return this.totalPhysicalConnections;
	}
	
	public synchronized long getConnectionGroupProxyID(){
		return this.connectionGroupProxyID;
	}
	
	public synchronized String getCurrentActiveHost() {
		MySQLConnection c = this.currentConn;
		if(c != null){
			Object o = this.connectionsToHostsMap.get(c);
			if(o != null){
				return o.toString();
			}
		}
		return null;
	}
	
	public synchronized long getCurrentTransactionDuration(){

		if (this.inTransaction && this.transactionStartTime > 0) {
			return getLocalTimeBestResolution() - this.transactionStartTime;
		}
		
		return 0;
	}
	
	protected void syncSessionState(Connection initial, Connection target)
			throws SQLException {
		if (initial == null || target == null) {
			return;
		}
		target.setAutoCommit(initial.getAutoCommit());
		target.setCatalog(initial.getCatalog());
		target.setTransactionIsolation(initial.getTransactionIsolation());
		target.setReadOnly(initial.isReadOnly());

	}

} public interface MySQLConnection extends Connection, ConnectionProperties {
	
	public boolean isProxySet();

	void abortInternal() throws SQLException;

	void checkClosed() throws SQLException;

	void createNewIO(boolean isForReconnect) throws SQLException;

	void dumpTestcaseQuery(String query);

	Connection duplicate() throws SQLException;

	ResultSetInternalMethods execSQL(StatementImpl callingStatement,
			String sql, int maxRows, Buffer packet, int resultSetType,
			int resultSetConcurrency, boolean streamResults, String catalog,
			Field[] cachedMetadata) throws SQLException;

	ResultSetInternalMethods execSQL(StatementImpl callingStatement,
			String sql, int maxRows, Buffer packet, int resultSetType,
			int resultSetConcurrency, boolean streamResults, String catalog,
			Field[] cachedMetadata, boolean isBatch) throws SQLException;

	String extractSqlFromPacket(String possibleSqlQuery, Buffer queryPacket,
			int endOfQueryPacketPosition) throws SQLException;

	StringBuffer generateConnectionCommentBlock(StringBuffer buf);

	int getActiveStatementCount();

	int getAutoIncrementIncrement();

	CachedResultSetMetaData getCachedMetaData(String sql);

	Calendar getCalendarInstanceForSessionOrNew();

	Timer getCancelTimer();

	String getCharacterSetMetadata();

	SingleByteCharsetConverter getCharsetConverter(String javaEncodingName)
			throws SQLException;

	String getCharsetNameForIndex(int charsetIndex) throws SQLException;

	TimeZone getDefaultTimeZone();

	String getErrorMessageEncoding();

	ExceptionInterceptor getExceptionInterceptor();

	String getHost();

	long getId();

	long getIdleFor();

	MysqlIO getIO() throws SQLException;

	Log getLog() throws SQLException;

	int getMaxBytesPerChar(String javaCharsetName) throws SQLException;

	java.sql.Statement getMetadataSafeStatement() throws SQLException;

	int getNetBufferLength();

	Properties getProperties();

	boolean getRequiresEscapingEncoder();

	String getServerCharacterEncoding();

	int getServerMajorVersion();

	int getServerMinorVersion();

	int getServerSubMinorVersion();

	TimeZone getServerTimezoneTZ();

	String getServerVariable(String variableName);

	String getServerVersion();

	Calendar getSessionLockedCalendar();

	String getStatementComment();

	List getStatementInterceptorsInstances();

	String getURL();

	String getUser();

	Calendar getUtcCalendar();

	void incrementNumberOfPreparedExecutes();

	void incrementNumberOfPrepares();

	void incrementNumberOfResultSetsCreated();

	void initializeResultsMetadataFromCache(String sql,
			CachedResultSetMetaData cachedMetaData,
			ResultSetInternalMethods resultSet) throws SQLException;

	void initializeSafeStatementInterceptors() throws SQLException;

	boolean isAbonormallyLongQuery(long millisOrNanos);

	boolean isClientTzUTC();

	boolean isCursorFetchEnabled() throws SQLException;

	boolean isReadInfoMsgEnabled();

	public boolean isReadOnly() throws SQLException;

	boolean isRunningOnJDK13();

	boolean isServerTzUTC();

	boolean lowerCaseTableNames();

	void maxRowsChanged(Statement stmt);

	void pingInternal(boolean checkForClosedConnection, int timeoutMillis)
			throws SQLException;

	void realClose(boolean calledExplicitly, boolean issueRollback,
			boolean skipLocalTeardown, Throwable reason) throws SQLException;

	void recachePreparedStatement(ServerPreparedStatement pstmt)
			throws SQLException;

	void registerQueryExecutionTime(long queryTimeMs);

	void registerStatement(Statement stmt);

	void reportNumberOfTablesAccessed(int numTablesAccessed);

	boolean serverSupportsConvertFn() throws SQLException;

	void setProxy(MySQLConnection proxy);

	void setReadInfoMsgEnabled(boolean flag);

	void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException;

	void shutdownServer() throws SQLException;

	boolean storesLowerCaseTableName();

	void throwConnectionClosedException() throws SQLException;

	void transactionBegun() throws SQLException;

	void transactionCompleted() throws SQLException;

	void unregisterStatement(Statement stmt);

	void unSafeStatementInterceptors() throws SQLException;

	void unsetMaxRows(Statement stmt) throws SQLException;

	boolean useAnsiQuotedIdentifiers();

	boolean useMaxRows();
	
	MySQLConnection getLoadBalanceSafeProxy();
	

} public class MysqlDataTruncation extends DataTruncation {

	private String message;

	private int vendorErrorCode;
	
	/**
	 * Creates a new MysqlDataTruncation exception/warning.
	 * 
	 * @param message
	 *            the message from the server
	 * @param index
	 *            of column or parameter
	 * @param parameter
	 *            was a parameter?
	 * @param read
	 *            was truncated on read?
	 * @param dataSize
	 *            size requested
	 * @param transferSize
	 *            size actually used
	 */
	public MysqlDataTruncation(String message, int index, boolean parameter,
			boolean read, int dataSize, int transferSize, int vendorErrorCode) {
		super(index, parameter, read, dataSize, transferSize);

		this.message = message;
		this.vendorErrorCode = vendorErrorCode;
	}

	public int getErrorCode() {
		return this.vendorErrorCode;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Throwable#getMessage()
	 */
	public String getMessage() {
		return super.getMessage() + ": " + this.message; //$NON-NLS-1$
	}
} public class NonRegisteringDriver implements java.sql.Driver {
	private static final String ALLOWED_QUOTES = "\"'";

	private static final String REPLICATION_URL_PREFIX = "jdbc:mysql:replication://";

	private static final String URL_PREFIX = "jdbc:mysql://";

	private static final String MXJ_URL_PREFIX = "jdbc:mysql:mxj://";

	private static final String LOADBALANCE_URL_PREFIX = "jdbc:mysql:loadbalance://";

	/**
	 * Key used to retreive the database value from the properties instance
	 * passed to the driver.
	 */
	public static final String DBNAME_PROPERTY_KEY = "DBNAME";

	/** Should the driver generate debugging output? */
	public static final boolean DEBUG = false;

	/** Index for hostname coming out of parseHostPortPair(). */
	public final static int HOST_NAME_INDEX = 0;

	/**
	 * Key used to retreive the hostname value from the properties instance
	 * passed to the driver.
	 */
	public static final String HOST_PROPERTY_KEY = "HOST";

	public static final String NUM_HOSTS_PROPERTY_KEY = "NUM_HOSTS";
	
	/**
	 * Key used to retreive the password value from the properties instance
	 * passed to the driver.
	 */
	public static final String PASSWORD_PROPERTY_KEY = "password";

	/** Index for port # coming out of parseHostPortPair(). */
	public final static int PORT_NUMBER_INDEX = 1;

	/**
	 * Key used to retreive the port number value from the properties instance
	 * passed to the driver.
	 */
	public static final String PORT_PROPERTY_KEY = "PORT";

	public static final String PROPERTIES_TRANSFORM_KEY = "propertiesTransform";

	/** Should the driver generate method-call traces? */
	public static final boolean TRACE = false;

	public static final String USE_CONFIG_PROPERTY_KEY = "useConfigs";

	/**
	 * Key used to retreive the username value from the properties instance
	 * passed to the driver.
	 */
	public static final String USER_PROPERTY_KEY = "user";

	public static final String PROTOCOL_PROPERTY_KEY = "PROTOCOL";

	public static final String PATH_PROPERTY_KEY = "PATH";

	/**
	 * Gets the drivers major version number
	 * 
	 * @return the drivers major version number
	 */
	static int getMajorVersionInternal() {
		return safeIntParse("@MYSQL_CJ_MAJOR_VERSION@"); //$NON-NLS-1$
	}

	/**
	 * Get the drivers minor version number
	 * 
	 * @return the drivers minor version number
	 */
	static int getMinorVersionInternal() {
		return safeIntParse("@MYSQL_CJ_MINOR_VERSION@"); //$NON-NLS-1$
	}

	/**
	 * Parses hostPortPair in the form of [host][:port] into an array, with the
	 * element of index HOST_NAME_INDEX being the host (or null if not
	 * specified), and the element of index PORT_NUMBER_INDEX being the port (or
	 * null if not specified).
	 * 
	 * @param hostPortPair
	 *            host and port in form of of [host][:port]
	 * 
	 * @return array containing host and port as Strings
	 * 
	 * @throws SQLException
	 *             if a parse error occurs
	 */
	protected static String[] parseHostPortPair(String hostPortPair)
			throws SQLException {
		

		String[] splitValues = new String[2];

		if (StringUtils.startsWithIgnoreCaseAndWs(hostPortPair, "address")) {
			splitValues[HOST_NAME_INDEX] = hostPortPair.trim();
			splitValues[PORT_NUMBER_INDEX] = null;
			
			return splitValues;
		}
		
		int portIndex = hostPortPair.indexOf(":"); //$NON-NLS-1$
		
		String hostname = null;

		if (portIndex != -1) {
			if ((portIndex + 1) < hostPortPair.length()) {
				String portAsString = hostPortPair.substring(portIndex + 1);
				hostname = hostPortPair.substring(0, portIndex);

				splitValues[HOST_NAME_INDEX] = hostname;

				splitValues[PORT_NUMBER_INDEX] = portAsString;
			} else {
				throw SQLError.createSQLException(Messages
						.getString("NonRegisteringDriver.37"), //$NON-NLS-1$
						SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null);
			}
		} else {
			splitValues[HOST_NAME_INDEX] = hostPortPair;
			splitValues[PORT_NUMBER_INDEX] = null;
		}

		return splitValues;
	}

	private static int safeIntParse(String intAsString) {
		try {
			return Integer.parseInt(intAsString);
		} catch (NumberFormatException nfe) {
			return 0;
		}
	}

	/**
	 * Construct a new driver and register it with DriverManager
	 * 
	 * @throws SQLException
	 *             if a database error occurs.
	 */
	public NonRegisteringDriver() throws SQLException {
		// Required for Class.forName().newInstance()
	}

	/**
	 * Typically, drivers will return true if they understand the subprotocol
	 * specified in the URL and false if they don't. This driver's protocols
	 * start with jdbc:mysql:
	 * 
	 * @param url
	 *            the URL of the driver
	 * 
	 * @return true if this driver accepts the given URL
	 * 
	 * @exception SQLException
	 *                if a database-access error occurs
	 * 
	 * @see java.sql.Driver#acceptsURL
	 */
	public boolean acceptsURL(String url) throws SQLException {
		return (parseURL(url, null) != null);
	}

	//
	// return the database name property
	//

	/**
	 * Try to make a database connection to the given URL. The driver should
	 * return "null" if it realizes it is the wrong kind of driver to connect to
	 * the given URL. This will be common, as when the JDBC driverManager is
	 * asked to connect to a given URL, it passes the URL to each loaded driver
	 * in turn.
	 * 
	 * <p>
	 * The driver should raise an SQLException if it is the right driver to
	 * connect to the given URL, but has trouble connecting to the database.
	 * </p>
	 * 
	 * <p>
	 * The java.util.Properties argument can be used to pass arbitrary string
	 * tag/value pairs as connection arguments.
	 * </p>
	 * 
	 * <p>
	 * My protocol takes the form:
	 * 
	 * <PRE>
	 * 
	 * jdbc:mysql://host:port/database
	 * 
	 * </PRE>
	 * 
	 * </p>
	 * 
	 * @param url
	 *            the URL of the database to connect to
	 * @param info
	 *            a list of arbitrary tag/value pairs as connection arguments
	 * 
	 * @return a connection to the URL or null if it isnt us
	 * 
	 * @exception SQLException
	 *                if a database access error occurs
	 * 
	 * @see java.sql.Driver#connect
	 */
	public java.sql.Connection connect(String url, Properties info)
			throws SQLException {
		if (url != null) {
			if (StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX)) {
				return connectLoadBalanced(url, info);
			} else if (StringUtils.startsWithIgnoreCase(url,
					REPLICATION_URL_PREFIX)) {
				return connectReplicationConnection(url, info);
			}
		}

		Properties props = null;

		if ((props = parseURL(url, info)) == null) {
			return null;
		}

		if (!"1".equals(props.getProperty(NUM_HOSTS_PROPERTY_KEY))) {
			return connectFailover(url, info);
		}
		
		try {
			Connection newConn = com.mysql.jdbc.ConnectionImpl.getInstance(
					host(props), port(props), props, database(props), url);

			return newConn;
		} catch (SQLException sqlEx) {
			// Don't wrap SQLExceptions, throw
			// them un-changed.
			throw sqlEx;
		} catch (Exception ex) {
			SQLException sqlEx = SQLError.createSQLException(Messages
					.getString("NonRegisteringDriver.17") //$NON-NLS-1$
					+ ex.toString()
					+ Messages.getString("NonRegisteringDriver.18"), //$NON-NLS-1$
					SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, null);
			
			sqlEx.initCause(ex);
			
			throw sqlEx;
		}
	}

	private java.sql.Connection connectLoadBalanced(String url, Properties info)
			throws SQLException {
		Properties parsedProps = parseURL(url, info);

		// People tend to drop this in, it doesn't make sense
		parsedProps.remove("roundRobinLoadBalance");
		
		if (parsedProps == null) {
			return null;
		}

		int numHosts = Integer.parseInt(parsedProps.getProperty(NUM_HOSTS_PROPERTY_KEY));

		List<String> hostList = new ArrayList<String>();

		for (int i = 0; i < numHosts; i++) {
			int index = i + 1;
			
			hostList.add(parsedProps.getProperty(HOST_PROPERTY_KEY + "." + index) + ":" 
					+ parsedProps.getProperty(PORT_PROPERTY_KEY + "." + index));
		}

		LoadBalancingConnectionProxy proxyBal = new LoadBalancingConnectionProxy(
				hostList, parsedProps);

		return (java.sql.Connection) java.lang.reflect.Proxy.newProxyInstance(this
				.getClass().getClassLoader(),
				new Class[] { com.mysql.jdbc.Connection.class }, proxyBal);
	}
	
	private java.sql.Connection connectFailover(String url, Properties info)
			throws SQLException {
		Properties parsedProps = parseURL(url, info);

		// People tend to drop this in, it doesn't make sense
		parsedProps.remove("roundRobinLoadBalance");
		parsedProps.setProperty("autoReconnect", "false");
		
		if (parsedProps == null) {
			return null;
		}

		int numHosts = Integer.parseInt(parsedProps
				.getProperty(NUM_HOSTS_PROPERTY_KEY));

		List<String> hostList = new ArrayList<String>();
		
		for (int i = 0; i < numHosts; i++) {
			int index = i + 1;

			hostList.add(parsedProps.getProperty(HOST_PROPERTY_KEY + "."
					+ index)
					+ ":"
					+ parsedProps.getProperty(PORT_PROPERTY_KEY + "." + index));
		}

		FailoverConnectionProxy connProxy = new FailoverConnectionProxy(
				hostList, parsedProps);

		return (java.sql.Connection) java.lang.reflect.Proxy.newProxyInstance(
				this.getClass().getClassLoader(),
				new Class[] { com.mysql.jdbc.Connection.class }, connProxy);
	}

	protected java.sql.Connection connectReplicationConnection(String url, Properties info)
			throws SQLException {
		Properties parsedProps = parseURL(url, info);

		if (parsedProps == null) {
			return null;
		}

		Properties masterProps = (Properties) parsedProps.clone();
		Properties slavesProps = (Properties) parsedProps.clone();

		// Marker used for further testing later on, also when
		// debugging
		slavesProps.setProperty("com.mysql.jdbc.ReplicationConnection.isSlave",
				"true");
		
		int numHosts = Integer.parseInt(parsedProps.getProperty(NUM_HOSTS_PROPERTY_KEY));

		if (numHosts < 2) {
			throw SQLError
					.createSQLException(
							"Must specify at least one slave host to connect to for master/slave replication load-balancing functionality",
							SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null);
		}
		
		for (int i = 1; i < numHosts; i++) {
			int index = i + 1;
			
			masterProps.remove(HOST_PROPERTY_KEY + "." + index);
			masterProps.remove(PORT_PROPERTY_KEY + "." + index);
			
			slavesProps.setProperty(HOST_PROPERTY_KEY + "." + i, parsedProps.getProperty(HOST_PROPERTY_KEY + "." + index));
			slavesProps.setProperty(PORT_PROPERTY_KEY + "." + i, parsedProps.getProperty(PORT_PROPERTY_KEY + "." + index));
		}

		masterProps.setProperty(NUM_HOSTS_PROPERTY_KEY, "1");
		slavesProps.remove(HOST_PROPERTY_KEY + "." + numHosts);
		slavesProps.remove(PORT_PROPERTY_KEY + "." + numHosts);
		slavesProps.setProperty(NUM_HOSTS_PROPERTY_KEY, String.valueOf(numHosts - 1));
		slavesProps.setProperty(HOST_PROPERTY_KEY, slavesProps.getProperty(HOST_PROPERTY_KEY + ".1"));
		slavesProps.setProperty(PORT_PROPERTY_KEY, slavesProps.getProperty(PORT_PROPERTY_KEY + ".1"));

		return new ReplicationConnection(masterProps, slavesProps);
	}

	/**
	 * Returns the database property from <code>props</code>
	 * 
	 * @param props
	 *            the Properties to look for the database property.
	 * 
	 * @return the database name.
	 */
	public String database(Properties props) {
		return props.getProperty(DBNAME_PROPERTY_KEY); //$NON-NLS-1$
	}

	/**
	 * Gets the drivers major version number
	 * 
	 * @return the drivers major version number
	 */
	public int getMajorVersion() {
		return getMajorVersionInternal();
	}

	/**
	 * Get the drivers minor version number
	 * 
	 * @return the drivers minor version number
	 */
	public int getMinorVersion() {
		return getMinorVersionInternal();
	}

	/**
	 * The getPropertyInfo method is intended to allow a generic GUI tool to
	 * discover what properties it should prompt a human for in order to get
	 * enough information to connect to a database.
	 * 
	 * <p>
	 * Note that depending on the values the human has supplied so far,
	 * additional values may become necessary, so it may be necessary to iterate
	 * through several calls to getPropertyInfo
	 * </p>
	 * 
	 * @param url
	 *            the Url of the database to connect to
	 * @param info
	 *            a proposed list of tag/value pairs that will be sent on
	 *            connect open.
	 * 
	 * @return An array of DriverPropertyInfo objects describing possible
	 *         properties. This array may be an empty array if no properties are
	 *         required
	 * 
	 * @exception SQLException
	 *                if a database-access error occurs
	 * 
	 * @see java.sql.Driver#getPropertyInfo
	 */
	public DriverPropertyInfo[] getPropertyInfo(String url, Properties info)
			throws SQLException {
		if (info == null) {
			info = new Properties();
		}

		if ((url != null) && url.startsWith(URL_PREFIX)) { //$NON-NLS-1$
			info = parseURL(url, info);
		}

		DriverPropertyInfo hostProp = new DriverPropertyInfo(HOST_PROPERTY_KEY, //$NON-NLS-1$
				info.getProperty(HOST_PROPERTY_KEY)); //$NON-NLS-1$
		hostProp.required = true;
		hostProp.description = Messages.getString("NonRegisteringDriver.3"); //$NON-NLS-1$

		DriverPropertyInfo portProp = new DriverPropertyInfo(PORT_PROPERTY_KEY, //$NON-NLS-1$
				info.getProperty(PORT_PROPERTY_KEY, "3306")); //$NON-NLS-1$ //$NON-NLS-2$
		portProp.required = false;
		portProp.description = Messages.getString("NonRegisteringDriver.7"); //$NON-NLS-1$

		DriverPropertyInfo dbProp = new DriverPropertyInfo(DBNAME_PROPERTY_KEY, //$NON-NLS-1$
				info.getProperty(DBNAME_PROPERTY_KEY)); //$NON-NLS-1$
		dbProp.required = false;
		dbProp.description = "Database name"; //$NON-NLS-1$

		DriverPropertyInfo userProp = new DriverPropertyInfo(USER_PROPERTY_KEY, //$NON-NLS-1$
				info.getProperty(USER_PROPERTY_KEY)); //$NON-NLS-1$
		userProp.required = true;
		userProp.description = Messages.getString("NonRegisteringDriver.13"); //$NON-NLS-1$

		DriverPropertyInfo passwordProp = new DriverPropertyInfo(
				PASSWORD_PROPERTY_KEY, //$NON-NLS-1$
				info.getProperty(PASSWORD_PROPERTY_KEY)); //$NON-NLS-1$
		passwordProp.required = true;
		passwordProp.description = Messages
				.getString("NonRegisteringDriver.16"); //$NON-NLS-1$

		DriverPropertyInfo[] dpi = ConnectionPropertiesImpl
				.exposeAsDriverPropertyInfo(info, 5);

		dpi[0] = hostProp;
		dpi[1] = portProp;
		dpi[2] = dbProp;
		dpi[3] = userProp;
		dpi[4] = passwordProp;

		return dpi;
	}

	//
	// return the value of any property this driver knows about
	//

	/**
	 * Returns the hostname property
	 * 
	 * @param props
	 *            the java.util.Properties instance to retrieve the hostname
	 *            from.
	 * 
	 * @return the hostname
	 */
	public String host(Properties props) {
		return props.getProperty(HOST_PROPERTY_KEY, "localhost"); //$NON-NLS-1$ //$NON-NLS-2$
	}

	/**
	 * Report whether the driver is a genuine JDBC compliant driver. A driver
	 * may only report "true" here if it passes the JDBC compliance tests,
	 * otherwise it is required to return false. JDBC compliance requires full
	 * support for the JDBC API and full support for SQL 92 Entry Level.
	 * 
	 * <p>
	 * MySQL is not SQL92 compliant
	 * </p>
	 * 
	 * @return is this driver JDBC compliant?
	 */
	public boolean jdbcCompliant() {
		return false;
	}

	public Properties parseURL(String url, Properties defaults)
			throws java.sql.SQLException {
		Properties urlProps = (defaults != null) ? new Properties(defaults)
				: new Properties();

		if (url == null) {
			return null;
		}

		if (!StringUtils.startsWithIgnoreCase(url, URL_PREFIX)
				&& !StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX)
				&& !StringUtils.startsWithIgnoreCase(url,
						LOADBALANCE_URL_PREFIX)
				&& !StringUtils.startsWithIgnoreCase(url,
						REPLICATION_URL_PREFIX)) { //$NON-NLS-1$

			return null;
		}

		int beginningOfSlashes = url.indexOf("//");

		if (StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX)) {
			
			urlProps
					.setProperty("socketFactory",
							"com.mysql.management.driverlaunched.ServerLauncherSocketFactory");
		}

		/*
		 * Parse parameters after the ? in the URL and remove them from the
		 * original URL.
		 */
		int index = url.indexOf("?"); //$NON-NLS-1$

		if (index != -1) {
			String paramString = url.substring(index + 1, url.length());
			url = url.substring(0, index);

			StringTokenizer queryParams = new StringTokenizer(paramString, "&"); //$NON-NLS-1$

			while (queryParams.hasMoreTokens()) {
				String parameterValuePair = queryParams.nextToken();

				int indexOfEquals = StringUtils.indexOfIgnoreCase(0,
						parameterValuePair, "=");

				String parameter = null;
				String value = null;

				if (indexOfEquals != -1) {
					parameter = parameterValuePair.substring(0, indexOfEquals);

					if (indexOfEquals + 1 < parameterValuePair.length()) {
						value = parameterValuePair.substring(indexOfEquals + 1);
					}
				}

				if ((value != null && value.length() > 0)
						&& (parameter != null && parameter.length() > 0)) {
					try {
						urlProps.put(parameter, URLDecoder.decode(value,
								"UTF-8"));
					} catch (UnsupportedEncodingException badEncoding) {
						// punt
						urlProps.put(parameter, URLDecoder.decode(value));
					} catch (NoSuchMethodError nsme) {
						// punt again
						urlProps.put(parameter, URLDecoder.decode(value));
					}
				}
			}
		}

		url = url.substring(beginningOfSlashes + 2);

		String hostStuff = null;

		int slashIndex = StringUtils.indexOfIgnoreCaseRespectMarker(0, url, "/", ALLOWED_QUOTES, ALLOWED_QUOTES, true); //$NON-NLS-1$

		if (slashIndex != -1) {
			hostStuff = url.substring(0, slashIndex);

			if ((slashIndex + 1) < url.length()) {
				urlProps.put(DBNAME_PROPERTY_KEY, //$NON-NLS-1$
						url.substring((slashIndex + 1), url.length()));
			}
		} else {
			hostStuff = url;
		}

		int numHosts = 0;
		
		if ((hostStuff != null) && (hostStuff.trim().length() > 0)) {
			List<String> hosts = StringUtils.split(hostStuff, ",", ALLOWED_QUOTES, ALLOWED_QUOTES, false);
			

			for (String hostAndPort : hosts) {
				numHosts++;
			
				String[] hostPortPair = parseHostPortPair(hostAndPort);

				if (hostPortPair[HOST_NAME_INDEX] != null && hostPortPair[HOST_NAME_INDEX].trim().length() > 0) {
					urlProps.setProperty(HOST_PROPERTY_KEY + "." + numHosts, hostPortPair[HOST_NAME_INDEX]);
				} else {
					urlProps.setProperty(HOST_PROPERTY_KEY + "." + numHosts, "localhost");
				}
				
				if (hostPortPair[PORT_NUMBER_INDEX] != null) {
					urlProps.setProperty(PORT_PROPERTY_KEY + "." + numHosts, hostPortPair[PORT_NUMBER_INDEX]);
				} else {
					urlProps.setProperty(PORT_PROPERTY_KEY + "." + numHosts, "3306");
				}
			}
		} else {
			numHosts = 1;
			urlProps.setProperty(HOST_PROPERTY_KEY + ".1", "localhost");
			urlProps.setProperty(PORT_PROPERTY_KEY + ".1", "3306");
		}

		urlProps.setProperty(NUM_HOSTS_PROPERTY_KEY, String.valueOf(numHosts));
		urlProps.setProperty(HOST_PROPERTY_KEY, urlProps.getProperty(HOST_PROPERTY_KEY + ".1"));
		urlProps.setProperty(PORT_PROPERTY_KEY, urlProps.getProperty(PORT_PROPERTY_KEY + ".1"));
		
		String propertiesTransformClassName = urlProps
				.getProperty(PROPERTIES_TRANSFORM_KEY);

		if (propertiesTransformClassName != null) {
			try {
				ConnectionPropertiesTransform propTransformer = (ConnectionPropertiesTransform) Class
						.forName(propertiesTransformClassName).newInstance();

				urlProps = propTransformer.transformProperties(urlProps);
			} catch (InstantiationException e) {
				throw SQLError.createSQLException(
						"Unable to create properties transform instance '"
								+ propertiesTransformClassName
								+ "' due to underlying exception: "
								+ e.toString(),
						SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null);
			} catch (IllegalAccessException e) {
				throw SQLError.createSQLException(
						"Unable to create properties transform instance '"
								+ propertiesTransformClassName
								+ "' due to underlying exception: "
								+ e.toString(),
						SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null);
			} catch (ClassNotFoundException e) {
				throw SQLError.createSQLException(
						"Unable to create properties transform instance '"
								+ propertiesTransformClassName
								+ "' due to underlying exception: "
								+ e.toString(),
						SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null);
			}
		}

		if (Util.isColdFusion() &&
				urlProps.getProperty("autoConfigureForColdFusion", "true").equalsIgnoreCase("true")) {
			String configs = urlProps.getProperty(USE_CONFIG_PROPERTY_KEY);
			
			StringBuffer newConfigs = new StringBuffer();
			
			if (configs != null) {
				newConfigs.append(configs);
				newConfigs.append(",");
			}
			
			newConfigs.append("coldFusion");
			
			urlProps.setProperty(USE_CONFIG_PROPERTY_KEY, newConfigs.toString());
		}
		
		// If we use a config, it actually should get overridden by anything in
		// the URL or passed-in properties

		String configNames = null;

		if (defaults != null) {
			configNames = defaults.getProperty(USE_CONFIG_PROPERTY_KEY);
		}

		if (configNames == null) {
			configNames = urlProps.getProperty(USE_CONFIG_PROPERTY_KEY);
		}

		if (configNames != null) {
			List splitNames = StringUtils.split(configNames, ",", true);

			Properties configProps = new Properties();

			Iterator namesIter = splitNames.iterator();

			while (namesIter.hasNext()) {
				String configName = (String) namesIter.next();

				try {
					InputStream configAsStream = getClass()
							.getResourceAsStream(
									"configs/" + configName + ".properties");

					if (configAsStream == null) {
						throw SQLError
								.createSQLException(
										"Can't find configuration template named '"
												+ configName + "'",
										SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null);
					}
					configProps.load(configAsStream);
				} catch (IOException ioEx) {
					SQLException sqlEx = SQLError.createSQLException(
							"Unable to load configuration template '"
									+ configName
									+ "' due to underlying IOException: "
									+ ioEx,
							SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null);
					sqlEx.initCause(ioEx);
					
					throw sqlEx;
				}
			}

			Iterator propsIter = urlProps.keySet().iterator();

			while (propsIter.hasNext()) {
				String key = propsIter.next().toString();
				String property = urlProps.getProperty(key);
				configProps.setProperty(key, property);
			}

			urlProps = configProps;
		}

		// Properties passed in should override ones in URL

		if (defaults != null) {
			Iterator propsIter = defaults.keySet().iterator();

			while (propsIter.hasNext()) {
				String key = propsIter.next().toString();
				if (!key.equals(NUM_HOSTS_PROPERTY_KEY)) {
					String property = defaults.getProperty(key);
					urlProps.setProperty(key, property);
				}
			}
		}

		return urlProps;
	}

	/**
	 * Returns the port number property
	 * 
	 * @param props
	 *            the properties to get the port number from
	 * 
	 * @return the port number
	 */
	public int port(Properties props) {
		return Integer.parseInt(props.getProperty(PORT_PROPERTY_KEY, "3306")); //$NON-NLS-1$ //$NON-NLS-2$
	}

	/**
	 * Returns the given property from <code>props</code>
	 * 
	 * @param name
	 *            the property name
	 * @param props
	 *            the property instance to look in
	 * 
	 * @return the property value, or null if not found.
	 */
	public String property(String name, Properties props) {
		return props.getProperty(name);
	}
	
	
	/**
	 * Expands hosts of the form address=(protocol=tcp)(host=localhost)(port=3306)
	 * into a java.util.Properties. Special characters (in this case () and =) must be quoted.
	 * Any values that are string-quoted ("" or '') are also stripped of quotes.
	 */
	public static Properties expandHostKeyValues(String host) {
		Properties hostProps = new Properties();
		
		if (isHostPropertiesList(host)) {
			host = host.substring("address=".length() + 1);
			List<String> hostPropsList = StringUtils.split(host, ")", "'\"", "'\"", true);
			
			for (String propDef : hostPropsList) {
				if (propDef.startsWith("(")) {
					propDef = propDef.substring(1);
				}
				
				List<String> kvp = StringUtils.split(propDef, "=", "'\"", "'\"", true);
				
				String key = kvp.get(0);
				String value = kvp.size() > 1 ? kvp.get(1) : null;
				
				if (value != null && ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'")))) {
					value = value.substring(1, value.length() - 1);
				}
				
				if (value != null) {
					if (HOST_PROPERTY_KEY.equalsIgnoreCase(key) ||
							DBNAME_PROPERTY_KEY.equalsIgnoreCase(key) ||
							PORT_PROPERTY_KEY.equalsIgnoreCase(key) ||
							PROTOCOL_PROPERTY_KEY.equalsIgnoreCase(key) ||
							PATH_PROPERTY_KEY.equalsIgnoreCase(key)) {
						key = key.toUpperCase(Locale.ENGLISH);
					} else if (USER_PROPERTY_KEY.equalsIgnoreCase(key) ||
							PASSWORD_PROPERTY_KEY.equalsIgnoreCase(key)) {
						key = key.toLowerCase(Locale.ENGLISH);
					}
					
					hostProps.setProperty(key, value);
				}
			}
		}
		
		return hostProps;
	}
	
	public static boolean isHostPropertiesList(String host) {
		return host != null && StringUtils.startsWithIgnoreCase(host, "address=");
	}
} public class ReplicationConnection implements Connection, PingTarget {
	protected Connection currentConnection;

	protected Connection masterConnection;

	protected Connection slavesConnection;

	protected ReplicationConnection() {}
	
	public ReplicationConnection(Properties masterProperties,
			Properties slaveProperties) throws SQLException {
		NonRegisteringDriver driver = new NonRegisteringDriver();

		StringBuffer masterUrl = new StringBuffer("jdbc:mysql://");
        StringBuffer slaveUrl = new StringBuffer("jdbc:mysql:loadbalance://");

        String masterHost = masterProperties
        	.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);
        
        if (masterHost != null) {
        	masterUrl.append(masterHost);
        }
 
        int numHosts = Integer.parseInt(slaveProperties.getProperty(
        		NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY));
        
        for(int i = 1; i <= numHosts; i++){
	        String slaveHost = slaveProperties
	        	.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY + "." + i);
	        
	        if (slaveHost != null) {
	        	if(i > 1){
	        		slaveUrl.append(',');
	        	}
	        	slaveUrl.append(slaveHost);
	        }
        }

        String masterDb = masterProperties
        	.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY);

        masterUrl.append("/");
        
        if (masterDb != null) {
        	masterUrl.append(masterDb);
        }
        
        String slaveDb = slaveProperties
        	.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY);
        
        slaveUrl.append("/");
        
        if (slaveDb != null) {
        	slaveUrl.append(slaveDb);
        }
        
        slaveProperties.setProperty("roundRobinLoadBalance", "true");
        
        this.masterConnection = (com.mysql.jdbc.Connection) driver.connect(
                masterUrl.toString(), masterProperties);
        this.slavesConnection = (com.mysql.jdbc.Connection) driver.connect(
                slaveUrl.toString(), slaveProperties);
        this.slavesConnection.setReadOnly(true);
        
		this.currentConnection = this.masterConnection;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#clearWarnings()
	 */
	public synchronized void clearWarnings() throws SQLException {
		this.currentConnection.clearWarnings();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#close()
	 */
	public synchronized void close() throws SQLException {
		this.masterConnection.close();
		this.slavesConnection.close();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#commit()
	 */
	public synchronized void commit() throws SQLException {
		this.currentConnection.commit();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#createStatement()
	 */
	public Statement createStatement() throws SQLException {
		Statement stmt = this.currentConnection.createStatement();
		((com.mysql.jdbc.Statement) stmt).setPingTarget(this);
		
		return stmt;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#createStatement(int, int)
	 */
	public synchronized Statement createStatement(int resultSetType,
			int resultSetConcurrency) throws SQLException {
		Statement stmt = this.currentConnection.createStatement(resultSetType,
				resultSetConcurrency);
		
		((com.mysql.jdbc.Statement) stmt).setPingTarget(this);
		
		return stmt;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#createStatement(int, int, int)
	 */
	public synchronized Statement createStatement(int resultSetType,
			int resultSetConcurrency, int resultSetHoldability)
			throws SQLException {
		Statement stmt = this.currentConnection.createStatement(resultSetType,
				resultSetConcurrency, resultSetHoldability);
		
		((com.mysql.jdbc.Statement) stmt).setPingTarget(this);
		
		return stmt;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#getAutoCommit()
	 */
	public synchronized boolean getAutoCommit() throws SQLException {
		return this.currentConnection.getAutoCommit();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#getCatalog()
	 */
	public synchronized String getCatalog() throws SQLException {
		return this.currentConnection.getCatalog();
	}

	public synchronized Connection getCurrentConnection() {
		return this.currentConnection;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#getHoldability()
	 */
	public synchronized int getHoldability() throws SQLException {
		return this.currentConnection.getHoldability();
	}

	public synchronized Connection getMasterConnection() {
		return this.masterConnection;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#getMetaData()
	 */
	public synchronized DatabaseMetaData getMetaData() throws SQLException {
		return this.currentConnection.getMetaData();
	}

	public synchronized Connection getSlavesConnection() {
		return this.slavesConnection;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#getTransactionIsolation()
	 */
	public synchronized int getTransactionIsolation() throws SQLException {
		return this.currentConnection.getTransactionIsolation();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#getTypeMap()
	 */
	public synchronized Map getTypeMap() throws SQLException {
		return this.currentConnection.getTypeMap();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#getWarnings()
	 */
	public synchronized SQLWarning getWarnings() throws SQLException {
		return this.currentConnection.getWarnings();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#isClosed()
	 */
	public synchronized boolean isClosed() throws SQLException {
		return this.currentConnection.isClosed();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#isReadOnly()
	 */
	public synchronized boolean isReadOnly() throws SQLException {
		return this.currentConnection == this.slavesConnection;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#nativeSQL(java.lang.String)
	 */
	public synchronized String nativeSQL(String sql) throws SQLException {
		return this.currentConnection.nativeSQL(sql);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#prepareCall(java.lang.String)
	 */
	public CallableStatement prepareCall(String sql) throws SQLException {
		return this.currentConnection.prepareCall(sql);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#prepareCall(java.lang.String, int, int)
	 */
	public synchronized CallableStatement prepareCall(String sql,
			int resultSetType, int resultSetConcurrency) throws SQLException {
		return this.currentConnection.prepareCall(sql, resultSetType,
				resultSetConcurrency);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#prepareCall(java.lang.String, int, int, int)
	 */
	public synchronized CallableStatement prepareCall(String sql,
			int resultSetType, int resultSetConcurrency,
			int resultSetHoldability) throws SQLException {
		return this.currentConnection.prepareCall(sql, resultSetType,
				resultSetConcurrency, resultSetHoldability);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#prepareStatement(java.lang.String)
	 */
	public PreparedStatement prepareStatement(String sql) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.prepareStatement(sql);
		
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#prepareStatement(java.lang.String, int)
	 */
	public synchronized PreparedStatement prepareStatement(String sql,
			int autoGeneratedKeys) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, autoGeneratedKeys);

		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#prepareStatement(java.lang.String, int, int)
	 */
	public synchronized PreparedStatement prepareStatement(String sql,
			int resultSetType, int resultSetConcurrency) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, resultSetType,
				resultSetConcurrency);
		
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#prepareStatement(java.lang.String, int, int,
	 *      int)
	 */
	public synchronized PreparedStatement prepareStatement(String sql,
			int resultSetType, int resultSetConcurrency,
			int resultSetHoldability) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, resultSetType,
				resultSetConcurrency, resultSetHoldability);

		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#prepareStatement(java.lang.String, int[])
	 */
	public synchronized PreparedStatement prepareStatement(String sql,
			int[] columnIndexes) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, columnIndexes);
		
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#prepareStatement(java.lang.String,
	 *      java.lang.String[])
	 */
	public synchronized PreparedStatement prepareStatement(String sql,
			String[] columnNames) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, columnNames);

		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

  @Override
  public java.sql.Clob createClob() throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public Blob createBlob() throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public NClob createNClob() throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public SQLXML createSQLXML() throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public boolean isValid(int i) throws SQLException {
    return false;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public void setClientInfo(String s, String s1) throws SQLClientInfoException {
    //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public void setClientInfo(Properties properties) throws SQLClientInfoException {
    //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public String getClientInfo(String s) throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public Properties getClientInfo() throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public Array createArrayOf(String s, Object[] objects) throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public Struct createStruct(String s, Object[] objects) throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  /*
    * (non-Javadoc)
    *
    * @see java.sql.Connection#releaseSavepoint(java.sql.Savepoint)
    */
	public synchronized void releaseSavepoint(Savepoint savepoint)
			throws SQLException {
		this.currentConnection.releaseSavepoint(savepoint);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#rollback()
	 */
	public synchronized void rollback() throws SQLException {
		this.currentConnection.rollback();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#rollback(java.sql.Savepoint)
	 */
	public synchronized void rollback(Savepoint savepoint) throws SQLException {
		this.currentConnection.rollback(savepoint);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#setAutoCommit(boolean)
	 */
	public synchronized void setAutoCommit(boolean autoCommit)
			throws SQLException {
		this.currentConnection.setAutoCommit(autoCommit);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#setCatalog(java.lang.String)
	 */
	public synchronized void setCatalog(String catalog) throws SQLException {
		this.currentConnection.setCatalog(catalog);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#setHoldability(int)
	 */
	public synchronized void setHoldability(int holdability)
			throws SQLException {
		this.currentConnection.setHoldability(holdability);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#setReadOnly(boolean)
	 */
	public synchronized void setReadOnly(boolean readOnly) throws SQLException {
		if (readOnly) {
			if (currentConnection != slavesConnection) {
				switchToSlavesConnection();
			}
		} else {
			if (currentConnection != masterConnection) {
				switchToMasterConnection();
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#setSavepoint()
	 */
	public synchronized Savepoint setSavepoint() throws SQLException {
		return this.currentConnection.setSavepoint();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#setSavepoint(java.lang.String)
	 */
	public synchronized Savepoint setSavepoint(String name) throws SQLException {
		return this.currentConnection.setSavepoint(name);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.sql.Connection#setTransactionIsolation(int)
	 */
	public synchronized void setTransactionIsolation(int level)
			throws SQLException {
		this.currentConnection.setTransactionIsolation(level);
	}

	// For testing

	private synchronized void switchToMasterConnection() throws SQLException {
		swapConnections(this.masterConnection, this.slavesConnection);
	}

	private synchronized void switchToSlavesConnection() throws SQLException {
		swapConnections(this.slavesConnection, this.masterConnection);
	}
	
	/**
	 * Swaps current context (catalog, autocommit and txn_isolation) from
	 * sourceConnection to targetConnection, and makes targetConnection
	 * the "current" connection that will be used for queries.
	 * 
	 * @param switchToConnection the connection to swap from
	 * @param switchFromConnection the connection to swap to
	 * 
	 * @throws SQLException if an error occurs
	 */
	private synchronized void swapConnections(Connection switchToConnection, 
			Connection switchFromConnection) throws SQLException {
		String switchFromCatalog = switchFromConnection.getCatalog();
		String switchToCatalog = switchToConnection.getCatalog();

		if (switchToCatalog != null && !switchToCatalog.equals(switchFromCatalog)) {
			switchToConnection.setCatalog(switchFromCatalog);
		} else if (switchFromCatalog != null) {
			switchToConnection.setCatalog(switchFromCatalog);
		}

		boolean switchToAutoCommit = switchToConnection.getAutoCommit();
		boolean switchFromConnectionAutoCommit = switchFromConnection.getAutoCommit();
		
		if (switchFromConnectionAutoCommit != switchToAutoCommit) {
			switchToConnection.setAutoCommit(switchFromConnectionAutoCommit);
		}

		int switchToIsolation = switchToConnection
				.getTransactionIsolation();

		int switchFromIsolation = switchFromConnection.getTransactionIsolation();
		
		if (switchFromIsolation != switchToIsolation) {
			switchToConnection
					.setTransactionIsolation(switchFromIsolation);
		}
		
		this.currentConnection = switchToConnection;
	}

	public synchronized void doPing() throws SQLException {
		if (this.masterConnection != null) {
			this.masterConnection.ping();
		}
		
		if (this.slavesConnection != null) {
			this.slavesConnection.ping();
		}
	}

	public synchronized void changeUser(String userName, String newPassword)
			throws SQLException {
		this.masterConnection.changeUser(userName, newPassword);
		this.slavesConnection.changeUser(userName, newPassword);
	}

	public synchronized void clearHasTriedMaster() {
		this.masterConnection.clearHasTriedMaster();
		this.slavesConnection.clearHasTriedMaster();
		
	}

	public synchronized PreparedStatement clientPrepareStatement(String sql)
			throws SQLException {
		PreparedStatement pstmt = this.currentConnection.clientPrepareStatement(sql);
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	public synchronized PreparedStatement clientPrepareStatement(String sql,
			int autoGenKeyIndex) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.clientPrepareStatement(sql, autoGenKeyIndex);
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	public synchronized PreparedStatement clientPrepareStatement(String sql,
			int resultSetType, int resultSetConcurrency) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.clientPrepareStatement(sql, resultSetType, resultSetConcurrency);
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	public synchronized PreparedStatement clientPrepareStatement(String sql,
			int[] autoGenKeyIndexes) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.clientPrepareStatement(sql, autoGenKeyIndexes);
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	public synchronized PreparedStatement clientPrepareStatement(String sql,
			int resultSetType, int resultSetConcurrency,
			int resultSetHoldability) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.clientPrepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	public synchronized PreparedStatement clientPrepareStatement(String sql,
			String[] autoGenKeyColNames) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.clientPrepareStatement(sql, autoGenKeyColNames);
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	public synchronized int getActiveStatementCount() {
		return this.currentConnection.getActiveStatementCount();
	}

	public synchronized long getIdleFor() {
		return this.currentConnection.getIdleFor();
	}

	public synchronized Log getLog() throws SQLException {
		return this.currentConnection.getLog();
	}

	public synchronized String getServerCharacterEncoding() {
		return this.currentConnection.getServerCharacterEncoding();
	}

	public synchronized TimeZone getServerTimezoneTZ() {
		return this.currentConnection.getServerTimezoneTZ();
	}

	public synchronized String getStatementComment() {
		return this.currentConnection.getStatementComment();
	}

	public synchronized boolean hasTriedMaster() {
		return this.currentConnection.hasTriedMaster();
	}

	public synchronized void initializeExtension(Extension ex) throws SQLException {
		this.currentConnection.initializeExtension(ex);
	}

	public synchronized boolean isAbonormallyLongQuery(long millisOrNanos) {
		return this.currentConnection.isAbonormallyLongQuery(millisOrNanos);
	}

	public synchronized boolean isInGlobalTx() {
		return this.currentConnection.isInGlobalTx();
	}

	public synchronized boolean isMasterConnection() {
		return this.currentConnection.isMasterConnection();
	}

	public synchronized boolean isNoBackslashEscapesSet() {
		return this.currentConnection.isNoBackslashEscapesSet();
	}

	public synchronized boolean lowerCaseTableNames() {
		return this.currentConnection.lowerCaseTableNames();
	}

	public synchronized boolean parserKnowsUnicode() {
		return this.currentConnection.parserKnowsUnicode();
	}

	public synchronized void ping() throws SQLException {
		this.masterConnection.ping();
		this.slavesConnection.ping();
	}

	public synchronized void reportQueryTime(long millisOrNanos) {
		this.currentConnection.reportQueryTime(millisOrNanos);
	}

	public synchronized void resetServerState() throws SQLException {
		this.currentConnection.resetServerState();
	}

	public synchronized PreparedStatement serverPrepareStatement(String sql)
			throws SQLException {
		PreparedStatement pstmt = this.currentConnection.serverPrepareStatement(sql);
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	public synchronized PreparedStatement serverPrepareStatement(String sql,
			int autoGenKeyIndex) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.serverPrepareStatement(sql, autoGenKeyIndex);
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	public synchronized PreparedStatement serverPrepareStatement(String sql,
			int resultSetType, int resultSetConcurrency) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.serverPrepareStatement(sql, resultSetType, resultSetConcurrency);
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	public synchronized PreparedStatement serverPrepareStatement(String sql,
			int resultSetType, int resultSetConcurrency,
			int resultSetHoldability) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.serverPrepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	public synchronized PreparedStatement serverPrepareStatement(String sql,
			int[] autoGenKeyIndexes) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.serverPrepareStatement(sql, autoGenKeyIndexes);
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	public synchronized PreparedStatement serverPrepareStatement(String sql,
			String[] autoGenKeyColNames) throws SQLException {
		PreparedStatement pstmt = this.currentConnection.serverPrepareStatement(sql, autoGenKeyColNames);
		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
		
		return pstmt;
	}

	public synchronized void setFailedOver(boolean flag) {
		this.currentConnection.setFailedOver(flag);
	}

	public synchronized void setPreferSlaveDuringFailover(boolean flag) {
		this.currentConnection.setPreferSlaveDuringFailover(flag);
	}

	public synchronized void setStatementComment(String comment) {
		this.masterConnection.setStatementComment(comment);
		this.slavesConnection.setStatementComment(comment);
	}

	public synchronized void shutdownServer() throws SQLException {
		this.currentConnection.shutdownServer();
	}

	public synchronized boolean supportsIsolationLevel() {
		return this.currentConnection.supportsIsolationLevel();
	}

	public synchronized boolean supportsQuotedIdentifiers() {
		return this.currentConnection.supportsQuotedIdentifiers();
	}

	public synchronized boolean supportsTransactions() {
		return this.currentConnection.supportsTransactions();
	}

	public synchronized boolean versionMeetsMinimum(int major, int minor, int subminor)
			throws SQLException {
		return this.currentConnection.versionMeetsMinimum(major, minor, subminor);
	}

	public synchronized String exposeAsXml() throws SQLException {
		return this.currentConnection.exposeAsXml();
	}

	public synchronized boolean getAllowLoadLocalInfile() {
		return this.currentConnection.getAllowLoadLocalInfile();
	}

	public synchronized boolean getAllowMultiQueries() {
		return this.currentConnection.getAllowMultiQueries();
	}

	public synchronized boolean getAllowNanAndInf() {
		return this.currentConnection.getAllowNanAndInf();
	}

	public synchronized boolean getAllowUrlInLocalInfile() {
		return this.currentConnection.getAllowUrlInLocalInfile();
	}

	public synchronized boolean getAlwaysSendSetIsolation() {
		return this.currentConnection.getAlwaysSendSetIsolation();
	}

	public synchronized boolean getAutoClosePStmtStreams() {
		return this.currentConnection.getAutoClosePStmtStreams();
	}

	public synchronized boolean getAutoDeserialize() {
		return this.currentConnection.getAutoDeserialize();
	}

	public synchronized boolean getAutoGenerateTestcaseScript() {
		return this.currentConnection.getAutoGenerateTestcaseScript();
	}

	public synchronized boolean getAutoReconnectForPools() {
		return this.currentConnection.getAutoReconnectForPools();
	}

	public synchronized boolean getAutoSlowLog() {
		return this.currentConnection.getAutoSlowLog();
	}

	public synchronized int getBlobSendChunkSize() {
		return this.currentConnection.getBlobSendChunkSize();
	}

	public synchronized boolean getBlobsAreStrings() {
		return this.currentConnection.getBlobsAreStrings();
	}

	public synchronized boolean getCacheCallableStatements() {
		return this.currentConnection.getCacheCallableStatements();
	}

	public synchronized boolean getCacheCallableStmts() {
		return this.currentConnection.getCacheCallableStmts();
	}

	public synchronized boolean getCachePrepStmts() {
		return this.currentConnection.getCachePrepStmts();
	}

	public synchronized boolean getCachePreparedStatements() {
		return this.currentConnection.getCachePreparedStatements();
	}

	public synchronized boolean getCacheResultSetMetadata() {
		return this.currentConnection.getCacheResultSetMetadata();
	}

	public synchronized boolean getCacheServerConfiguration() {
		return this.currentConnection.getCacheServerConfiguration();
	}

	public synchronized int getCallableStatementCacheSize() {
		return this.currentConnection.getCallableStatementCacheSize();
	}

	public synchronized int getCallableStmtCacheSize() {
		return this.currentConnection.getCallableStmtCacheSize();
	}

	public synchronized boolean getCapitalizeTypeNames() {
		return this.currentConnection.getCapitalizeTypeNames();
	}

	public synchronized String getCharacterSetResults() {
		return this.currentConnection.getCharacterSetResults();
	}

	public synchronized String getClientCertificateKeyStorePassword() {
		return this.currentConnection.getClientCertificateKeyStorePassword();
	}

	public synchronized String getClientCertificateKeyStoreType() {
		return this.currentConnection.getClientCertificateKeyStoreType();
	}

	public synchronized String getClientCertificateKeyStoreUrl() {
		return this.currentConnection.getClientCertificateKeyStoreUrl();
	}

	public synchronized String getClientInfoProvider() {
		return this.currentConnection.getClientInfoProvider();
	}

	public synchronized String getClobCharacterEncoding() {
		return this.currentConnection.getClobCharacterEncoding();
	}

	public synchronized boolean getClobberStreamingResults() {
		return this.currentConnection.getClobberStreamingResults();
	}

	public synchronized int getConnectTimeout() {
		return this.currentConnection.getConnectTimeout();
	}

	public synchronized String getConnectionCollation() {
		return this.currentConnection.getConnectionCollation();
	}

	public synchronized String getConnectionLifecycleInterceptors() {
		return this.currentConnection.getConnectionLifecycleInterceptors();
	}

	public synchronized boolean getContinueBatchOnError() {
		return this.currentConnection.getContinueBatchOnError();
	}

	public synchronized boolean getCreateDatabaseIfNotExist() {
		return this.currentConnection.getCreateDatabaseIfNotExist();
	}

	public synchronized int getDefaultFetchSize() {
		return this.currentConnection.getDefaultFetchSize();
	}

	public synchronized boolean getDontTrackOpenResources() {
		return this.currentConnection.getDontTrackOpenResources();
	}

	public synchronized boolean getDumpMetadataOnColumnNotFound() {
		return this.currentConnection.getDumpMetadataOnColumnNotFound();
	}

	public synchronized boolean getDumpQueriesOnException() {
		return this.currentConnection.getDumpQueriesOnException();
	}

	public synchronized boolean getDynamicCalendars() {
		return this.currentConnection.getDynamicCalendars();
	}

	public synchronized boolean getElideSetAutoCommits() {
		return this.currentConnection.getElideSetAutoCommits();
	}

	public synchronized boolean getEmptyStringsConvertToZero() {
		return this.currentConnection.getEmptyStringsConvertToZero();
	}

	public synchronized boolean getEmulateLocators() {
		return this.currentConnection.getEmulateLocators();
	}

	public synchronized boolean getEmulateUnsupportedPstmts() {
		return this.currentConnection.getEmulateUnsupportedPstmts();
	}

	public synchronized boolean getEnablePacketDebug() {
		return this.currentConnection.getEnablePacketDebug();
	}

	public synchronized boolean getEnableQueryTimeouts() {
		return this.currentConnection.getEnableQueryTimeouts();
	}

	public synchronized String getEncoding() {
		return this.currentConnection.getEncoding();
	}

	public synchronized boolean getExplainSlowQueries() {
		return this.currentConnection.getExplainSlowQueries();
	}

	public synchronized boolean getFailOverReadOnly() {
		return this.currentConnection.getFailOverReadOnly();
	}

	public synchronized boolean getFunctionsNeverReturnBlobs() {
		return this.currentConnection.getFunctionsNeverReturnBlobs();
	}

	public synchronized boolean getGatherPerfMetrics() {
		return this.currentConnection.getGatherPerfMetrics();
	}

	public synchronized boolean getGatherPerformanceMetrics() {
		return this.currentConnection.getGatherPerformanceMetrics();
	}

	public synchronized boolean getGenerateSimpleParameterMetadata() {
		return this.currentConnection.getGenerateSimpleParameterMetadata();
	}

	public synchronized boolean getHoldResultsOpenOverStatementClose() {
		return this.currentConnection.getHoldResultsOpenOverStatementClose();
	}

	public synchronized boolean getIgnoreNonTxTables() {
		return this.currentConnection.getIgnoreNonTxTables();
	}

	public synchronized boolean getIncludeInnodbStatusInDeadlockExceptions() {
		return this.currentConnection.getIncludeInnodbStatusInDeadlockExceptions();
	}

	public synchronized int getInitialTimeout() {
		return this.currentConnection.getInitialTimeout();
	}

	public synchronized boolean getInteractiveClient() {
		return this.currentConnection.getInteractiveClient();
	}

	public synchronized boolean getIsInteractiveClient() {
		return this.currentConnection.getIsInteractiveClient();
	}

	public synchronized boolean getJdbcCompliantTruncation() {
		return this.currentConnection.getJdbcCompliantTruncation();
	}

	public synchronized boolean getJdbcCompliantTruncationForReads() {
		return this.currentConnection.getJdbcCompliantTruncationForReads();
	}

	public synchronized String getLargeRowSizeThreshold() {
		return this.currentConnection.getLargeRowSizeThreshold();
	}

	public synchronized String getLoadBalanceStrategy() {
		return this.currentConnection.getLoadBalanceStrategy();
	}

	public synchronized String getLocalSocketAddress() {
		return this.currentConnection.getLocalSocketAddress();
	}

	public synchronized int getLocatorFetchBufferSize() {
		return this.currentConnection.getLocatorFetchBufferSize();
	}

	public synchronized boolean getLogSlowQueries() {
		return this.currentConnection.getLogSlowQueries();
	}

	public synchronized boolean getLogXaCommands() {
		return this.currentConnection.getLogXaCommands();
	}

	public synchronized String getLogger() {
		return this.currentConnection.getLogger();
	}

	public synchronized String getLoggerClassName() {
		return this.currentConnection.getLoggerClassName();
	}

	public synchronized boolean getMaintainTimeStats() {
		return this.currentConnection.getMaintainTimeStats();
	}

	public synchronized int getMaxQuerySizeToLog() {
		return this.currentConnection.getMaxQuerySizeToLog();
	}

	public synchronized int getMaxReconnects() {
		return this.currentConnection.getMaxReconnects();
	}

	public synchronized int getMaxRows() {
		return this.currentConnection.getMaxRows();
	}

	public synchronized int getMetadataCacheSize() {
		return this.currentConnection.getMetadataCacheSize();
	}

	public synchronized int getNetTimeoutForStreamingResults() {
		return this.currentConnection.getNetTimeoutForStreamingResults();
	}

	public synchronized boolean getNoAccessToProcedureBodies() {
		return this.currentConnection.getNoAccessToProcedureBodies();
	}

	public synchronized boolean getNoDatetimeStringSync() {
		return this.currentConnection.getNoDatetimeStringSync();
	}

	public synchronized boolean getNoTimezoneConversionForTimeType() {
		return this.currentConnection.getNoTimezoneConversionForTimeType();
	}

	public synchronized boolean getNullCatalogMeansCurrent() {
		return this.currentConnection.getNullCatalogMeansCurrent();
	}

	public synchronized boolean getNullNamePatternMatchesAll() {
		return this.currentConnection.getNullNamePatternMatchesAll();
	}

	public synchronized boolean getOverrideSupportsIntegrityEnhancementFacility() {
		return this.currentConnection.getOverrideSupportsIntegrityEnhancementFacility();
	}

	public synchronized int getPacketDebugBufferSize() {
		return this.currentConnection.getPacketDebugBufferSize();
	}

	public synchronized boolean getPadCharsWithSpace() {
		return this.currentConnection.getPadCharsWithSpace();
	}

	public synchronized boolean getParanoid() {
		return this.currentConnection.getParanoid();
	}

	public synchronized boolean getPedantic() {
		return this.currentConnection.getPedantic();
	}

	public synchronized boolean getPinGlobalTxToPhysicalConnection() {
		return this.currentConnection.getPinGlobalTxToPhysicalConnection();
	}

	public synchronized boolean getPopulateInsertRowWithDefaultValues() {
		return this.currentConnection.getPopulateInsertRowWithDefaultValues();
	}

	public synchronized int getPrepStmtCacheSize() {
		return this.currentConnection.getPrepStmtCacheSize();
	}

	public synchronized int getPrepStmtCacheSqlLimit() {
		return this.currentConnection.getPrepStmtCacheSqlLimit();
	}

	public synchronized int getPreparedStatementCacheSize() {
		return this.currentConnection.getPreparedStatementCacheSize();
	}

	public synchronized int getPreparedStatementCacheSqlLimit() {
		return this.currentConnection.getPreparedStatementCacheSqlLimit();
	}

	public synchronized boolean getProcessEscapeCodesForPrepStmts() {
		return this.currentConnection.getProcessEscapeCodesForPrepStmts();
	}

	public synchronized boolean getProfileSQL() {
		return this.currentConnection.getProfileSQL();
	}

	public synchronized boolean getProfileSql() {
		return this.currentConnection.getProfileSql();
	}

	public synchronized String getProfilerEventHandler() {
		return this.currentConnection.getProfilerEventHandler();
	}

	public synchronized String getPropertiesTransform() {
		return this.currentConnection.getPropertiesTransform();
	}

	public synchronized int getQueriesBeforeRetryMaster() {
		return this.currentConnection.getQueriesBeforeRetryMaster();
	}

	public synchronized boolean getReconnectAtTxEnd() {
		return this.currentConnection.getReconnectAtTxEnd();
	}

	public synchronized boolean getRelaxAutoCommit() {
		return this.currentConnection.getRelaxAutoCommit();
	}

	public synchronized int getReportMetricsIntervalMillis() {
		return this.currentConnection.getReportMetricsIntervalMillis();
	}

	public synchronized boolean getRequireSSL() {
		return this.currentConnection.getRequireSSL();
	}

	public synchronized String getResourceId() {
		return this.currentConnection.getResourceId();
	}

	public synchronized int getResultSetSizeThreshold() {
		return this.currentConnection.getResultSetSizeThreshold();
	}

	public synchronized boolean getRewriteBatchedStatements() {
		return this.currentConnection.getRewriteBatchedStatements();
	}

	public synchronized boolean getRollbackOnPooledClose() {
		return this.currentConnection.getRollbackOnPooledClose();
	}

	public synchronized boolean getRoundRobinLoadBalance() {
		return this.currentConnection.getRoundRobinLoadBalance();
	}

	public synchronized boolean getRunningCTS13() {
		return this.currentConnection.getRunningCTS13();
	}

	public synchronized int getSecondsBeforeRetryMaster() {
		return this.currentConnection.getSecondsBeforeRetryMaster();
	}

	public synchronized int getSelfDestructOnPingMaxOperations() {
		return this.currentConnection.getSelfDestructOnPingMaxOperations();
	}

	public synchronized int getSelfDestructOnPingSecondsLifetime() {
		return this.currentConnection.getSelfDestructOnPingSecondsLifetime();
	}

	public synchronized String getServerTimezone() {
		return this.currentConnection.getServerTimezone();
	}

	public synchronized String getSessionVariables() {
		return this.currentConnection.getSessionVariables();
	}

	public synchronized int getSlowQueryThresholdMillis() {
		return this.currentConnection.getSlowQueryThresholdMillis();
	}

	public synchronized long getSlowQueryThresholdNanos() {
		return this.currentConnection.getSlowQueryThresholdNanos();
	}

	public synchronized String getSocketFactory() {
		return this.currentConnection.getSocketFactory();
	}

	public synchronized String getSocketFactoryClassName() {
		return this.currentConnection.getSocketFactoryClassName();
	}

	public synchronized int getSocketTimeout() {
		return this.currentConnection.getSocketTimeout();
	}

	public synchronized String getStatementInterceptors() {
		return this.currentConnection.getStatementInterceptors();
	}

	public synchronized boolean getStrictFloatingPoint() {
		return this.currentConnection.getStrictFloatingPoint();
	}

	public synchronized boolean getStrictUpdates() {
		return this.currentConnection.getStrictUpdates();
	}

	public synchronized boolean getTcpKeepAlive() {
		return this.currentConnection.getTcpKeepAlive();
	}

	public synchronized boolean getTcpNoDelay() {
		return this.currentConnection.getTcpNoDelay();
	}

	public synchronized int getTcpRcvBuf() {
		return this.currentConnection.getTcpRcvBuf();
	}

	public synchronized int getTcpSndBuf() {
		return this.currentConnection.getTcpSndBuf();
	}

	public synchronized int getTcpTrafficClass() {
		return this.currentConnection.getTcpTrafficClass();
	}

	public synchronized boolean getTinyInt1isBit() {
		return this.currentConnection.getTinyInt1isBit();
	}

	public synchronized boolean getTraceProtocol() {
		return this.currentConnection.getTraceProtocol();
	}

	public synchronized boolean getTransformedBitIsBoolean() {
		return this.currentConnection.getTransformedBitIsBoolean();
	}

	public synchronized boolean getTreatUtilDateAsTimestamp() {
		return this.currentConnection.getTreatUtilDateAsTimestamp();
	}

	public synchronized String getTrustCertificateKeyStorePassword() {
		return this.currentConnection.getTrustCertificateKeyStorePassword();
	}

	public synchronized String getTrustCertificateKeyStoreType() {
		return this.currentConnection.getTrustCertificateKeyStoreType();
	}

	public synchronized String getTrustCertificateKeyStoreUrl() {
		return this.currentConnection.getTrustCertificateKeyStoreUrl();
	}

	public synchronized boolean getUltraDevHack() {
		return this.currentConnection.getUltraDevHack();
	}

	public synchronized boolean getUseBlobToStoreUTF8OutsideBMP() {
		return this.currentConnection.getUseBlobToStoreUTF8OutsideBMP();
	}

	public synchronized boolean getUseCompression() {
		return this.currentConnection.getUseCompression();
	}

	public synchronized String getUseConfigs() {
		return this.currentConnection.getUseConfigs();
	}

	public synchronized boolean getUseCursorFetch() {
		return this.currentConnection.getUseCursorFetch();
	}

	public synchronized boolean getUseDirectRowUnpack() {
		return this.currentConnection.getUseDirectRowUnpack();
	}

	public synchronized boolean getUseDynamicCharsetInfo() {
		return this.currentConnection.getUseDynamicCharsetInfo();
	}

	public synchronized boolean getUseFastDateParsing() {
		return this.currentConnection.getUseFastDateParsing();
	}

	public synchronized boolean getUseFastIntParsing() {
		return this.currentConnection.getUseFastIntParsing();
	}

	public synchronized boolean getUseGmtMillisForDatetimes() {
		return this.currentConnection.getUseGmtMillisForDatetimes();
	}

	public synchronized boolean getUseHostsInPrivileges() {
		return this.currentConnection.getUseHostsInPrivileges();
	}

	public synchronized boolean getUseInformationSchema() {
		return this.currentConnection.getUseInformationSchema();
	}

	public synchronized boolean getUseJDBCCompliantTimezoneShift() {
		return this.currentConnection.getUseJDBCCompliantTimezoneShift();
	}

	public synchronized boolean getUseJvmCharsetConverters() {
		return this.currentConnection.getUseJvmCharsetConverters();
	}

	public synchronized boolean getUseLegacyDatetimeCode() {
		return this.currentConnection.getUseLegacyDatetimeCode();
	}

	public synchronized boolean getUseLocalSessionState() {
		return this.currentConnection.getUseLocalSessionState();
	}

	public synchronized boolean getUseNanosForElapsedTime() {
		return this.currentConnection.getUseNanosForElapsedTime();
	}

	public synchronized boolean getUseOldAliasMetadataBehavior() {
		return this.currentConnection.getUseOldAliasMetadataBehavior();
	}

	public synchronized boolean getUseOldUTF8Behavior() {
		return this.currentConnection.getUseOldUTF8Behavior();
	}

	public synchronized boolean getUseOnlyServerErrorMessages() {
		return this.currentConnection.getUseOnlyServerErrorMessages();
	}

	public synchronized boolean getUseReadAheadInput() {
		return this.currentConnection.getUseReadAheadInput();
	}

	public synchronized boolean getUseSSL() {
		return this.currentConnection.getUseSSL();
	}

	public synchronized boolean getUseSSPSCompatibleTimezoneShift() {
		return this.currentConnection.getUseSSPSCompatibleTimezoneShift();
	}

	public synchronized boolean getUseServerPrepStmts() {
		return this.currentConnection.getUseServerPrepStmts();
	}

	public synchronized boolean getUseServerPreparedStmts() {
		return this.currentConnection.getUseServerPreparedStmts();
	}

	public synchronized boolean getUseSqlStateCodes() {
		return this.currentConnection.getUseSqlStateCodes();
	}

	public synchronized boolean getUseStreamLengthsInPrepStmts() {
		return this.currentConnection.getUseStreamLengthsInPrepStmts();
	}

	public synchronized boolean getUseTimezone() {
		return this.currentConnection.getUseTimezone();
	}

	public synchronized boolean getUseUltraDevWorkAround() {
		return this.currentConnection.getUseUltraDevWorkAround();
	}

	public synchronized boolean getUseUnbufferedInput() {
		return this.currentConnection.getUseUnbufferedInput();
	}

	public synchronized boolean getUseUnicode() {
		return this.currentConnection.getUseUnicode();
	}

	public synchronized boolean getUseUsageAdvisor() {
		return this.currentConnection.getUseUsageAdvisor();
	}

	public synchronized String getUtf8OutsideBmpExcludedColumnNamePattern() {
		return this.currentConnection.getUtf8OutsideBmpExcludedColumnNamePattern();
	}

	public synchronized String getUtf8OutsideBmpIncludedColumnNamePattern() {
		return this.currentConnection.getUtf8OutsideBmpIncludedColumnNamePattern();
	}

	public synchronized boolean getVerifyServerCertificate() {
		return this.currentConnection.getVerifyServerCertificate();
	}

	public synchronized boolean getYearIsDateType() {
		return this.currentConnection.getYearIsDateType();
	}

	public synchronized String getZeroDateTimeBehavior() {
		return this.currentConnection.getZeroDateTimeBehavior();
	}

	public synchronized void setAllowLoadLocalInfile(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setAllowMultiQueries(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setAllowNanAndInf(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setAllowUrlInLocalInfile(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setAlwaysSendSetIsolation(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setAutoClosePStmtStreams(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setAutoDeserialize(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setAutoGenerateTestcaseScript(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setAutoReconnect(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setAutoReconnectForConnectionPools(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setAutoReconnectForPools(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setAutoSlowLog(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setBlobSendChunkSize(String value) throws SQLException {
		// not runtime configurable
		
	}

	public synchronized void setBlobsAreStrings(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setCacheCallableStatements(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setCacheCallableStmts(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setCachePrepStmts(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setCachePreparedStatements(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setCacheResultSetMetadata(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setCacheServerConfiguration(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setCallableStatementCacheSize(int size) {
		// not runtime configurable
		
	}

	public synchronized void setCallableStmtCacheSize(int cacheSize) {
		// not runtime configurable
		
	}

	public synchronized void setCapitalizeDBMDTypes(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setCapitalizeTypeNames(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setCharacterEncoding(String encoding) {
		// not runtime configurable
		
	}

	public synchronized void setCharacterSetResults(String characterSet) {
		// not runtime configurable
		
	}

	public synchronized void setClientCertificateKeyStorePassword(String value) {
		// not runtime configurable
		
	}

	public synchronized void setClientCertificateKeyStoreType(String value) {
		// not runtime configurable
		
	}

	public synchronized void setClientCertificateKeyStoreUrl(String value) {
		// not runtime configurable
		
	}

	public synchronized void setClientInfoProvider(String classname) {
		// not runtime configurable
		
	}

	public synchronized void setClobCharacterEncoding(String encoding) {
		// not runtime configurable
		
	}

	public synchronized void setClobberStreamingResults(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setConnectTimeout(int timeoutMs) {
		// not runtime configurable
		
	}

	public synchronized void setConnectionCollation(String collation) {
		// not runtime configurable
		
	}

	public synchronized void setConnectionLifecycleInterceptors(String interceptors) {
		// not runtime configurable
		
	}

	public synchronized void setContinueBatchOnError(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setCreateDatabaseIfNotExist(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setDefaultFetchSize(int n) {
		// not runtime configurable
		
	}

	public synchronized void setDetectServerPreparedStmts(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setDontTrackOpenResources(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setDumpMetadataOnColumnNotFound(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setDumpQueriesOnException(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setDynamicCalendars(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setElideSetAutoCommits(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setEmptyStringsConvertToZero(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setEmulateLocators(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setEmulateUnsupportedPstmts(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setEnablePacketDebug(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setEnableQueryTimeouts(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setEncoding(String property) {
		// not runtime configurable
		
	}

	public synchronized void setExplainSlowQueries(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setFailOverReadOnly(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setFunctionsNeverReturnBlobs(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setGatherPerfMetrics(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setGatherPerformanceMetrics(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setGenerateSimpleParameterMetadata(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setHoldResultsOpenOverStatementClose(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setIgnoreNonTxTables(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setIncludeInnodbStatusInDeadlockExceptions(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setInitialTimeout(int property) {
		// not runtime configurable
		
	}

	public synchronized void setInteractiveClient(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setIsInteractiveClient(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setJdbcCompliantTruncation(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setJdbcCompliantTruncationForReads(
			boolean jdbcCompliantTruncationForReads) {
		// not runtime configurable
		
	}

	public synchronized void setLargeRowSizeThreshold(String value) {
		// not runtime configurable
		
	}

	public synchronized void setLoadBalanceStrategy(String strategy) {
		// not runtime configurable
		
	}

	public synchronized void setLocalSocketAddress(String address) {
		// not runtime configurable
		
	}

	public synchronized void setLocatorFetchBufferSize(String value) throws SQLException {
		// not runtime configurable
		
	}

	public synchronized void setLogSlowQueries(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setLogXaCommands(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setLogger(String property) {
		// not runtime configurable
		
	}

	public synchronized void setLoggerClassName(String className) {
		// not runtime configurable
		
	}

	public synchronized void setMaintainTimeStats(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setMaxQuerySizeToLog(int sizeInBytes) {
		// not runtime configurable
		
	}

	public synchronized void setMaxReconnects(int property) {
		// not runtime configurable
		
	}

	public synchronized void setMaxRows(int property) {
		// not runtime configurable
		
	}

	public synchronized void setMetadataCacheSize(int value) {
		// not runtime configurable
		
	}

	public synchronized void setNetTimeoutForStreamingResults(int value) {
		// not runtime configurable
		
	}

	public synchronized void setNoAccessToProcedureBodies(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setNoDatetimeStringSync(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setNoTimezoneConversionForTimeType(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setNullCatalogMeansCurrent(boolean value) {
		// not runtime configurable
		
	}

	public synchronized void setNullNamePatternMatchesAll(boolean value) {
		// not runtime configurable
		
	}

	public synchronized void setOverrideSupportsIntegrityEnhancementFacility(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setPacketDebugBufferSize(int size) {
		// not runtime configurable
		
	}

	public synchronized void setPadCharsWithSpace(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setParanoid(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setPedantic(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setPinGlobalTxToPhysicalConnection(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setPopulateInsertRowWithDefaultValues(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setPrepStmtCacheSize(int cacheSize) {
		// not runtime configurable
		
	}

	public synchronized void setPrepStmtCacheSqlLimit(int sqlLimit) {
		// not runtime configurable
		
	}

	public synchronized void setPreparedStatementCacheSize(int cacheSize) {
		// not runtime configurable
		
	}

	public synchronized void setPreparedStatementCacheSqlLimit(int cacheSqlLimit) {
		// not runtime configurable
		
	}

	public synchronized void setProcessEscapeCodesForPrepStmts(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setProfileSQL(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setProfileSql(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setProfilerEventHandler(String handler) {
		// not runtime configurable
		
	}

	public synchronized void setPropertiesTransform(String value) {
		// not runtime configurable
		
	}

	public synchronized void setQueriesBeforeRetryMaster(int property) {
		// not runtime configurable
		
	}

	public synchronized void setReconnectAtTxEnd(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setRelaxAutoCommit(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setReportMetricsIntervalMillis(int millis) {
		// not runtime configurable
		
	}

	public synchronized void setRequireSSL(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setResourceId(String resourceId) {
		// not runtime configurable
		
	}

	public synchronized void setResultSetSizeThreshold(int threshold) {
		// not runtime configurable
		
	}

	public synchronized void setRetainStatementAfterResultSetClose(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setRewriteBatchedStatements(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setRollbackOnPooledClose(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setRoundRobinLoadBalance(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setRunningCTS13(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setSecondsBeforeRetryMaster(int property) {
		// not runtime configurable
		
	}

	public synchronized void setSelfDestructOnPingMaxOperations(int maxOperations) {
		// not runtime configurable
		
	}

	public synchronized void setSelfDestructOnPingSecondsLifetime(int seconds) {
		// not runtime configurable
		
	}

	public synchronized void setServerTimezone(String property) {
		// not runtime configurable
		
	}

	public synchronized void setSessionVariables(String variables) {
		// not runtime configurable
		
	}

	public synchronized void setSlowQueryThresholdMillis(int millis) {
		// not runtime configurable
		
	}

	public synchronized void setSlowQueryThresholdNanos(long nanos) {
		// not runtime configurable
		
	}

	public synchronized void setSocketFactory(String name) {
		// not runtime configurable
		
	}

	public synchronized void setSocketFactoryClassName(String property) {
		// not runtime configurable
		
	}

	public synchronized void setSocketTimeout(int property) {
		// not runtime configurable
		
	}

	public synchronized void setStatementInterceptors(String value) {
		// not runtime configurable
		
	}

	public synchronized void setStrictFloatingPoint(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setStrictUpdates(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setTcpKeepAlive(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setTcpNoDelay(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setTcpRcvBuf(int bufSize) {
		// not runtime configurable
		
	}

	public synchronized void setTcpSndBuf(int bufSize) {
		// not runtime configurable
		
	}

	public synchronized void setTcpTrafficClass(int classFlags) {
		// not runtime configurable
		
	}

	public synchronized void setTinyInt1isBit(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setTraceProtocol(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setTransformedBitIsBoolean(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setTreatUtilDateAsTimestamp(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setTrustCertificateKeyStorePassword(String value) {
		// not runtime configurable
		
	}

	public synchronized void setTrustCertificateKeyStoreType(String value) {
		// not runtime configurable
		
	}

	public synchronized void setTrustCertificateKeyStoreUrl(String value) {
		// not runtime configurable
		
	}

	public synchronized void setUltraDevHack(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseBlobToStoreUTF8OutsideBMP(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseCompression(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setUseConfigs(String configs) {
		// not runtime configurable
		
	}

	public synchronized void setUseCursorFetch(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseDirectRowUnpack(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseDynamicCharsetInfo(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseFastDateParsing(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseFastIntParsing(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseGmtMillisForDatetimes(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseHostsInPrivileges(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setUseInformationSchema(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseJDBCCompliantTimezoneShift(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseJvmCharsetConverters(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseLegacyDatetimeCode(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseLocalSessionState(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseNanosForElapsedTime(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseOldAliasMetadataBehavior(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseOldUTF8Behavior(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseOnlyServerErrorMessages(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseReadAheadInput(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseSSL(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setUseSSPSCompatibleTimezoneShift(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseServerPrepStmts(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseServerPreparedStmts(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseSqlStateCodes(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseStreamLengthsInPrepStmts(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setUseTimezone(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setUseUltraDevWorkAround(boolean property) {
		// not runtime configurable
		
	}

	public synchronized void setUseUnbufferedInput(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseUnicode(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setUseUsageAdvisor(boolean useUsageAdvisorFlag) {
		// not runtime configurable
		
	}

	public synchronized void setUtf8OutsideBmpExcludedColumnNamePattern(String regexPattern) {
		// not runtime configurable
		
	}

	public synchronized void setUtf8OutsideBmpIncludedColumnNamePattern(String regexPattern) {
		// not runtime configurable
		
	}

	public synchronized void setVerifyServerCertificate(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setYearIsDateType(boolean flag) {
		// not runtime configurable
		
	}

	public synchronized void setZeroDateTimeBehavior(String behavior) {
		// not runtime configurable
		
	}

	public synchronized boolean useUnbufferedInput() {
		return this.currentConnection.useUnbufferedInput();
	}

	public synchronized boolean isSameResource(Connection c) {
		return this.currentConnection.isSameResource(c);
	}

	public void setInGlobalTx(boolean flag) {
		this.currentConnection.setInGlobalTx(flag);
	}

	public boolean getUseColumnNamesInFindColumn() {
		return this.currentConnection.getUseColumnNamesInFindColumn();
	}

	public void setUseColumnNamesInFindColumn(boolean flag) {
		// not runtime configurable
	}

	public boolean getUseLocalTransactionState() {
		return this.currentConnection.getUseLocalTransactionState();
	}

	public void setUseLocalTransactionState(boolean flag) {
		// not runtime configurable
		
	}

	public boolean getCompensateOnDuplicateKeyUpdateCounts() {
		return this.currentConnection.getCompensateOnDuplicateKeyUpdateCounts();
	}

	public void setCompensateOnDuplicateKeyUpdateCounts(boolean flag) {
		// not runtime configurable
		
	}

	public boolean getUseAffectedRows() {
		return this.currentConnection.getUseAffectedRows();
	}

	public void setUseAffectedRows(boolean flag) {
		// not runtime configurable
		
	}

	public String getPasswordCharacterEncoding() {
		return this.currentConnection.getPasswordCharacterEncoding();
	}

	public void setPasswordCharacterEncoding(String characterSet) {
		this.currentConnection.setPasswordCharacterEncoding(characterSet);
	}

	public int getAutoIncrementIncrement() {
		return this.currentConnection.getAutoIncrementIncrement();
	}

	public int getLoadBalanceBlacklistTimeout() {
		return this.currentConnection.getLoadBalanceBlacklistTimeout();
	}

	public void setLoadBalanceBlacklistTimeout(int loadBalanceBlacklistTimeout) {
		this.currentConnection.setLoadBalanceBlacklistTimeout(loadBalanceBlacklistTimeout);
	}
	
	public int getLoadBalancePingTimeout() {
		return this.currentConnection.getLoadBalancePingTimeout();
	}

	public void setLoadBalancePingTimeout(int loadBalancePingTimeout) {
		this.currentConnection.setLoadBalancePingTimeout(loadBalancePingTimeout);
	}
	
	public boolean getLoadBalanceValidateConnectionOnSwapServer() {
		return this.currentConnection.getLoadBalanceValidateConnectionOnSwapServer();
	}

	public void setLoadBalanceValidateConnectionOnSwapServer(boolean loadBalanceValidateConnectionOnSwapServer) {
		this.currentConnection.setLoadBalanceValidateConnectionOnSwapServer(loadBalanceValidateConnectionOnSwapServer);
	}

	public int getRetriesAllDown() {
		return this.currentConnection.getRetriesAllDown();
	}

	public void setRetriesAllDown(int retriesAllDown) {
		this.currentConnection.setRetriesAllDown(retriesAllDown);
	}

	public ExceptionInterceptor getExceptionInterceptor() {
		return this.currentConnection.getExceptionInterceptor();
	}

	public String getExceptionInterceptors() {
		return this.currentConnection.getExceptionInterceptors();
	}

	public void setExceptionInterceptors(String exceptionInterceptors) {
		this.currentConnection.setExceptionInterceptors(exceptionInterceptors);
	}

	public boolean getQueryTimeoutKillsConnection() {
		return this.currentConnection.getQueryTimeoutKillsConnection();
	}

	public void setQueryTimeoutKillsConnection(
			boolean queryTimeoutKillsConnection) {
		this.currentConnection.setQueryTimeoutKillsConnection(queryTimeoutKillsConnection);
	}

	public boolean hasSameProperties(Connection c) {
		return this.masterConnection.hasSameProperties(c) && 
			this.slavesConnection.hasSameProperties(c);
	}
	
	public Properties getProperties() {
		Properties props = new Properties();
		props.putAll(this.masterConnection.getProperties());
		props.putAll(this.slavesConnection.getProperties());
		
		return props;
	}

   public String getHost() {
      return currentConnection.getHost();
   }

   public void setProxy(MySQLConnection proxy) {
      currentConnection.setProxy(proxy);
   }

 	public synchronized boolean getRetainStatementAfterResultSetClose() {
		return currentConnection.getRetainStatementAfterResultSetClose();
	}

 	public int getMaxAllowedPacket() {
		return currentConnection.getMaxAllowedPacket();
	}
 	
	public String getLoadBalanceConnectionGroup() {
		return currentConnection.getLoadBalanceConnectionGroup();
	}

	public boolean getLoadBalanceEnableJMX() {
		return currentConnection.getLoadBalanceEnableJMX();
	}

	public String getLoadBalanceExceptionChecker() {
		return currentConnection
				.getLoadBalanceExceptionChecker();
	}

	public String getLoadBalanceSQLExceptionSubclassFailover() {
		return currentConnection
				.getLoadBalanceSQLExceptionSubclassFailover();
	}

	public String getLoadBalanceSQLStateFailover() {
		return currentConnection
				.getLoadBalanceSQLStateFailover();
	}

	public void setLoadBalanceConnectionGroup(String loadBalanceConnectionGroup) {
		currentConnection
				.setLoadBalanceConnectionGroup(loadBalanceConnectionGroup);
		
	}

	public void setLoadBalanceEnableJMX(boolean loadBalanceEnableJMX) {
		currentConnection
				.setLoadBalanceEnableJMX(loadBalanceEnableJMX);
		
	}

	public void setLoadBalanceExceptionChecker(
			String loadBalanceExceptionChecker) {
		currentConnection
				.setLoadBalanceExceptionChecker(loadBalanceExceptionChecker);
		
	}

	public void setLoadBalanceSQLExceptionSubclassFailover(
			String loadBalanceSQLExceptionSubclassFailover) {
		currentConnection
				.setLoadBalanceSQLExceptionSubclassFailover(loadBalanceSQLExceptionSubclassFailover);
		
	}

	public void setLoadBalanceSQLStateFailover(
			String loadBalanceSQLStateFailover) {
		currentConnection
				.setLoadBalanceSQLStateFailover(loadBalanceSQLStateFailover);
		
	}

	public String getLoadBalanceAutoCommitStatementRegex() {
		return currentConnection.getLoadBalanceAutoCommitStatementRegex();
	}

	public int getLoadBalanceAutoCommitStatementThreshold() {
		return currentConnection.getLoadBalanceAutoCommitStatementThreshold();
	}

	public void setLoadBalanceAutoCommitStatementRegex(
			String loadBalanceAutoCommitStatementRegex) {
		currentConnection.setLoadBalanceAutoCommitStatementRegex(loadBalanceAutoCommitStatementRegex);
		
	}

	public void setLoadBalanceAutoCommitStatementThreshold(
			int loadBalanceAutoCommitStatementThreshold) {
		currentConnection.setLoadBalanceAutoCommitStatementThreshold(loadBalanceAutoCommitStatementThreshold);
		
	}

	public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
		// TODO Auto-generated method stub
		
	}

	public boolean getIncludeThreadDumpInDeadlockExceptions() {
		return currentConnection.getIncludeThreadDumpInDeadlockExceptions();
	}

	public void setIncludeThreadDumpInDeadlockExceptions(boolean flag) {
		currentConnection.setIncludeThreadDumpInDeadlockExceptions(flag);
		
	}

	public boolean getIncludeThreadNamesAsStatementComment() {
		return currentConnection.getIncludeThreadNamesAsStatementComment();
	}

	public void setIncludeThreadNamesAsStatementComment(boolean flag) {
		currentConnection.setIncludeThreadNamesAsStatementComment(flag);
	}

	public synchronized boolean isServerLocal() throws SQLException {
		return this.currentConnection.isServerLocal();
	}

  @Override
  public <T> T unwrap(Class<T> tClass) throws SQLException {
    return null;  //To change body of implemented methods use File | Settings | File Templates.
  }

  @Override
  public boolean isWrapperFor(Class<?> aClass) throws SQLException {
    return false;  //To change body of implemented methods use File | Settings | File Templates.
  }
} public class SQLError {
	static final int ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196;

	private static Map mysqlToSql99State;

	private static Map mysqlToSqlState;

	public static final String SQL_STATE_BASE_TABLE_NOT_FOUND = "S0002"; //$NON-NLS-1$

	public static final String SQL_STATE_BASE_TABLE_OR_VIEW_ALREADY_EXISTS = "S0001"; //$NON-NLS-1$

	public static final String SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND = "42S02"; //$NON-NLS-1$

	public static final String SQL_STATE_COLUMN_ALREADY_EXISTS = "S0021"; //$NON-NLS-1$

	public static final String SQL_STATE_COLUMN_NOT_FOUND = "S0022"; //$NON-NLS-1$

	public static final String SQL_STATE_COMMUNICATION_LINK_FAILURE = "08S01"; //$NON-NLS-1$

	public static final String SQL_STATE_CONNECTION_FAIL_DURING_TX = "08007"; //$NON-NLS-1$

	public static final String SQL_STATE_CONNECTION_IN_USE = "08002"; //$NON-NLS-1$

	public static final String SQL_STATE_CONNECTION_NOT_OPEN = "08003"; //$NON-NLS-1$

	public static final String SQL_STATE_CONNECTION_REJECTED = "08004"; //$NON-NLS-1$

	public static final String SQL_STATE_DATE_TRUNCATED = "01004"; //$NON-NLS-1$

	public static final String SQL_STATE_DATETIME_FIELD_OVERFLOW = "22008"; //$NON-NLS-1$

	public static final String SQL_STATE_DEADLOCK = "41000"; //$NON-NLS-1$

	public static final String SQL_STATE_DISCONNECT_ERROR = "01002"; //$NON-NLS-1$

	public static final String SQL_STATE_DIVISION_BY_ZERO = "22012"; //$NON-NLS-1$

	public static final String SQL_STATE_DRIVER_NOT_CAPABLE = "S1C00"; //$NON-NLS-1$

	public static final String SQL_STATE_ERROR_IN_ROW = "01S01"; //$NON-NLS-1$

	public static final String SQL_STATE_GENERAL_ERROR = "S1000"; //$NON-NLS-1$

	public static final String SQL_STATE_ILLEGAL_ARGUMENT = "S1009"; //$NON-NLS-1$

	public static final String SQL_STATE_INDEX_ALREADY_EXISTS = "S0011"; //$NON-NLS-1$

	public static final String SQL_STATE_INDEX_NOT_FOUND = "S0012"; //$NON-NLS-1$

	public static final String SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST = "21S01"; //$NON-NLS-1$

	public static final String SQL_STATE_INVALID_AUTH_SPEC = "28000"; //$NON-NLS-1$

	public static final String SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST = "22018"; // $NON_NLS-1$

	public static final String SQL_STATE_INVALID_COLUMN_NUMBER = "S1002"; //$NON-NLS-1$

	public static final String SQL_STATE_INVALID_CONNECTION_ATTRIBUTE = "01S00"; //$NON-NLS-1$

	public static final String SQL_STATE_MEMORY_ALLOCATION_FAILURE = "S1001"; //$NON-NLS-1$

	public static final String SQL_STATE_MORE_THAN_ONE_ROW_UPDATED_OR_DELETED = "01S04"; //$NON-NLS-1$

	public static final String SQL_STATE_NO_DEFAULT_FOR_COLUMN = "S0023"; //$NON-NLS-1$

	public static final String SQL_STATE_NO_ROWS_UPDATED_OR_DELETED = "01S03"; //$NON-NLS-1$

	public static final String SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE = "22003"; //$NON-NLS-1$

	public static final String SQL_STATE_PRIVILEGE_NOT_REVOKED = "01006"; //$NON-NLS-1$

	public static final String SQL_STATE_SYNTAX_ERROR = "42000"; //$NON-NLS-1$

	public static final String SQL_STATE_TIMEOUT_EXPIRED = "S1T00"; //$NON-NLS-1$

	public static final String SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN = "08007"; // $NON_NLS-1$

	public static final String SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE = "08001"; //$NON-NLS-1$

	public static final String SQL_STATE_WRONG_NO_OF_PARAMETERS = "07001"; //$NON-NLS-1$

	public static final String SQL_STATE_INVALID_TRANSACTION_TERMINATION = "2D000"; // $NON_NLS-1$

	private static Map sqlStateMessages;

	private static final long DEFAULT_WAIT_TIMEOUT_SECONDS = 28800;

	private static final int DUE_TO_TIMEOUT_FALSE = 0;

	private static final int DUE_TO_TIMEOUT_MAYBE = 2;

	private static final int DUE_TO_TIMEOUT_TRUE = 1;
	
	private static final Constructor JDBC_4_COMMUNICATIONS_EXCEPTION_CTOR;
	
	private static Method THROWABLE_INIT_CAUSE_METHOD;
	
	static {
		if (Util.isJdbc4()) {
			try {
				JDBC_4_COMMUNICATIONS_EXCEPTION_CTOR = Class.forName(
						"com.mysql.jdbc.exceptions.jdbc4.CommunicationsException")
						.getConstructor(
								new Class[] { MySQLConnection.class, Long.TYPE, Long.TYPE, Exception.class });
			} catch (SecurityException e) {
				throw new RuntimeException(e);
			} catch (NoSuchMethodException e) {
				throw new RuntimeException(e);
			} catch (ClassNotFoundException e) {
				throw new RuntimeException(e);
			}
		} else {
			JDBC_4_COMMUNICATIONS_EXCEPTION_CTOR = null;
		}
		
		try {
			THROWABLE_INIT_CAUSE_METHOD = Throwable.class.getMethod("initCause", new Class[] {Throwable.class});
		} catch (Throwable t) {
			// we're not on a VM that has it
			THROWABLE_INIT_CAUSE_METHOD = null;
		}
		
		sqlStateMessages = new HashMap();
		sqlStateMessages.put(SQL_STATE_DISCONNECT_ERROR, Messages
				.getString("SQLError.35")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_DATE_TRUNCATED, Messages
				.getString("SQLError.36")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_PRIVILEGE_NOT_REVOKED, Messages
				.getString("SQLError.37")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, Messages
				.getString("SQLError.38")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_ERROR_IN_ROW, Messages
				.getString("SQLError.39")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_NO_ROWS_UPDATED_OR_DELETED, Messages
				.getString("SQLError.40")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_MORE_THAN_ONE_ROW_UPDATED_OR_DELETED,
				Messages.getString("SQLError.41")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_WRONG_NO_OF_PARAMETERS, Messages
				.getString("SQLError.42")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE,
				Messages.getString("SQLError.43")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_CONNECTION_IN_USE, Messages
				.getString("SQLError.44")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_CONNECTION_NOT_OPEN, Messages
				.getString("SQLError.45")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_CONNECTION_REJECTED, Messages
				.getString("SQLError.46")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_CONNECTION_FAIL_DURING_TX, Messages
				.getString("SQLError.47")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_COMMUNICATION_LINK_FAILURE, Messages
				.getString("SQLError.48")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST,
				Messages.getString("SQLError.49")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE, Messages
				.getString("SQLError.50")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_DATETIME_FIELD_OVERFLOW, Messages
				.getString("SQLError.51")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_DIVISION_BY_ZERO, Messages
				.getString("SQLError.52")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_DEADLOCK, Messages
				.getString("SQLError.53")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_INVALID_AUTH_SPEC, Messages
				.getString("SQLError.54")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_SYNTAX_ERROR, Messages
				.getString("SQLError.55")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND, Messages
				.getString("SQLError.56")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_BASE_TABLE_OR_VIEW_ALREADY_EXISTS,
				Messages.getString("SQLError.57")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_BASE_TABLE_NOT_FOUND, Messages
				.getString("SQLError.58")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_INDEX_ALREADY_EXISTS, Messages
				.getString("SQLError.59")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_INDEX_NOT_FOUND, Messages
				.getString("SQLError.60")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_COLUMN_ALREADY_EXISTS, Messages
				.getString("SQLError.61")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_COLUMN_NOT_FOUND, Messages
				.getString("SQLError.62")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_NO_DEFAULT_FOR_COLUMN, Messages
				.getString("SQLError.63")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_GENERAL_ERROR, Messages
				.getString("SQLError.64")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_MEMORY_ALLOCATION_FAILURE, Messages
				.getString("SQLError.65")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_INVALID_COLUMN_NUMBER, Messages
				.getString("SQLError.66")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_ILLEGAL_ARGUMENT, Messages
				.getString("SQLError.67")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_DRIVER_NOT_CAPABLE, Messages
				.getString("SQLError.68")); //$NON-NLS-1$
		sqlStateMessages.put(SQL_STATE_TIMEOUT_EXPIRED, Messages
				.getString("SQLError.69")); //$NON-NLS-1$

		mysqlToSqlState = new Hashtable();

		//
		// Communications Errors
		//
		// ER_CON_COUNT_ERROR 1040
		// ER_BAD_HOST_ERROR 1042
		// ER_HANDSHAKE_ERROR 1043
		// ER_UNKNOWN_COM_ERROR 1047
		// ER_IPSOCK_ERROR 1081
		//
		mysqlToSqlState.put(Integer.valueOf(1040), SQL_STATE_CONNECTION_REJECTED);
		mysqlToSqlState.put(Integer.valueOf(1042), SQL_STATE_CONNECTION_REJECTED);
		mysqlToSqlState.put(Integer.valueOf(1043), SQL_STATE_CONNECTION_REJECTED);
		mysqlToSqlState.put(Integer.valueOf(1047),
				SQL_STATE_COMMUNICATION_LINK_FAILURE);
		mysqlToSqlState.put(Integer.valueOf(1081),
				SQL_STATE_COMMUNICATION_LINK_FAILURE);

		// ER_HOST_IS_BLOCKED 1129
		// ER_HOST_NOT_PRIVILEGED 1130
		mysqlToSqlState.put(Integer.valueOf(1129), SQL_STATE_CONNECTION_REJECTED);
		mysqlToSqlState.put(Integer.valueOf(1130), SQL_STATE_CONNECTION_REJECTED);

		//
		// Authentication Errors
		//
		// ER_ACCESS_DENIED_ERROR 1045
		//
		mysqlToSqlState.put(Integer.valueOf(1045), SQL_STATE_INVALID_AUTH_SPEC);

		//
		// Resource errors
		//
		// ER_CANT_CREATE_FILE 1004
		// ER_CANT_CREATE_TABLE 1005
		// ER_CANT_LOCK 1015
		// ER_DISK_FULL 1021
		// ER_CON_COUNT_ERROR 1040
		// ER_OUT_OF_RESOURCES 1041
		//
		// Out-of-memory errors
		//
		// ER_OUTOFMEMORY 1037
		// ER_OUT_OF_SORTMEMORY 1038
		//
		mysqlToSqlState.put(Integer.valueOf(1037),
				SQL_STATE_MEMORY_ALLOCATION_FAILURE);
		mysqlToSqlState.put(Integer.valueOf(1038),
				SQL_STATE_MEMORY_ALLOCATION_FAILURE);

		//
		// Syntax Errors
		//
		// ER_PARSE_ERROR 1064
		// ER_EMPTY_QUERY 1065
		//
		mysqlToSqlState.put(Integer.valueOf(1064), SQL_STATE_SYNTAX_ERROR);
		mysqlToSqlState.put(Integer.valueOf(1065), SQL_STATE_SYNTAX_ERROR);

		//
		// Invalid argument errors
		//
		// ER_WRONG_FIELD_WITH_GROUP 1055
		// ER_WRONG_GROUP_FIELD 1056
		// ER_WRONG_SUM_SELECT 1057
		// ER_TOO_LONG_IDENT 1059
		// ER_DUP_FIELDNAME 1060
		// ER_DUP_KEYNAME 1061
		// ER_DUP_ENTRY 1062
		// ER_WRONG_FIELD_SPEC 1063
		// ER_NONUNIQ_TABLE 1066
		// ER_INVALID_DEFAULT 1067
		// ER_MULTIPLE_PRI_KEY 1068
		// ER_TOO_MANY_KEYS 1069
		// ER_TOO_MANY_KEY_PARTS 1070
		// ER_TOO_LONG_KEY 1071
		// ER_KEY_COLUMN_DOES_NOT_EXIST 1072
		// ER_BLOB_USED_AS_KEY 1073
		// ER_TOO_BIG_FIELDLENGTH 1074
		// ER_WRONG_AUTO_KEY 1075
		// ER_NO_SUCH_INDEX 1082
		// ER_WRONG_FIELD_TERMINATORS 1083
		// ER_BLOBS_AND_NO_TERMINATED 1084
		//
		mysqlToSqlState.put(Integer.valueOf(1055), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1056), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1057), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1059), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1060), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1061), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1062), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1063), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1066), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1067), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1068), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1069), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1070), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1071), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1072), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1073), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1074), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1075), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1082), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1083), SQL_STATE_ILLEGAL_ARGUMENT);
		mysqlToSqlState.put(Integer.valueOf(1084), SQL_STATE_ILLEGAL_ARGUMENT);

		//
		// ER_WRONG_VALUE_COUNT 1058
		//
		mysqlToSqlState.put(Integer.valueOf(1058),
				SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST);

		// ER_CANT_CREATE_DB 1006
		// ER_DB_CREATE_EXISTS 1007
		// ER_DB_DROP_EXISTS 1008
		// ER_DB_DROP_DELETE 1009
		// ER_DB_DROP_RMDIR 1010
		// ER_CANT_DELETE_FILE 1011
		// ER_CANT_FIND_SYSTEM_REC 1012
		// ER_CANT_GET_STAT 1013
		// ER_CANT_GET_WD 1014
		// ER_UNEXPECTED_EOF 1039
		// ER_CANT_OPEN_FILE 1016
		// ER_FILE_NOT_FOUND 1017
		// ER_CANT_READ_DIR 1018
		// ER_CANT_SET_WD 1019
		// ER_CHECKREAD 1020
		// ER_DUP_KEY 1022
		// ER_ERROR_ON_CLOSE 1023
		// ER_ERROR_ON_READ 1024
		// ER_ERROR_ON_RENAME 1025
		// ER_ERROR_ON_WRITE 1026
		// ER_FILE_USED 1027
		// ER_FILSORT_ABORT 1028
		// ER_FORM_NOT_FOUND 1029
		// ER_GET_ERRNO 1030
		// ER_ILLEGAL_HA 1031
		// ER_KEY_NOT_FOUND 1032
		// ER_NOT_FORM_FILE 1033
		// ER_DBACCESS_DENIED_ERROR 1044
		// ER_NO_DB_ERROR 1046
		// ER_BAD_NULL_ERROR 1048
		// ER_BAD_DB_ERROR 1049
		// ER_TABLE_EXISTS_ERROR 1050
		// ER_BAD_TABLE_ERROR 1051
		mysqlToSqlState.put(Integer.valueOf(1051),
				SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND);

		// ER_NON_UNIQ_ERROR 1052
		// ER_BAD_FIELD_ERROR 1054
		mysqlToSqlState.put(Integer.valueOf(1054), SQL_STATE_COLUMN_NOT_FOUND);

		// ER_TEXTFILE_NOT_READABLE 1085
		// ER_FILE_EXISTS_ERROR 1086
		// ER_LOAD_INFO 1087
		// ER_ALTER_INFO 1088
		// ER_WRONG_SUB_KEY 1089
		// ER_CANT_REMOVE_ALL_FIELDS 1090
		// ER_CANT_DROP_FIELD_OR_KEY 1091
		// ER_INSERT_INFO 1092
		// ER_INSERT_TABLE_USED 1093
		// ER_LOCK_DEADLOCK 1213
		mysqlToSqlState.put(Integer.valueOf(1205), SQL_STATE_DEADLOCK);
		mysqlToSqlState.put(Integer.valueOf(1213), SQL_STATE_DEADLOCK);

		mysqlToSql99State = new HashMap();

		mysqlToSql99State.put(Integer.valueOf(1205), SQL_STATE_DEADLOCK);
		mysqlToSql99State.put(Integer.valueOf(1213), SQL_STATE_DEADLOCK);
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_DUP_KEY),
				"23000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_OUTOFMEMORY),
				"HY001");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_OUT_OF_SORTMEMORY), "HY001");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_CON_COUNT_ERROR), "08004");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_BAD_HOST_ERROR),
				"08S01");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_HANDSHAKE_ERROR), "08S01");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_DBACCESS_DENIED_ERROR), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_ACCESS_DENIED_ERROR), "28000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_TABLE_EXISTS_ERROR), "42S01");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_BAD_TABLE_ERROR), "42S02");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_NON_UNIQ_ERROR),
				"23000");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_SERVER_SHUTDOWN), "08S01");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_BAD_FIELD_ERROR), "42S22");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WRONG_FIELD_WITH_GROUP), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WRONG_GROUP_FIELD), "42000");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_WRONG_SUM_SELECT), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WRONG_VALUE_COUNT), "21S01");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_TOO_LONG_IDENT),
				"42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_DUP_FIELDNAME),
				"42S21");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_DUP_KEYNAME),
				"42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_DUP_ENTRY),
				"23000");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_WRONG_FIELD_SPEC), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_PARSE_ERROR),
				"42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_EMPTY_QUERY),
				"42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_NONUNIQ_TABLE),
				"42000");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_INVALID_DEFAULT), "42000");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_MULTIPLE_PRI_KEY), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_TOO_MANY_KEYS),
				"42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_TOO_MANY_KEY_PARTS), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_TOO_LONG_KEY),
				"42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_KEY_COLUMN_DOES_NOT_EXITS), "42000");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_BLOB_USED_AS_KEY), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_TOO_BIG_FIELDLENGTH), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_WRONG_AUTO_KEY),
				"42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_FORCING_CLOSE),
				"08S01");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_IPSOCK_ERROR),
				"08S01");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_NO_SUCH_INDEX),
				"42S12");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WRONG_FIELD_TERMINATORS), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_BLOBS_AND_NO_TERMINATED), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_CANT_REMOVE_ALL_FIELDS), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_CANT_DROP_FIELD_OR_KEY), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_BLOB_CANT_HAVE_DEFAULT), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_WRONG_DB_NAME),
				"42000");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_WRONG_TABLE_NAME), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_TOO_BIG_SELECT),
				"42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_UNKNOWN_PROCEDURE), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WRONG_PARAMCOUNT_TO_PROCEDURE), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_UNKNOWN_TABLE),
				"42S02");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_FIELD_SPECIFIED_TWICE), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_UNSUPPORTED_EXTENSION), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_TABLE_MUST_HAVE_COLUMNS), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_UNKNOWN_CHARACTER_SET), "42000");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_TOO_BIG_ROWSIZE), "42000");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_WRONG_OUTER_JOIN), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NULL_COLUMN_IN_INDEX), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_PASSWORD_ANONYMOUS_USER), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_PASSWORD_NOT_ALLOWED), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_PASSWORD_NO_MATCH), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WRONG_VALUE_COUNT_ON_ROW), "21S01");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_INVALID_USE_OF_NULL), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_REGEXP_ERROR),
				"42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_MIX_OF_GROUP_FUNC_AND_FIELDS), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NONEXISTING_GRANT), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_TABLEACCESS_DENIED_ERROR), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_COLUMNACCESS_DENIED_ERROR), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_ILLEGAL_GRANT_FOR_TABLE), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_GRANT_WRONG_HOST_OR_USER), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_NO_SUCH_TABLE),
				"42S02");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NONEXISTING_TABLE_GRANT), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NOT_ALLOWED_COMMAND), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_SYNTAX_ERROR),
				"42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_ABORTING_CONNECTION), "08S01");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NET_PACKET_TOO_LARGE), "08S01");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NET_READ_ERROR_FROM_PIPE), "08S01");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_NET_FCNTL_ERROR), "08S01");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NET_PACKETS_OUT_OF_ORDER), "08S01");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NET_UNCOMPRESS_ERROR), "08S01");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_NET_READ_ERROR),
				"08S01");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NET_READ_INTERRUPTED), "08S01");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NET_ERROR_ON_WRITE), "08S01");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NET_WRITE_INTERRUPTED), "08S01");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_TOO_LONG_STRING), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_TABLE_CANT_HANDLE_BLOB), "42000");
		mysqlToSql99State
				.put(Integer.valueOf(
						MysqlErrorNumbers.ER_TABLE_CANT_HANDLE_AUTO_INCREMENT),
						"42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WRONG_COLUMN_NAME), "42000");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_WRONG_KEY_COLUMN), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_DUP_UNIQUE),
				"23000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_BLOB_KEY_WITHOUT_LENGTH), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_PRIMARY_CANT_HAVE_NULL), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_TOO_MANY_ROWS),
				"42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_REQUIRES_PRIMARY_KEY), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_CHECK_NO_SUCH_TABLE), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_CHECK_NOT_IMPLEMENTED), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_CANT_DO_THIS_DURING_AN_TRANSACTION),
				"25000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NEW_ABORTING_CONNECTION), "08S01");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_MASTER_NET_READ), "08S01");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_MASTER_NET_WRITE), "08S01");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_TOO_MANY_USER_CONNECTIONS), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_READ_ONLY_TRANSACTION), "25000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NO_PERMISSION_TO_CREATE_USER), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_LOCK_DEADLOCK),
				"40001");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NO_REFERENCED_ROW), "23000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_ROW_IS_REFERENCED), "23000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_CONNECT_TO_MASTER), "08S01");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),
				"21000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_USER_LIMIT_REACHED), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_NO_DEFAULT),
				"42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WRONG_VALUE_FOR_VAR), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WRONG_TYPE_FOR_VAR), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_CANT_USE_OPTION_HERE), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NOT_SUPPORTED_YET), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_WRONG_FK_DEF),
				"42000");
		mysqlToSql99State.put(
				Integer.valueOf(MysqlErrorNumbers.ER_OPERAND_COLUMNS), "21000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_SUBQUERY_NO_1_ROW), "21000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_ILLEGAL_REFERENCE), "42S22");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_DERIVED_MUST_HAVE_ALIAS), "42000");
		mysqlToSql99State.put(Integer.valueOf(MysqlErrorNumbers.ER_SELECT_REDUCED),
				"01000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_TABLENAME_NOT_ALLOWED_HERE), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_NOT_SUPPORTED_AUTH_MODE), "08004");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_SPATIAL_CANT_HAVE_NULL), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_COLLATION_CHARSET_MISMATCH), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WARN_TOO_FEW_RECORDS), "01000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WARN_TOO_MANY_RECORDS), "01000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WARN_NULL_TO_NOTNULL), "01000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WARN_DATA_OUT_OF_RANGE), "01000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WARN_DATA_TRUNCATED), "01000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WRONG_NAME_FOR_INDEX), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_WRONG_NAME_FOR_CATALOG), "42000");
		mysqlToSql99State.put(Integer.valueOf(
				MysqlErrorNumbers.ER_UNKNOWN_STORAGE_ENGINE), "42000");
	}

	/**
	 * Turns output of 'SHOW WARNINGS' into JDBC SQLWarning instances.
	 * 
	 * If 'forTruncationOnly' is true, only looks for truncation warnings, and
	 * actually throws DataTruncation as an exception.
	 * 
	 * @param connection
	 *            the connection to use for getting warnings.
	 * 
	 * @return the SQLWarning chain (or null if no warnings)
	 * 
	 * @throws SQLException
	 *             if the warnings could not be retrieved
	 */
	static SQLWarning convertShowWarningsToSQLWarnings(Connection connection)
			throws SQLException {
		return convertShowWarningsToSQLWarnings(connection, 0, false);
	}

	/**
	 * Turns output of 'SHOW WARNINGS' into JDBC SQLWarning instances.
	 * 
	 * If 'forTruncationOnly' is true, only looks for truncation warnings, and
	 * actually throws DataTruncation as an exception.
	 * 
	 * @param connection
	 *            the connection to use for getting warnings.
	 * @param warningCountIfKnown
	 *            the warning count (if known), otherwise set it to 0.
	 * @param forTruncationOnly
	 *            if this method should only scan for data truncation warnings
	 * 
	 * @return the SQLWarning chain (or null if no warnings)
	 * 
	 * @throws SQLException
	 *             if the warnings could not be retrieved, or if data truncation
	 *             is being scanned for and truncations were found.
	 */
	static SQLWarning convertShowWarningsToSQLWarnings(Connection connection,
			int warningCountIfKnown, boolean forTruncationOnly)
			throws SQLException {
		java.sql.Statement stmt = null;
		java.sql.ResultSet warnRs = null;

		SQLWarning currentWarning = null;

		try {
			if (warningCountIfKnown < 100) {
				stmt = connection.createStatement();

				if (stmt.getMaxRows() != 0) {
					stmt.setMaxRows(0);
				}
			} else {
				// stream large warning counts
				stmt = connection.createStatement(
						java.sql.ResultSet.TYPE_FORWARD_ONLY,
						java.sql.ResultSet.CONCUR_READ_ONLY);
				stmt.setFetchSize(Integer.MIN_VALUE);
			}

			/*
			 * +---------+------+---------------------------------------------+ |
			 * Level | Code | Message |
			 * +---------+------+---------------------------------------------+ |
			 * Warning | 1265 | Data truncated for column 'field1' at row 1 |
			 * +---------+------+---------------------------------------------+
			 */
			warnRs = stmt.executeQuery("SHOW WARNINGS"); //$NON-NLS-1$

			while (warnRs.next()) {
				int code = warnRs.getInt("Code"); //$NON-NLS-1$

				if (forTruncationOnly) {
					if (code == 1265 || code == 1264) {
						DataTruncation newTruncation = new MysqlDataTruncation(
								warnRs.getString("Message"), 0, false, false, 0, 0, code); //$NON-NLS-1$

						if (currentWarning == null) {
							currentWarning = newTruncation;
						} else {
							currentWarning.setNextWarning(newTruncation);
						}
					}
				} else {
					String level = warnRs.getString("Level"); //$NON-NLS-1$
					String message = warnRs.getString("Message"); //$NON-NLS-1$

					SQLWarning newWarning = new SQLWarning(message, SQLError
							.mysqlToSqlState(code, connection
									.getUseSqlStateCodes()), code);

					if (currentWarning == null) {
						currentWarning = newWarning;
					} else {
						currentWarning.setNextWarning(newWarning);
					}
				}
			}

			if (forTruncationOnly && (currentWarning != null)) {
				throw currentWarning;
			}

			return currentWarning;
		} finally {
			SQLException reThrow = null;

			if (warnRs != null) {
				try {
					warnRs.close();
				} catch (SQLException sqlEx) {
					reThrow = sqlEx;
				}
			}

			if (stmt != null) {
				try {
					stmt.close();
				} catch (SQLException sqlEx) {
					// ideally, we'd use chained exceptions here,
					// but we still support JDK-1.2.x with this driver
					// which doesn't have them....
					reThrow = sqlEx;
				}
			}

			if (reThrow != null) {
				throw reThrow;
			}
		}
	}

	public static void dumpSqlStatesMappingsAsXml() throws Exception {
		TreeMap allErrorNumbers = new TreeMap();
		Map mysqlErrorNumbersToNames = new HashMap();

		Integer errorNumber = null;

		// 
		// First create a list of all 'known' error numbers that
		// are mapped.
		//
		for (Iterator mysqlErrorNumbers = mysqlToSql99State.keySet().iterator(); mysqlErrorNumbers
				.hasNext();) {
			errorNumber = (Integer) mysqlErrorNumbers.next();
			allErrorNumbers.put(errorNumber, errorNumber);
		}

		for (Iterator mysqlErrorNumbers = mysqlToSqlState.keySet().iterator(); mysqlErrorNumbers
				.hasNext();) {
			errorNumber = (Integer) mysqlErrorNumbers.next();
			allErrorNumbers.put(errorNumber, errorNumber);
		}

		//
		// Now create a list of the actual MySQL error numbers we know about
		//
		java.lang.reflect.Field[] possibleFields = MysqlErrorNumbers.class
				.getDeclaredFields();

		for (int i = 0; i < possibleFields.length; i++) {
			String fieldName = possibleFields[i].getName();

			if (fieldName.startsWith("ER_")) {
				mysqlErrorNumbersToNames.put(possibleFields[i].get(null),
						fieldName);
			}
		}

		System.out.println("<ErrorMappings>");

		for (Iterator allErrorNumbersIter = allErrorNumbers.keySet().iterator(); allErrorNumbersIter
				.hasNext();) {
			errorNumber = (Integer) allErrorNumbersIter.next();

			String sql92State = mysqlToSql99(errorNumber.intValue());
			String oldSqlState = mysqlToXOpen(errorNumber.intValue());

			System.out.println("   <ErrorMapping mysqlErrorNumber=\""
					+ errorNumber + "\" mysqlErrorName=\""
					+ mysqlErrorNumbersToNames.get(errorNumber)
					+ "\" legacySqlState=\""
					+ ((oldSqlState == null) ? "" : oldSqlState)
					+ "\" sql92SqlState=\""
					+ ((sql92State == null) ? "" : sql92State) + "\"/>");
		}

		System.out.println("</ErrorMappings>");
	}

	static String get(String stateCode) {
		return (String) sqlStateMessages.get(stateCode);
	}

	private static String mysqlToSql99(int errno) {
		Integer err = Integer.valueOf(errno);

		if (mysqlToSql99State.containsKey(err)) {
			return (String) mysqlToSql99State.get(err);
		}

		return "HY000";
	}

	/**
	 * Map MySQL error codes to X/Open or SQL-92 error codes
	 * 
	 * @param errno
	 *            the MySQL error code
	 * 
	 * @return the corresponding X/Open or SQL-92 error code
	 */
	static String mysqlToSqlState(int errno, boolean useSql92States) {
		if (useSql92States) {
			return mysqlToSql99(errno);
		}

		return mysqlToXOpen(errno);
	}

	private static String mysqlToXOpen(int errno) {
		Integer err = Integer.valueOf(errno);

		if (mysqlToSqlState.containsKey(err)) {
			return (String) mysqlToSqlState.get(err);
		}

		return SQL_STATE_GENERAL_ERROR;
	}

	/*
	 * SQL State Class SQLNonTransientException Subclass 08
	 * SQLNonTransientConnectionException 22 SQLDataException 23
	 * SQLIntegrityConstraintViolationException N/A
	 * SQLInvalidAuthorizationException 42 SQLSyntaxErrorException
	 * 
	 * SQL State Class SQLTransientException Subclass 08
	 * SQLTransientConnectionException 40 SQLTransactionRollbackException N/A
	 * SQLTimeoutException
	 */

	public static SQLException createSQLException(String message,
			String sqlState, ExceptionInterceptor interceptor) {
		return createSQLException(message, sqlState, 0, interceptor);
	}

	public static SQLException createSQLException(String message, ExceptionInterceptor interceptor) {
		return createSQLException(message, interceptor, null);
	}
	public static SQLException createSQLException(String message, ExceptionInterceptor interceptor, Connection conn) {
		SQLException sqlEx = new SQLException(message);
		
		if (interceptor != null) {
			SQLException interceptedEx = interceptor.interceptException(sqlEx, conn);
			
			if (interceptedEx != null) {
				return interceptedEx;
			}
		}
		
		return sqlEx;
	}

	public static SQLException createSQLException(String message, String sqlState, Throwable cause, ExceptionInterceptor interceptor) {
		return createSQLException(message, sqlState, cause, interceptor, null);
	}
	public static SQLException createSQLException(String message, String sqlState, Throwable cause, ExceptionInterceptor interceptor,
			Connection conn) {
		if (THROWABLE_INIT_CAUSE_METHOD == null) {
			if (cause != null) {
				message = message + " due to " + cause.toString();
			}
		}
		
		SQLException sqlEx = createSQLException(message, sqlState, interceptor);
		
		if (cause != null && THROWABLE_INIT_CAUSE_METHOD != null) {
			try {
				THROWABLE_INIT_CAUSE_METHOD.invoke(sqlEx, new Object[] {cause});
			} catch (Throwable t) {
				// we're not going to muck with that here, since it's
				// an error condition anyway!
			}
		}
		
		if (interceptor != null) {
			SQLException interceptedEx = interceptor.interceptException(sqlEx, conn);
			
			if (interceptedEx != null) {
				return interceptedEx;
			}
		}
		
		return sqlEx;
	}
	
	public static SQLException createSQLException(String message,
			String sqlState, int vendorErrorCode, ExceptionInterceptor interceptor) {
		return createSQLException(message, sqlState, vendorErrorCode, false, interceptor);
	}
	
	public static SQLException createSQLException(String message,
			String sqlState, int vendorErrorCode, boolean isTransient, ExceptionInterceptor interceptor) {
		return createSQLException(message, sqlState, vendorErrorCode, false, interceptor, null);
	}
	public static SQLException createSQLException(String message,
			String sqlState, int vendorErrorCode, boolean isTransient, ExceptionInterceptor interceptor, Connection conn) {
		try {
			SQLException sqlEx = null;
			
			if (sqlState != null) {
				if (sqlState.startsWith("08")) {
					if (isTransient) {
						if (!Util.isJdbc4()) {
							sqlEx = new MySQLTransientConnectionException(
									message, sqlState, vendorErrorCode);
						} else {
							sqlEx = (SQLException) Util
								.getInstance(
										"com.mysql.jdbc.exceptions.jdbc4.MySQLTransientConnectionException",
										new Class[] { String.class,
												String.class, Integer.TYPE },
										new Object[] { message, sqlState,
												Integer.valueOf(vendorErrorCode) }, interceptor);
						}
					} else if (!Util.isJdbc4()) {
						sqlEx = new MySQLNonTransientConnectionException(
								message, sqlState, vendorErrorCode);
					} else {
						sqlEx = (SQLException) Util.getInstance(
									"com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException",
									new Class[] { String.class, String.class,
											Integer.TYPE  }, new Object[] {
											message, sqlState,
											Integer.valueOf(vendorErrorCode) }, interceptor);
					}
				} else if (sqlState.startsWith("22")) {
					if (!Util.isJdbc4()) {
						sqlEx = new MySQLDataException(message, sqlState,
								vendorErrorCode);
					} else {
						sqlEx = (SQLException) Util
							.getInstance(
									"com.mysql.jdbc.exceptions.jdbc4.MySQLDataException",
									new Class[] { String.class, String.class,
											Integer.TYPE  }, new Object[] {
											message, sqlState,
											Integer.valueOf(vendorErrorCode) }, interceptor);
					}
				} else if (sqlState.startsWith("23")) {

					if (!Util.isJdbc4()) {
						sqlEx = new MySQLIntegrityConstraintViolationException(
								message, sqlState, vendorErrorCode);
					} else {
						sqlEx =  (SQLException) Util
							.getInstance(
									"com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException",
									new Class[] { String.class, String.class,
											Integer.TYPE  }, new Object[] {
											message, sqlState,
											Integer.valueOf(vendorErrorCode) }, interceptor);
					}
				} else if (sqlState.startsWith("42")) {
					if (!Util.isJdbc4()) {
						sqlEx = new MySQLSyntaxErrorException(message, sqlState,
								vendorErrorCode);
					} else {
						sqlEx = (SQLException) Util.getInstance(
									"com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException",
									new Class[] { String.class, String.class,
											Integer.TYPE  }, new Object[] {
											message, sqlState,
											Integer.valueOf(vendorErrorCode) }, interceptor);
					}
				} else if (sqlState.startsWith("40")) {
					if (!Util.isJdbc4()) {
						sqlEx = new MySQLTransactionRollbackException(message,
								sqlState, vendorErrorCode);
					} else {
						sqlEx = (SQLException) Util
							.getInstance(
									"com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException",
									new Class[] { String.class, String.class,
											Integer.TYPE  }, new Object[] {
											message, sqlState,
											Integer.valueOf(vendorErrorCode) }, interceptor);
					}
				} else {
					sqlEx = new SQLException(message, sqlState, vendorErrorCode);
				}
			} else {
				sqlEx = new SQLException(message, sqlState, vendorErrorCode);
			}
			
			if (interceptor != null) {
				SQLException interceptedEx = interceptor.interceptException(sqlEx, conn);
				
				if (interceptedEx != null) {
					return interceptedEx;
				}
			}
			
			return sqlEx;
		} catch (SQLException sqlEx) {
			SQLException unexpectedEx = new SQLException(
					"Unable to create correct SQLException class instance, error class/codes may be incorrect. Reason: "
							+ Util.stackTraceToString(sqlEx),
					SQL_STATE_GENERAL_ERROR);
			
			if (interceptor != null) {
				SQLException interceptedEx = interceptor.interceptException(unexpectedEx, conn);
				
				if (interceptedEx != null) {
					return interceptedEx;
				}
			}
			
			return unexpectedEx;
		}
	}
	
	public static SQLException createCommunicationsException(MySQLConnection conn, long lastPacketSentTimeMs,
			long lastPacketReceivedTimeMs,
			Exception underlyingException, ExceptionInterceptor interceptor) {
		SQLException exToReturn = null;
		
		if (!Util.isJdbc4()) {
			exToReturn = new CommunicationsException(conn, lastPacketSentTimeMs, lastPacketReceivedTimeMs, underlyingException);
		} else {
		
			try {
				exToReturn = (SQLException) Util.handleNewInstance(JDBC_4_COMMUNICATIONS_EXCEPTION_CTOR, new Object[] {
					conn, Long.valueOf(lastPacketSentTimeMs), Long.valueOf(lastPacketReceivedTimeMs), underlyingException}, interceptor);
			} catch (SQLException sqlEx) {
				// We should _never_ get this, but let's not swallow it either
				
				return sqlEx;
			}
		}
		
		if (THROWABLE_INIT_CAUSE_METHOD != null && underlyingException != null) {
			try {
				THROWABLE_INIT_CAUSE_METHOD.invoke(exToReturn, new Object[] {underlyingException});
			} catch (Throwable t) {
				// we're not going to muck with that here, since it's
				// an error condition anyway!
			}
		}
		
		if (interceptor != null) {
			SQLException interceptedEx = interceptor.interceptException(exToReturn, conn);
			
			if (interceptedEx != null) {
				return interceptedEx;
			}
		}
		
		return exToReturn;
	}
	
	/**
	 * Creates a communications link failure message to be used
	 * in CommunicationsException that (hopefully) has some better
	 * information and suggestions based on heuristics.
	 *  
	 * @param conn
	 * @param lastPacketSentTimeMs
	 * @param underlyingException
	 * @param streamingResultSetInPlay
	 * @return
	 */
	public static String createLinkFailureMessageBasedOnHeuristics(
			MySQLConnection conn,
			long lastPacketSentTimeMs, 
			long lastPacketReceivedTimeMs,
			Exception underlyingException,
			boolean streamingResultSetInPlay) {
		long serverTimeoutSeconds = 0;
		boolean isInteractiveClient = false;

		if (conn != null) {
			isInteractiveClient = conn.getInteractiveClient();

			String serverTimeoutSecondsStr = null;

			if (isInteractiveClient) {
				serverTimeoutSecondsStr = conn
						.getServerVariable("interactive_timeout"); //$NON-NLS-1$
			} else {
				serverTimeoutSecondsStr = conn
						.getServerVariable("wait_timeout"); //$NON-NLS-1$
			}

			if (serverTimeoutSecondsStr != null) {
				try {
					serverTimeoutSeconds = Long
							.parseLong(serverTimeoutSecondsStr);
				} catch (NumberFormatException nfe) {
					serverTimeoutSeconds = 0;
				}
			}
		}

		StringBuffer exceptionMessageBuf = new StringBuffer();
		
		long nowMs = System.currentTimeMillis();
		
		if (lastPacketSentTimeMs == 0) {
			lastPacketSentTimeMs = nowMs;
		}

		long timeSinceLastPacketSentMs = (nowMs - lastPacketSentTimeMs);
		long timeSinceLastPacketSeconds = timeSinceLastPacketSentMs / 1000;
		
		long timeSinceLastPacketReceivedMs = (nowMs - lastPacketReceivedTimeMs);
		
		int dueToTimeout = DUE_TO_TIMEOUT_FALSE;

		StringBuffer timeoutMessageBuf = null;

		if (streamingResultSetInPlay) {
			exceptionMessageBuf.append(Messages
					.getString("CommunicationsException.ClientWasStreaming")); //$NON-NLS-1$
		} else {
			if (serverTimeoutSeconds != 0) {
				if (timeSinceLastPacketSeconds > serverTimeoutSeconds) {
					dueToTimeout = DUE_TO_TIMEOUT_TRUE;

					timeoutMessageBuf = new StringBuffer();

					timeoutMessageBuf.append(Messages
							.getString("CommunicationsException.2")); //$NON-NLS-1$

					if (!isInteractiveClient) {
						timeoutMessageBuf.append(Messages
								.getString("CommunicationsException.3")); //$NON-NLS-1$
					} else {
						timeoutMessageBuf.append(Messages
								.getString("CommunicationsException.4")); //$NON-NLS-1$
					}

				}
			} else if (timeSinceLastPacketSeconds > DEFAULT_WAIT_TIMEOUT_SECONDS) {
				dueToTimeout = DUE_TO_TIMEOUT_MAYBE;

				timeoutMessageBuf = new StringBuffer();

				timeoutMessageBuf.append(Messages
						.getString("CommunicationsException.5")); //$NON-NLS-1$
				timeoutMessageBuf.append(Messages
						.getString("CommunicationsException.6")); //$NON-NLS-1$
				timeoutMessageBuf.append(Messages
						.getString("CommunicationsException.7")); //$NON-NLS-1$
				timeoutMessageBuf.append(Messages
						.getString("CommunicationsException.8")); //$NON-NLS-1$
			}

			if (dueToTimeout == DUE_TO_TIMEOUT_TRUE
					|| dueToTimeout == DUE_TO_TIMEOUT_MAYBE) {

				if (lastPacketReceivedTimeMs != 0) {
					Object[] timingInfo = {
							Long.valueOf(timeSinceLastPacketReceivedMs),
							Long.valueOf(timeSinceLastPacketSentMs)
					};
					exceptionMessageBuf.append(Messages
							.getString("CommunicationsException.ServerPacketTimingInfo", //$NON-NLS-1$
									timingInfo));
				} else {
					exceptionMessageBuf.append(Messages
							.getString("CommunicationsException.ServerPacketTimingInfoNoRecv", //$NON-NLS-1$
									new Object[] { Long.valueOf(timeSinceLastPacketSentMs)}));
				}

				if (timeoutMessageBuf != null) {
					exceptionMessageBuf.append(timeoutMessageBuf);
				}

				exceptionMessageBuf.append(Messages
						.getString("CommunicationsException.11")); //$NON-NLS-1$
				exceptionMessageBuf.append(Messages
						.getString("CommunicationsException.12")); //$NON-NLS-1$
				exceptionMessageBuf.append(Messages
						.getString("CommunicationsException.13")); //$NON-NLS-1$

			} else {
				//
				// Attempt to determine the reason for the underlying exception
				// (we can only make a best-guess here)
				//

				if (underlyingException instanceof BindException) {
					if (conn.getLocalSocketAddress() != null
							&& !Util.interfaceExists(conn
									.getLocalSocketAddress())) {
						exceptionMessageBuf.append(Messages
								.getString("CommunicationsException.LocalSocketAddressNotAvailable")); //$NON-NLS-1$
					} else {
						// too many client connections???
						exceptionMessageBuf.append(Messages
								.getString("CommunicationsException.TooManyClientConnections")); //$NON-NLS-1$
					}
				}
			}
		}

		if (exceptionMessageBuf.length() == 0) {
			// We haven't figured out a good reason, so copy it.
			exceptionMessageBuf.append(Messages
					.getString("CommunicationsException.20")); //$NON-NLS-1$

			if (THROWABLE_INIT_CAUSE_METHOD == null && 
					underlyingException != null) {
				exceptionMessageBuf.append(Messages
						.getString("CommunicationsException.21")); //$NON-NLS-1$
				exceptionMessageBuf.append(Util
						.stackTraceToString(underlyingException));
			}

			if (conn != null && conn.getMaintainTimeStats()
					&& !conn.getParanoid()) {
				exceptionMessageBuf.append("\n\n"); //$NON-NLS-1$
				if (lastPacketReceivedTimeMs != 0) {
					Object[] timingInfo = {
							Long.valueOf(timeSinceLastPacketReceivedMs),
							Long.valueOf(timeSinceLastPacketSentMs)
					};
					exceptionMessageBuf.append(Messages
							.getString("CommunicationsException.ServerPacketTimingInfo", //$NON-NLS-1$
									timingInfo));
				} else {
					exceptionMessageBuf.append(Messages
							.getString("CommunicationsException.ServerPacketTimingInfoNoRecv", //$NON-NLS-1$
									new Object[] {Long.valueOf(timeSinceLastPacketSentMs)}));
				}
			}
		}
		
		return exceptionMessageBuf.toString();
	}
	
	public static SQLException notImplemented() {
		if (Util.isJdbc4()) {
			try {
				return (SQLException) Class.forName(
						"java.sql.SQLFeatureNotSupportedException")
						.newInstance();
			} catch (Throwable t) {
				// proceed
			}
		}

		return new NotImplemented();
	}
} public class StandardSocketFactory implements SocketFactory, SocketMetadata {

	public static final String TCP_NO_DELAY_PROPERTY_NAME = "tcpNoDelay";

	public static final String TCP_KEEP_ALIVE_DEFAULT_VALUE = "true";

	public static final String TCP_KEEP_ALIVE_PROPERTY_NAME = "tcpKeepAlive";

	public static final String TCP_RCV_BUF_PROPERTY_NAME = "tcpRcvBuf";

	public static final String TCP_SND_BUF_PROPERTY_NAME = "tcpSndBuf";

	public static final String TCP_TRAFFIC_CLASS_PROPERTY_NAME = "tcpTrafficClass";

	public static final String TCP_RCV_BUF_DEFAULT_VALUE = "0";

	public static final String TCP_SND_BUF_DEFAULT_VALUE = "0";

	public static final String TCP_TRAFFIC_CLASS_DEFAULT_VALUE = "0";

	public static final String TCP_NO_DELAY_DEFAULT_VALUE = "true";

	/** Use reflection for pre-1.4 VMs */

	private static Method setTraficClassMethod;

	static {
		try {
			setTraficClassMethod = Socket.class.getMethod("setTrafficClass",
					new Class[] { Integer.TYPE });
		} catch (SecurityException e) {
			setTraficClassMethod = null;
		} catch (NoSuchMethodException e) {
			setTraficClassMethod = null;
		}
	}

	/** The hostname to connect to */
	protected String host = null;

	/** The port number to connect to */
	protected int port = 3306;

	/** The underlying TCP/IP socket to use */
	protected Socket rawSocket = null;

	/**
	 * Called by the driver after issuing the MySQL protocol handshake and
	 * reading the results of the handshake.
	 * 
	 * @throws SocketException
	 *             if a socket error occurs
	 * @throws IOException
	 *             if an I/O error occurs
	 * 
	 * @return The socket to use after the handshake
	 */
	public Socket afterHandshake() throws SocketException, IOException {
		return this.rawSocket;
	}

	/**
	 * Called by the driver before issuing the MySQL protocol handshake. Should
	 * return the socket instance that should be used during the handshake.
	 * 
	 * @throws SocketException
	 *             if a socket error occurs
	 * @throws IOException
	 *             if an I/O error occurs
	 * 
	 * @return the socket to use before the handshake
	 */
	public Socket beforeHandshake() throws SocketException, IOException {
		return this.rawSocket;
	}

	/**
	 * Configures socket properties based on properties from the connection
	 * (tcpNoDelay, snd/rcv buf, traffic class, etc).
	 * 
	 * @param props
	 * @throws SocketException
	 * @throws IOException
	 */
	private void configureSocket(Socket sock, Properties props) throws SocketException,
			IOException {
		try {
			sock.setTcpNoDelay(Boolean.valueOf(
					props.getProperty(TCP_NO_DELAY_PROPERTY_NAME,
							TCP_NO_DELAY_DEFAULT_VALUE)).booleanValue());

			String keepAlive = props.getProperty(TCP_KEEP_ALIVE_PROPERTY_NAME,
					TCP_KEEP_ALIVE_DEFAULT_VALUE);

			if (keepAlive != null && keepAlive.length() > 0) {
				sock.setKeepAlive(Boolean.valueOf(keepAlive)
						.booleanValue());
			}

			int receiveBufferSize = Integer.parseInt(props.getProperty(
					TCP_RCV_BUF_PROPERTY_NAME, TCP_RCV_BUF_DEFAULT_VALUE));

			if (receiveBufferSize > 0) {
				sock.setReceiveBufferSize(receiveBufferSize);
			}

			int sendBufferSize = Integer.parseInt(props.getProperty(
					TCP_SND_BUF_PROPERTY_NAME, TCP_SND_BUF_DEFAULT_VALUE));

			if (sendBufferSize > 0) {
				sock.setSendBufferSize(sendBufferSize);
			}

			int trafficClass = Integer.parseInt(props.getProperty(
					TCP_TRAFFIC_CLASS_PROPERTY_NAME,
					TCP_TRAFFIC_CLASS_DEFAULT_VALUE));

			if (trafficClass > 0 && setTraficClassMethod != null) {
				setTraficClassMethod.invoke(sock,
						new Object[] { Integer.valueOf(trafficClass) });
			}
		} catch (Throwable t) {
			unwrapExceptionToProperClassAndThrowIt(t);
		}
	}

	/**
	 * @see com.mysql.jdbc.SocketFactory#createSocket(Properties)
	 */
	public Socket connect(String hostname, int portNumber, Properties props)
			throws SocketException, IOException {

		if (props != null) {
			this.host = hostname;

			this.port = portNumber;

			Method connectWithTimeoutMethod = null;
			Method socketBindMethod = null;
			Class socketAddressClass = null;

			String localSocketHostname = props
					.getProperty("localSocketAddress");

			String connectTimeoutStr = props.getProperty("connectTimeout");

			int connectTimeout = 0;

			boolean wantsTimeout = (connectTimeoutStr != null
					&& connectTimeoutStr.length() > 0 && !connectTimeoutStr
					.equals("0"));

			boolean wantsLocalBind = (localSocketHostname != null && localSocketHostname
					.length() > 0);

			boolean needsConfigurationBeforeConnect = socketNeedsConfigurationBeforeConnect(props);
			
			if (wantsTimeout || wantsLocalBind || needsConfigurationBeforeConnect) {

				if (connectTimeoutStr != null) {
					try {
						connectTimeout = Integer.parseInt(connectTimeoutStr);
					} catch (NumberFormatException nfe) {
						throw new SocketException("Illegal value '"
								+ connectTimeoutStr + "' for connectTimeout");
					}
				}

				try {
					// Have to do this with reflection, otherwise older JVMs
					// croak
					socketAddressClass = Class
							.forName("java.net.SocketAddress");

					connectWithTimeoutMethod = Socket.class.getMethod(
							"connect", new Class[] { socketAddressClass,
									Integer.TYPE });

					socketBindMethod = Socket.class.getMethod("bind",
							new Class[] { socketAddressClass });

				} catch (NoClassDefFoundError noClassDefFound) {
					// ignore, we give a better error below if needed
				} catch (NoSuchMethodException noSuchMethodEx) {
					// ignore, we give a better error below if needed
				} catch (Throwable catchAll) {
					// ignore, we give a better error below if needed
				}

				if (wantsLocalBind && socketBindMethod == null) {
					throw new SocketException(
							"Can't specify \"localSocketAddress\" on JVMs older than 1.4");
				}

				if (wantsTimeout && connectWithTimeoutMethod == null) {
					throw new SocketException(
							"Can't specify \"connectTimeout\" on JVMs older than 1.4");
				}
			}

			if (this.host != null) {
				if (!(wantsLocalBind || wantsTimeout || needsConfigurationBeforeConnect)) {
					InetAddress[] possibleAddresses = InetAddress
							.getAllByName(this.host);

					Throwable caughtWhileConnecting = null;

					// Need to loop through all possible addresses, in case
					// someone has IPV6 configured (SuSE, for example...)

					for (int i = 0; i < possibleAddresses.length; i++) {
						try {
							this.rawSocket = new Socket(possibleAddresses[i],
									port);

							configureSocket(this.rawSocket, props);

							break;
						} catch (Exception ex) {
							caughtWhileConnecting = ex;
						}
					}

					if (rawSocket == null) {
						unwrapExceptionToProperClassAndThrowIt(caughtWhileConnecting);
					}
				} else {
					// must explicitly state this due to classloader issues
					// when running on older JVMs :(
					try {

						InetAddress[] possibleAddresses = InetAddress
								.getAllByName(this.host);

						Throwable caughtWhileConnecting = null;

						Object localSockAddr = null;

						Class inetSocketAddressClass = null;

						Constructor addrConstructor = null;

						try {
							inetSocketAddressClass = Class
									.forName("java.net.InetSocketAddress");

							addrConstructor = inetSocketAddressClass
									.getConstructor(new Class[] {
											InetAddress.class, Integer.TYPE });

							if (wantsLocalBind) {
								localSockAddr = addrConstructor
										.newInstance(new Object[] {
												InetAddress
														.getByName(localSocketHostname),
												new Integer(0 /*
																 * use ephemeral
																 * port
																 */) });

							}
						} catch (Throwable ex) {
							unwrapExceptionToProperClassAndThrowIt(ex);
						}

						// Need to loop through all possible addresses, in case
						// someone has IPV6 configured (SuSE, for example...)

						for (int i = 0; i < possibleAddresses.length; i++) {

							try {
								this.rawSocket = new Socket();

								configureSocket(this.rawSocket, props);

								Object sockAddr = addrConstructor
										.newInstance(new Object[] {
												possibleAddresses[i],
												Integer.valueOf(port) });
								// bind to the local port, null is 'ok', it
								// means
								// use the ephemeral port
								socketBindMethod.invoke(rawSocket,
										new Object[] { localSockAddr });

								connectWithTimeoutMethod.invoke(rawSocket,
										new Object[] { sockAddr,
												Integer.valueOf(connectTimeout) });

								break;
							} catch (Exception ex) {	
								this.rawSocket = null;

								caughtWhileConnecting = ex;
							}
						}

						if (this.rawSocket == null) {
							unwrapExceptionToProperClassAndThrowIt(caughtWhileConnecting);
						}

					} catch (Throwable t) {
						unwrapExceptionToProperClassAndThrowIt(t);
					}
				}

				return this.rawSocket;
			}
		}

		throw new SocketException("Unable to create socket");
	}

	/**
	 * Does the configureSocket() need to be called before the socket is
	 * connect()d based on the properties supplied?
	 * 
	 */
	private boolean socketNeedsConfigurationBeforeConnect(Properties props) {
		int receiveBufferSize = Integer.parseInt(props.getProperty(
				TCP_RCV_BUF_PROPERTY_NAME, TCP_RCV_BUF_DEFAULT_VALUE));

		if (receiveBufferSize > 0) {
			return true;
		}

		int sendBufferSize = Integer.parseInt(props.getProperty(
				TCP_SND_BUF_PROPERTY_NAME, TCP_SND_BUF_DEFAULT_VALUE));

		if (sendBufferSize > 0) {
			return true;
		}

		int trafficClass = Integer.parseInt(props.getProperty(
				TCP_TRAFFIC_CLASS_PROPERTY_NAME,
				TCP_TRAFFIC_CLASS_DEFAULT_VALUE));

		if (trafficClass > 0 && setTraficClassMethod != null) {
			return true;
		}

		return false;
	}

	private void unwrapExceptionToProperClassAndThrowIt(
			Throwable caughtWhileConnecting) throws SocketException,
			IOException {
		if (caughtWhileConnecting instanceof InvocationTargetException) {

			// Replace it with the target, don't use 1.4 chaining as this still
			// needs to run on older VMs
			caughtWhileConnecting = ((InvocationTargetException) caughtWhileConnecting)
					.getTargetException();
		}

		if (caughtWhileConnecting instanceof SocketException) {
			throw (SocketException) caughtWhileConnecting;
		}

		if (caughtWhileConnecting instanceof IOException) {
			throw (IOException) caughtWhileConnecting;
		}

		throw new SocketException(caughtWhileConnecting.toString());
	}

	public boolean isLocallyConnected(com.mysql.jdbc.ConnectionImpl conn) throws SQLException {
		long threadId = conn.getId();
		java.sql.Statement processListStmt = conn.getMetadataSafeStatement();
		ResultSet rs = null;
		
		try {
			String processHost = null;
			
			rs = processListStmt.executeQuery("SHOW PROCESSLIST");
			
			while (rs.next()) {
				 long id = rs.getLong(1);

                 if (threadId == id) {
                	 processHost = rs.getString(3);
                     
                     break;
                 }
			}
             
             if (processHost != null) {
                 if (processHost.indexOf(":") != -1) {
                	 processHost = processHost.split(":")[0];

                     try {
                         boolean isLocal;

                    	 isLocal = InetAddress.getByName(processHost).equals(
                    			 rawSocket.getLocalAddress());

                         return isLocal;
                     } catch (UnknownHostException e) {
                         conn.getLog().logWarn(Messages.getString("Connection.CantDetectLocalConnect", new Object[] {host}), e);

                         return false;
                     }
                 }
             }
             
             return false;
		} finally {
			processListStmt.close();
		}
	}
} public class MysqlConnectionPoolDataSource extends MysqlDataSource implements
		ConnectionPoolDataSource {
	// ~ Methods
	// ----------------------------------------------------------------

	/**
	 * Returns a pooled connection.
	 * 
	 * @exception SQLException
	 *                if an error occurs
	 * @return a PooledConnection
	 */
	public synchronized PooledConnection getPooledConnection()
			throws SQLException {
		Connection connection = getConnection();
		MysqlPooledConnection mysqlPooledConnection = MysqlPooledConnection.getInstance(
				(com.mysql.jdbc.Connection)connection);

		return mysqlPooledConnection;
	}

	/**
	 * This method is invoked by the container. Obtains physical connection
	 * using mySql.Driver class and returns a mysqlPooledConnection object.
	 * 
	 * @param s
	 *            user name
	 * @param s1
	 *            password
	 * @exception SQLException
	 *                if an error occurs
	 * @return a PooledConnection
	 */
	public synchronized PooledConnection getPooledConnection(String s, String s1)
			throws SQLException {
		Connection connection = getConnection(s, s1);
		MysqlPooledConnection mysqlPooledConnection = MysqlPooledConnection.getInstance(
				(com.mysql.jdbc.Connection)connection);

		return mysqlPooledConnection;
	}
} public class MysqlXADataSource extends MysqlDataSource implements
		javax.sql.XADataSource {

	/**
	 * @see javax.sql.XADataSource#getXAConnection()
	 */
	public XAConnection getXAConnection() throws SQLException {

		Connection conn = getConnection();

		return wrapConnection(conn);
	}

	/**
	 * @see javax.sql.XADataSource#getXAConnection(String, String)
	 */
	public XAConnection getXAConnection(String u, String p)
			throws SQLException {

		Connection conn = getConnection(u, p);

		return wrapConnection(conn);
	}

	/**
	 * Wraps a connection as a 'fake' XAConnection
	 */

	private XAConnection wrapConnection(Connection conn) throws SQLException {
		if (getPinGlobalTxToPhysicalConnection() || 
				((com.mysql.jdbc.Connection)conn).getPinGlobalTxToPhysicalConnection()) {
			return SuspendableXAConnection.getInstance((com.mysql.jdbc.ConnectionImpl) conn);
		}
		
		return MysqlXAConnection.getInstance((com.mysql.jdbc.ConnectionImpl) conn, getLogXaCommands());
	}
} public class MysqlXid implements Xid {

	int hash = 0;

	byte[] myBqual;

	int myFormatId;

	byte[] myGtrid;

	public MysqlXid(byte[] gtrid, byte[] bqual, int formatId) {
		this.myGtrid = gtrid;
		this.myBqual = bqual;
		this.myFormatId = formatId;
	}

	public boolean equals(Object another) {

		if (another instanceof Xid) {
			Xid anotherAsXid = (Xid) another;

			if (this.myFormatId != anotherAsXid.getFormatId()) {
				return false;
			}

			byte[] otherBqual = anotherAsXid.getBranchQualifier();
			byte[] otherGtrid = anotherAsXid.getGlobalTransactionId();

			if (otherGtrid != null && otherGtrid.length == this.myGtrid.length) {
				int length = otherGtrid.length;

				for (int i = 0; i < length; i++) {
					if (otherGtrid[i] != this.myGtrid[i]) {
						return false;
					}
				}

				if (otherBqual != null && otherBqual.length == myBqual.length) {
					length = otherBqual.length;

					for (int i = 0; i < length; i++) {
						if (otherBqual[i] != this.myBqual[i]) {
							return false;
						}
					}
				} else {
					return false;
				}

				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	public byte[] getBranchQualifier() {
		return this.myBqual;
	}

	public int getFormatId() {
		return this.myFormatId;
	};

	public byte[] getGlobalTransactionId() {
		return this.myGtrid;
	}

	public synchronized int hashCode() {
		if (this.hash == 0) {
			for (int i = 0; i < this.myGtrid.length; i++) {
				this.hash = 33 * this.hash + this.myGtrid[i];
			}
		}

		return this.hash;
	}
}
 public class StandardLogger implements Log {
	private static final int FATAL = 0;

	private static final int ERROR = 1;

	private static final int WARN = 2;

	private static final int INFO = 3;

	private static final int DEBUG = 4;

	private static final int TRACE = 5;

	public static StringBuffer bufferedLog = null;

	private boolean logLocationInfo = true;

	/**
	 * Creates a new StandardLogger object.
	 * 
	 * @param name
	 *            the name of the configuration to use -- ignored
	 */
	public StandardLogger(String name) {
		this(name, false);
	}

	public StandardLogger(String name, boolean logLocationInfo) {
		this.logLocationInfo = logLocationInfo;
	}

	public static void saveLogsToBuffer() {
		if (bufferedLog == null) {
			bufferedLog = new StringBuffer();
		}
	}

	/**
	 * @see com.mysql.jdbc.log.Log#isDebugEnabled()
	 */
	public boolean isDebugEnabled() {
		return true;
	}

	/**
	 * @see com.mysql.jdbc.log.Log#isErrorEnabled()
	 */
	public boolean isErrorEnabled() {
		return true;
	}

	/**
	 * @see com.mysql.jdbc.log.Log#isFatalEnabled()
	 */
	public boolean isFatalEnabled() {
		return true;
	}

	/**
	 * @see com.mysql.jdbc.log.Log#isInfoEnabled()
	 */
	public boolean isInfoEnabled() {
		return true;
	}

	/**
	 * @see com.mysql.jdbc.log.Log#isTraceEnabled()
	 */
	public boolean isTraceEnabled() {
		return true;
	}

	/**
	 * @see com.mysql.jdbc.log.Log#isWarnEnabled()
	 */
	public boolean isWarnEnabled() {
		return true;
	}

	/**
	 * Logs the given message instance using the 'debug' level
	 * 
	 * @param message
	 *            the message to log
	 */
	public void logDebug(Object message) {
		logInternal(DEBUG, message, null);
	}

	/**
	 * Logs the given message and Throwable at the 'debug' level.
	 * 
	 * @param message
	 *            the message to log
	 * @param exception
	 *            the throwable to log (may be null)
	 */
	public void logDebug(Object message, Throwable exception) {
		logInternal(DEBUG, message, exception);
	}

	/**
	 * Logs the given message instance using the 'error' level
	 * 
	 * @param message
	 *            the message to log
	 */
	public void logError(Object message) {
		logInternal(ERROR, message, null);
	}

	/**
	 * Logs the given message and Throwable at the 'error' level.
	 * 
	 * @param message
	 *            the message to log
	 * @param exception
	 *            the throwable to log (may be null)
	 */
	public void logError(Object message, Throwable exception) {
		logInternal(ERROR, message, exception);
	}

	/**
	 * Logs the given message instance using the 'fatal' level
	 * 
	 * @param message
	 *            the message to log
	 */
	public void logFatal(Object message) {
		logInternal(FATAL, message, null);
	}

	/**
	 * Logs the given message and Throwable at the 'fatal' level.
	 * 
	 * @param message
	 *            the message to log
	 * @param exception
	 *            the throwable to log (may be null)
	 */
	public void logFatal(Object message, Throwable exception) {
		logInternal(FATAL, message, exception);
	}

	/**
	 * Logs the given message instance using the 'info' level
	 * 
	 * @param message
	 *            the message to log
	 */
	public void logInfo(Object message) {
		logInternal(INFO, message, null);
	}

	/**
	 * Logs the given message and Throwable at the 'info' level.
	 * 
	 * @param message
	 *            the message to log
	 * @param exception
	 *            the throwable to log (may be null)
	 */
	public void logInfo(Object message, Throwable exception) {
		logInternal(INFO, message, exception);
	}

	/**
	 * Logs the given message instance using the 'trace' level
	 * 
	 * @param message
	 *            the message to log
	 */
	public void logTrace(Object message) {
		logInternal(TRACE, message, null);
	}

	/**
	 * Logs the given message and Throwable at the 'trace' level.
	 * 
	 * @param message
	 *            the message to log
	 * @param exception
	 *            the throwable to log (may be null)
	 */
	public void logTrace(Object message, Throwable exception) {
		logInternal(TRACE, message, exception);
	}

	/**
	 * Logs the given message instance using the 'warn' level
	 * 
	 * @param message
	 *            the message to log
	 */
	public void logWarn(Object message) {
		logInternal(WARN, message, null);
	}

	/**
	 * Logs the given message and Throwable at the 'warn' level.
	 * 
	 * @param message
	 *            the message to log
	 * @param exception
	 *            the throwable to log (may be null)
	 */
	public void logWarn(Object message, Throwable exception) {
		logInternal(WARN, message, exception);
	}

	private void logInternal(int level, Object msg, Throwable exception) {
		StringBuffer msgBuf = new StringBuffer();
		msgBuf.append(new Date().toString());
		msgBuf.append(" ");

		switch (level) {
		case FATAL:
			msgBuf.append("FATAL: ");

			break;

		case ERROR:
			msgBuf.append("ERROR: ");

			break;

		case WARN:
			msgBuf.append("WARN: ");

			break;

		case INFO:
			msgBuf.append("INFO: ");

			break;

		case DEBUG:
			msgBuf.append("DEBUG: ");

			break;

		case TRACE:
			msgBuf.append("TRACE: ");

			break;
		}

		if (msg instanceof ProfilerEvent) {
			msgBuf.append(LogUtils.expandProfilerEventIfNecessary(msg));

		} else {
			if (this.logLocationInfo && level != TRACE) {
				Throwable locationException = new Throwable();
				msgBuf.append(LogUtils
						.findCallingClassAndMethod(locationException));
				msgBuf.append(" ");
			}
			
			if (msg != null) {
				msgBuf.append(String.valueOf(msg));
			}
		}

		if (exception != null) {
			msgBuf.append("\n");
			msgBuf.append("\n");
			msgBuf.append("EXCEPTION STACK TRACE:");
			msgBuf.append("\n");
			msgBuf.append("\n");
			msgBuf.append(Util.stackTraceToString(exception));
		}

		String messageAsString = msgBuf.toString();

		System.err.println(messageAsString);

		if (bufferedLog != null) {
			bufferedLog.append(messageAsString);
		}
	}
}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.net.SocketException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import testsuite.BaseTestCase;
import testsuite.UnreliableSocketFactory;
import com.mysql.jdbc.ConnectionImpl;
import com.mysql.jdbc.Driver;
import com.mysql.jdbc.LoadBalancingConnectionProxy;
import com.mysql.jdbc.Messages;
import com.mysql.jdbc.MySQLConnection;
import com.mysql.jdbc.MysqlDataTruncation;
import com.mysql.jdbc.NonRegisteringDriver;
import com.mysql.jdbc.RandomBalanceStrategy;
import com.mysql.jdbc.ReplicationConnection;
import com.mysql.jdbc.SQLError;
import com.mysql.jdbc.StandardSocketFactory;
import com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker;
import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import com.mysql.jdbc.jdbc2.optional.MysqlXid;
import com.mysql.jdbc.jdbc2.optional.SuspendableXAConnection;
import com.mysql.jdbc.log.StandardLogger;
	public void testBug46637() throws Exception {
		NonRegisteringDriver driver = new NonRegisteringDriver();
		Properties props = new Properties();
		copyBasePropertiesIntoProps(props, driver);
		String hostname = getPortFreeHostname(props, driver);
		UnreliableSocketFactory.flushAllHostLists();
		UnreliableSocketFactory.downHost(hostname);

		try {
			Connection noConn = getConnectionWithProps("socketFactory=testsuite.UnreliableSocketFactory");
		} catch (SQLException sqlEx) {
			assertTrue(sqlEx.getMessage().indexOf("has not received") != -1);
		} finally {
			UnreliableSocketFactory.flushAllHostLists();
		}
	}

	public void testBug32216() throws Exception {
		checkBug32216("www.mysql.com", "12345", "my_database");
		checkBug32216("www.mysql.com", null, "my_database");
	}

	private void checkBug32216(String host, String port, String dbname)
			throws SQLException {
		NonRegisteringDriver driver = new NonRegisteringDriver();

		StringBuffer url = new StringBuffer("jdbc:mysql://");
		url.append(host);

		if (port != null) {
			url.append(':');
			url.append(port);
		}

		url.append('/');
		url.append(dbname);

		Properties result = driver.parseURL(url.toString(), new Properties());

		assertEquals("hostname not equal", host,
				result.getProperty(Driver.HOST_PROPERTY_KEY));
		if (port != null) {
			assertEquals("port not equal", port,
					result.getProperty(Driver.PORT_PROPERTY_KEY));
		} else {
			assertEquals("port default incorrect", "3306",
					result.getProperty(Driver.PORT_PROPERTY_KEY));
		}

		assertEquals("dbname not equal", dbname,
				result.getProperty(Driver.DBNAME_PROPERTY_KEY));
	}

	public void testBug44324() throws Exception {
		createTable(
				"bug44324",
				"(Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, SomeVChar VARCHAR(10)) ENGINE=MyISAM;");

		try {
			this.stmt
					.executeUpdate("INSERT INTO bug44324 values (null, 'Some text much longer than 10 characters')");
		} catch (MysqlDataTruncation sqlEx) {
			assertTrue(0 != sqlEx.getErrorCode());
		}

	}

	public void testBug46925() throws Exception {
		MysqlXADataSource xads1 = new MysqlXADataSource();
		MysqlXADataSource xads2 = new MysqlXADataSource();

		Xid txid = new MysqlXid(new byte[] { 0x1 }, new byte[] { 0xf }, 3306);

		xads1.setPinGlobalTxToPhysicalConnection(true);
		xads1.setUrl(dbUrl);

		xads2.setPinGlobalTxToPhysicalConnection(true);
		xads2.setUrl(dbUrl);

		XAConnection c1 = xads1.getXAConnection();
		assertTrue(c1 instanceof SuspendableXAConnection);
		// start a transaction on one connection
		c1.getXAResource().start(txid, XAResource.TMNOFLAGS);
		c1.getXAResource().end(txid, XAResource.TMSUCCESS);

		XAConnection c2 = xads2.getXAConnection();
		assertTrue(c2 instanceof SuspendableXAConnection);
		// prepare on another one. Since we are using a "pinned" connection
		// we should have the same "currentXAConnection" for both
		// SuspendableXAConnection
		c2.getXAResource().prepare(txid); // this will fail without the fix.
		c2.getXAResource().commit(txid, false);
	}

	public void testBug47494() throws Exception {
		try {
			getConnectionWithProps("jdbc:mysql://localhost:9999/test?socketFactory=testsuite.regression.ConnectionRegressionTest$PortNumberSocketFactory");
		} catch (SQLException sqlEx) {
			assertTrue(sqlEx.getCause() instanceof IOException);
		}

		try {
			getConnectionWithProps("jdbc:mysql://:9999/test?socketFactory=testsuite.regression.ConnectionRegressionTest$PortNumberSocketFactory");
		} catch (SQLException sqlEx) {
			assertTrue(sqlEx.getCause() instanceof IOException);
		}

		try {
			getConnectionWithProps("jdbc:mysql://:9999,:9999/test?socketFactory=testsuite.regression.ConnectionRegressionTest$PortNumberSocketFactory");
		} catch (SQLException sqlEx) {
			assertTrue(sqlEx.getCause() instanceof IOException);
		}

		try {
			getConnectionWithProps("jdbc:mysql://localhost:9999,localhost:9999/test?socketFactory=testsuite.regression.ConnectionRegressionTest$PortNumberSocketFactory");
		} catch (SQLException sqlEx) {
			assertTrue(sqlEx.getCause() instanceof IOException);
		}
	}

	public static class PortNumberSocketFactory extends StandardSocketFactory {