/* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/J is licensed under the terms of the GPLv2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception <http://www.mysql.com/about/legal/licensing/foss-exception.html>. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package testsuite.regression; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.lang.management.ManagementFactory; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; 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.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.TimeZone; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.management.MBeanServer; import javax.management.MBeanServerInvocationHandler; import javax.management.ObjectName; import javax.sql.XAConnection; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import testsuite.BaseStatementInterceptor; import testsuite.BaseTestCase; import testsuite.UnreliableSocketFactory; import com.mysql.jdbc.AuthenticationPlugin; import com.mysql.jdbc.Buffer; import com.mysql.jdbc.CharsetMapping; import com.mysql.jdbc.ConnectionImpl; import com.mysql.jdbc.ConnectionProperties; import com.mysql.jdbc.Driver; import com.mysql.jdbc.ExceptionInterceptor; import com.mysql.jdbc.LoadBalanceExceptionChecker; import com.mysql.jdbc.LoadBalancingConnectionProxy; import com.mysql.jdbc.Messages; import com.mysql.jdbc.MySQLConnection; import com.mysql.jdbc.MysqlDataTruncation; import com.mysql.jdbc.MysqlErrorNumbers; import com.mysql.jdbc.NonRegisteringDriver; import com.mysql.jdbc.RandomBalanceStrategy; import com.mysql.jdbc.ReplicationConnection; import com.mysql.jdbc.ReplicationConnectionGroupManager; import com.mysql.jdbc.ResultSetInternalMethods; import com.mysql.jdbc.SQLError; import com.mysql.jdbc.StandardSocketFactory; import com.mysql.jdbc.StringUtils; import com.mysql.jdbc.TimeUtil; import com.mysql.jdbc.Util; import com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException; 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.jmx.ReplicationGroupManagerMBean; import com.mysql.jdbc.log.StandardLogger; /** * Regression tests for Connections */ public class ConnectionRegressionTest extends BaseTestCase { /** * @param name * the name of the testcase */ public ConnectionRegressionTest(String name) { super(name); } /** * Runs all test cases in this test suite * * @param args */ public static void main(String[] args) { junit.textui.TestRunner.run(ConnectionRegressionTest.class); } public void testBug1914() throws Exception { System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), BIGINT)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), BINARY)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), BIT)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), CHAR)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), DATE)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), DECIMAL)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), DOUBLE)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), FLOAT)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), INTEGER)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), LONGVARBINARY)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), LONGVARCHAR)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), TIME)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), TIMESTAMP)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), TINYINT)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), VARBINARY)}")); System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), VARCHAR)}")); } /** * Tests fix for BUG#3554 - Not specifying database in URL causes * MalformedURL exception. * * @throws Exception * if an error ocurrs. */ public void testBug3554() throws Exception { try { new NonRegisteringDriver().connect("jdbc:mysql://localhost:3306/?user=root&password=root", new Properties()); } catch (SQLException sqlEx) { assertTrue(sqlEx.getMessage().indexOf("Malformed") == -1); } } public void testBug3790() throws Exception { String field2OldValue = "foo"; String field2NewValue = "bar"; int field1OldValue = 1; Connection conn1 = null; Connection conn2 = null; Statement stmt1 = null; Statement stmt2 = null; ResultSet rs2 = null; Properties props = new Properties(); try { createTable("testBug3790", "(field1 INT NOT NULL PRIMARY KEY, field2 VARCHAR(32)) ", "InnoDB"); this.stmt.executeUpdate("INSERT INTO testBug3790 VALUES (" + field1OldValue + ", '" + field2OldValue + "')"); conn1 = getConnectionWithProps(props); // creates a new connection conn2 = getConnectionWithProps(props); // creates another new // connection conn1.setAutoCommit(false); conn2.setAutoCommit(false); stmt1 = conn1.createStatement(); stmt1.executeUpdate("UPDATE testBug3790 SET field2 = '" + field2NewValue + "' WHERE field1=" + field1OldValue); conn1.commit(); stmt2 = conn2.createStatement(); rs2 = stmt2.executeQuery("SELECT field1, field2 FROM testBug3790"); assertTrue(rs2.next()); assertTrue(rs2.getInt(1) == field1OldValue); assertTrue(rs2.getString(2).equals(field2NewValue)); } finally { if (rs2 != null) { rs2.close(); } if (stmt2 != null) { stmt2.close(); } if (stmt1 != null) { stmt1.close(); } if (conn1 != null) { conn1.close(); } if (conn2 != null) { conn2.close(); } } } /** * Tests if the driver configures character sets correctly for 4.1.x * servers. Requires that the 'admin connection' is configured, as this test * needs to create/drop databases. * * @throws Exception * if an error occurs */ public void testCollation41() throws Exception { if (versionMeetsMinimum(4, 1) && isAdminConnectionConfigured()) { Map<String, String> charsetsAndCollations = getCharacterSetsAndCollations(); charsetsAndCollations.remove("latin7"); // Maps to multiple Java // charsets charsetsAndCollations.remove("ucs2"); // can't be used as a // connection charset for (String charsetName : charsetsAndCollations.keySet()) { Connection charsetConn = null; Statement charsetStmt = null; try { //String collationName = charsetsAndCollations.get(charsetName); Properties props = new Properties(); props.put("characterEncoding", charsetName); System.out.println("Testing character set " + charsetName); charsetConn = getAdminConnectionWithProps(props); charsetStmt = charsetConn.createStatement(); charsetStmt.executeUpdate("DROP DATABASE IF EXISTS testCollation41"); charsetStmt.executeUpdate("DROP TABLE IF EXISTS testCollation41"); charsetStmt.executeUpdate("CREATE DATABASE testCollation41 DEFAULT CHARACTER SET " + charsetName); charsetConn.setCatalog("testCollation41"); // We've switched catalogs, so we need to recreate the // statement to pick this up... charsetStmt = charsetConn.createStatement(); StringBuilder createTableCommand = new StringBuilder("CREATE TABLE testCollation41(field1 VARCHAR(255), field2 INT)"); charsetStmt.executeUpdate(createTableCommand.toString()); charsetStmt.executeUpdate("INSERT INTO testCollation41 VALUES ('abc', 0)"); int updateCount = charsetStmt.executeUpdate("UPDATE testCollation41 SET field2=1 WHERE field1='abc'"); assertTrue(updateCount == 1); } finally { if (charsetStmt != null) { charsetStmt.executeUpdate("DROP TABLE IF EXISTS testCollation41"); charsetStmt.executeUpdate("DROP DATABASE IF EXISTS testCollation41"); charsetStmt.close(); } if (charsetConn != null) { charsetConn.close(); } } } } } /** * Tests setReadOnly() being reset during failover * * @throws Exception * if an error occurs. */ public void testSetReadOnly() throws Exception { Properties props = new Properties(); props.put("autoReconnect", "true"); String sepChar = "?"; if (BaseTestCase.dbUrl.indexOf("?") != -1) { sepChar = "&"; } Connection reconnectableConn = DriverManager.getConnection(BaseTestCase.dbUrl + sepChar + "autoReconnect=true", props); this.rs = reconnectableConn.createStatement().executeQuery("SELECT CONNECTION_ID()"); this.rs.next(); String connectionId = this.rs.getString(1); reconnectableConn.setReadOnly(true); boolean isReadOnly = reconnectableConn.isReadOnly(); Connection killConn = getConnectionWithProps((Properties) null); killConn.createStatement().executeUpdate("KILL " + connectionId); Thread.sleep(2000); SQLException caughtException = null; int numLoops = 8; while (caughtException == null && numLoops > 0) { numLoops--; try { reconnectableConn.createStatement().executeQuery("SELECT 1"); } catch (SQLException sqlEx) { caughtException = sqlEx; } } System.out.println("Executing statement on reconnectable connection..."); this.rs = reconnectableConn.createStatement().executeQuery("SELECT CONNECTION_ID()"); this.rs.next(); assertTrue("Connection is not a reconnected-connection", !connectionId.equals(this.rs.getString(1))); try { reconnectableConn.createStatement().executeQuery("SELECT 1"); } catch (SQLException sqlEx) { // ignore } reconnectableConn.createStatement().executeQuery("SELECT 1"); assertTrue(reconnectableConn.isReadOnly() == isReadOnly); } private Map<String, String> getCharacterSetsAndCollations() throws Exception { Map<String, String> charsetsToLoad = new HashMap<String, String>(); try { this.rs = this.stmt.executeQuery("SHOW character set"); while (this.rs.next()) { charsetsToLoad.put(this.rs.getString("Charset"), this.rs.getString("Default collation")); } // // These don't have mappings in Java... // charsetsToLoad.remove("swe7"); charsetsToLoad.remove("hp8"); charsetsToLoad.remove("dec8"); charsetsToLoad.remove("koi8u"); charsetsToLoad.remove("keybcs2"); charsetsToLoad.remove("geostd8"); charsetsToLoad.remove("armscii8"); } finally { if (this.rs != null) { this.rs.close(); } } return charsetsToLoad; } /** * Tests fix for BUG#4334, port #'s not being picked up for * failover/autoreconnect. * * @throws Exception * if an error occurs. */ public void testBug4334() throws Exception { if (isAdminConnectionConfigured()) { Connection adminConnection = null; try { adminConnection = getAdminConnection(); int bogusPortNumber = 65534; NonRegisteringDriver driver = new NonRegisteringDriver(); Properties oldProps = driver.parseURL(BaseTestCase.dbUrl, null); String host = driver.host(oldProps); int port = driver.port(oldProps); String database = oldProps.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); String user = oldProps.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY); String password = oldProps.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY); StringBuilder newUrlToTestPortNum = new StringBuilder("jdbc:mysql://"); if (host != null) { newUrlToTestPortNum.append(host); } newUrlToTestPortNum.append(":").append(port); newUrlToTestPortNum.append(","); if (host != null) { newUrlToTestPortNum.append(host); } newUrlToTestPortNum.append(":").append(bogusPortNumber); newUrlToTestPortNum.append("/"); if (database != null) { newUrlToTestPortNum.append(database); } if ((user != null) || (password != null)) { newUrlToTestPortNum.append("?"); if (user != null) { newUrlToTestPortNum.append("user=").append(user); if (password != null) { newUrlToTestPortNum.append("&"); } } if (password != null) { newUrlToTestPortNum.append("password=").append(password); } } Properties autoReconnectProps = new Properties(); autoReconnectProps.put("autoReconnect", "true"); System.out.println(newUrlToTestPortNum); // // First test that port #'s are being correctly picked up // // We do this by looking at the error message that is returned // Connection portNumConn = DriverManager.getConnection(newUrlToTestPortNum.toString(), autoReconnectProps); Statement portNumStmt = portNumConn.createStatement(); this.rs = portNumStmt.executeQuery("SELECT connection_id()"); this.rs.next(); killConnection(adminConnection, this.rs.getString(1)); try { portNumStmt.executeQuery("SELECT connection_id()"); } catch (SQLException sqlEx) { // we expect this one } try { portNumStmt.executeQuery("SELECT connection_id()"); } catch (SQLException sqlEx) { assertTrue(sqlEx.getMessage().toLowerCase().indexOf("connection refused") != -1); } // // Now make sure failover works // StringBuilder newUrlToTestFailover = new StringBuilder("jdbc:mysql://"); if (host != null) { newUrlToTestFailover.append(host); } newUrlToTestFailover.append(":").append(port); newUrlToTestFailover.append(","); if (host != null) { newUrlToTestFailover.append(host); } newUrlToTestFailover.append(":").append(bogusPortNumber); newUrlToTestFailover.append("/"); if (database != null) { newUrlToTestFailover.append(database); } if ((user != null) || (password != null)) { newUrlToTestFailover.append("?"); if (user != null) { newUrlToTestFailover.append("user=").append(user); if (password != null) { newUrlToTestFailover.append("&"); } } if (password != null) { newUrlToTestFailover.append("password=").append(password); } } Connection failoverConn = DriverManager.getConnection(newUrlToTestFailover.toString(), autoReconnectProps); Statement failoverStmt = failoverConn.createStatement(); this.rs = failoverStmt.executeQuery("SELECT connection_id()"); this.rs.next(); killConnection(adminConnection, this.rs.getString(1)); try { failoverStmt.executeQuery("SELECT connection_id()"); } catch (SQLException sqlEx) { // we expect this one } failoverStmt.executeQuery("SELECT connection_id()"); } finally { if (adminConnection != null) { adminConnection.close(); } } } } private static void killConnection(Connection adminConn, String threadId) throws SQLException { adminConn.createStatement().execute("KILL " + threadId); } /** * Tests fix for BUG#6966, connections starting up failed-over (due to down * master) never retry master. * * @throws Exception * if the test fails...Note, test is timing-dependent, but * should work in most cases. */ public void testBug6966() throws Exception { Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null); props.setProperty("autoReconnect", "true"); props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); Properties urlProps = new NonRegisteringDriver().parseURL(dbUrl, null); String host = urlProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); String port = urlProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); props.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY); props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1"); props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1"); props.setProperty("queriesBeforeRetryMaster", "50"); props.setProperty("maxReconnects", "1"); UnreliableSocketFactory.mapHost("master", host); UnreliableSocketFactory.mapHost("slave", host); UnreliableSocketFactory.downHost("master"); Connection failoverConnection = null; try { failoverConnection = getConnectionWithProps("jdbc:mysql://master:" + port + ",slave:" + port + "/", props); failoverConnection.setAutoCommit(false); String originalConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString(); for (int i = 0; i < 50; i++) { failoverConnection.createStatement().executeQuery("SELECT 1"); } UnreliableSocketFactory.dontDownHost("master"); failoverConnection.setAutoCommit(true); String newConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString(); assertEquals("/master", UnreliableSocketFactory.getHostFromLastConnection()); assertFalse(newConnectionId.equals(originalConnectionId)); failoverConnection.createStatement().executeQuery("SELECT 1"); } finally { UnreliableSocketFactory.flushAllStaticData(); if (failoverConnection != null) { failoverConnection.close(); } } } /** * Test fix for BUG#7952 -- Infinite recursion when 'falling back' to master * in failover configuration. * * @throws Exception * if the tests fails. */ public void testBug7952() throws Exception { Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null); props.setProperty("autoReconnect", "true"); String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); if (!NonRegisteringDriver.isHostPropertiesList(host)) { String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); host = host + ":" + port; } host = host + "," + host; props.remove("PORT"); props.remove("HOST"); props.setProperty("queriesBeforeRetryMaster", "10"); props.setProperty("maxReconnects", "1"); Connection failoverConnection = null; Connection killerConnection = getConnectionWithProps((String) null); try { failoverConnection = getConnectionWithProps("jdbc:mysql://" + host + "/", props); failoverConnection.setAutoCommit(false); String failoverConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString(); System.out.println("Connection id: " + failoverConnectionId); killConnection(killerConnection, failoverConnectionId); Thread.sleep(3000); // This can take some time.... try { failoverConnection.createStatement().executeQuery("SELECT 1"); } catch (SQLException sqlEx) { assertTrue("08S01".equals(sqlEx.getSQLState())); } ((com.mysql.jdbc.Connection) failoverConnection).setFailedOver(true); failoverConnection.setAutoCommit(true); String failedConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString(); System.out.println("Failed over connection id: " + failedConnectionId); ((com.mysql.jdbc.Connection) failoverConnection).setFailedOver(true); for (int i = 0; i < 30; i++) { failoverConnection.setAutoCommit(true); System.out.println(getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()")); // failoverConnection.createStatement().executeQuery("SELECT // 1"); failoverConnection.setAutoCommit(true); } String fallbackConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString(); System.out.println("fallback connection id: " + fallbackConnectionId); /* * long begin = System.currentTimeMillis(); * * failoverConnection.setAutoCommit(true); * * long end = System.currentTimeMillis(); * * assertTrue("Probably didn't try failing back to the * master....check test", (end - begin) > 500); * * failoverConnection.createStatement().executeQuery("SELECT 1"); */ } finally { if (failoverConnection != null) { failoverConnection.close(); } } } /** * Tests fix for BUG#7607 - MS932, SHIFT_JIS and Windows_31J not recog. as * aliases for sjis. * * @throws Exception * if the test fails. */ public void testBug7607() throws Exception { if (versionMeetsMinimum(4, 1)) { Connection ms932Conn = null, cp943Conn = null, shiftJisConn = null, windows31JConn = null; try { Properties props = new Properties(); props.setProperty("characterEncoding", "MS932"); ms932Conn = getConnectionWithProps(props); this.rs = ms932Conn.createStatement().executeQuery("SHOW VARIABLES LIKE 'character_set_client'"); assertTrue(this.rs.next()); String encoding = this.rs.getString(2); if (!versionMeetsMinimum(5, 0, 3) && !versionMeetsMinimum(4, 1, 11)) { assertEquals("sjis", encoding.toLowerCase(Locale.ENGLISH)); } else { assertEquals("cp932", encoding.toLowerCase(Locale.ENGLISH)); } this.rs = ms932Conn.createStatement().executeQuery("SELECT 'abc'"); assertTrue(this.rs.next()); String charsetToCheck = "ms932"; assertEquals(charsetToCheck, ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(1).toLowerCase(Locale.ENGLISH)); try { ms932Conn.createStatement().executeUpdate("drop table if exists testBug7607"); ms932Conn.createStatement().executeUpdate("create table testBug7607 (sortCol int, col1 varchar(100) ) character set sjis"); ms932Conn.createStatement().executeUpdate("insert into testBug7607 values(1, 0x835C)"); // standard // sjis ms932Conn.createStatement().executeUpdate("insert into testBug7607 values(2, 0x878A)"); // NEC // kanji this.rs = ms932Conn.createStatement().executeQuery("SELECT col1 FROM testBug7607 ORDER BY sortCol ASC"); assertTrue(this.rs.next()); String asString = this.rs.getString(1); assertTrue("\u30bd".equals(asString)); // Can't be fixed unless server is fixed, // this is fixed in 4.1.7. assertTrue(this.rs.next()); asString = this.rs.getString(1); assertEquals("\u3231", asString); } finally { ms932Conn.createStatement().executeUpdate("drop table if exists testBug7607"); } props = new Properties(); props.setProperty("characterEncoding", "SHIFT_JIS"); shiftJisConn = getConnectionWithProps(props); this.rs = shiftJisConn.createStatement().executeQuery("SHOW VARIABLES LIKE 'character_set_client'"); assertTrue(this.rs.next()); encoding = this.rs.getString(2); assertTrue("sjis".equalsIgnoreCase(encoding)); this.rs = shiftJisConn.createStatement().executeQuery("SELECT 'abc'"); assertTrue(this.rs.next()); String charSetUC = ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(1).toUpperCase(Locale.US); if (isRunningOnJdk131()) { assertEquals("WINDOWS-31J", charSetUC); } else { // assertEquals("SHIFT_JIS", charSetUC); } props = new Properties(); props.setProperty("characterEncoding", "WINDOWS-31J"); windows31JConn = getConnectionWithProps(props); this.rs = windows31JConn.createStatement().executeQuery("SHOW VARIABLES LIKE 'character_set_client'"); assertTrue(this.rs.next()); encoding = this.rs.getString(2); if (!versionMeetsMinimum(5, 0, 3) && !versionMeetsMinimum(4, 1, 11)) { assertEquals("sjis", encoding.toLowerCase(Locale.ENGLISH)); } else { assertEquals("cp932", encoding.toLowerCase(Locale.ENGLISH)); } this.rs = windows31JConn.createStatement().executeQuery("SELECT 'abc'"); assertTrue(this.rs.next()); if (!versionMeetsMinimum(4, 1, 11)) { assertEquals("sjis".toLowerCase(Locale.ENGLISH), ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(1) .toLowerCase(Locale.ENGLISH)); } else { assertEquals("windows-31j".toLowerCase(Locale.ENGLISH), ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(1) .toLowerCase(Locale.ENGLISH)); } props = new Properties(); props.setProperty("characterEncoding", "CP943"); cp943Conn = getConnectionWithProps(props); this.rs = cp943Conn.createStatement().executeQuery("SHOW VARIABLES LIKE 'character_set_client'"); assertTrue(this.rs.next()); encoding = this.rs.getString(2); assertTrue("sjis".equalsIgnoreCase(encoding)); this.rs = cp943Conn.createStatement().executeQuery("SELECT 'abc'"); assertTrue(this.rs.next()); charSetUC = ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(1).toUpperCase(Locale.US); if (isRunningOnJdk131()) { assertEquals("WINDOWS-31J", charSetUC); } else { assertEquals("CP943", charSetUC); } } finally { if (ms932Conn != null) { ms932Conn.close(); } if (shiftJisConn != null) { shiftJisConn.close(); } if (windows31JConn != null) { windows31JConn.close(); } if (cp943Conn != null) { cp943Conn.close(); } } } } /** * In some case Connector/J's round-robin function doesn't work. * * I had 2 mysqld, node1 "localhost:3306" and node2 "localhost:3307". * * 1. node1 is up, node2 is up * * 2. java-program connect to node1 by using properties * "autoRecconect=true", * "roundRobinLoadBalance=true","failOverReadOnly=false". * * 3. node1 is down, node2 is up * * 4. java-program execute a query and fail, but Connector/J's round-robin * fashion failover work and if java-program retry a query it can succeed * (connection is change to node2 by Connector/j) * * 5. node1 is up, node2 is up * * 6. node1 is up, node2 is down * * 7. java-program execute a query, but this time Connector/J doesn't work * althought node1 is up and usable. * * * @throws Exception */ /* * FIXME: This test is no longer valid with random selection of hosts public * void testBug8643() throws Exception { if (runMultiHostTests()) { * Properties defaultProps = getMasterSlaveProps(); * * defaultProps.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); * defaultProps.remove(NonRegisteringDriver.PORT_PROPERTY_KEY); * * defaultProps.put("autoReconnect", "true"); * defaultProps.put("roundRobinLoadBalance", "true"); * defaultProps.put("failOverReadOnly", "false"); * * Connection con = null; try { con = * DriverManager.getConnection(getMasterSlaveUrl(), defaultProps); Statement * stmt1 = con.createStatement(); * * ResultSet rs1 = stmt1 .executeQuery("show variables like 'port'"); * rs1.next(); * * rs1 = stmt1.executeQuery("select connection_id()"); rs1.next(); String * originalConnectionId = rs1.getString(1); this.stmt.executeUpdate("kill " * + originalConnectionId); * * int numLoops = 8; * * SQLException caughtException = null; * * while (caughtException == null && numLoops > 0) { numLoops--; * * try { rs1 = stmt1.executeQuery("show variables like 'port'"); } catch * (SQLException sqlEx) { caughtException = sqlEx; } } * * assertNotNull(caughtException); * * // failover and retry rs1 = * stmt1.executeQuery("show variables like 'port'"); * * rs1.next(); assertTrue(!((com.mysql.jdbc.Connection) con) * .isMasterConnection()); * * rs1 = stmt1.executeQuery("select connection_id()"); rs1.next(); String * nextConnectionId = rs1.getString(1); * assertTrue(!nextConnectionId.equals(originalConnectionId)); * * this.stmt.executeUpdate("kill " + nextConnectionId); * * numLoops = 8; * * caughtException = null; * * while (caughtException == null && numLoops > 0) { numLoops--; * * try { rs1 = stmt1.executeQuery("show variables like 'port'"); } catch * (SQLException sqlEx) { caughtException = sqlEx; } } * * assertNotNull(caughtException); * * // failover and retry rs1 = * stmt1.executeQuery("show variables like 'port'"); * * rs1.next(); assertTrue(((com.mysql.jdbc.Connection) con) * .isMasterConnection()); * * } finally { if (con != null) { try { con.close(); } catch (Exception e) { * e.printStackTrace(); } } } } } */ /** * Tests fix for BUG#9206, can not use 'UTF-8' for characterSetResults * configuration property. */ public void testBug9206() throws Exception { Properties props = new Properties(); props.setProperty("characterSetResults", "UTF-8"); getConnectionWithProps(props).close(); } /** * These two charsets have different names depending on version of MySQL * server. * * @throws Exception * if the test fails. */ public void testNewCharsetsConfiguration() throws Exception { Properties props = new Properties(); props.setProperty("useUnicode", "true"); props.setProperty("characterEncoding", "EUC_KR"); getConnectionWithProps(props).close(); props = new Properties(); props.setProperty("useUnicode", "true"); props.setProperty("characterEncoding", "KOI8_R"); getConnectionWithProps(props).close(); } /** * Tests fix for BUG#10144 - Memory leak in ServerPreparedStatement if * serverPrepare() fails. */ public void testBug10144() throws Exception { if (versionMeetsMinimum(4, 1)) { Properties props = new Properties(); props.setProperty("emulateUnsupportedPstmts", "false"); props.setProperty("useServerPrepStmts", "true"); Connection bareConn = getConnectionWithProps(props); int currentOpenStatements = ((com.mysql.jdbc.Connection) bareConn).getActiveStatementCount(); try { bareConn.prepareStatement("Boo!"); fail("Should not've been able to prepare that one!"); } catch (SQLException sqlEx) { assertEquals(currentOpenStatements, ((com.mysql.jdbc.Connection) bareConn).getActiveStatementCount()); } finally { if (bareConn != null) { bareConn.close(); } } } } /** * Tests fix for BUG#10496 - SQLException is thrown when using property * "characterSetResults" */ public void testBug10496() throws Exception { if (versionMeetsMinimum(5, 0, 3)) { Properties props = new Properties(); props.setProperty("useUnicode", "true"); props.setProperty("characterEncoding", "WINDOWS-31J"); props.setProperty("characterSetResults", "WINDOWS-31J"); getConnectionWithProps(props).close(); props = new Properties(); props.setProperty("useUnicode", "true"); props.setProperty("characterEncoding", "EUC_JP"); props.setProperty("characterSetResults", "EUC_JP"); getConnectionWithProps(props).close(); } } /** * Tests fix for BUG#11259, autoReconnect ping causes exception on * connection startup. * * @throws Exception * if the test fails. */ public void testBug11259() throws Exception { Connection dsConn = null; try { Properties props = new Properties(); props.setProperty("autoReconnect", "true"); dsConn = getConnectionWithProps(props); } finally { if (dsConn != null) { dsConn.close(); } } } /** * Tests fix for BUG#11879 -- ReplicationConnection won't switch to slave, * throws "Catalog can't be null" exception. * * @throws Exception * if the test fails */ public void testBug11879() throws Exception { if (runMultiHostTests()) { Connection replConn = null; try { replConn = getMasterSlaveReplicationConnection(); replConn.setReadOnly(true); replConn.setReadOnly(false); } finally { if (replConn != null) { replConn.close(); } } } } /** * Tests fix for BUG#11976 - maxPerformance.properties mis-spells * "elideSetAutoCommits". * * @throws Exception * if the test fails. */ public void testBug11976() throws Exception { if (isRunningOnJdk131()) { return; // test not valid on JDK-1.3.1 } if (!versionMeetsMinimum(6, 0)) { return; // server status is broken until MySQL-6.0 } Properties props = new Properties(); props.setProperty("useConfigs", "maxPerformance"); Connection maxPerfConn = getConnectionWithProps(props); assertEquals(true, ((com.mysql.jdbc.Connection) maxPerfConn).getElideSetAutoCommits()); } /** * Tests fix for BUG#12218, properties shared between master and slave with * replication connection. * * @throws Exception * if the test fails. */ public void testBug12218() throws Exception { if (runMultiHostTests()) { Connection replConn = null; try { replConn = getMasterSlaveReplicationConnection(); assertTrue(!((MySQLConnection) ((ReplicationConnection) replConn).getMasterConnection()).hasSameProperties(((ReplicationConnection) replConn) .getSlavesConnection())); } finally { if (replConn != null) { replConn.close(); } } } } /** * Tests fix for BUG#12229 - explainSlowQueries hangs with server-side * prepared statements. * * @throws Exception * if the test fails. */ public void testBug12229() throws Exception { createTable("testBug12229", "(`int_field` integer )"); this.stmt.executeUpdate("insert into testBug12229 values (123456),(1)"); Properties props = new Properties(); props.put("profileSQL", "true"); props.put("slowQueryThresholdMillis", "0"); props.put("logSlowQueries", "true"); props.put("explainSlowQueries", "true"); props.put("useServerPrepStmts", "true"); Connection explainConn = getConnectionWithProps(props); this.pstmt = explainConn.prepareStatement("SELECT `int_field` FROM `testBug12229` WHERE `int_field` = ?"); this.pstmt.setInt(1, 1); this.rs = this.pstmt.executeQuery(); assertTrue(this.rs.next()); this.rs = this.pstmt.executeQuery(); assertTrue(this.rs.next()); this.rs = this.pstmt.executeQuery(); assertTrue(this.rs.next()); } /** * Tests fix for BUG#12752 - Cp1251 incorrectly mapped to win1251 for * servers newer than 4.0.x. * * @throws Exception * if the test fails. */ public void testBug12752() throws Exception { Properties props = new Properties(); props.setProperty("characterEncoding", "Cp1251"); getConnectionWithProps(props).close(); } /** * Tests fix for BUG#12753, sessionVariables=....=...., doesn't work as it's * tokenized incorrectly. * * @throws Exception * if the test fails. */ public void testBug12753() throws Exception { if (versionMeetsMinimum(4, 1)) { Properties props = new Properties(); props.setProperty("sessionVariables", "sql_mode=ansi"); Connection sessionConn = null; try { sessionConn = getConnectionWithProps(props); String sqlMode = getMysqlVariable(sessionConn, "sql_mode"); assertTrue(sqlMode.indexOf("ANSI") != -1); } finally { if (sessionConn != null) { sessionConn.close(); sessionConn = null; } } } } /** * Tests fix for BUG#13048 - maxQuerySizeToLog is not respected. * * @throws Exception * if the test fails */ public void testBug13048() throws Exception { Connection profileConn = null; PrintStream oldErr = System.err; try { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); System.setErr(new PrintStream(bOut)); Properties props = new Properties(); props.setProperty("profileSQL", "true"); props.setProperty("maxQuerySizeToLog", "2"); props.setProperty("logger", "com.mysql.jdbc.log.StandardLogger"); profileConn = getConnectionWithProps(props); StringBuilder queryBuf = new StringBuilder("SELECT '"); for (int i = 0; i < 500; i++) { queryBuf.append("a"); } queryBuf.append("'"); this.rs = profileConn.createStatement().executeQuery(queryBuf.toString()); this.rs.close(); String logString = new String(bOut.toString("ISO8859-1")); assertTrue(logString.indexOf("... (truncated)") != -1); bOut = new ByteArrayOutputStream(); System.setErr(new PrintStream(bOut)); this.rs = profileConn.prepareStatement(queryBuf.toString()).executeQuery(); logString = new String(bOut.toString("ISO8859-1")); assertTrue(logString.indexOf("... (truncated)") != -1); } finally { System.setErr(oldErr); if (profileConn != null) { profileConn.close(); } if (this.rs != null) { ResultSet toClose = this.rs; this.rs = null; toClose.close(); } } } /** * Tests fix for BUG#13453 - can't use & or = in URL configuration values * (we now allow you to use www-form-encoding). * * @throws Exception * if the test fails */ public void testBug13453() throws Exception { StringBuilder urlBuf = new StringBuilder(dbUrl); if (dbUrl.indexOf('?') == -1) { urlBuf.append('?'); } else { urlBuf.append('&'); } urlBuf.append("sessionVariables=@testBug13453='%25%26+%3D'"); Connection encodedConn = null; try { encodedConn = DriverManager.getConnection(urlBuf.toString(), null); this.rs = encodedConn.createStatement().executeQuery("SELECT @testBug13453"); assertTrue(this.rs.next()); assertEquals("%& =", this.rs.getString(1)); } finally { if (this.rs != null) { this.rs.close(); this.rs = null; } if (encodedConn != null) { encodedConn.close(); } } } /** * Tests fix for BUG#15065 - Usage advisor complains about unreferenced * columns, even though they've been referenced. * * @throws Exception * if the test fails. */ public void testBug15065() throws Exception { if (isRunningOnJdk131()) { return; // test not valid on JDK-1.3.1 } createTable("testBug15065", "(field1 int)"); this.stmt.executeUpdate("INSERT INTO testBug15065 VALUES (1)"); Connection advisorConn = null; Statement advisorStmt = null; try { Properties props = new Properties(); props.setProperty("useUsageAdvisor", "true"); props.setProperty("logger", "com.mysql.jdbc.log.StandardLogger"); advisorConn = getConnectionWithProps(props); advisorStmt = advisorConn.createStatement(); Method[] getMethods = ResultSet.class.getMethods(); PrintStream oldErr = System.err; try { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); System.setErr(new PrintStream(bOut)); HashMap<String, String> methodsToSkipMap = new HashMap<String, String>(); // Needs an actual URL methodsToSkipMap.put("getURL", null); // Java6 JDBC4.0 methods we don't implement methodsToSkipMap.put("getNCharacterStream", null); methodsToSkipMap.put("getNClob", null); methodsToSkipMap.put("getNString", null); methodsToSkipMap.put("getRowId", null); methodsToSkipMap.put("getSQLXML", null); for (int j = 0; j < 2; j++) { for (int i = 0; i < getMethods.length; i++) { String methodName = getMethods[i].getName(); if (methodName.startsWith("get") && !methodsToSkipMap.containsKey(methodName)) { Class<?>[] parameterTypes = getMethods[i].getParameterTypes(); if (parameterTypes.length == 1 && parameterTypes[0] == Integer.TYPE) { if (j == 0) { this.rs = advisorStmt.executeQuery("SELECT COUNT(*) FROM testBug15065"); } else { this.rs = advisorConn.prepareStatement("SELECT COUNT(*) FROM testBug15065").executeQuery(); } this.rs.next(); try { getMethods[i].invoke(this.rs, new Object[] { new Integer(1) }); } catch (InvocationTargetException invokeEx) { // we don't care about bad values, just that // the // column gets "touched" if (!invokeEx.getCause().getClass().isAssignableFrom(java.sql.SQLException.class) && !invokeEx.getCause().getClass().getName().equals("com.mysql.jdbc.NotImplemented") && !invokeEx.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException")) { throw invokeEx; } } this.rs.close(); this.rs = null; } } } } String logOut = bOut.toString("ISO8859-1"); if (logOut.indexOf(".Level") != -1) { return; // we ignore for warnings } assertTrue("Usage advisor complained about columns:\n\n" + logOut, logOut.indexOf("columns") == -1); } finally { System.setErr(oldErr); } } finally { if (advisorConn != null) { advisorConn.close(); } } } /** * Tests fix for BUG#15544, no "dos" character set in MySQL > 4.1.0 * * @throws Exception * if the test fails */ public void testBug15544() throws Exception { Properties props = new Properties(); props.setProperty("characterEncoding", "Cp437"); Connection dosConn = null; try { dosConn = getConnectionWithProps(props); } finally { if (dosConn != null) { dosConn.close(); } } } public void testCSC5765() throws Exception { if (isRunningOnJdk131()) { return; // test not valid on JDK-1.3.1 } Properties props = new Properties(); props.setProperty("useUnicode", "true"); props.setProperty("characterEncoding", "utf8"); props.setProperty("characterSetResults", "utf8"); props.setProperty("connectionCollation", "utf8_bin"); Connection utf8Conn = null; try { utf8Conn = getConnectionWithProps(props); this.rs = utf8Conn.createStatement().executeQuery("SHOW VARIABLES LIKE 'character_%'"); while (this.rs.next()) { System.out.println(this.rs.getString(1) + " = " + this.rs.getString(2)); } this.rs = utf8Conn.createStatement().executeQuery("SHOW VARIABLES LIKE 'collation_%'"); while (this.rs.next()) { System.out.println(this.rs.getString(1) + " = " + this.rs.getString(2)); } } finally { if (utf8Conn != null) { utf8Conn.close(); } } } /** * Tests fix for BUG#15570 - ReplicationConnection incorrectly copies state, * doesn't transfer connection context correctly when transitioning between * the same read-only states. * * (note, this test will fail if the test user doesn't have permission to * "USE 'mysql'". * * @throws Exception * if the test fails. */ public void testBug15570() throws Exception { Connection replConn = null; try { replConn = getMasterSlaveReplicationConnection(); int masterConnectionId = Integer.parseInt(getSingleIndexedValueWithQuery(replConn, 1, "SELECT CONNECTION_ID()").toString()); replConn.setReadOnly(false); assertEquals(masterConnectionId, Integer.parseInt(getSingleIndexedValueWithQuery(replConn, 1, "SELECT CONNECTION_ID()").toString())); String currentCatalog = replConn.getCatalog(); replConn.setCatalog(currentCatalog); assertEquals(currentCatalog, replConn.getCatalog()); replConn.setReadOnly(true); int slaveConnectionId = Integer.parseInt(getSingleIndexedValueWithQuery(replConn, 1, "SELECT CONNECTION_ID()").toString()); // The following test is okay for now, as the chance // of MySQL wrapping the connection id counter during our // testsuite is very small. assertTrue("Slave id " + slaveConnectionId + " is not newer than master id " + masterConnectionId, slaveConnectionId > masterConnectionId); assertEquals(currentCatalog, replConn.getCatalog()); String newCatalog = "mysql"; replConn.setCatalog(newCatalog); assertEquals(newCatalog, replConn.getCatalog()); replConn.setReadOnly(true); assertEquals(newCatalog, replConn.getCatalog()); replConn.setReadOnly(false); assertEquals(masterConnectionId, Integer.parseInt(getSingleIndexedValueWithQuery(replConn, 1, "SELECT CONNECTION_ID()").toString())); } finally { if (replConn != null) { replConn.close(); } } } /** * Tests bug where downed slave caused round robin load balance not to cycle * back to first host in the list. * * @throws Exception * if the test fails...Note, test is timing-dependent, but * should work in most cases. */ public void testBug23281() throws Exception { Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null); props.setProperty("autoReconnect", "false"); props.setProperty("roundRobinLoadBalance", "true"); props.setProperty("failoverReadOnly", "false"); if (!isRunningOnJdk131()) { props.setProperty("connectTimeout", "5000"); } String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); if (!NonRegisteringDriver.isHostPropertiesList(host)) { String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); host = host + ":" + port; } props.remove("PORT"); props.remove("HOST"); StringBuilder newHostBuf = new StringBuilder(); newHostBuf.append(host); newHostBuf.append(","); // newHostBuf.append(host); newHostBuf.append("192.0.2.1"); // non-exsitent machine from RFC3330 // test network newHostBuf.append(":65532"); // make sure the slave fails props.remove("PORT"); props.remove("HOST"); Connection failoverConnection = null; try { failoverConnection = getConnectionWithProps("jdbc:mysql://" + newHostBuf.toString() + "/", props); String originalConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString(); System.out.println(originalConnectionId); Connection nextConnection = getConnectionWithProps("jdbc:mysql://" + newHostBuf.toString() + "/", props); String nextId = getSingleIndexedValueWithQuery(nextConnection, 1, "SELECT CONNECTION_ID()").toString(); System.out.println(nextId); } finally { if (failoverConnection != null) { failoverConnection.close(); } } } /** * Tests to insure proper behavior for BUG#24706. * * @throws Exception * if the test fails. */ public void testBug24706() throws Exception { if (!versionMeetsMinimum(6, 0)) { return; // server status isn't there to support this feature } Properties props = new Properties(); props.setProperty("elideSetAutoCommits", "true"); props.setProperty("logger", "StandardLogger"); props.setProperty("profileSQL", "true"); Connection c = null; StandardLogger.startLoggingToBuffer(); try { c = getConnectionWithProps(props); c.setAutoCommit(true); c.createStatement().execute("SELECT 1"); c.setAutoCommit(true); c.setAutoCommit(false); c.createStatement().execute("SELECT 1"); c.setAutoCommit(false); // We should only see _one_ "set autocommit=" sent to the server String log = StandardLogger.getBuffer().toString(); int searchFrom = 0; int count = 0; int found = 0; while ((found = log.indexOf("SET autocommit=", searchFrom)) != -1) { searchFrom = found + 1; count++; } // The SELECT doesn't actually start a transaction, so being pedantic the driver issues SET autocommit=0 again in this case. assertEquals(2, count); } finally { StandardLogger.dropBuffer(); if (c != null) { c.close(); } } } /** * Tests fix for BUG#25514 - Timer instance used for * Statement.setQueryTimeout() created per-connection, rather than per-VM, * causing memory leak. * * @throws Exception * if the test fails. */ public void testBug25514() throws Exception { for (int i = 0; i < 10; i++) { getConnectionWithProps((Properties) null).close(); } ThreadGroup root = Thread.currentThread().getThreadGroup().getParent(); while (root.getParent() != null) { root = root.getParent(); } int numThreadsNamedTimer = findNamedThreadCount(root, "Timer"); if (numThreadsNamedTimer == 0) { numThreadsNamedTimer = findNamedThreadCount(root, "MySQL Statement Cancellation Timer"); } // Notice that this seems impossible to test on JDKs prior to 1.5, as there is no reliable way to find the TimerThread, so we have to rely on new JDKs // for this test. assertTrue("More than one timer for cancel was created", numThreadsNamedTimer <= 1); } private int findNamedThreadCount(ThreadGroup group, String nameStart) { int count = 0; int numThreads = group.activeCount(); Thread[] threads = new Thread[numThreads * 2]; numThreads = group.enumerate(threads, false); for (int i = 0; i < numThreads; i++) { if (threads[i].getName().startsWith(nameStart)) { count++; } } int numGroups = group.activeGroupCount(); ThreadGroup[] groups = new ThreadGroup[numGroups * 2]; numGroups = group.enumerate(groups, false); for (int i = 0; i < numGroups; i++) { count += findNamedThreadCount(groups[i], nameStart); } return count; } /** * Ensures that we don't miss getters/setters for driver properties in * ConnectionProperties so that names given in documentation work with * DataSources which will use JavaBean-style names and reflection to set the * values (and often fail silently! when the method isn't available). * * @throws Exception */ public void testBug23626() throws Exception { Class<?> clazz = this.conn.getClass(); DriverPropertyInfo[] dpi = new NonRegisteringDriver().getPropertyInfo(dbUrl, null); StringBuilder missingSettersBuf = new StringBuilder(); StringBuilder missingGettersBuf = new StringBuilder(); Class<?>[][] argTypes = { new Class[] { String.class }, new Class[] { Integer.TYPE }, new Class[] { Long.TYPE }, new Class[] { Boolean.TYPE } }; for (int i = 0; i < dpi.length; i++) { String propertyName = dpi[i].name; if (propertyName.equals("HOST") || propertyName.equals("PORT") || propertyName.equals("DBNAME") || propertyName.equals("user") || propertyName.equals("password")) { continue; } StringBuilder mutatorName = new StringBuilder("set"); mutatorName.append(Character.toUpperCase(propertyName.charAt(0))); mutatorName.append(propertyName.substring(1)); StringBuilder accessorName = new StringBuilder("get"); accessorName.append(Character.toUpperCase(propertyName.charAt(0))); accessorName.append(propertyName.substring(1)); try { clazz.getMethod(accessorName.toString(), (Class[]) null); } catch (NoSuchMethodException nsme) { missingGettersBuf.append(accessorName.toString()); missingGettersBuf.append("\n"); } boolean foundMethod = false; for (int j = 0; j < argTypes.length; j++) { try { clazz.getMethod(mutatorName.toString(), argTypes[j]); foundMethod = true; break; } catch (NoSuchMethodException nsme) { } } if (!foundMethod) { missingSettersBuf.append(mutatorName); missingSettersBuf.append("\n"); } } assertEquals("Missing setters for listed configuration properties.", "", missingSettersBuf.toString()); assertEquals("Missing getters for listed configuration properties.", "", missingSettersBuf.toString()); } /** * Tests fix for BUG#25545 - Client flags not sent correctly during * handshake when using SSL. * * Requires test certificates from testsuite/ssl-test-certs to be installed * on the server being tested. * * @throws Exception * if the test fails. */ public void testBug25545() throws Exception { if (!versionMeetsMinimum(5, 0)) { return; } if (isRunningOnJdk131()) { return; } createProcedure("testBug25545", "() BEGIN SELECT 1; END"); String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; System.setProperty("javax.net.ssl.keyStore", trustStorePath); System.setProperty("javax.net.ssl.keyStorePassword", "password"); System.setProperty("javax.net.ssl.trustStore", trustStorePath); System.setProperty("javax.net.ssl.trustStorePassword", "password"); Connection sslConn = null; try { Properties props = new Properties(); props.setProperty("useSSL", "true"); props.setProperty("requireSSL", "true"); if (Util.getJVMVersion() < 8 && versionMeetsMinimum(5, 7, 6) && isCommunityEdition()) { props.setProperty("enabledSSLCipherSuites", SSL_CIPHERS_FOR_576); } sslConn = getConnectionWithProps(props); sslConn.prepareCall("{ call testBug25545()}").execute(); } finally { if (sslConn != null) { sslConn.close(); } } } /** * Tests fix for BUG#36948 - Trying to use trustCertificateKeyStoreUrl * causes an IllegalStateException. * * Requires test certificates from testsuite/ssl-test-certs to be installed * on the server being tested. * * @throws Exception * if the test fails. */ public void testBug36948() throws Exception { Connection _conn = null; try { Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); String db = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY, "test"); String hostSpec = host; if (!NonRegisteringDriver.isHostPropertiesList(host)) { hostSpec = host + ":" + port; } final boolean sslRequirementsFor576 = Util.getJVMVersion() < 8 && versionMeetsMinimum(5, 7, 6) && isCommunityEdition(); final String url = "jdbc:mysql://" + hostSpec + "/" + db + "?useSSL=true&requireSSL=true&verifyServerCertificate=true" + "&trustCertificateKeyStoreUrl=file:src/testsuite/ssl-test-certs/test-cert-store&trustCertificateKeyStoreType=JKS" + "&trustCertificateKeyStorePassword=password" + (sslRequirementsFor576 ? "&enabledSSLCipherSuites=" + SSL_CIPHERS_FOR_576 : ""); _conn = DriverManager.getConnection(url, (String) this.getPropertiesFromTestsuiteUrl().get("user"), (String) this.getPropertiesFromTestsuiteUrl() .get("password")); assertTrue(true); } finally { if (_conn != null) { _conn.close(); } } } /** * Tests fix for BUG#27655 - getTransactionIsolation() uses * "SHOW VARIABLES LIKE" which is very inefficient on MySQL-5.0+ * * @throws Exception */ public void testBug27655() throws Exception { Properties props = new Properties(); props.setProperty("profileSQL", "true"); props.setProperty("logger", "StandardLogger"); StandardLogger.startLoggingToBuffer(); Connection loggedConn = null; try { loggedConn = getConnectionWithProps(props); loggedConn.getTransactionIsolation(); if (versionMeetsMinimum(4, 0, 3)) { assertEquals(-1, StandardLogger.getBuffer().toString().indexOf("SHOW VARIABLES LIKE 'tx_isolation'")); } } finally { StandardLogger.dropBuffer(); if (loggedConn != null) { loggedConn.close(); } } } /** * Tests fix for issue where a failed-over connection would let an * application call setReadOnly(false), when that call should be ignored * until the connection is reconnected to a writable master. * * @throws Exception * if the test fails. */ public void testFailoverReadOnly() throws Exception { Properties props = getMasterSlaveProps(); props.setProperty("autoReconnect", "true"); props.setProperty("queriesBeforeRetryMaster", "0"); props.setProperty("secondsBeforeRetryMaster", "0"); // +^ enable fall back to primary as soon as possible Connection failoverConn = null; Statement failoverStmt = null; try { failoverConn = getConnectionWithProps(getMasterSlaveUrl(), props); failoverStmt = failoverConn.createStatement(); String masterConnectionId = getSingleIndexedValueWithQuery(failoverConn, 1, "SELECT connection_id()").toString(); this.stmt.execute("KILL " + masterConnectionId); // die trying, so we get the next host for (int i = 0; i < 100; i++) { try { failoverStmt.executeQuery("SELECT 1"); } catch (SQLException sqlEx) { break; } } String slaveConnectionId = getSingleIndexedValueWithQuery(failoverConn, 1, "SELECT connection_id()").toString(); assertTrue("Didn't get a new physical connection", !masterConnectionId.equals(slaveConnectionId)); failoverConn.setReadOnly(false); // this should be ignored assertTrue(failoverConn.isReadOnly()); this.stmt.execute("KILL " + slaveConnectionId); // we can't issue this on our own connection :p // die trying, so we get the next host for (int i = 0; i < 100; i++) { try { failoverStmt.executeQuery("SELECT 1"); } catch (SQLException sqlEx) { break; } } String newMasterId = getSingleIndexedValueWithQuery(failoverConn, 1, "SELECT connection_id()").toString(); assertTrue("Didn't get a new physical connection", !slaveConnectionId.equals(newMasterId)); failoverConn.setReadOnly(false); assertFalse(failoverConn.isReadOnly()); } finally { if (failoverStmt != null) { failoverStmt.close(); } if (failoverConn != null) { failoverConn.close(); } } } public void testPropertiesDescriptionsKeys() throws Exception { DriverPropertyInfo[] dpi = new NonRegisteringDriver().getPropertyInfo(dbUrl, null); for (int i = 0; i < dpi.length; i++) { String description = dpi[i].description; String propertyName = dpi[i].name; if (description.indexOf("Missing error message for key '") != -1 || description.startsWith("!")) { fail("Missing message for configuration property " + propertyName); } if (description.length() < 10) { fail("Suspiciously short description for configuration property " + propertyName); } } } public void testBug29106() throws Exception { ClassLoader cl = Thread.currentThread().getContextClassLoader(); Class<?> checkerClass = cl.loadClass("com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker"); ((MysqlValidConnectionChecker) checkerClass.newInstance()).isValidConnection(this.conn); } public void testBug29852() throws Exception { Connection lbConn = getLoadBalancedConnection(); assertTrue(!lbConn.getClass().getName().startsWith("com.mysql.jdbc")); lbConn.close(); } /** * Test of a new feature to fix BUG 22643, specifying a "validation query" * in your connection pool that starts with "slash-star ping slash-star" * _exactly_ will cause the driver to " + instead send a ping to the server * (much lighter weight), and when using a ReplicationConnection or a * LoadBalancedConnection, will send the ping across all active connections. * * @throws Exception */ public void testBug22643() throws Exception { checkPingQuery(this.conn); Connection replConnection = getMasterSlaveReplicationConnection(); try { checkPingQuery(replConnection); } finally { if (replConnection != null) { replConnection.close(); } } Connection lbConn = getLoadBalancedConnection(); try { checkPingQuery(lbConn); } finally { if (lbConn != null) { lbConn.close(); } } } private void checkPingQuery(Connection c) throws SQLException { // Yes, I know we're sending 2, and looking for 1 that's part of the test, since we don't _really_ send the query to the server! String aPingQuery = "/* ping */ SELECT 2"; Statement pingStmt = c.createStatement(); PreparedStatement pingPStmt = null; this.rs = pingStmt.executeQuery(aPingQuery); assertTrue(this.rs.next()); assertEquals(this.rs.getInt(1), 1); assertTrue(pingStmt.execute(aPingQuery)); this.rs = pingStmt.getResultSet(); assertTrue(this.rs.next()); assertEquals(this.rs.getInt(1), 1); pingPStmt = c.prepareStatement(aPingQuery); assertTrue(pingPStmt.execute()); this.rs = pingPStmt.getResultSet(); assertTrue(this.rs.next()); assertEquals(this.rs.getInt(1), 1); this.rs = pingPStmt.executeQuery(); assertTrue(this.rs.next()); assertEquals(this.rs.getInt(1), 1); } public void testBug31053() throws Exception { Properties props = new Properties(); props.setProperty("connectTimeout", "2000"); props.setProperty("loadBalanceStrategy", "random"); Connection lbConn = getLoadBalancedConnection(2, "localhost:23", props); lbConn.setAutoCommit(false); for (int i = 0; i < 10; i++) { lbConn.commit(); } } public void testBug32877() throws Exception { Properties props = new Properties(); props.setProperty("connectTimeout", "2000"); props.setProperty("loadBalanceStrategy", "bestResponseTime"); Connection lbConn = getLoadBalancedConnection(1, "localhost:23", props); lbConn.setAutoCommit(false); long begin = System.currentTimeMillis(); for (int i = 0; i < 4; i++) { lbConn.commit(); } assertTrue(System.currentTimeMillis() - begin < 10000); } /** * Tests fix for BUG#33734 - NullPointerException when using client-side * prepared statements and enabling caching of prepared statements (only * present in nightly builds of 5.1). * * @throws Exception */ public void testBug33734() throws Exception { Connection testConn = getConnectionWithProps("cachePrepStmts=true,useServerPrepStmts=false"); try { testConn.prepareStatement("SELECT 1"); } finally { testConn.close(); } } /** 34703 [NEW]: isValild() aborts Connection on timeout */ public void testBug34703() throws Exception { if (!com.mysql.jdbc.Util.isJdbc4()) { return; } Method isValid = java.sql.Connection.class.getMethod("isValid", new Class[] { Integer.TYPE }); Connection newConn = getConnectionWithProps((Properties) null); isValid.invoke(newConn, new Object[] { new Integer(1) }); Thread.sleep(2000); assertTrue(((Boolean) isValid.invoke(newConn, new Object[] { new Integer(0) })).booleanValue()); } public void testBug34937() throws Exception { com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource(); StringBuilder urlBuf = new StringBuilder(); urlBuf.append(getMasterSlaveUrl()); urlBuf.append("?"); Properties props = getMasterSlaveProps(); String key = null; Enumeration<Object> keyEnum = props.keys(); while (keyEnum.hasMoreElements()) { key = (String) keyEnum.nextElement(); urlBuf.append(key); urlBuf.append("="); urlBuf.append(props.get(key)); urlBuf.append("&"); } String url = urlBuf.toString(); url = "jdbc:mysql:replication:" + url.substring(url.indexOf("jdbc:mysql:") + "jdbc:mysql:".length()); ds.setURL(url); Connection replConn = ds.getPooledConnection().getConnection(); boolean readOnly = false; for (int i = 0; i < 10; i++) { this.rs = replConn.createStatement().executeQuery("SELECT 1"); assertTrue(this.rs.next()); this.rs = replConn.prepareStatement("SELECT 1").executeQuery(); assertTrue(this.rs.next()); readOnly = !readOnly; replConn.setReadOnly(readOnly); } } public void testBug35660() throws Exception { Connection lbConn = getLoadBalancedConnection(null); Connection lbConn2 = getLoadBalancedConnection(null); try { assertEquals(this.conn, this.conn); assertEquals(lbConn, lbConn); assertFalse(lbConn.equals(this.conn)); assertFalse(lbConn.equals(lbConn2)); } finally { lbConn.close(); lbConn2.close(); } } public void testBug37570() throws Exception { Properties props = new Properties(); props.setProperty("characterEncoding", "utf-8"); props.setProperty("passwordCharacterEncoding", "utf-8"); Connection adminConn = getAdminConnectionWithProps(props); if (adminConn != null) { String unicodePassword = "\u0430\u0431\u0432"; // Cyrillic string String user = "bug37570"; Statement adminStmt = adminConn.createStatement(); adminStmt.executeUpdate("grant usage on *.* to '" + user + "'@'127.0.0.1' identified by 'foo'"); adminStmt.executeUpdate("update mysql.user set password=PASSWORD('" + unicodePassword + "') where user = '" + user + "'"); adminStmt.executeUpdate("flush privileges"); try { ((MySQLConnection) adminConn).changeUser(user, unicodePassword); } catch (SQLException sqle) { assertTrue("Connection with non-latin1 password failed", false); } } } public void testUnreliableSocketFactory() throws Exception { Properties props = new Properties(); props.setProperty("loadBalanceStrategy", "bestResponseTime"); Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); assertNotNull("Connection should not be null", this.conn); conn2.createStatement().execute("SELECT 1"); conn2.createStatement().execute("SELECT 1"); // both connections are live now UnreliableSocketFactory.downHost("first"); UnreliableSocketFactory.downHost("second"); try { conn2.createStatement().execute("SELECT 1"); fail("Should hang here."); } catch (SQLException sqlEx) { assertEquals("08S01", sqlEx.getSQLState()); } } public void testReplicationConnectionGroupHostManagement() throws Exception { String replicationGroup1 = "rg1"; Properties props = new Properties(); props.setProperty("replicationConnectionGroup", replicationGroup1); props.setProperty("retriesAllDown", "3"); ReplicationConnection conn2 = this.getUnreliableReplicationConnection(new String[] { "first", "second", "third" }, props); assertNotNull("Connection should not be null", this.conn); conn2.setAutoCommit(false); String port = getPort(props, new NonRegisteringDriver()); String firstHost = "first:" + port; String secondHost = "second:" + port; String thirdHost = "third:" + port; // "first" should be master, "second" and "third" should be slaves. assertEquals(1, ReplicationConnectionGroupManager.getConnectionCountWithHostAsMaster(replicationGroup1, firstHost)); assertEquals(0, ReplicationConnectionGroupManager.getConnectionCountWithHostAsSlave(replicationGroup1, firstHost)); // remove "third" from slave pool: conn2.removeSlave(thirdHost); assertEquals(0, ReplicationConnectionGroupManager.getConnectionCountWithHostAsMaster(replicationGroup1, thirdHost)); assertEquals(0, ReplicationConnectionGroupManager.getConnectionCountWithHostAsSlave(replicationGroup1, thirdHost)); // add "third" back into slave pool: conn2.addSlaveHost(thirdHost); assertEquals(0, ReplicationConnectionGroupManager.getConnectionCountWithHostAsMaster(replicationGroup1, thirdHost)); assertEquals(1, ReplicationConnectionGroupManager.getConnectionCountWithHostAsSlave(replicationGroup1, thirdHost)); conn2.setReadOnly(false); assertEquals(0, ReplicationConnectionGroupManager.getNumberOfMasterPromotion(replicationGroup1)); // failover to "second" as master ReplicationConnectionGroupManager.promoteSlaveToMaster(replicationGroup1, secondHost); assertEquals(1, ReplicationConnectionGroupManager.getNumberOfMasterPromotion(replicationGroup1)); // "first" is still a master: assertEquals(1, ReplicationConnectionGroupManager.getConnectionCountWithHostAsMaster(replicationGroup1, firstHost)); assertEquals(0, ReplicationConnectionGroupManager.getConnectionCountWithHostAsSlave(replicationGroup1, firstHost)); assertEquals(1, ReplicationConnectionGroupManager.getConnectionCountWithHostAsMaster(replicationGroup1, secondHost)); assertEquals(0, ReplicationConnectionGroupManager.getConnectionCountWithHostAsSlave(replicationGroup1, secondHost)); ReplicationConnectionGroupManager.removeMasterHost(replicationGroup1, firstHost); conn2.createStatement().execute("SELECT 1"); assertFalse(conn2.isClosed()); conn2.commit(); // validate that queries are successful: conn2.createStatement().execute("SELECT 1"); assertTrue(conn2.isHostMaster(secondHost)); // master is now offline UnreliableSocketFactory.downHost("second"); try { Statement lstmt = conn2.createStatement(); lstmt.execute("SELECT 1"); fail("Should fail here due to closed connection"); } catch (SQLException sqlEx) { assertEquals("08S01", sqlEx.getSQLState()); } } public void testReplicationConnectionHostManagement() throws Exception { Properties props = new Properties(); props.setProperty("retriesAllDown", "3"); ReplicationConnection conn2 = this.getUnreliableReplicationConnection(new String[] { "first", "second", "third" }, props); conn2.setAutoCommit(false); String port = getPort(props, new NonRegisteringDriver()); String firstHost = "first:" + port; String secondHost = "second:" + port; String thirdHost = "third:" + port; // "first" should be master, "second" and "third" should be slaves. assertTrue(conn2.isHostMaster(firstHost)); assertTrue(conn2.isHostSlave(secondHost)); assertTrue(conn2.isHostSlave(thirdHost)); assertFalse(conn2.isHostSlave(firstHost)); assertFalse(conn2.isHostMaster(secondHost)); assertFalse(conn2.isHostMaster(thirdHost)); // remove "third" from slave pool: conn2.removeSlave(thirdHost); assertFalse(conn2.isHostSlave(thirdHost)); assertFalse(conn2.isHostMaster(thirdHost)); // add "third" back into slave pool: conn2.addSlaveHost(thirdHost); assertTrue(conn2.isHostSlave(thirdHost)); assertFalse(conn2.isHostMaster(thirdHost)); conn2.setReadOnly(false); // failover to "second" as master, "first" // can still be used: conn2.promoteSlaveToMaster(secondHost); assertTrue(conn2.isHostMaster(firstHost)); assertFalse(conn2.isHostSlave(firstHost)); assertFalse(conn2.isHostSlave(secondHost)); assertTrue(conn2.isHostMaster(secondHost)); assertTrue(conn2.isHostSlave(thirdHost)); assertFalse(conn2.isHostMaster(thirdHost)); conn2.removeMasterHost(firstHost); // "first" should no longer be used: conn2.promoteSlaveToMaster(secondHost); assertFalse(conn2.isHostMaster(firstHost)); assertFalse(conn2.isHostSlave(firstHost)); assertFalse(conn2.isHostSlave(secondHost)); assertTrue(conn2.isHostMaster(secondHost)); assertTrue(conn2.isHostSlave(thirdHost)); assertFalse(conn2.isHostMaster(thirdHost)); conn2.createStatement().execute("SELECT 1"); assertFalse(conn2.isClosed()); // check that we're waiting until transaction boundary to fail over. // assertTrue(conn2.hasPendingNewMaster()); assertFalse(conn2.isClosed()); conn2.commit(); assertFalse(conn2.isClosed()); assertTrue(conn2.isHostMaster(secondHost)); assertFalse(conn2.isClosed()); assertTrue(conn2.isMasterConnection()); assertFalse(conn2.isClosed()); // validate that queries are successful: conn2.createStatement().execute("SELECT 1"); assertTrue(conn2.isHostMaster(secondHost)); // master is now offline UnreliableSocketFactory.downHost("second"); try { Statement lstmt = conn2.createStatement(); lstmt.execute("SELECT 1"); fail("Should fail here due to closed connection"); } catch (SQLException sqlEx) { assertEquals("08S01", sqlEx.getSQLState()); } UnreliableSocketFactory.dontDownHost("second"); try { // won't work now even though master is back up connection has already been implicitly closed when a new master host cannot be found: conn2.createStatement().execute("SELECT 1"); fail("Will fail because inability to find new master host implicitly closes connection."); } catch (SQLException e) { assertEquals("08003", e.getSQLState()); } } public void testReplicationConnectWithNoMaster() throws Exception { Properties props = new Properties(); props.setProperty("retriesAllDown", "3"); props.setProperty("allowMasterDownConnections", "true"); Set<String> downedHosts = new HashSet<String>(); downedHosts.add("first"); ReplicationConnection conn2 = this.getUnreliableReplicationConnection(new String[] { "first", "second", "third" }, props, downedHosts); assertTrue(conn2.isReadOnly()); assertFalse(conn2.isMasterConnection()); try { conn2.createStatement().execute("SELECT 1"); } catch (SQLException e) { fail("Should not fail to execute SELECT statements!"); } UnreliableSocketFactory.flushAllStaticData(); conn2.setReadOnly(false); assertFalse(conn2.isReadOnly()); assertTrue(conn2.isMasterConnection()); try { conn2.createStatement().execute("DROP TABLE IF EXISTS testRepTable"); conn2.createStatement().execute("CREATE TABLE testRepTable (a INT)"); conn2.createStatement().execute("INSERT INTO testRepTable VALUES (1)"); conn2.createStatement().execute("DROP TABLE IF EXISTS testRepTable"); } catch (SQLException e) { fail("Should not fail to execute CREATE/INSERT/DROP statements."); } } public void testReplicationConnectWithMultipleMasters() throws Exception { Properties props = new Properties(); props.setProperty("retriesAllDown", "3"); Set<MockConnectionConfiguration> configs = new HashSet<MockConnectionConfiguration>(); MockConnectionConfiguration first = new MockConnectionConfiguration("first", "slave", null, false); MockConnectionConfiguration second = new MockConnectionConfiguration("second", "master", null, false); MockConnectionConfiguration third = new MockConnectionConfiguration("third", "master", null, false); configs.add(first); configs.add(second); configs.add(third); ReplicationConnection conn2 = this.getUnreliableReplicationConnection(configs, props); assertFalse(conn2.isReadOnly()); assertTrue(conn2.isMasterConnection()); assertTrue(conn2.isHostSlave(first.getAddress())); assertTrue(conn2.isHostMaster(second.getAddress())); assertTrue(conn2.isHostMaster(third.getAddress())); } public void testReplicationConnectionMemory() throws Exception { Properties props = new Properties(); props.setProperty("retriesAllDown", "3"); String replicationGroup = "memoryGroup"; props.setProperty("replicationConnectionGroup", replicationGroup); Set<MockConnectionConfiguration> configs = new HashSet<MockConnectionConfiguration>(); MockConnectionConfiguration first = new MockConnectionConfiguration("first", "slave", null, false); MockConnectionConfiguration second = new MockConnectionConfiguration("second", "master", null, false); MockConnectionConfiguration third = new MockConnectionConfiguration("third", "slave", null, false); configs.add(first); configs.add(second); configs.add(third); ReplicationConnection conn2 = this.getUnreliableReplicationConnection(configs, props); ReplicationConnectionGroupManager.promoteSlaveToMaster(replicationGroup, first.getAddress()); ReplicationConnectionGroupManager.removeMasterHost(replicationGroup, second.getAddress()); ReplicationConnectionGroupManager.addSlaveHost(replicationGroup, second.getAddress()); conn2.setReadOnly(false); assertFalse(conn2.isReadOnly()); assertTrue(conn2.isMasterConnection()); assertTrue(conn2.isHostMaster(first.getAddress())); assertTrue(conn2.isHostSlave(second.getAddress())); assertTrue(conn2.isHostSlave(third.getAddress())); // make sure state changes made are reflected in new connections: ReplicationConnection conn3 = this.getUnreliableReplicationConnection(configs, props); conn3.setReadOnly(false); assertFalse(conn3.isReadOnly()); assertTrue(conn3.isMasterConnection()); assertTrue(conn3.isHostMaster(first.getAddress())); assertTrue(conn3.isHostSlave(second.getAddress())); assertTrue(conn3.isHostSlave(third.getAddress())); } public void testReplicationJMXInterfaces() throws Exception { Properties props = new Properties(); props.setProperty("retriesAllDown", "3"); String replicationGroup = "testReplicationJMXInterfaces"; props.setProperty("replicationConnectionGroup", replicationGroup); props.setProperty("replicationEnableJMX", "true"); Set<MockConnectionConfiguration> configs = new HashSet<MockConnectionConfiguration>(); MockConnectionConfiguration first = new MockConnectionConfiguration("first", "slave", null, false); MockConnectionConfiguration second = new MockConnectionConfiguration("second", "master", null, false); MockConnectionConfiguration third = new MockConnectionConfiguration("third", "slave", null, false); configs.add(first); configs.add(second); configs.add(third); ReplicationConnection conn2 = this.getUnreliableReplicationConnection(configs, props); ReplicationGroupManagerMBean bean = getReplicationMBean(); assertEquals(1, bean.getActiveLogicalConnectionCount(replicationGroup)); assertEquals(1, bean.getTotalLogicalConnectionCount(replicationGroup)); assertEquals(0, bean.getSlavePromotionCount(replicationGroup)); assertEquals(1, bean.getActiveMasterHostCount(replicationGroup)); assertEquals(2, bean.getActiveSlaveHostCount(replicationGroup)); bean.removeSlaveHost(replicationGroup, first.getAddress()); assertFalse(bean.getSlaveHostsList(replicationGroup).contains(first.getAddress())); assertEquals(1, bean.getActiveSlaveHostCount(replicationGroup)); conn2.close(); assertEquals(0, bean.getActiveLogicalConnectionCount(replicationGroup)); conn2 = this.getUnreliableReplicationConnection(configs, props); assertEquals(1, bean.getActiveLogicalConnectionCount(replicationGroup)); assertEquals(2, bean.getTotalLogicalConnectionCount(replicationGroup)); assertEquals(1, bean.getActiveSlaveHostCount(replicationGroup)); assertEquals(1, bean.getActiveMasterHostCount(replicationGroup)); bean.promoteSlaveToMaster(replicationGroup, third.getAddress()); assertEquals(2, bean.getActiveMasterHostCount(replicationGroup)); assertEquals(0, bean.getActiveSlaveHostCount(replicationGroup)); // confirm this works when no group filter is specified: bean.addSlaveHost(null, first.getAddress()); assertEquals(1, bean.getActiveSlaveHostCount(replicationGroup)); assertEquals(2, bean.getActiveMasterHostCount(replicationGroup)); bean.removeMasterHost(replicationGroup, second.getAddress()); assertEquals(1, bean.getActiveSlaveHostCount(replicationGroup)); assertEquals(1, bean.getActiveMasterHostCount(replicationGroup)); ReplicationConnection conn3 = this.getUnreliableReplicationConnection(configs, props); assertEquals(2, bean.getActiveLogicalConnectionCount(replicationGroup)); assertEquals(3, bean.getTotalLogicalConnectionCount(replicationGroup)); assertTrue(bean.getMasterHostsList(replicationGroup).contains(third.getAddress())); assertFalse(bean.getMasterHostsList(replicationGroup).contains(first.getAddress())); assertFalse(bean.getMasterHostsList(replicationGroup).contains(second.getAddress())); assertFalse(bean.getSlaveHostsList(replicationGroup).contains(third.getAddress())); assertTrue(bean.getSlaveHostsList(replicationGroup).contains(first.getAddress())); assertFalse(bean.getSlaveHostsList(replicationGroup).contains(second.getAddress())); assertTrue(bean.getMasterHostsList(replicationGroup).contains(conn3.getMasterConnection().getHost())); assertTrue(bean.getSlaveHostsList(replicationGroup).contains(conn3.getSlavesConnection().getHost())); assertTrue(bean.getRegisteredConnectionGroups().contains(replicationGroup)); } private ReplicationGroupManagerMBean getReplicationMBean() throws Exception { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); ObjectName mbeanName = new ObjectName("com.mysql.jdbc.jmx:type=ReplicationGroupManager"); return (ReplicationGroupManagerMBean) MBeanServerInvocationHandler.newProxyInstance(mbs, mbeanName, ReplicationGroupManagerMBean.class, false); } public void testBug43421() throws Exception { Properties props = new Properties(); props.setProperty("loadBalanceStrategy", "bestResponseTime"); Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); conn2.createStatement().execute("SELECT 1"); conn2.createStatement().execute("SELECT 1"); // both connections are live now UnreliableSocketFactory.downHost("second"); UnreliableSocketFactory.downHost("first"); try { conn2.createStatement().execute("/* ping */"); fail("Pings will not succeed when one host is down and using loadbalance w/o global blacklist."); } catch (SQLException sqlEx) { } UnreliableSocketFactory.flushAllStaticData(); props = new Properties(); props.setProperty("globalBlacklistTimeout", "200"); props.setProperty("loadBalanceStrategy", "bestResponseTime"); conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); assertNotNull("Connection should not be null", this.conn); conn2.createStatement().execute("SELECT 1"); conn2.createStatement().execute("SELECT 1"); // both connections are live now UnreliableSocketFactory.downHost("second"); try { conn2.createStatement().execute("/* ping */"); } catch (SQLException sqlEx) { fail("Pings should succeed even though host is down."); } } public void testBug48442() throws Exception { Properties props = new Properties(); props.setProperty("loadBalanceStrategy", "random"); Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); assertNotNull("Connection should not be null", conn2); conn2.setAutoCommit(false); UnreliableSocketFactory.downHost("second"); int hc = 0; try { conn2.createStatement().execute("SELECT 1"); } catch (SQLException e) { conn2.createStatement().execute("SELECT 1"); } hc = conn2.hashCode(); conn2.commit(); UnreliableSocketFactory.dontDownHost("second"); UnreliableSocketFactory.downHost("first"); try { conn2.commit(); } catch (SQLException e) { } assertTrue(hc == conn2.hashCode()); } public void testBug45171() throws Exception { List<Statement> statementsToTest = new LinkedList<Statement>(); statementsToTest.add(this.conn.createStatement()); statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("SELECT 1")); statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("SELECT 1", Statement.RETURN_GENERATED_KEYS)); statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("SELECT 1", new int[0])); statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("SELECT 1", new String[0])); statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("SELECT 1")); statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("SELECT 1", Statement.RETURN_GENERATED_KEYS)); statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("SELECT 1", new int[0])); statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("SELECT 1", new String[0])); for (Statement toTest : statementsToTest) { assertEquals(toTest.getResultSetType(), ResultSet.TYPE_FORWARD_ONLY); assertEquals(toTest.getResultSetConcurrency(), ResultSet.CONCUR_READ_ONLY); } } /** * Tests fix for BUG#44587, provide last packet sent/received timing in all * connection failure errors. */ public void testBug44587() throws Exception { Exception e = null; String msg = SQLError.createLinkFailureMessageBasedOnHeuristics((MySQLConnection) this.conn, System.currentTimeMillis() - 1000, System.currentTimeMillis() - 2000, e); assertTrue(containsMessage(msg, "CommunicationsException.ServerPacketTimingInfo")); } /** * Tests fix for BUG#45419, ensure that time is not converted to seconds * before being reported as milliseconds. */ public void testBug45419() throws Exception { Exception e = null; String msg = SQLError.createLinkFailureMessageBasedOnHeuristics((MySQLConnection) this.conn, System.currentTimeMillis() - 1000, System.currentTimeMillis() - 2000, e); Matcher m = Pattern.compile("([\\d\\,\\.]+)", Pattern.MULTILINE).matcher(msg); assertTrue(m.find()); assertTrue(Long.parseLong(m.group(0).replaceAll("[,.]", "")) >= 2000); assertTrue(Long.parseLong(m.group(1).replaceAll("[,.]", "")) >= 1000); } public static boolean containsMessage(String msg, String key) { String[] expectedFragments = Messages.getString(key).split("\\{\\d\\}"); for (int i = 0; i < expectedFragments.length; i++) { if (msg.indexOf(expectedFragments[i]) < 0) { return false; } } return true; } public void testBug46637() throws Exception { NonRegisteringDriver driver = new NonRegisteringDriver(); Properties props = new Properties(); copyBasePropertiesIntoProps(props, driver); String hostname = getPortFreeHostname(props, driver); UnreliableSocketFactory.flushAllStaticData(); UnreliableSocketFactory.downHost(hostname); try { Connection noConn = getConnectionWithProps("socketFactory=testsuite.UnreliableSocketFactory"); noConn.close(); } catch (SQLException sqlEx) { assertTrue(sqlEx.getMessage().indexOf("has not received") != -1); } finally { UnreliableSocketFactory.flushAllStaticData(); } } 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(); StringBuilder url = new StringBuilder("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(NonRegisteringDriver.HOST_PROPERTY_KEY)); if (port != null) { assertEquals("port not equal", port, result.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY)); } else { assertEquals("port default incorrect", "3306", result.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY)); } assertEquals("dbname not equal", dbname, result.getProperty(NonRegisteringDriver.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 { public PortNumberSocketFactory() { } @Override public Socket connect(String hostname, int portNumber, Properties props) throws SocketException, IOException { assertEquals(9999, portNumber); throw new IOException(); } } public void testBug48486() throws Exception { Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); String hostSpec = host; if (!NonRegisteringDriver.isHostPropertiesList(host)) { hostSpec = host + ":" + port; } String database = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); removeHostRelatedProps(props); props.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY); StringBuilder configs = new StringBuilder(); for (@SuppressWarnings("rawtypes") Map.Entry entry : props.entrySet()) { configs.append(entry.getKey()); configs.append("="); configs.append(entry.getValue()); configs.append("&"); } String newUrl = String.format("jdbc:mysql:loadbalance://%s,%s/%s?%s", hostSpec, hostSpec, database, configs.toString()); MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource(); ds.setUrl(newUrl); Connection c = ds.getPooledConnection().getConnection(); c.createStatement().executeQuery("SELECT 1"); c.prepareStatement("SELECT 1").executeQuery(); } public void testBug48605() throws Exception { Properties props = new Properties(); props.setProperty("loadBalanceStrategy", "random"); props.setProperty("selfDestructOnPingMaxOperations", "5"); Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); assertNotNull("Connection should not be null", conn2); conn2.setAutoCommit(false); conn2.createStatement().execute("SELECT 1"); conn2.createStatement().execute("SELECT 1"); conn2.createStatement().execute("SELECT 1"); conn2.createStatement().execute("SELECT 1"); conn2.createStatement().execute("SELECT 1"); conn2.commit(); try { conn2.createStatement().execute("/* ping */ SELECT 1"); // don't care about this - we want the SQLExceptions passed up early // for ping failures, rather // than waiting until commit/rollback and pickNewConnection(). } catch (SQLException e) { } assertTrue(conn2.isClosed()); try { conn2.createStatement().execute("SELECT 1"); fail("Should throw Exception, connection is closed."); } catch (SQLException e) { } } public void testBug49700() throws Exception { Connection c = getConnectionWithProps("sessionVariables=@foo='bar'"); assertEquals("bar", getSingleIndexedValueWithQuery(c, 1, "SELECT @foo")); ((com.mysql.jdbc.Connection) c).resetServerState(); assertEquals("bar", getSingleIndexedValueWithQuery(c, 1, "SELECT @foo")); } public void testBug51266() throws Exception { Properties props = new Properties(); props.setProperty("roundRobinLoadBalance", "true"); // shouldn't be // needed, but used // in reported bug, // it's removed by // the driver Set<String> downedHosts = new HashSet<String>(); downedHosts.add("first"); // this loop will hang on the first unreliable host if the bug isn't // fixed. for (int i = 0; i < 20; i++) { getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props, downedHosts).close(); } } // Tests fix for Bug#51643 - connection chosen by load balancer "sticks" to statements that live past commit()/rollback(). public void testBug51643() throws Exception { Properties props = new Properties(); props.setProperty("loadBalanceStrategy", "com.mysql.jdbc.SequentialBalanceStrategy"); Connection lbConn = getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); try { PreparedStatement cPstmt = lbConn.prepareStatement("SELECT connection_id()"); PreparedStatement serverPstmt = lbConn.prepareStatement("SELECT connection_id()"); Statement plainStmt = lbConn.createStatement(); lbConn.setAutoCommit(false); this.rs = cPstmt.executeQuery(); this.rs.next(); String cPstmtConnId = this.rs.getString(1); this.rs = serverPstmt.executeQuery(); this.rs.next(); String serverPstmtConnId = this.rs.getString(1); this.rs = plainStmt.executeQuery("SELECT connection_id()"); this.rs.next(); String plainStmtConnId = this.rs.getString(1); lbConn.commit(); lbConn.setAutoCommit(false); this.rs = cPstmt.executeQuery(); this.rs.next(); String cPstmtConnId2 = this.rs.getString(1); assertFalse(cPstmtConnId2.equals(cPstmtConnId)); this.rs = serverPstmt.executeQuery(); this.rs.next(); String serverPstmtConnId2 = this.rs.getString(1); assertFalse(serverPstmtConnId2.equals(serverPstmtConnId)); this.rs = plainStmt.executeQuery("SELECT connection_id()"); this.rs.next(); String plainStmtConnId2 = this.rs.getString(1); assertFalse(plainStmtConnId2.equals(plainStmtConnId)); } finally { lbConn.close(); } } public void testBug51783() throws Exception { Properties props = new Properties(); props.setProperty("loadBalanceStrategy", ForcedLoadBalanceStrategy.class.getName()); props.setProperty("loadBalanceBlacklistTimeout", "5000"); props.setProperty("loadBalancePingTimeout", "100"); props.setProperty("loadBalanceValidateConnectionOnSwapServer", "true"); String portNumber = new NonRegisteringDriver().parseURL(dbUrl, null).getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); if (portNumber == null) { portNumber = "3306"; } ForcedLoadBalanceStrategy.forceFutureServer("first:" + portNumber, -1); Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); conn2.setAutoCommit(false); conn2.createStatement().execute("SELECT 1"); ForcedLoadBalanceStrategy.forceFutureServer("second:" + portNumber, -1); UnreliableSocketFactory.downHost("second"); try { conn2.commit(); // will be on second after this assertTrue("Connection should be closed", conn2.isClosed()); } catch (SQLException e) { fail("Should not error because failure to get another server."); } conn2.close(); props = new Properties(); props.setProperty("loadBalanceStrategy", ForcedLoadBalanceStrategy.class.getName()); props.setProperty("loadBalanceBlacklistTimeout", "5000"); props.setProperty("loadBalancePingTimeout", "100"); props.setProperty("loadBalanceValidateConnectionOnSwapServer", "false"); ForcedLoadBalanceStrategy.forceFutureServer("first:" + portNumber, -1); conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); conn2.setAutoCommit(false); conn2.createStatement().execute("SELECT 1"); ForcedLoadBalanceStrategy.forceFutureServer("second:" + portNumber, 1); UnreliableSocketFactory.downHost("second"); try { conn2.commit(); // will be on second after this assertFalse("Connection should not be closed, should be able to connect to first", conn2.isClosed()); } catch (SQLException e) { fail("Should not error because failure to get another server."); } } public static class ForcedLoadBalanceStrategy extends RandomBalanceStrategy { private static String forcedFutureServer = null; private static int forceFutureServerTimes = 0; public static void forceFutureServer(String host, int times) { forcedFutureServer = host; forceFutureServerTimes = times; } public static void dontForceFutureServer() { forcedFutureServer = null; forceFutureServerTimes = 0; } @Override public com.mysql.jdbc.ConnectionImpl pickConnection(LoadBalancingConnectionProxy proxy, List<String> configuredHosts, Map<String, ConnectionImpl> liveConnections, long[] responseTimes, int numRetries) throws SQLException { if (forcedFutureServer == null || forceFutureServerTimes == 0 || !configuredHosts.contains(forcedFutureServer)) { return super.pickConnection(proxy, configuredHosts, liveConnections, responseTimes, numRetries); } if (forceFutureServerTimes > 0) { forceFutureServerTimes--; } ConnectionImpl conn = liveConnections.get(forcedFutureServer); if (conn == null) { conn = proxy.createConnectionForHost(forcedFutureServer); } return conn; } @Override public void destroy() { super.destroy(); } @Override public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { super.init(conn, props); } } public void testAutoCommitLB() throws Exception { Properties props = new Properties(); props.setProperty("loadBalanceStrategy", CountingReBalanceStrategy.class.getName()); props.setProperty("loadBalanceAutoCommitStatementThreshold", "3"); String portNumber = new NonRegisteringDriver().parseURL(dbUrl, null).getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); if (portNumber == null) { portNumber = "3306"; } Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); conn2.setAutoCommit(true); CountingReBalanceStrategy.resetTimesRebalanced(); conn2.createStatement().execute("SELECT 1"); conn2.createStatement().execute("SELECT 2"); assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); conn2.createStatement().execute("SELECT 3"); assertEquals(1, CountingReBalanceStrategy.getTimesRebalanced()); conn2.setAutoCommit(false); CountingReBalanceStrategy.resetTimesRebalanced(); assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); conn2.createStatement().execute("SELECT 1"); conn2.createStatement().execute("SELECT 2"); conn2.createStatement().execute("SELECT 3"); assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); conn2.close(); props.remove("loadBalanceAutoCommitStatementThreshold"); conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); conn2.setAutoCommit(true); CountingReBalanceStrategy.resetTimesRebalanced(); conn2.createStatement().execute("SELECT 1"); conn2.createStatement().execute("SELECT 2"); conn2.createStatement().execute("SELECT 3"); assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); conn2.setAutoCommit(false); CountingReBalanceStrategy.resetTimesRebalanced(); assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); conn2.createStatement().execute("SELECT 1"); conn2.createStatement().execute("SELECT 2"); conn2.createStatement().execute("SELECT 3"); assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); conn2.close(); props.setProperty("loadBalanceAutoCommitStatementThreshold", "3"); props.setProperty("loadBalanceAutoCommitStatementRegex", ".*2.*"); conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); conn2.setAutoCommit(true); CountingReBalanceStrategy.resetTimesRebalanced(); conn2.createStatement().execute("SELECT 1"); conn2.createStatement().execute("SELECT 2"); conn2.createStatement().execute("SELECT 3"); conn2.createStatement().execute("SELECT 2"); assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); conn2.createStatement().execute("SELECT 2"); assertEquals(1, CountingReBalanceStrategy.getTimesRebalanced()); conn2.close(); } public static class CountingReBalanceStrategy extends RandomBalanceStrategy { private static int rebalancedTimes = 0; public static int getTimesRebalanced() { return rebalancedTimes; } public static void resetTimesRebalanced() { rebalancedTimes = 0; } @Override public com.mysql.jdbc.ConnectionImpl pickConnection(LoadBalancingConnectionProxy proxy, List<String> configuredHosts, Map<String, ConnectionImpl> liveConnections, long[] responseTimes, int numRetries) throws SQLException { rebalancedTimes++; return super.pickConnection(proxy, configuredHosts, liveConnections, responseTimes, numRetries); } @Override public void destroy() { super.destroy(); } @Override public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { super.init(conn, props); } } public void testBug56429() throws Exception { Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null); props.setProperty("autoReconnect", "true"); props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); Properties urlProps = new NonRegisteringDriver().parseURL(BaseTestCase.dbUrl, null); String host = urlProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); String port = urlProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); props.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY); props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1"); props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1"); props.setProperty("queriesBeforeRetryMaster", "50"); props.setProperty("maxReconnects", "1"); UnreliableSocketFactory.mapHost("master", host); UnreliableSocketFactory.mapHost("slave", host); Connection failoverConnection = null; try { failoverConnection = getConnectionWithProps("jdbc:mysql://master:" + port + ",slave:" + port + "/", props); String userHost = getSingleIndexedValueWithQuery(1, "SELECT USER()").toString(); String[] userParts = userHost.split("@"); this.rs = this.stmt.executeQuery("SHOW PROCESSLIST"); int startConnCount = 0; while (this.rs.next()) { if (this.rs.getString("User").equals(userParts[0]) && this.rs.getString("Host").equals(userParts[1])) { startConnCount++; } } assert (startConnCount > 0); failoverConnection.setAutoCommit(false); // this will fail if state // not copied over for (int i = 0; i < 20; i++) { failoverConnection.commit(); } this.rs = this.stmt.executeQuery("SHOW PROCESSLIST"); int endConnCount = 0; while (this.rs.next()) { if (this.rs.getString("User").equals(userParts[0]) && this.rs.getString("Host").equals(userParts[1])) { endConnCount++; } } assert (endConnCount > 0); if (endConnCount - startConnCount >= 20) { // this may be bogus if run on a real system, we should probably look to see they're coming from this // testsuite? fail("We're leaking connections even when not failed over"); } } finally { if (failoverConnection != null) { failoverConnection.close(); } } } public void testBug56955() throws Exception { assertEquals("JKS", ((com.mysql.jdbc.Connection) this.conn).getTrustCertificateKeyStoreType()); assertEquals("JKS", ((com.mysql.jdbc.Connection) this.conn).getClientCertificateKeyStoreType()); } public void testBug57262() throws Exception { Properties props = new Properties(); props.setProperty("characterEncoding", "utf-8"); props.setProperty("useUnicode", "true"); props.setProperty("useOldUTF8Behavior", "true"); Connection c = getConnectionWithProps(props); ResultSet r = c.createStatement().executeQuery("SHOW SESSION VARIABLES LIKE 'character_set_connection'"); r.next(); assertEquals("latin1", r.getString(2)); } public void testBug58706() throws Exception { Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null); props.setProperty("autoReconnect", "true"); props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); Properties urlProps = new NonRegisteringDriver().parseURL(dbUrl, null); String host = urlProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); String port = urlProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); props.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY); props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1"); props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1"); props.setProperty("queriesBeforeRetryMaster", "0"); props.setProperty("secondsBeforeRetryMaster", "1"); props.setProperty("failOverReadOnly", "false"); UnreliableSocketFactory.mapHost("master", host); UnreliableSocketFactory.mapHost("slave", host); Connection failoverConnection = null; try { failoverConnection = getConnectionWithProps("jdbc:mysql://master:" + port + ",slave:" + port + "/", props); failoverConnection.setAutoCommit(false); assertEquals("/master", UnreliableSocketFactory.getHostFromLastConnection()); for (int i = 0; i < 50; i++) { failoverConnection.createStatement().executeQuery("SELECT 1"); } UnreliableSocketFactory.downHost("master"); try { failoverConnection.createStatement().executeQuery("SELECT 1"); // this should fail and trigger failover fail("Expected exception"); } catch (SQLException sqlEx) { assertEquals("08S01", sqlEx.getSQLState()); } failoverConnection.setAutoCommit(true); assertEquals("/slave", UnreliableSocketFactory.getHostFromLastConnection()); assertTrue(!failoverConnection.isReadOnly()); failoverConnection.createStatement().executeQuery("SELECT 1"); failoverConnection.createStatement().executeQuery("SELECT 1"); UnreliableSocketFactory.dontDownHost("master"); Thread.sleep(2000); failoverConnection.setAutoCommit(true); failoverConnection.createStatement().executeQuery("SELECT 1"); assertEquals("/master", UnreliableSocketFactory.getHostFromLastConnection()); failoverConnection.createStatement().executeQuery("SELECT 1"); } finally { UnreliableSocketFactory.flushAllStaticData(); if (failoverConnection != null) { failoverConnection.close(); } } } public void testStatementComment() throws Exception { Connection c = getConnectionWithProps("autoGenerateTestcaseScript=true,logger=StandardLogger"); PrintStream oldErr = System.err; try { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); PrintStream printStream = new PrintStream(bOut); System.setErr(printStream); ((com.mysql.jdbc.Connection) c).setStatementComment("Hi there"); c.setAutoCommit(false); c.createStatement().execute("SELECT 1"); c.commit(); c.rollback(); Pattern pattern = Pattern.compile("Hi"); String loggedData = new String(bOut.toByteArray()); Matcher matcher = pattern.matcher(loggedData); int count = 0; while (matcher.find()) { count++; } assertEquals(4, count); } finally { System.setErr(oldErr); } } public void testReconnectWithCachedConfig() throws Exception { Connection rConn = getConnectionWithProps("autoReconnect=true,initialTimeout=2,maxReconnects=3,cacheServerConfiguration=true,elideSetAutoCommits=true"); String threadId = getSingleIndexedValueWithQuery(rConn, 1, "select connection_id()").toString(); killConnection(this.conn, threadId); boolean detectedDeadConn = false; for (int i = 0; i < 100; i++) { try { rConn.createStatement().executeQuery("SELECT 1"); } catch (SQLException sqlEx) { detectedDeadConn = true; break; } } assertTrue(detectedDeadConn); rConn.prepareStatement("SELECT 1").executeQuery(); Connection rConn2 = getConnectionWithProps("autoReconnect=true,initialTimeout=2,maxReconnects=3,cacheServerConfiguration=true,elideSetAutoCommits=true"); rConn2.prepareStatement("SELECT 1").executeQuery(); } public void testBug61201() throws Exception { Properties props = new Properties(); props.setProperty("sessionVariables", "FOREIGN_KEY_CHECKS=0"); props.setProperty("characterEncoding", "latin1"); props.setProperty("profileSQL", "true"); Connection varConn = getConnectionWithProps(props); varConn.close(); } public void testChangeUser() throws Exception { Properties props = getPropertiesFromTestsuiteUrl(); Connection testConn = getConnectionWithProps(props); Statement testStmt = testConn.createStatement(); for (int i = 0; i < 500; i++) { ((com.mysql.jdbc.Connection) testConn).changeUser(props.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY), props.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY)); if (i % 10 == 0) { try { ((com.mysql.jdbc.Connection) testConn).changeUser("bubba", props.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY)); } catch (SQLException sqlEx) { if (versionMeetsMinimum(5, 6, 13)) { assertTrue(testConn.isClosed()); testConn = getConnectionWithProps(props); testStmt = testConn.createStatement(); } } } testStmt.executeQuery("SELECT 1"); } testConn.close(); } public void testChangeUserClosedConn() throws Exception { Properties props = getPropertiesFromTestsuiteUrl(); Connection newConn = getConnectionWithProps((Properties) null); try { newConn.close(); ((com.mysql.jdbc.Connection) newConn).changeUser(props.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY), props.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY)); fail("Expected SQL Exception"); } catch (SQLException ex) { // expected if (!ex.getClass().getName().endsWith("MySQLNonTransientConnectionException")) { throw ex; } } finally { newConn.close(); } } public void testBug63284() throws Exception { Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null); props.setProperty("autoReconnect", "true"); props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); Properties urlProps = new NonRegisteringDriver().parseURL(BaseTestCase.dbUrl, null); String host = urlProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); String port = urlProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); props.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY); props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1"); props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1"); props.setProperty("queriesBeforeRetryMaster", "50"); props.setProperty("maxReconnects", "1"); UnreliableSocketFactory.mapHost("master", host); UnreliableSocketFactory.mapHost("slave", host); Connection failoverConnection1 = null; Connection failoverConnection2 = null; try { failoverConnection1 = getConnectionWithProps("jdbc:mysql://master:" + port + ",slave:" + port + "/", props); failoverConnection2 = getConnectionWithProps("jdbc:mysql://master:" + port + ",slave:" + port + "/", props); assert (((com.mysql.jdbc.Connection) failoverConnection1).isMasterConnection()); // Two different Connection objects should not equal each other: assert (!failoverConnection1.equals(failoverConnection2)); int hc = failoverConnection1.hashCode(); UnreliableSocketFactory.downHost("master"); for (int i = 0; i < 3; i++) { try { failoverConnection1.createStatement().execute("SELECT 1"); } catch (SQLException e) { // do nothing, expect SQLException when failing over initially goal here is to ensure valid connection against a slave } } // ensure we're now connected to the slave assert (!((com.mysql.jdbc.Connection) failoverConnection1).isMasterConnection()); // ensure that hashCode() result is persistent across failover events when proxy state changes assert (failoverConnection1.hashCode() == hc); } finally { if (failoverConnection1 != null) { failoverConnection1.close(); } if (failoverConnection2 != null) { failoverConnection2.close(); } } } public void testDefaultPlugin() throws Exception { if (versionMeetsMinimum(5, 5, 7)) { Connection testConn = null; Properties props = new Properties(); props.setProperty("defaultAuthenticationPlugin", ""); try { testConn = getConnectionWithProps(props); assertTrue("Exception is expected due to incorrect defaultAuthenticationPlugin value", false); } catch (SQLException sqlEx) { assertTrue(true); } finally { if (testConn != null) { testConn.close(); } } props.setProperty("defaultAuthenticationPlugin", "mysql_native_password"); try { testConn = getConnectionWithProps(props); assertTrue("Exception is expected due to incorrect defaultAuthenticationPlugin value (mechanism name instead of class name)", false); } catch (SQLException sqlEx) { assertTrue(true); } finally { if (testConn != null) { testConn.close(); } } props.setProperty("defaultAuthenticationPlugin", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); try { testConn = getConnectionWithProps(props); assertTrue("Exception is expected due to defaultAuthenticationPlugin value is not listed", false); } catch (SQLException sqlEx) { assertTrue(true); } finally { if (testConn != null) { testConn.close(); } } props.setProperty("authenticationPlugins", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); props.setProperty("defaultAuthenticationPlugin", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); try { testConn = getConnectionWithProps(props); assertTrue(true); } catch (SQLException sqlEx) { assertTrue("Exception is not expected due to defaultAuthenticationPlugin value is correctly listed", false); } finally { if (testConn != null) { testConn.close(); } } } } public void testDisabledPlugins() throws Exception { if (versionMeetsMinimum(5, 5, 7)) { Connection testConn = null; Properties props = new Properties(); props.setProperty("disabledAuthenticationPlugins", "mysql_native_password"); try { testConn = getConnectionWithProps(props); assertTrue("Exception is expected due to disabled defaultAuthenticationPlugin", false); } catch (SQLException sqlEx) { assertTrue(true); } finally { if (testConn != null) { testConn.close(); } } props.setProperty("disabledAuthenticationPlugins", "com.mysql.jdbc.authentication.MysqlNativePasswordPlugin"); try { testConn = getConnectionWithProps(props); assertTrue("Exception is expected due to disabled defaultAuthenticationPlugin", false); } catch (SQLException sqlEx) { assertTrue(true); } finally { if (testConn != null) { testConn.close(); } } props.setProperty("authenticationPlugins", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); props.setProperty("defaultAuthenticationPlugin", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); props.setProperty("disabledAuthenticationPlugins", "auth_test_plugin"); try { testConn = getConnectionWithProps(props); assertTrue("Exception is expected due to disabled defaultAuthenticationPlugin", false); } catch (SQLException sqlEx) { assertTrue(true); } finally { if (testConn != null) { testConn.close(); } } props.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.MysqlNativePasswordPlugin"); props.setProperty("authenticationPlugins", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); props.setProperty("disabledAuthenticationPlugins", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); try { testConn = getConnectionWithProps(props); assertTrue(true); } catch (SQLException sqlEx) { assertTrue("Exception is not expected due to disabled plugin is not default", false); } finally { if (testConn != null) { testConn.close(); } } } } public void testAuthTestPlugin() throws Exception { if (versionMeetsMinimum(5, 5, 7)) { boolean install_plugin_in_runtime = false; try { // install plugin if required this.rs = this.stmt.executeQuery("select (PLUGIN_LIBRARY LIKE 'auth_test_plugin%') as `TRUE`" + " FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='test_plugin_server'"); if (this.rs.next()) { if (!this.rs.getBoolean(1)) { install_plugin_in_runtime = true; } } else { install_plugin_in_runtime = true; } if (install_plugin_in_runtime) { String ext = System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") > -1 ? ".dll" : ".so"; this.stmt.executeUpdate("INSTALL PLUGIN test_plugin_server SONAME 'auth_test_plugin" + ext + "'"); } Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); if (dbname == null) { assertTrue("No database selected", false); } // create proxy users this.stmt.executeUpdate("grant usage on *.* to 'wl5851user'@'%' identified WITH test_plugin_server AS 'plug_dest'"); this.stmt.executeUpdate("grant usage on *.* to 'plug_dest'@'%' IDENTIFIED BY 'foo'"); this.stmt.executeUpdate("GRANT PROXY ON 'plug_dest'@'%' TO 'wl5851user'@'%'"); this.stmt.executeUpdate("delete from mysql.db where user='plug_dest'"); this.stmt .executeUpdate("insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv,Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', '" + dbname + "', 'plug_dest', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); this.stmt .executeUpdate("insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv,Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', 'information\\_schema', 'plug_dest', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); this.stmt.executeUpdate("flush privileges"); props = new Properties(); props.setProperty("user", "wl5851user"); props.setProperty("password", "plug_dest"); props.setProperty("authenticationPlugins", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); Connection testConn = null; Statement testSt = null; ResultSet testRs = null; try { testConn = getConnectionWithProps(props); testSt = testConn.createStatement(); testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); testRs.next(); assertEquals("wl5851user", testRs.getString(1).split("@")[0]); assertEquals("plug_dest", testRs.getString(2).split("@")[0]); } finally { if (testRs != null) { testRs.close(); } if (testSt != null) { testSt.close(); } if (testConn != null) { testConn.close(); } } } finally { this.stmt.executeUpdate("drop user 'wl5851user'@'%'"); this.stmt.executeUpdate("drop user 'plug_dest'@'%'"); if (install_plugin_in_runtime) { this.stmt.executeUpdate("UNINSTALL PLUGIN test_plugin_server"); } } } } public void testTwoQuestionsPlugin() throws Exception { if (versionMeetsMinimum(5, 5, 7)) { boolean install_plugin_in_runtime = false; try { // install plugin if required this.rs = this.stmt.executeQuery("select (PLUGIN_LIBRARY LIKE 'two_questions%') as `TRUE`" + " FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='two_questions'"); if (this.rs.next()) { if (!this.rs.getBoolean(1)) { install_plugin_in_runtime = true; } } else { install_plugin_in_runtime = true; } if (install_plugin_in_runtime) { String ext = System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") > -1 ? ".dll" : ".so"; this.stmt.executeUpdate("INSTALL PLUGIN two_questions SONAME 'auth" + ext + "'"); } Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); if (dbname == null) { assertTrue("No database selected", false); } this.stmt.executeUpdate("grant usage on *.* to 'wl5851user2'@'%' identified WITH two_questions AS 'two_questions_password'"); this.stmt.executeUpdate("delete from mysql.db where user='wl5851user2'"); this.stmt .executeUpdate("insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv,Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', '" + dbname + "', 'wl5851user2', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); this.stmt .executeUpdate("insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv,Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', 'information\\_schema', 'wl5851user2', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); this.stmt.executeUpdate("flush privileges"); props = new Properties(); props.setProperty("user", "wl5851user2"); props.setProperty("password", "two_questions_password"); props.setProperty("authenticationPlugins", "testsuite.regression.ConnectionRegressionTest$TwoQuestionsPlugin"); Connection testConn = null; Statement testSt = null; ResultSet testRs = null; try { testConn = getConnectionWithProps(props); testSt = testConn.createStatement(); testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); testRs.next(); assertEquals("wl5851user2", testRs.getString(1).split("@")[0]); } finally { if (testRs != null) { testRs.close(); } if (testSt != null) { testSt.close(); } if (testConn != null) { testConn.close(); } } } finally { this.stmt.executeUpdate("drop user 'wl5851user2'@'%'"); if (install_plugin_in_runtime) { this.stmt.executeUpdate("UNINSTALL PLUGIN two_questions"); } } } } public void testThreeAttemptsPlugin() throws Exception { if (versionMeetsMinimum(5, 5, 7)) { boolean install_plugin_in_runtime = false; try { // install plugin if required this.rs = this.stmt.executeQuery("select (PLUGIN_LIBRARY LIKE 'three_attempts%') as `TRUE`" + " FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='three_attempts'"); if (this.rs.next()) { if (!this.rs.getBoolean(1)) { install_plugin_in_runtime = true; } } else { install_plugin_in_runtime = true; } if (install_plugin_in_runtime) { String ext = System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") > -1 ? ".dll" : ".so"; this.stmt.executeUpdate("INSTALL PLUGIN three_attempts SONAME 'auth" + ext + "'"); } Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); if (dbname == null) { assertTrue("No database selected", false); } this.stmt.executeUpdate("grant usage on *.* to 'wl5851user3'@'%' identified WITH three_attempts AS 'three_attempts_password'"); this.stmt.executeUpdate("delete from mysql.db where user='wl5851user3'"); this.stmt .executeUpdate("insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv,Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', '" + dbname + "', 'wl5851user3', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); this.stmt .executeUpdate("insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv,Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', 'information\\_schema', 'wl5851user3', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); this.stmt.executeUpdate("flush privileges"); props = new Properties(); props.setProperty("user", "wl5851user3"); props.setProperty("password", "three_attempts_password"); props.setProperty("authenticationPlugins", "testsuite.regression.ConnectionRegressionTest$ThreeAttemptsPlugin"); Connection testConn = null; Statement testSt = null; ResultSet testRs = null; try { testConn = getConnectionWithProps(props); testSt = testConn.createStatement(); testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); testRs.next(); assertEquals("wl5851user3", testRs.getString(1).split("@")[0]); } finally { if (testRs != null) { testRs.close(); } if (testSt != null) { testSt.close(); } if (testConn != null) { testConn.close(); } } } finally { this.stmt.executeUpdate("drop user 'wl5851user3'@'%'"); if (install_plugin_in_runtime) { this.stmt.executeUpdate("UNINSTALL PLUGIN three_attempts"); } } } } public static class AuthTestPlugin implements AuthenticationPlugin { private String password = null; public void init(com.mysql.jdbc.Connection conn1, Properties props) throws SQLException { } public void destroy() { this.password = null; } public String getProtocolPluginName() { return "auth_test_plugin"; } public boolean requiresConfidentiality() { return false; } public boolean isReusable() { return true; } public void setAuthenticationParameters(String user, String password) { this.password = password; } public boolean nextAuthenticationStep(Buffer fromServer, List<Buffer> toServer) throws SQLException { toServer.clear(); Buffer bresp = new Buffer(StringUtils.getBytes(this.password)); toServer.add(bresp); return true; } } public static class TwoQuestionsPlugin implements AuthenticationPlugin { private String password = null; public void init(com.mysql.jdbc.Connection conn1, Properties props) throws SQLException { } public void destroy() { this.password = null; } public String getProtocolPluginName() { return "dialog"; } public boolean requiresConfidentiality() { return false; } public boolean isReusable() { return true; } public void setAuthenticationParameters(String user, String password) { this.password = password; } public boolean nextAuthenticationStep(Buffer fromServer, List<Buffer> toServer) throws SQLException { toServer.clear(); if ((fromServer.getByteBuffer()[0] & 0xff) == 4) { Buffer bresp = new Buffer(StringUtils.getBytes(this.password)); toServer.add(bresp); } else { Buffer bresp = new Buffer(StringUtils.getBytes("yes, of course")); toServer.add(bresp); } return true; } } public static class ThreeAttemptsPlugin implements AuthenticationPlugin { private String password = null; private int counter = 0; public void init(com.mysql.jdbc.Connection conn1, Properties props) throws SQLException { this.counter = 0; } public void destroy() { this.password = null; this.counter = 0; } public String getProtocolPluginName() { return "dialog"; } public boolean requiresConfidentiality() { return false; } public boolean isReusable() { return true; } public void setAuthenticationParameters(String user, String password) { this.password = password; } public boolean nextAuthenticationStep(Buffer fromServer, List<Buffer> toServer) throws SQLException { toServer.clear(); this.counter++; if ((fromServer.getByteBuffer()[0] & 0xff) == 4) { Buffer bresp = new Buffer(StringUtils.getBytes(this.counter > 2 ? this.password : "wrongpassword" + this.counter)); toServer.add(bresp); } else { Buffer bresp = new Buffer(fromServer.getByteBuffer()); toServer.add(bresp); } return true; } } public void testOldPasswordPlugin() throws Exception { if (!versionMeetsMinimum(5, 5, 7) || versionMeetsMinimum(5, 7, 5)) { // As of 5.7.5, support for mysql_old_password is removed. System.out.println("testOldPasswordPlugin was skipped: This test is only run for 5.5.7 - 5.7.4 server versions."); return; } Connection testConn = null; try { this.stmt.executeUpdate("SET @current_secure_auth = @@global.secure_auth"); this.stmt.executeUpdate("SET GLOBAL secure_auth= off"); this.stmt.executeUpdate("CREATE USER 'bug64983user1'@'%' IDENTIFIED WITH mysql_old_password"); this.stmt.executeUpdate("set password for 'bug64983user1'@'%' = OLD_PASSWORD('pwd')"); this.stmt.executeUpdate("grant all on *.* to 'bug64983user1'@'%'"); this.stmt.executeUpdate("CREATE USER 'bug64983user2'@'%' IDENTIFIED WITH mysql_old_password"); this.stmt.executeUpdate("set password for 'bug64983user2'@'%' = OLD_PASSWORD('')"); this.stmt.executeUpdate("grant all on *.* to 'bug64983user2'@'%'"); this.stmt.executeUpdate("CREATE USER 'bug64983user3'@'%' IDENTIFIED WITH mysql_old_password"); this.stmt.executeUpdate("grant all on *.* to 'bug64983user3'@'%'"); this.stmt.executeUpdate("flush privileges"); Properties props = new Properties(); // connect with default plugin props.setProperty("user", "bug64983user1"); props.setProperty("password", "pwd"); testConn = getConnectionWithProps(props); ResultSet testRs = testConn.createStatement().executeQuery("select USER()"); testRs.next(); assertEquals("bug64983user1", testRs.getString(1).split("@")[0]); testConn.close(); props.setProperty("user", "bug64983user2"); props.setProperty("password", ""); testConn = getConnectionWithProps(props); testRs = testConn.createStatement().executeQuery("select USER()"); testRs.next(); assertEquals("bug64983user2", testRs.getString(1).split("@")[0]); testConn.close(); props.setProperty("user", "bug64983user3"); props.setProperty("password", ""); testConn = getConnectionWithProps(props); testRs = testConn.createStatement().executeQuery("select USER()"); testRs.next(); assertEquals("bug64983user3", testRs.getString(1).split("@")[0]); testConn.close(); // connect with MysqlOldPasswordPlugin plugin props.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.MysqlOldPasswordPlugin"); props.setProperty("user", "bug64983user1"); props.setProperty("password", "pwd"); testConn = getConnectionWithProps(props); testRs = testConn.createStatement().executeQuery("select USER()"); testRs.next(); assertEquals("bug64983user1", testRs.getString(1).split("@")[0]); testConn.close(); props.setProperty("user", "bug64983user2"); props.setProperty("password", ""); testConn = getConnectionWithProps(props); testRs = testConn.createStatement().executeQuery("select USER()"); testRs.next(); assertEquals("bug64983user2", testRs.getString(1).split("@")[0]); testConn.close(); props.setProperty("user", "bug64983user3"); props.setProperty("password", ""); testConn = getConnectionWithProps(props); testRs = testConn.createStatement().executeQuery("select USER()"); testRs.next(); assertEquals("bug64983user3", testRs.getString(1).split("@")[0]); // changeUser ((MySQLConnection) testConn).changeUser("bug64983user1", "pwd"); testRs = testConn.createStatement().executeQuery("select USER()"); testRs.next(); assertEquals("bug64983user1", testRs.getString(1).split("@")[0]); ((MySQLConnection) testConn).changeUser("bug64983user2", ""); testRs = testConn.createStatement().executeQuery("select USER()"); testRs.next(); assertEquals("bug64983user2", testRs.getString(1).split("@")[0]); ((MySQLConnection) testConn).changeUser("bug64983user3", ""); testRs = testConn.createStatement().executeQuery("select USER()"); testRs.next(); assertEquals("bug64983user3", testRs.getString(1).split("@")[0]); } finally { try { this.stmt.executeUpdate("SET GLOBAL secure_auth = @current_secure_auth"); this.stmt.executeUpdate("drop user 'bug64983user1'@'%'"); this.stmt.executeUpdate("drop user 'bug64983user2'@'%'"); this.stmt.executeUpdate("drop user 'bug64983user3'@'%'"); if (testConn != null) { testConn.close(); } } catch (Exception ex) { System.err.println("Exception during cleanup:"); ex.printStackTrace(); } } } public void testAuthCleartextPlugin() throws Exception { if (versionMeetsMinimum(5, 5, 7)) { boolean install_plugin_in_runtime = false; try { // install plugin if required this.rs = this.stmt.executeQuery("select (PLUGIN_LIBRARY LIKE 'auth_test_plugin%') as `TRUE`" + " FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='cleartext_plugin_server'"); if (this.rs.next()) { if (!this.rs.getBoolean(1)) { install_plugin_in_runtime = true; } } else { install_plugin_in_runtime = true; } if (install_plugin_in_runtime) { String ext = System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") > -1 ? ".dll" : ".so"; this.stmt.executeUpdate("INSTALL PLUGIN cleartext_plugin_server SONAME 'auth_test_plugin" + ext + "'"); } Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); if (dbname == null) { assertTrue("No database selected", false); } // create proxy users this.stmt.executeUpdate("grant usage on *.* to 'wl5735user'@'%' identified WITH cleartext_plugin_server AS ''"); this.stmt.executeUpdate("delete from mysql.db where user='wl5735user'"); this.stmt.executeUpdate("insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, " + "Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv," + "Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', '" + dbname + "', 'wl5735user', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); this.stmt.executeUpdate("insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, " + "Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv," + "Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES " + "('%', 'information\\_schema', 'wl5735user', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', " + "'Y', 'Y', 'N', 'N')"); this.stmt.executeUpdate("flush privileges"); props = new Properties(); props.setProperty("user", "wl5735user"); props.setProperty("password", ""); Connection testConn = null; Statement testSt = null; ResultSet testRs = null; try { testConn = getConnectionWithProps(props); fail("SQLException expected due to SSL connection is required"); } catch (Exception e) { assertEquals("SSL connection required for plugin 'mysql_clear_password'. Check if \"useSSL\" is set to \"true\".", e.getMessage()); } finally { if (testConn != null) { testConn.close(); } } try { String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; System.setProperty("javax.net.ssl.keyStore", trustStorePath); System.setProperty("javax.net.ssl.keyStorePassword", "password"); System.setProperty("javax.net.ssl.trustStore", trustStorePath); System.setProperty("javax.net.ssl.trustStorePassword", "password"); props.setProperty("useSSL", "true"); if (Util.getJVMVersion() < 8 && versionMeetsMinimum(5, 7, 6) && isCommunityEdition()) { props.setProperty("enabledSSLCipherSuites", SSL_CIPHERS_FOR_576); } testConn = getConnectionWithProps(props); assertTrue("SSL connection isn't actually established!", ((MySQLConnection) testConn).getIO().isSSLEstablished()); testSt = testConn.createStatement(); testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); testRs.next(); assertEquals("wl5735user", testRs.getString(1).split("@")[0]); assertEquals("wl5735user", testRs.getString(2).split("@")[0]); } finally { if (testRs != null) { testRs.close(); } if (testSt != null) { testSt.close(); } if (testConn != null) { testConn.close(); } } } finally { this.stmt.executeUpdate("drop user 'wl5735user'@'%'"); if (install_plugin_in_runtime) { this.stmt.executeUpdate("UNINSTALL PLUGIN cleartext_plugin_server"); } } } } /** * This test requires two server instances: * 1) main test server pointed by com.mysql.jdbc.testsuite.url variable * configured without RSA encryption support * 2) additional server instance pointed by com.mysql.jdbc.testsuite.url.sha256default * variable configured with default-authentication-plugin=sha256_password * and RSA encryption enabled. * * To run this test please add this variable to ant call: * -Dcom.mysql.jdbc.testsuite.url.sha256default=jdbc:mysql://localhost:3307/test?user=root&password=pwd * * @throws Exception */ public void testSha256PasswordPlugin() throws Exception { String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; System.setProperty("javax.net.ssl.keyStore", trustStorePath); System.setProperty("javax.net.ssl.keyStorePassword", "password"); System.setProperty("javax.net.ssl.trustStore", trustStorePath); System.setProperty("javax.net.ssl.trustStorePassword", "password"); /* * test against server without RSA support */ if (versionMeetsMinimum(5, 6, 5)) { if (!pluginIsActive(this.stmt, "sha256_password")) { fail("sha256_password required to run this test"); } if (allowsRsa(this.stmt)) { fail("RSA encryption must be disabled on " + System.getProperty("com.mysql.jdbc.testsuite.url") + " to run this test"); } try { this.stmt.executeUpdate("SET @current_old_passwords = @@global.old_passwords"); this.stmt.executeUpdate("grant all on *.* to 'wl5602user'@'%' identified WITH sha256_password"); this.stmt.executeUpdate("grant all on *.* to 'wl5602nopassword'@'%' identified WITH sha256_password"); this.stmt.executeUpdate("SET GLOBAL old_passwords= 2"); this.stmt.executeUpdate("SET SESSION old_passwords= 2"); this.stmt.executeUpdate(versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'wl5602user'@'%' IDENTIFIED BY 'pwd'" : "set password for 'wl5602user'@'%' = PASSWORD('pwd')"); this.stmt.executeUpdate("flush privileges"); final Properties propsNoRetrieval = new Properties(); propsNoRetrieval.setProperty("user", "wl5602user"); propsNoRetrieval.setProperty("password", "pwd"); final Properties propsNoRetrievalNoPassword = new Properties(); propsNoRetrievalNoPassword.setProperty("user", "wl5602nopassword"); propsNoRetrievalNoPassword.setProperty("password", ""); final Properties propsAllowRetrieval = new Properties(); propsAllowRetrieval.setProperty("user", "wl5602user"); propsAllowRetrieval.setProperty("password", "pwd"); propsAllowRetrieval.setProperty("allowPublicKeyRetrieval", "true"); final Properties propsAllowRetrievalNoPassword = new Properties(); propsAllowRetrievalNoPassword.setProperty("user", "wl5602nopassword"); propsAllowRetrievalNoPassword.setProperty("password", ""); propsAllowRetrievalNoPassword.setProperty("allowPublicKeyRetrieval", "true"); // 1. without SSL // SQLException expected due to server doesn't recognize Public Key Retrieval packet assertThrows(SQLException.class, "Public Key Retrieval is not allowed", new Callable<Void>() { public Void call() throws Exception { getConnectionWithProps(propsNoRetrieval); return null; } }); assertThrows(SQLException.class, "Access denied for user 'wl5602user'.*", new Callable<Void>() { public Void call() throws Exception { getConnectionWithProps(propsAllowRetrieval); return null; } }); assertCurrentUser(null, propsNoRetrievalNoPassword, "wl5602nopassword", false); assertCurrentUser(null, propsAllowRetrievalNoPassword, "wl5602nopassword", false); // 2. with serverRSAPublicKeyFile specified // SQLException expected due to server doesn't recognize RSA encrypted payload propsNoRetrieval.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); propsNoRetrievalNoPassword.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); propsAllowRetrieval.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); propsAllowRetrievalNoPassword.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); assertThrows(SQLException.class, "Access denied for user 'wl5602user'.*", new Callable<Void>() { public Void call() throws Exception { getConnectionWithProps(propsNoRetrieval); return null; } }); assertThrows(SQLException.class, "Access denied for user 'wl5602user'.*", new Callable<Void>() { public Void call() throws Exception { getConnectionWithProps(propsAllowRetrieval); return null; } }); assertCurrentUser(null, propsNoRetrievalNoPassword, "wl5602nopassword", false); assertCurrentUser(null, propsAllowRetrievalNoPassword, "wl5602nopassword", false); // 3. over SSL propsNoRetrieval.setProperty("useSSL", "true"); propsNoRetrievalNoPassword.setProperty("useSSL", "true"); propsAllowRetrieval.setProperty("useSSL", "true"); propsAllowRetrievalNoPassword.setProperty("useSSL", "true"); if (Util.getJVMVersion() < 8 && versionMeetsMinimum(5, 7, 6) && isCommunityEdition()) { propsNoRetrieval.setProperty("enabledSSLCipherSuites", SSL_CIPHERS_FOR_576); propsNoRetrievalNoPassword.setProperty("enabledSSLCipherSuites", SSL_CIPHERS_FOR_576); propsAllowRetrieval.setProperty("enabledSSLCipherSuites", SSL_CIPHERS_FOR_576); propsAllowRetrievalNoPassword.setProperty("enabledSSLCipherSuites", SSL_CIPHERS_FOR_576); } assertCurrentUser(null, propsNoRetrieval, "wl5602user", true); assertCurrentUser(null, propsNoRetrievalNoPassword, "wl5602nopassword", false); assertCurrentUser(null, propsAllowRetrieval, "wl5602user", true); assertCurrentUser(null, propsAllowRetrievalNoPassword, "wl5602nopassword", false); // over SSL with client-default Sha256PasswordPlugin propsNoRetrieval.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); propsNoRetrievalNoPassword.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); propsAllowRetrieval.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); propsAllowRetrievalNoPassword.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); assertCurrentUser(null, propsNoRetrieval, "wl5602user", true); assertCurrentUser(null, propsNoRetrievalNoPassword, "wl5602nopassword", false); assertCurrentUser(null, propsAllowRetrieval, "wl5602user", true); assertCurrentUser(null, propsAllowRetrievalNoPassword, "wl5602nopassword", false); } finally { this.stmt.executeUpdate("drop user 'wl5602user'@'%'"); this.stmt.executeUpdate("drop user 'wl5602nopassword'@'%'"); this.stmt.executeUpdate("flush privileges"); this.stmt.executeUpdate("SET GLOBAL old_passwords = @current_old_passwords"); } } /* * test against server with RSA support */ final String sha256defaultDbUrl = System.getProperty("com.mysql.jdbc.testsuite.url.sha256default"); if (sha256defaultDbUrl != null) { Properties props = new Properties(); props.setProperty("allowPublicKeyRetrieval", "true"); Connection c1 = getConnectionWithProps(sha256defaultDbUrl, props); Statement s1 = c1.createStatement(); if (!pluginIsActive(s1, "sha256_password")) { fail("sha256_password required to run this test"); } if (!allowsRsa(s1)) { fail("RSA encryption must be enabled on " + sha256defaultDbUrl + " to run this test"); } try { // create user with long password and sha256_password auth s1.executeUpdate("SET @current_old_passwords = @@global.old_passwords"); s1.executeUpdate("grant all on *.* to 'wl5602user'@'%' identified WITH sha256_password"); s1.executeUpdate("grant all on *.* to 'wl5602nopassword'@'%' identified WITH sha256_password"); s1.executeUpdate("SET GLOBAL old_passwords= 2"); s1.executeUpdate("SET SESSION old_passwords= 2"); s1.executeUpdate(((MySQLConnection) c1).versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'wl5602user'@'%' IDENTIFIED BY 'pwd'" : "set password for 'wl5602user'@'%' = PASSWORD('pwd')"); s1.executeUpdate("flush privileges"); final Properties propsNoRetrieval = new Properties(); propsNoRetrieval.setProperty("user", "wl5602user"); propsNoRetrieval.setProperty("password", "pwd"); final Properties propsNoRetrievalNoPassword = new Properties(); propsNoRetrievalNoPassword.setProperty("user", "wl5602nopassword"); propsNoRetrievalNoPassword.setProperty("password", ""); final Properties propsAllowRetrieval = new Properties(); propsAllowRetrieval.setProperty("user", "wl5602user"); propsAllowRetrieval.setProperty("password", "pwd"); propsAllowRetrieval.setProperty("allowPublicKeyRetrieval", "true"); final Properties propsAllowRetrievalNoPassword = new Properties(); propsAllowRetrievalNoPassword.setProperty("user", "wl5602nopassword"); propsAllowRetrievalNoPassword.setProperty("password", ""); propsAllowRetrievalNoPassword.setProperty("allowPublicKeyRetrieval", "true"); // 1. with client-default MysqlNativePasswordPlugin propsNoRetrieval.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.MysqlNativePasswordPlugin"); propsAllowRetrieval.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.MysqlNativePasswordPlugin"); // 1.1. RSA propsNoRetrieval.setProperty("useSSL", "false"); propsAllowRetrieval.setProperty("useSSL", "false"); assertThrows(SQLException.class, "Public Key Retrieval is not allowed", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsNoRetrieval); return null; } }); assertCurrentUser(sha256defaultDbUrl, propsNoRetrievalNoPassword, "wl5602nopassword", false); assertCurrentUser(sha256defaultDbUrl, propsAllowRetrieval, "wl5602user", false); assertCurrentUser(sha256defaultDbUrl, propsAllowRetrievalNoPassword, "wl5602nopassword", false); // 1.2. over SSL propsNoRetrieval.setProperty("useSSL", "true"); propsNoRetrievalNoPassword.setProperty("useSSL", "true"); propsAllowRetrieval.setProperty("useSSL", "true"); propsAllowRetrievalNoPassword.setProperty("useSSL", "true"); assertCurrentUser(sha256defaultDbUrl, propsNoRetrieval, "wl5602user", true); assertCurrentUser(sha256defaultDbUrl, propsNoRetrievalNoPassword, "wl5602nopassword", false); assertCurrentUser(sha256defaultDbUrl, propsAllowRetrieval, "wl5602user", true); assertCurrentUser(sha256defaultDbUrl, propsAllowRetrievalNoPassword, "wl5602nopassword", false); // 2. with client-default Sha256PasswordPlugin propsNoRetrieval.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); propsNoRetrievalNoPassword.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); propsAllowRetrieval.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); propsAllowRetrievalNoPassword.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); // 2.1. RSA propsNoRetrieval.setProperty("useSSL", "false"); propsNoRetrievalNoPassword.setProperty("useSSL", "false"); propsAllowRetrieval.setProperty("useSSL", "false"); propsAllowRetrievalNoPassword.setProperty("useSSL", "false"); assertThrows(SQLException.class, "Public Key Retrieval is not allowed", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsNoRetrieval); return null; } }); assertCurrentUser(sha256defaultDbUrl, propsNoRetrievalNoPassword, "wl5602nopassword", false); assertCurrentUser(sha256defaultDbUrl, propsAllowRetrieval, "wl5602user", false); assertCurrentUser(sha256defaultDbUrl, propsAllowRetrievalNoPassword, "wl5602nopassword", false); // 2.2. over SSL propsNoRetrieval.setProperty("useSSL", "true"); propsNoRetrievalNoPassword.setProperty("useSSL", "true"); propsAllowRetrieval.setProperty("useSSL", "true"); propsAllowRetrievalNoPassword.setProperty("useSSL", "true"); assertCurrentUser(sha256defaultDbUrl, propsNoRetrieval, "wl5602user", true); assertCurrentUser(sha256defaultDbUrl, propsNoRetrievalNoPassword, "wl5602nopassword", false); assertCurrentUser(sha256defaultDbUrl, propsAllowRetrieval, "wl5602user", false); assertCurrentUser(sha256defaultDbUrl, propsAllowRetrievalNoPassword, "wl5602nopassword", false); // 3. with serverRSAPublicKeyFile specified propsNoRetrieval.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); propsNoRetrievalNoPassword.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); propsAllowRetrieval.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); propsAllowRetrievalNoPassword.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); // 3.1. RSA propsNoRetrieval.setProperty("useSSL", "false"); propsNoRetrievalNoPassword.setProperty("useSSL", "false"); propsAllowRetrieval.setProperty("useSSL", "false"); propsAllowRetrievalNoPassword.setProperty("useSSL", "false"); assertCurrentUser(sha256defaultDbUrl, propsNoRetrieval, "wl5602user", false); assertCurrentUser(sha256defaultDbUrl, propsNoRetrievalNoPassword, "wl5602nopassword", false); assertCurrentUser(sha256defaultDbUrl, propsAllowRetrieval, "wl5602user", false); assertCurrentUser(sha256defaultDbUrl, propsAllowRetrievalNoPassword, "wl5602nopassword", false); // 3.2. Runtime setServerRSAPublicKeyFile must be denied final Connection c2 = getConnectionWithProps(sha256defaultDbUrl, propsNoRetrieval); assertThrows(SQLException.class, "Dynamic change of ''serverRSAPublicKeyFile'' is not allowed.", new Callable<Void>() { public Void call() throws Exception { ((ConnectionProperties) c2).setServerRSAPublicKeyFile("src/testsuite/ssl-test-certs/mykey.pub"); return null; } }); c2.close(); // 3.3. Runtime setAllowPublicKeyRetrieval must be denied final Connection c3 = getConnectionWithProps(sha256defaultDbUrl, propsNoRetrieval); assertThrows(SQLException.class, "Dynamic change of ''allowPublicKeyRetrieval'' is not allowed.", new Callable<Void>() { public Void call() throws Exception { ((ConnectionProperties) c3).setAllowPublicKeyRetrieval(true); return null; } }); c3.close(); // 3.4. over SSL propsNoRetrieval.setProperty("useSSL", "true"); propsNoRetrievalNoPassword.setProperty("useSSL", "true"); propsAllowRetrieval.setProperty("useSSL", "true"); propsAllowRetrievalNoPassword.setProperty("useSSL", "true"); assertCurrentUser(sha256defaultDbUrl, propsNoRetrieval, "wl5602user", true); assertCurrentUser(sha256defaultDbUrl, propsNoRetrievalNoPassword, "wl5602nopassword", false); assertCurrentUser(sha256defaultDbUrl, propsAllowRetrieval, "wl5602user", true); assertCurrentUser(sha256defaultDbUrl, propsAllowRetrievalNoPassword, "wl5602nopassword", false); // 4. with wrong serverRSAPublicKeyFile specified propsNoRetrieval.setProperty("serverRSAPublicKeyFile", "unexistant/dummy.pub"); propsNoRetrievalNoPassword.setProperty("serverRSAPublicKeyFile", "unexistant/dummy.pub"); propsAllowRetrieval.setProperty("serverRSAPublicKeyFile", "unexistant/dummy.pub"); propsAllowRetrievalNoPassword.setProperty("serverRSAPublicKeyFile", "unexistant/dummy.pub"); // 4.1. RSA propsNoRetrieval.setProperty("useSSL", "false"); propsNoRetrievalNoPassword.setProperty("useSSL", "false"); propsAllowRetrieval.setProperty("useSSL", "false"); propsAllowRetrievalNoPassword.setProperty("useSSL", "false"); propsNoRetrieval.setProperty("paranoid", "false"); propsNoRetrievalNoPassword.setProperty("paranoid", "false"); propsAllowRetrieval.setProperty("paranoid", "false"); propsAllowRetrievalNoPassword.setProperty("paranoid", "false"); assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsNoRetrieval); return null; } }); assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsNoRetrievalNoPassword); return null; } }); assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsAllowRetrieval); return null; } }); assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsAllowRetrievalNoPassword); return null; } }); propsNoRetrieval.setProperty("paranoid", "true"); propsNoRetrievalNoPassword.setProperty("paranoid", "true"); propsAllowRetrieval.setProperty("paranoid", "true"); propsAllowRetrievalNoPassword.setProperty("paranoid", "true"); assertThrows(SQLException.class, "Unable to read public key ", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsNoRetrieval); return null; } }); assertThrows(SQLException.class, "Unable to read public key ", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsNoRetrievalNoPassword); return null; } }); assertThrows(SQLException.class, "Unable to read public key ", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsAllowRetrieval); return null; } }); assertThrows(SQLException.class, "Unable to read public key ", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsAllowRetrievalNoPassword); return null; } }); // 4.2. over SSL propsNoRetrieval.setProperty("useSSL", "true"); propsNoRetrievalNoPassword.setProperty("useSSL", "true"); propsAllowRetrieval.setProperty("useSSL", "true"); propsAllowRetrievalNoPassword.setProperty("useSSL", "true"); propsNoRetrieval.setProperty("paranoid", "false"); propsNoRetrievalNoPassword.setProperty("paranoid", "false"); propsAllowRetrieval.setProperty("paranoid", "false"); propsAllowRetrievalNoPassword.setProperty("paranoid", "false"); assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsNoRetrieval); return null; } }); assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsNoRetrievalNoPassword); return null; } }); assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsAllowRetrieval); return null; } }); assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsAllowRetrievalNoPassword); return null; } }); propsNoRetrieval.setProperty("paranoid", "true"); propsNoRetrievalNoPassword.setProperty("paranoid", "true"); propsAllowRetrieval.setProperty("paranoid", "true"); propsAllowRetrievalNoPassword.setProperty("paranoid", "true"); assertThrows(SQLException.class, "Unable to read public key ", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsNoRetrieval); return null; } }); assertThrows(SQLException.class, "Unable to read public key ", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsNoRetrievalNoPassword); return null; } }); assertThrows(SQLException.class, "Unable to read public key ", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsAllowRetrieval); return null; } }); assertThrows(SQLException.class, "Unable to read public key ", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(sha256defaultDbUrl, propsAllowRetrievalNoPassword); return null; } }); } finally { if (c1 != null) { if (s1 != null) { s1.executeUpdate("drop user 'wl5602user'@'%'"); s1.executeUpdate("drop user 'wl5602nopassword'@'%'"); s1.executeUpdate("flush privileges"); s1.executeUpdate("SET GLOBAL old_passwords = @current_old_passwords"); s1.close(); } c1.close(); } } } } private void assertCurrentUser(String url, Properties props, String expectedUser, boolean sslRequired) throws SQLException { Connection connection = url == null ? getConnectionWithProps(props) : getConnectionWithProps(url, props); if (sslRequired) { assertTrue("SSL connection isn't actually established!", ((MySQLConnection) connection).getIO().isSSLEstablished()); } Statement st = connection.createStatement(); ResultSet rset = st.executeQuery("select USER(),CURRENT_USER()"); rset.next(); assertEquals(expectedUser, rset.getString(1).split("@")[0]); assertEquals(expectedUser, rset.getString(2).split("@")[0]); connection.close(); } private boolean pluginIsActive(Statement st, String plugin) throws SQLException { ResultSet rset = st.executeQuery("select (PLUGIN_STATUS='ACTIVE') as `TRUE` from INFORMATION_SCHEMA.PLUGINS where PLUGIN_NAME='" + plugin + "'"); boolean pluginIsActive = false; if (rset.next()) { pluginIsActive = rset.getBoolean(1); } return pluginIsActive; } private boolean allowsRsa(Statement st) throws SQLException { boolean allowsRSA = false; ResultSet rset = st.executeQuery("SHOW STATUS LIKE 'Rsa_public_key'"); if (rset.next()) { String key = rset.getString(2); if (key != null) { String value = rset.getString(2); allowsRSA = (value != null && value.length() > 0); } } return allowsRSA; } public void testBug36662() throws Exception { try { String tz1 = TimeUtil.getCanonicalTimezone("MEST", null); assertNotNull(tz1); } catch (Exception e1) { String mes1 = e1.getMessage(); mes1 = mes1.substring(mes1.lastIndexOf("The timezones that 'MEST' maps to are:") + 39); try { String tz2 = TimeUtil.getCanonicalTimezone("CEST", null); assertEquals(mes1, tz2); } catch (Exception e2) { String mes2 = e2.getMessage(); mes2 = mes2.substring(mes2.lastIndexOf("The timezones that 'CEST' maps to are:") + 39); assertEquals(mes1, mes2); } } } public void testBug37931() throws Exception { Connection _conn = null; Properties props = new Properties(); props.setProperty("characterSetResults", "ISO88591"); try { _conn = getConnectionWithProps(props); assertTrue("This point should not be reached.", false); } catch (Exception e) { assertEquals("Can't map ISO88591 given for characterSetResults to a supported MySQL encoding.", e.getMessage()); } finally { if (_conn != null) { _conn.close(); } } props.setProperty("characterSetResults", "null"); try { _conn = getConnectionWithProps(props); Statement _stmt = _conn.createStatement(); ResultSet _rs = _stmt.executeQuery("show variables where variable_name='character_set_results'"); if (_rs.next()) { String res = _rs.getString(2); if (res == null || "NULL".equalsIgnoreCase(res) || res.length() == 0) { assertTrue(true); } else { assertTrue(false); } } } finally { if (_conn != null) { _conn.close(); } } } public void testBug64205() throws Exception { if (versionMeetsMinimum(5, 5, 0)) { Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); if (dbname == null) { assertTrue("No database selected", false); } props = new Properties(); props.setProperty("characterEncoding", "EUC_JP"); Connection testConn = null; Statement testSt = null; ResultSet testRs = null; try { testConn = getConnectionWithProps(props); testSt = testConn.createStatement(); testRs = testSt.executeQuery("SELECT * FROM `" + dbname + "`.`\u307b\u3052\u307b\u3052`"); } catch (SQLException e1) { if (e1.getClass().getName().endsWith("MySQLSyntaxErrorException")) { assertEquals("Table '" + dbname + ".\u307B\u3052\u307b\u3052' doesn't exist", e1.getMessage()); } else if (e1.getErrorCode() == MysqlErrorNumbers.ER_FILE_NOT_FOUND) { // this could happen on Windows with 5.5 and 5.6 servers where BUG#14642248 exists assertTrue(e1.getMessage().contains("Can't find file")); } else { throw e1; } try { props.setProperty("characterSetResults", "SJIS"); testConn = getConnectionWithProps(props); testSt = testConn.createStatement(); testSt.execute("SET lc_messages = 'ru_RU'"); testRs = testSt.executeQuery("SELECT * FROM `" + dbname + "`.`\u307b\u3052\u307b\u3052`"); } catch (SQLException e2) { if (e2.getClass().getName().endsWith("MySQLSyntaxErrorException")) { assertEquals("\u0422\u0430\u0431\u043b\u0438\u0446\u0430 '" + dbname + ".\u307b\u3052\u307b\u3052' \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442", e2.getMessage()); } else if (e2.getErrorCode() == MysqlErrorNumbers.ER_FILE_NOT_FOUND) { // this could happen on Windows with 5.5 and 5.6 servers where BUG#14642248 exists assertTrue("File not found error message should be russian but is this one: " + e2.getMessage(), e2.getMessage().indexOf("\u0444\u0430\u0439\u043b") > -1); } else { throw e2; } } } finally { if (testRs != null) { testRs.close(); } if (testSt != null) { testSt.close(); } if (testConn != null) { testConn.close(); } } // also test with explicit characterSetResults and cacheServerConfiguration try { props.setProperty("characterSetResults", "EUC_JP"); props.setProperty("cacheServerConfiguration", "true"); testConn = getConnectionWithProps(props); testSt = testConn.createStatement(); testRs = testSt.executeQuery("SELECT * FROM `" + dbname + "`.`\u307b\u3052\u307b\u3052`"); fail("Exception should be thrown for attemping to query non-existing table"); } catch (SQLException e1) { if (e1.getClass().getName().endsWith("MySQLSyntaxErrorException")) { assertEquals("Table '" + dbname + ".\u307B\u3052\u307b\u3052' doesn't exist", e1.getMessage()); } else if (e1.getErrorCode() == MysqlErrorNumbers.ER_FILE_NOT_FOUND) { // this could happen on Windows with 5.5 and 5.6 servers where BUG#14642248 exists assertTrue(e1.getMessage().contains("Can't find file")); } else { throw e1; } } finally { testConn.close(); } // Error messages may also be received after the handshake but before connection initialization is complete. This tests the interpretation of // errors thrown during this time window using an invalid session variable try { props.setProperty("characterSetResults", "EUC_JP"); props.setProperty("sessionVariables", "lc_messages=ru_RU,invalidVar=1"); testConn = getConnectionWithProps(props); fail("Exception should be thrown for attempting to set an unknown system variable"); } catch (SQLException e1) { // The Russian version of this error message is 45 characters long. A mis-interpretation, e.g. decoding as latin1, would return a length of 75 assertEquals(45, e1.getMessage().length()); } finally { testConn.close(); } } } public void testIsLocal() throws Exception { boolean normalState = ((ConnectionImpl) this.conn).isServerLocal(); if (normalState) { boolean isNotLocal = ((ConnectionImpl) getConnectionWithProps(StandardSocketFactory.IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME + "=www.oracle.com:3306")).isServerLocal(); assertFalse(isNotLocal == normalState); } } /** * Tests fix for BUG#57662, Incorrect Query Duration When useNanosForElapsedTime Enabled * * @throws Exception * if the test fails. */ public void testBug57662() throws Exception { createTable("testBug57662", "(x VARCHAR(10) NOT NULL DEFAULT '')"); Connection conn_is = null; try { Properties props = new Properties(); props.setProperty("profileSQL", "true"); props.setProperty("useNanosForElapsedTime", "true"); props.setProperty("logger", "testsuite.simple.TestBug57662Logger"); conn_is = getConnectionWithProps(props); this.rs = conn_is.getMetaData().getColumns(null, null, "testBug57662", "%"); assertFalse(((testsuite.simple.TestBug57662Logger) ((ConnectionImpl) conn_is).getLog()).hasNegativeDurations); } finally { if (conn_is != null) { conn_is.close(); } } } public void testBug14563127() throws Exception { Properties props = new Properties(); props.setProperty("loadBalanceStrategy", ForcedLoadBalanceStrategy.class.getName()); props.setProperty("loadBalanceBlacklistTimeout", "5000"); props.setProperty("loadBalancePingTimeout", "100"); props.setProperty("loadBalanceValidateConnectionOnSwapServer", "true"); String portNumber = new NonRegisteringDriver().parseURL(dbUrl, null).getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); if (portNumber == null) { portNumber = "3306"; } ForcedLoadBalanceStrategy.forceFutureServer("first:" + portNumber, -1); Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); conn2.setAutoCommit(false); conn2.createStatement().execute("SELECT 1"); // make sure second is added to active connections cache: ForcedLoadBalanceStrategy.forceFutureServer("second:" + portNumber, -1); conn2.commit(); // switch back to first: ForcedLoadBalanceStrategy.forceFutureServer("first:" + portNumber, -1); conn2.commit(); // kill second while still in cache: UnreliableSocketFactory.downHost("second"); // force second host to be selected next time: ForcedLoadBalanceStrategy.forceFutureServer("second:" + portNumber, 1); try { conn2.commit(); // will be on second after this assertTrue("Connection should not be closed", !conn2.isClosed()); } catch (SQLException e) { fail("Should not error because failure to select another server."); } conn2.close(); } /** * Tests fix for BUG#11237 useCompression=true and LOAD DATA LOCAL INFILE SQL Command * * @throws Exception * if any errors occur */ public void testBug11237() throws Exception { this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'max_allowed_packet'"); this.rs.next(); if (this.rs.getInt(2) < 4 + 1024 * 1024 * 16 - 1) { fail("You need to increase max_allowed_packet to at least " + (4 + 1024 * 1024 * 16 - 1) + " before running this test!"); } int requiredSize = 1024 * 1024 * 300; int fieldLength = 1023; int loops = requiredSize / 2 / (fieldLength + 1); File testFile = File.createTempFile("cj-testloaddata", ".dat"); testFile.deleteOnExit(); // TODO: following cleanup doesn't work correctly during concurrent execution of testsuite // cleanupTempFiles(testFile, "cj-testloaddata"); BufferedOutputStream bOut = new BufferedOutputStream(new FileOutputStream(testFile)); for (int i = 0; i < loops; i++) { for (int j = 0; j < fieldLength; j++) { bOut.write("a".getBytes()[0]); } bOut.write("\t".getBytes()[0]); for (int j = 0; j < fieldLength; j++) { bOut.write("b".getBytes()[0]); } bOut.write("\n".getBytes()[0]); } bOut.flush(); bOut.close(); createTable("testBug11237", "(field1 VARCHAR(1024), field2 VARCHAR(1024))"); StringBuilder fileNameBuf = null; if (File.separatorChar == '\\') { fileNameBuf = new StringBuilder(); String fileName = testFile.getAbsolutePath(); int fileNameLength = fileName.length(); for (int i = 0; i < fileNameLength; i++) { char c = fileName.charAt(i); if (c == '\\') { fileNameBuf.append("/"); } else { fileNameBuf.append(c); } } } else { fileNameBuf = new StringBuilder(testFile.getAbsolutePath()); } Properties props = new Properties(); props.put("useCompression", "true"); Connection conn1 = getConnectionWithProps(props); Statement stmt1 = conn1.createStatement(); int updateCount = stmt1.executeUpdate("LOAD DATA LOCAL INFILE '" + fileNameBuf.toString() + "' INTO TABLE testBug11237 CHARACTER SET " + CharsetMapping.getMysqlCharsetForJavaEncoding(((MySQLConnection) this.conn).getEncoding(), (com.mysql.jdbc.Connection) conn1)); assertTrue(updateCount == loops); } public void testStackOverflowOnMissingInterceptor() throws Exception { try { Properties props = new Properties(); props.setProperty("statementInterceptors", "fooBarBaz"); getConnectionWithProps(props).close(); } catch (Exception e) { } } public void testExpiredPassword() throws Exception { if (versionMeetsMinimum(5, 6, 10)) { Connection testConn = null; Statement testSt = null; ResultSet testRs = null; Properties urlProps = new NonRegisteringDriver().parseURL(dbUrl, null); String dbname = urlProps.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); try { this.stmt.executeUpdate("grant all on `" + dbname + "`.* to 'must_change1'@'%' IDENTIFIED BY 'aha'"); this.stmt.executeUpdate("grant all on `" + dbname + "`.* to 'must_change2'@'%' IDENTIFIED BY 'aha'"); this.stmt.executeUpdate(versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'must_change1'@'%', 'must_change2'@'%' PASSWORD EXPIRE" : "ALTER USER 'must_change1'@'%' PASSWORD EXPIRE, 'must_change2'@'%' PASSWORD EXPIRE"); Properties props = new Properties(); // ALTER USER can be prepared as of 5.6.8 (BUG#14646014) if (versionMeetsMinimum(5, 6, 8)) { props.setProperty("useServerPrepStmts", "true"); testConn = getConnectionWithProps(props); this.pstmt = testConn.prepareStatement(versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'must_change1'@'%', 'must_change2'@'%' PASSWORD EXPIRE" : "ALTER USER 'must_change1'@'%' PASSWORD EXPIRE, 'must_change2'@'%' PASSWORD EXPIRE"); this.pstmt.executeUpdate(); this.pstmt.close(); this.pstmt = testConn.prepareStatement(versionMeetsMinimum(5, 7, 6) ? "ALTER USER ?, 'must_change2'@'%' PASSWORD EXPIRE" : "ALTER USER ? PASSWORD EXPIRE, 'must_change2'@'%' PASSWORD EXPIRE"); this.pstmt.setString(1, "must_change1"); this.pstmt.executeUpdate(); this.pstmt.close(); testConn.close(); } props.setProperty("user", "must_change1"); props.setProperty("password", "aha"); try { testConn = getConnectionWithProps(props); fail("SQLException expected due to password expired"); } catch (SQLException e1) { if (e1.getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || e1.getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN) { props.setProperty("disconnectOnExpiredPasswords", "false"); try { testConn = getConnectionWithProps(props); testSt = testConn.createStatement(); testRs = testSt.executeQuery("SHOW VARIABLES LIKE 'disconnect_on_expired_password'"); fail("SQLException expected due to password expired"); } catch (SQLException e3) { if (e3.getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN) { testConn = getConnectionWithProps(props); testSt = testConn.createStatement(); } testSt.executeUpdate(versionMeetsMinimum(5, 7, 6) ? "ALTER USER USER() IDENTIFIED BY 'newpwd'" : "SET PASSWORD = PASSWORD('newpwd')"); testConn.close(); props.setProperty("user", "must_change1"); props.setProperty("password", "newpwd"); props.setProperty("disconnectOnExpiredPasswords", "true"); testConn = getConnectionWithProps(props); testSt = testConn.createStatement(); testRs = testSt.executeQuery("SHOW VARIABLES LIKE 'disconnect_on_expired_password'"); assertTrue(testRs.next()); // change user try { ((MySQLConnection) testConn).changeUser("must_change2", "aha"); fail("SQLException expected due to password expired"); } catch (SQLException e4) { if (e4.getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || e4.getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN) { props.setProperty("disconnectOnExpiredPasswords", "false"); testConn = getConnectionWithProps(props); try { ((MySQLConnection) testConn).changeUser("must_change2", "aha"); testSt = testConn.createStatement(); testRs = testSt.executeQuery("SHOW VARIABLES LIKE 'disconnect_on_expired_password'"); fail("SQLException expected due to password expired"); } catch (SQLException e5) { if (e5.getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN) { testConn = getConnectionWithProps(props); testSt = testConn.createStatement(); } testSt.executeUpdate(versionMeetsMinimum(5, 7, 6) ? "ALTER USER USER() IDENTIFIED BY 'newpwd'" : "SET PASSWORD = PASSWORD('newpwd')"); testConn.close(); props.setProperty("user", "must_change2"); props.setProperty("password", "newpwd"); props.setProperty("disconnectOnExpiredPasswords", "true"); testConn = getConnectionWithProps(props); testSt = testConn.createStatement(); testRs = testSt.executeQuery("SHOW VARIABLES LIKE 'disconnect_on_expired_password'"); assertTrue(testRs.next()); } } else { throw e4; } } } } else { throw e1; } } } finally { if (testRs != null) { testRs.close(); } if (testSt != null) { testSt.close(); } if (testConn != null) { testConn.close(); } this.stmt.executeUpdate("drop user 'must_change1'@'%'"); this.stmt.executeUpdate("drop user 'must_change2'@'%'"); } } } public void testBug68011() throws Exception { Connection c = null; try { Properties props = new Properties(); props.setProperty("noDatetimeStringSync", "true"); props.setProperty("useTimezone", "true"); c = getConnectionWithProps(props); } catch (SQLException e) { assertTrue(e.getMessage().contains("noDatetimeStringSync")); } finally { if (c != null) { c.close(); } } } /** * Tests connection attributes * * @throws Exception */ public void testConnectionAttributes() throws Exception { if (!versionMeetsMinimum(5, 6)) { return; } Properties props = new Properties(); props.setProperty("connectionAttributes", "first:one,again:two"); props.setProperty("user", "root"); Connection attConn = super.getConnectionWithProps(props); ResultSet rslt = attConn.createStatement() .executeQuery("SELECT * FROM performance_schema.session_connect_attrs WHERE processlist_id = CONNECTION_ID()"); Map<String, Integer> matchedCounts = new HashMap<String, Integer>(); // disabling until standard values are defined and implemented // matchedCounts.put("_os", 0); // matchedCounts.put("_platform", 0); matchedCounts.put("_runtime_version", 0); matchedCounts.put("_runtime_vendor", 0); matchedCounts.put("_client_version", 0); matchedCounts.put("_client_license", 0); matchedCounts.put("_client_name", 0); matchedCounts.put("first", 0); matchedCounts.put("again", 0); while (rslt.next()) { String key = rslt.getString(2); String val = rslt.getString(3); if (!matchedCounts.containsKey(key)) { fail("Unexpected connection attribute key: " + key); } matchedCounts.put(key, matchedCounts.get(key) + 1); if (key.equals("_runtime_version")) { assertEquals(System.getProperty("java.version"), val); } else if (key.equals("_os")) { assertEquals(NonRegisteringDriver.OS, val); } else if (key.equals("_platform")) { assertEquals(NonRegisteringDriver.PLATFORM, val); } else if (key.equals("_runtime_vendor")) { assertEquals(System.getProperty("java.vendor"), val); } else if (key.equals("_client_version")) { assertEquals(NonRegisteringDriver.VERSION, val); } else if (key.equals("_client_license")) { assertEquals(NonRegisteringDriver.LICENSE, val); } else if (key.equals("_client_name")) { assertEquals(NonRegisteringDriver.NAME, val); } else if (key.equals("first")) { assertEquals("one", val); } else if (key.equals("again")) { assertEquals("two", val); } } rslt.close(); attConn.close(); for (String key : matchedCounts.keySet()) { if (matchedCounts.get(key) != 1) { fail("Incorrect number of entries for key \"" + key + "\": " + matchedCounts.get(key)); } } props.setProperty("connectionAttributes", "none"); attConn = super.getConnectionWithProps(props); rslt = attConn.createStatement().executeQuery("SELECT * FROM performance_schema.session_connect_attrs WHERE processlist_id = CONNECTION_ID()"); if (rslt.next()) { fail("Expected no connection attributes."); } } /** * Tests fix for BUG#16224249 - Deadlock on concurrently used LoadBalancedMySQLConnection * * @throws Exception */ public void testBug16224249() throws Exception { Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); String hostSpec = host; if (!NonRegisteringDriver.isHostPropertiesList(host)) { hostSpec = host + ":" + port; } String database = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); removeHostRelatedProps(props); props.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY); StringBuilder configs = new StringBuilder(); for (@SuppressWarnings("rawtypes") Map.Entry entry : props.entrySet()) { configs.append(entry.getKey()); configs.append("="); configs.append(entry.getValue()); configs.append("&"); } String loadbalanceUrl = String.format("jdbc:mysql:loadbalance://%s,%s/%s?%s", hostSpec, hostSpec, database, configs.toString()); String failoverUrl = String.format("jdbc:mysql://%s,%s/%s?%s", hostSpec, "127.0.0.1:" + port, database, configs.toString()); Connection[] loadbalancedconnection = new Connection[] { new NonRegisteringDriver().connect(loadbalanceUrl, null), new NonRegisteringDriver().connect(loadbalanceUrl, null), new NonRegisteringDriver().connect(loadbalanceUrl, null) }; Connection[] failoverconnection = new Connection[] { new NonRegisteringDriver().connect(failoverUrl, null), new NonRegisteringDriver().connect(failoverUrl, null), new NonRegisteringDriver().connect(failoverUrl, null) }; // WebLogic-style test Class<?> mysqlCls = null; Class<?> jcls = failoverconnection[0].getClass(); // the driver-level connection, a Proxy in this case... ClassLoader jcl = jcls.getClassLoader(); if (jcl != null) { mysqlCls = jcl.loadClass("com.mysql.jdbc.Connection"); } else { mysqlCls = Class.forName("com.mysql.jdbc.Connection", true, null); } if ((mysqlCls != null) && (mysqlCls.isAssignableFrom(jcls))) { Method abort = mysqlCls.getMethod("abortInternal", new Class[] {}); boolean hasAbortMethod = abort != null; assertTrue("abortInternal() method should be found for connection class " + jcls, hasAbortMethod); } else { fail("com.mysql.jdbc.Connection interface IS NOT ASSIGNABE from connection class " + jcls); } //------------- // Concurrent test System.out.println("Warming up"); for (int i = 0; i < failoverconnection.length; i++) { this.stmt = failoverconnection[i].createStatement(); this.pstmt = failoverconnection[i].prepareStatement("SELECT 1 FROM DUAL"); for (int j = 0; j < 10000; j++) { this.pstmt.executeQuery(); this.stmt.executeQuery("SELECT 1 FROM DUAL"); } } ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(12); ScheduledFuture<?> f1 = scheduler.schedule(new PollTask(failoverconnection[0], 1), 500, TimeUnit.MILLISECONDS); ScheduledFuture<?> f2 = scheduler.schedule(new PollTask(failoverconnection[1], 2), 500, TimeUnit.MILLISECONDS); ScheduledFuture<?> f3 = scheduler.schedule(new PollTask(failoverconnection[2], 3), 500, TimeUnit.MILLISECONDS); ScheduledFuture<?> f4 = scheduler.schedule(new PollTask(loadbalancedconnection[0], 4), 500, TimeUnit.MILLISECONDS); ScheduledFuture<?> f5 = scheduler.schedule(new PollTask(loadbalancedconnection[1], 5), 500, TimeUnit.MILLISECONDS); ScheduledFuture<?> f6 = scheduler.schedule(new PollTask(loadbalancedconnection[2], 6), 500, TimeUnit.MILLISECONDS); ScheduledFuture<?> f7 = scheduler.schedule(new CancelTask(failoverconnection[0], 7), 600, TimeUnit.MILLISECONDS); ScheduledFuture<?> f8 = scheduler.schedule(new CancelTask(failoverconnection[1], 8), 600, TimeUnit.MILLISECONDS); ScheduledFuture<?> f9 = scheduler.schedule(new CancelTask(failoverconnection[2], 9), 600, TimeUnit.MILLISECONDS); ScheduledFuture<?> f10 = scheduler.schedule(new CancelTask(loadbalancedconnection[0], 10), 600, TimeUnit.MILLISECONDS); ScheduledFuture<?> f11 = scheduler.schedule(new CancelTask(loadbalancedconnection[1], 11), 600, TimeUnit.MILLISECONDS); ScheduledFuture<?> f12 = scheduler.schedule(new CancelTask(loadbalancedconnection[2], 12), 600, TimeUnit.MILLISECONDS); try { while (f1.get(5, TimeUnit.SECONDS) != null || f2.get(5, TimeUnit.SECONDS) != null || f3.get(5, TimeUnit.SECONDS) != null || f4.get(5, TimeUnit.SECONDS) != null || f5.get(5, TimeUnit.SECONDS) != null || f6.get(5, TimeUnit.SECONDS) != null || f7.get(5, TimeUnit.SECONDS) != null || f8.get(5, TimeUnit.SECONDS) != null || f9.get(5, TimeUnit.SECONDS) != null || f10.get(5, TimeUnit.SECONDS) != null || f11.get(5, TimeUnit.SECONDS) != null || f12.get(5, TimeUnit.SECONDS) != null) { System.out.println("waiting"); } } catch (Exception e) { System.out.println(e.getMessage()); } if (this.testServerPrepStmtDeadlockCounter < 12) { Map<Thread, StackTraceElement[]> tr = Thread.getAllStackTraces(); for (StackTraceElement[] el : tr.values()) { System.out.println(); for (StackTraceElement stackTraceElement : el) { System.out.println(stackTraceElement); } } } for (int i = 0; i < failoverconnection.length; i++) { try { this.rs = failoverconnection[i].createStatement().executeQuery("SELECT 1"); } catch (Exception e1) { try { this.rs = failoverconnection[i].createStatement().executeQuery("SELECT 1"); fail("Connection should be explicitly closed."); } catch (Exception e2) { assertTrue(true); } } } scheduler.shutdown(); } /** * Tests fix for BUG#68763, ReplicationConnection.isMasterConnection() returns false always * * @throws Exception * if the test fails. */ public void testBug68763() throws Exception { ReplicationConnection replConn = null; replConn = (ReplicationConnection) getMasterSlaveReplicationConnection(); replConn.setReadOnly(true); assertFalse("isMasterConnection() should be false for slave connection", replConn.isMasterConnection()); replConn.setReadOnly(false); assertTrue("isMasterConnection() should be true for master connection", replConn.isMasterConnection()); } /** * Tests fix for BUG#68733, ReplicationConnection does not ping all underlying * active physical connections to slaves. * * @throws Exception * if the test fails. */ public void testBug68733() throws Exception { Properties props = new Properties(); props.setProperty("loadBalanceStrategy", ForcedLoadBalanceStrategy.class.getName()); props.setProperty("loadBalancePingTimeout", "100"); props.setProperty("autoReconnect", "true"); props.setProperty("retriesAllDown", "1"); String portNumber = new NonRegisteringDriver().parseURL(dbUrl, null).getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); if (portNumber == null) { portNumber = "3306"; } ForcedLoadBalanceStrategy.forceFutureServer("slave1:" + portNumber, -1); // throw Exception if slave2 gets ping UnreliableSocketFactory.downHost("slave2"); ReplicationConnection conn2 = this.getUnreliableReplicationConnection(new String[] { "master", "slave1", "slave2" }, props); assertTrue("Is not actually on master!", conn2.isMasterConnection()); conn2.setAutoCommit(false); conn2.commit(); // go to slaves: conn2.setReadOnly(true); // should succeed, as slave2 has not yet been activated: conn2.createStatement().execute("/* ping */ SELECT 1"); // allow connections to slave2: UnreliableSocketFactory.dontDownHost("slave2"); // force next re-balance to slave2: ForcedLoadBalanceStrategy.forceFutureServer("slave2:" + portNumber, -1); // re-balance: conn2.commit(); // down slave1 (active but not selected slave connection): UnreliableSocketFactory.downHost("slave1"); // should succeed, as slave2 is currently selected: conn2.createStatement().execute("/* ping */ SELECT 1"); // make all hosts available UnreliableSocketFactory.flushAllStaticData(); // peg connection to slave2: ForcedLoadBalanceStrategy.forceFutureServer("slave2:" + portNumber, -1); conn2.commit(); this.rs = conn2.createStatement().executeQuery("SELECT CONNECTION_ID()"); this.rs.next(); int slave2id = this.rs.getInt(1); // peg connection to slave1 now: ForcedLoadBalanceStrategy.forceFutureServer("slave1:" + portNumber, -1); conn2.commit(); // this is a really hacky way to confirm ping was processed // by an inactive load-balanced connection, but we lack COM_PING // counters on the server side, and need to create infrastructure // to capture what's being sent by the driver separately. Thread.sleep(2000); conn2.createStatement().execute("/* ping */ SELECT 1"); this.rs = conn2.createStatement().executeQuery("SELECT time FROM information_schema.processlist WHERE id = " + slave2id); this.rs.next(); assertTrue("Processlist should be less than 2 seconds due to ping", this.rs.getInt(1) < 2); // peg connection to slave2: ForcedLoadBalanceStrategy.forceFutureServer("slave2:" + portNumber, -1); conn2.commit(); // leaving connection tied to slave2, bring slave2 down and slave1 up: UnreliableSocketFactory.downHost("slave2"); try { conn2.createStatement().execute("/* ping */ SELECT 1"); fail("Expected failure because current slave connection is down."); } catch (SQLException e) { } conn2.close(); ForcedLoadBalanceStrategy.forceFutureServer("slave1:" + portNumber, -1); UnreliableSocketFactory.flushAllStaticData(); conn2 = this.getUnreliableReplicationConnection(new String[] { "master", "slave1", "slave2" }, props); conn2.setAutoCommit(false); // go to slaves: conn2.setReadOnly(true); // on slave1 now: conn2.commit(); ForcedLoadBalanceStrategy.forceFutureServer("slave2:" + portNumber, -1); // on slave2 now: conn2.commit(); // disable master: UnreliableSocketFactory.downHost("master"); // ping should succeed, because we're still attached to slaves: conn2.createStatement().execute("/* ping */ SELECT 1"); // bring master back up: UnreliableSocketFactory.dontDownHost("master"); // get back to master, confirm it's recovered: conn2.commit(); conn2.createStatement().execute("/* ping */ SELECT 1"); try { conn2.setReadOnly(false); } catch (SQLException e) { } conn2.commit(); // take down both slaves: UnreliableSocketFactory.downHost("slave1"); UnreliableSocketFactory.downHost("slave2"); assertTrue(conn2.isMasterConnection()); // should succeed, as we're still on master: conn2.createStatement().execute("/* ping */ SELECT 1"); UnreliableSocketFactory.dontDownHost("slave1"); UnreliableSocketFactory.dontDownHost("slave2"); UnreliableSocketFactory.downHost("master"); try { conn2.createStatement().execute("/* ping */ SELECT 1"); fail("should have failed because master is offline"); } catch (SQLException e) { } UnreliableSocketFactory.dontDownHost("master"); conn2.createStatement().execute("SELECT 1"); // continue on slave2: conn2.setReadOnly(true); // should succeed, as slave2 is up: conn2.createStatement().execute("/* ping */ SELECT 1"); UnreliableSocketFactory.downHost("slave2"); try { conn2.createStatement().execute("/* ping */ SELECT 1"); fail("should have failed because slave2 is offline and the active chosen connection."); } catch (SQLException e) { } conn2.close(); } protected int testServerPrepStmtDeadlockCounter = 0; class PollTask implements Runnable { private Connection c; private int num = 0; private Statement st1 = null; private PreparedStatement pst1 = null; PollTask(Connection cn, int n) throws SQLException { this.c = cn; this.num = n; this.st1 = this.c.createStatement(); this.pst1 = this.c.prepareStatement("SELECT 1 FROM DUAL"); } public void run() { System.out.println(this.num + ". Start polling at " + new Date().getTime()); boolean connectionClosed = false; for (int i = 0; i < 20000; i++) { try { this.st1.executeQuery("SELECT 1 FROM DUAL").close(); this.pst1.executeQuery().close(); } catch (Exception ex1) { if (!connectionClosed) { System.out.println(this.num + "." + i + " " + ex1.getMessage()); connectionClosed = true; } else { break; } } } ConnectionRegressionTest.this.testServerPrepStmtDeadlockCounter++; System.out.println(this.num + ". Done!"); } } class CancelTask implements Runnable { private Connection c; private int num = 0; CancelTask(Connection cn, int n) throws SQLException { this.c = cn; this.num = n; } public void run() { System.out.println(this.num + ". Start cancelling at " + new Date().getTime()); if (Proxy.isProxyClass(this.c.getClass())) { try { if (this.num == 7 || this.num == 10) { Proxy.getInvocationHandler(this.c).invoke(this.c, Connection.class.getMethod("close", new Class[] {}), null); } else if (this.num == 8 || this.num == 11) { Proxy.getInvocationHandler(this.c).invoke(this.c, MySQLConnection.class.getMethod("abortInternal", new Class[] {}), null); } else if (this.num == 9 || this.num == 12) { Proxy.getInvocationHandler(this.c).invoke(this.c, com.mysql.jdbc.Connection.class.getMethod("abort", new Class[] { Executor.class }), new Object[] { new ThreadPerTaskExecutor() }); } ConnectionRegressionTest.this.testServerPrepStmtDeadlockCounter++; System.out.println(this.num + ". Done!"); } catch (Throwable e) { e.printStackTrace(); } } } } class ThreadPerTaskExecutor implements Executor { public void execute(Runnable r) { new Thread(r).start(); } } /** * Tests fix for BUG#68400 useCompression=true and connect to server, zip native method cause out of memory * * @throws Exception * if any errors occur */ public void testBug68400() throws Exception { Field f = com.mysql.jdbc.NonRegisteringDriver.class.getDeclaredField("connectionPhantomRefs"); f.setAccessible(true); Map<?, ?> connectionTrackingMap = (Map<?, ?>) f.get(com.mysql.jdbc.NonRegisteringDriver.class); Field referentField = java.lang.ref.Reference.class.getDeclaredField("referent"); referentField.setAccessible(true); createTable("testBug68400", "(x VARCHAR(255) NOT NULL DEFAULT '')"); String s1 = "a very very very very very very very very very very very very very very very very very very very very very very very very large string to ensure compression enabled"; this.stmt.executeUpdate("insert into testBug68400 values ('" + s1 + "')"); Properties props = new Properties(); props.setProperty("useCompression", "true"); props.setProperty("connectionAttributes", "testBug68400:true"); testMemLeakBatch(props, connectionTrackingMap, referentField, 0, 0, s1, "testBug68400:true"); testMemLeakBatch(props, connectionTrackingMap, referentField, 0, 1, s1, "testBug68400:true"); testMemLeakBatch(props, connectionTrackingMap, referentField, 0, 2, s1, "testBug68400:true"); System.out.println("Done."); } /** * @param props * @param connectionType * 0-ConnectionImpl, 1-LoadBalancedConnection, 2-FailoverConnection, 3-ReplicationConnection * @param finType * 0 - none, 1 - close(), 2 - abortInternal() * @throws Exception */ private void testMemLeakBatch(Properties props, Map<?, ?> connectionTrackingMap, Field referentField, int connectionType, int finType, String s1, String attributeValue) throws Exception { Connection connection = null; Statement statement = null; ResultSet resultSet = null; int connectionNumber = 0; String[] typeNames = new String[] { "ConnectionImpl", "LoadBalancedConnection", "FailoverConnection", "ReplicationConnection" }; System.out.println("\n" + typeNames[connectionType] + ", " + (finType == 0 ? "nullification" : (finType == 1 ? "close()" : "abortInternal()"))); // 1. Create 100 connections with "testBug68400:true" attribute for (int j = 0; j < 20; j++) { switch (connectionType) { case 1: //load-balanced connection connection = getLoadBalancedConnection(props); break; case 2: //failover connection Properties baseprops = new Driver().parseURL(BaseTestCase.dbUrl, null); baseprops.setProperty("autoReconnect", "true"); baseprops.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); Properties urlProps = new NonRegisteringDriver().parseURL(BaseTestCase.dbUrl, null); String host = urlProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); String port = urlProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); baseprops.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); baseprops.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY); baseprops.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1"); baseprops.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1"); baseprops.setProperty("queriesBeforeRetryMaster", "50"); baseprops.setProperty("maxReconnects", "1"); UnreliableSocketFactory.mapHost("master", host); UnreliableSocketFactory.mapHost("slave", host); baseprops.putAll(props); connection = getConnectionWithProps("jdbc:mysql://master:" + port + ",slave:" + port + "/", baseprops); break; case 3: //ReplicationConnection; Properties replProps = new Properties(); replProps.putAll(props); replProps.setProperty("loadBalanceStrategy", ForcedLoadBalanceStrategy.class.getName()); replProps.setProperty("loadBalancePingTimeout", "100"); replProps.setProperty("autoReconnect", "true"); connection = this.getUnreliableReplicationConnection(new String[] { "master", "slave1", "slave2" }, replProps); break; default: connection = getConnectionWithProps(props); break; } statement = connection.createStatement(); resultSet = statement.executeQuery("select /* a very very very very very very very very very very very very very very very very very very very " + "very very very very very large string to ensure compression enabled */ x from testBug68400"); if (resultSet.next()) { String s2 = resultSet.getString(1); assertEquals(s1, s2); } if (resultSet != null) { resultSet.close(); } if (statement != null) { statement.close(); } if (connection != null) { if (finType == 1) { connection.close(); } else if (finType == 2) { ((com.mysql.jdbc.Connection) connection).abortInternal(); } connection = null; } } // 2. Count connections before GC System.out.println("MAP: " + connectionTrackingMap.size()); connectionNumber = countTestConnections(connectionTrackingMap, referentField, false, attributeValue); System.out.println("Test related connections in MAP before GC: " + connectionNumber); // 3. Run GC Runtime.getRuntime().gc(); // 4. Sleep to ensure abandoned connection clean up occurred Thread.sleep(2000); // 5. Count connections before GC connectionNumber = countTestConnections(connectionTrackingMap, referentField, true, attributeValue); System.out.println("Test related connections in MAP after GC: " + connectionNumber); System.out.println("MAP: " + connectionTrackingMap.size()); assertEquals("No connection with \"" + attributeValue + "\" connection attribute should exist in NonRegisteringDriver.connectionPhantomRefs map after GC", 0, connectionNumber); } private int countTestConnections(Map<?, ?> connectionTrackingMap, Field referentField, boolean show, String attributValue) throws Exception { int connectionNumber = 0; for (Object o1 : connectionTrackingMap.keySet()) { com.mysql.jdbc.Connection ctmp = (com.mysql.jdbc.Connection) referentField.get(o1); try { if (ctmp != null && ctmp.getConnectionAttributes() != null && ctmp.getConnectionAttributes().equals(attributValue)) { connectionNumber++; if (show) { System.out.println(ctmp.toString()); } } } catch (NullPointerException e) { System.out.println("NullPointerException: \n" + ctmp + "\n" + ctmp.getConnectionAttributes()); } catch (MySQLNonTransientConnectionException e) { System.out.println("MySQLNonTransientConnectionException (expected for explicitly closed load-balanced connection)"); } } return connectionNumber; } /** * Tests fix for BUG#17251955, ARRAYINDEXOUTOFBOUNDSEXCEPTION ON LONG MULTI-BYTE DB/USER NAMES * * @throws Exception */ public void testBug17251955() throws Exception { Connection c1 = null; Statement st1 = null; Connection c2 = null; Properties props = new Properties(); Properties props1 = new NonRegisteringDriver().parseURL(dbUrl, null); String host = props1.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); String url = "jdbc:mysql://" + host; if (!NonRegisteringDriver.isHostPropertiesList(host)) { String port = props1.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); url = url + ":" + port; } try { props.setProperty("characterEncoding", "UTF-8"); c1 = getConnectionWithProps(props); st1 = c1.createStatement(); st1.execute("create database if not exists `\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8`"); st1.execute("grant all on `\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8`.* to '\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8'@'%' identified by 'msandbox'"); props = new Properties(); props.setProperty("user", "\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8"); props.setProperty("password", "msandbox"); c2 = DriverManager.getConnection(url + "/\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8", props); c2.createStatement().executeQuery("select 1"); c2.close(); } catch (SQLException e) { assertFalse("e.getCause() instanceof java.lang.ArrayIndexOutOfBoundsException", e.getCause() instanceof java.lang.ArrayIndexOutOfBoundsException); props.setProperty("user", "\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8"); c2 = DriverManager.getConnection(url + "/\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8", props); c2.createStatement().executeQuery("select 1"); c2.close(); } finally { if (c2 != null) { c2.close(); } if (st1 != null) { st1.executeUpdate("drop user '\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8'@'%'"); st1.executeUpdate("drop database if exists `\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8`"); st1.close(); } if (c1 != null) { c1.close(); } } } /** * Tests fix for BUG#69506 - XAER_DUPID error code is not returned when a duplicate XID is offered in Java. * * @throws Exception * if the test fails. */ public void testBug69506() throws Exception { MysqlXADataSource dataSource = new MysqlXADataSource(); dataSource.setUrl(dbUrl); XAConnection testXAConn1 = dataSource.getXAConnection(); XAConnection testXAConn2 = dataSource.getXAConnection(); Xid duplicateXID = new MysqlXid("1".getBytes(), "1".getBytes(), 1); testXAConn1.getXAResource().start(duplicateXID, 0); try { testXAConn2.getXAResource().start(duplicateXID, 0); fail("XAException was expected."); } catch (XAException e) { assertEquals("Wrong error code retured for duplicated XID.", XAException.XAER_DUPID, e.errorCode); } } /** * Tests fix for BUG#69746, ResultSet closed after Statement.close() when dontTrackOpenResources=true * active physical connections to slaves. * * @throws Exception * if the test fails. */ public void testBug69746() throws Exception { Connection testConnection; Statement testStatement; ResultSet testResultSet; /* * Test explicit closes */ testConnection = getConnectionWithProps("dontTrackOpenResources=true"); testStatement = testConnection.createStatement(); testResultSet = testStatement.executeQuery("SELECT 1"); assertFalse("Connection should not be closed.", testConnection.isClosed()); assertFalse("Statement should not be closed.", isStatementClosedForTestBug69746(testStatement)); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); testConnection.close(); assertTrue("Connection should be closed.", testConnection.isClosed()); assertFalse("Statement should not be closed.", isStatementClosedForTestBug69746(testStatement)); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); testStatement.close(); assertTrue("Connection should be closed.", testConnection.isClosed()); assertTrue("Statement should be closed.", isStatementClosedForTestBug69746(testStatement)); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); testResultSet.close(); assertTrue("Connection should be closed.", testConnection.isClosed()); assertTrue("Statement should be closed.", isStatementClosedForTestBug69746(testStatement)); assertTrue("ResultSet should be closed.", isResultSetClosedForTestBug69746(testResultSet)); /* * Test implicit closes */ // Prepare test objects createProcedure("testBug69746_proc", "() BEGIN SELECT 1; SELECT 2; SELECT 3; END"); createTable("testBug69746_tbl", "(fld1 INT NOT NULL AUTO_INCREMENT, fld2 INT, PRIMARY KEY(fld1))"); testConnection = getConnectionWithProps("dontTrackOpenResources=true"); testStatement = testConnection.createStatement(); testResultSet = testStatement.executeQuery("SELECT 1"); // 1. Statement.execute() & Statement.getMoreResults() testStatement.executeQuery("CALL testBug69746_proc"); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); ResultSet testResultSet2 = testStatement.getResultSet(); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet2)); testStatement.getMoreResults(); ResultSet testResultSet3 = testStatement.getResultSet(); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet2)); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet3)); testStatement.getMoreResults(Statement.KEEP_CURRENT_RESULT); ResultSet testResultSet4 = testStatement.getResultSet(); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet2)); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet3)); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet4)); testStatement.getMoreResults(Statement.CLOSE_ALL_RESULTS); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet2)); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet3)); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet4)); // 2. Statement.executeBatch() testStatement.addBatch("INSERT INTO testBug69746_tbl (fld2) VALUES (1)"); testStatement.addBatch("INSERT INTO testBug69746_tbl (fld2) VALUES (2)"); testStatement.executeBatch(); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); // 3. Statement.executeQuery() testStatement.executeQuery("SELECT 2"); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); // 4. Statement.executeUpdate() testStatement.executeUpdate("INSERT INTO testBug69746_tbl (fld2) VALUES (3)"); assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); testResultSet.close(); testResultSet2.close(); testResultSet3.close(); testResultSet4.close(); testStatement.close(); testConnection.close(); } private boolean isStatementClosedForTestBug69746(Statement statement) { try { statement.getResultSet(); } catch (SQLException ex) { return ex.getMessage().equalsIgnoreCase(Messages.getString("Statement.49")); } return false; } private boolean isResultSetClosedForTestBug69746(ResultSet resultSet) { try { resultSet.first(); } catch (SQLException ex) { return ex.getMessage().equalsIgnoreCase(Messages.getString("ResultSet.Operation_not_allowed_after_ResultSet_closed_144")); } return false; } /** * This test requires additional server instance configured with * default-authentication-plugin=sha256_password and RSA encryption enabled. * * To run this test please add this variable to ant call: * -Dcom.mysql.jdbc.testsuite.url.sha256default=jdbc:mysql://localhost:3307/test?user=root&password=pwd * * @throws Exception */ public void testLongAuthResponsePayload() throws Exception { String sha256defaultDbUrl = System.getProperty("com.mysql.jdbc.testsuite.url.sha256default"); if (sha256defaultDbUrl != null && versionMeetsMinimum(5, 6, 6)) { Properties props = new Properties(); props.setProperty("allowPublicKeyRetrieval", "true"); // check that sha256_password plugin is available Connection c1 = DriverManager.getConnection(sha256defaultDbUrl, props); Statement s1 = c1.createStatement(); if (!pluginIsActive(s1, "sha256_password")) { fail("sha256_password required to run this test"); } try { // create user with long password and sha256_password auth s1.executeUpdate("SET @current_old_passwords = @@global.old_passwords"); s1.executeUpdate("grant all on *.* to 'wl6134user'@'%' identified WITH sha256_password"); s1.executeUpdate("SET GLOBAL old_passwords= 2"); s1.executeUpdate("SET SESSION old_passwords= 2"); s1.executeUpdate(((MySQLConnection) c1).versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'wl6134user'@'%' IDENTIFIED BY 'aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee'" : "set password for 'wl6134user'@'%' = PASSWORD('aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee')"); s1.executeUpdate("flush privileges"); props.setProperty("user", "wl6134user"); props.setProperty("password", "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee"); props.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); Connection testConn = null; try { testConn = DriverManager.getConnection(sha256defaultDbUrl, props); fail("SQLException expected due to password is too long for RSA encryption"); } catch (Exception e) { assertTrue(e.getMessage().startsWith("Data must not be longer than")); } finally { if (testConn != null) { testConn.close(); } } try { String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; System.setProperty("javax.net.ssl.keyStore", trustStorePath); System.setProperty("javax.net.ssl.keyStorePassword", "password"); System.setProperty("javax.net.ssl.trustStore", trustStorePath); System.setProperty("javax.net.ssl.trustStorePassword", "password"); props.setProperty("useSSL", "true"); assertCurrentUser(sha256defaultDbUrl, props, "wl6134user", true); } catch (Exception e) { throw e; } finally { if (testConn != null) { testConn.close(); } } } finally { if (c1 != null) { if (s1 != null) { s1.executeUpdate("drop user 'wl6134user'@'%'"); s1.executeUpdate("flush privileges"); s1.executeUpdate("SET GLOBAL old_passwords = @current_old_passwords"); s1.close(); } c1.close(); } } } } /** * Tests fix for Bug#69452 - Memory size connection property doesn't support large values well * * @throws Exception * if the test fails. */ public void testBug69452() throws Exception { String[][] testMemUnits = new String[][] { { "k", "kb", "kB", "K", "Kb", "KB" }, { "m", "mb", "mB", "M", "Mb", "MB" }, { "g", "gb", "gB", "G", "Gb", "GB" } }; com.mysql.jdbc.Connection connWithMemProps; long[] memMultiplier = new long[] { 1024, 1024 * 1024, 1024 * 1024 * 1024 }; // reflection is needed to access protected info from ConnectionPropertiesImpl.largeRowSizeThreshold Field propField = com.mysql.jdbc.ConnectionPropertiesImpl.class.getDeclaredField("largeRowSizeThreshold"); propField.setAccessible(true); Class<?> propClass = null; for (Class<?> nestedClass : com.mysql.jdbc.ConnectionPropertiesImpl.class.getDeclaredClasses()) { if (nestedClass.getName().equals("com.mysql.jdbc.ConnectionPropertiesImpl$IntegerConnectionProperty")) { propClass = nestedClass; break; } } Method propMethod = propClass.getDeclaredMethod("getValueAsInt"); propMethod.setAccessible(true); for (int i = 0; i < testMemUnits.length; i++) { for (int j = 0; j < testMemUnits[i].length; j++) { // testing with memory values under 2GB because higher values aren't supported. connWithMemProps = (com.mysql.jdbc.Connection) getConnectionWithProps(String.format( "blobSendChunkSize=1.2%1$s,largeRowSizeThreshold=1.4%1$s,locatorFetchBufferSize=1.6%1$s", testMemUnits[i][j])); // test values of property 'blobSendChunkSize' assertEquals("Memory unit '" + testMemUnits[i][j] + "'; property 'blobSendChunkSize'", (int) (memMultiplier[i] * 1.2), connWithMemProps.getBlobSendChunkSize()); // test values of property 'largeRowSizeThreshold' assertEquals("Memory unit '" + testMemUnits[i][j] + "'; property 'largeRowSizeThreshold'", "1.4" + testMemUnits[i][j], connWithMemProps.getLargeRowSizeThreshold()); assertEquals("Memory unit '" + testMemUnits[i][j] + "'; property 'largeRowSizeThreshold'", (int) (memMultiplier[i] * 1.4), ((Integer) propMethod.invoke(propField.get(connWithMemProps))).intValue()); // test values of property 'locatorFetchBufferSize' assertEquals("Memory unit '" + testMemUnits[i][j] + "'; property 'locatorFetchBufferSize'", (int) (memMultiplier[i] * 1.6), connWithMemProps.getLocatorFetchBufferSize()); connWithMemProps.close(); } } } /** * Tests fix for Bug#69777 - Setting maxAllowedPacket below 8203 makes blobSendChunkSize negative. * * @throws Exception * if any errors occur */ public void testBug69777() throws Exception { final int maxPacketSizeThreshold = 8203; // ServerPreparedStatement.BLOB_STREAM_READ_BUF_SIZE + 11 // test maxAllowedPacket below threshold and useServerPrepStmts=true assertThrows(SQLException.class, "Connection setting too low for 'maxAllowedPacket'.*", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps("useServerPrepStmts=true,maxAllowedPacket=" + (maxPacketSizeThreshold - 1)).close(); return null; } }); assertThrows(SQLException.class, "Connection setting too low for 'maxAllowedPacket'.*", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps("useServerPrepStmts=true,maxAllowedPacket=" + maxPacketSizeThreshold).close(); return null; } }); // the following instructions should execute without any problem // test maxAllowedPacket above threshold and useServerPrepStmts=true getConnectionWithProps("useServerPrepStmts=true,maxAllowedPacket=" + (maxPacketSizeThreshold + 1)).close(); // test maxAllowedPacket below threshold and useServerPrepStmts=false getConnectionWithProps("useServerPrepStmts=false,maxAllowedPacket=" + (maxPacketSizeThreshold - 1)).close(); // test maxAllowedPacket on threshold and useServerPrepStmts=false getConnectionWithProps("useServerPrepStmts=false,maxAllowedPacket=" + maxPacketSizeThreshold).close(); // test maxAllowedPacket above threshold and useServerPrepStmts=false getConnectionWithProps("useServerPrepStmts=false,maxAllowedPacket=" + (maxPacketSizeThreshold + 1)).close(); } /** * Tests fix for BUG#69579 - DriverManager.setLoginTimeout not honored. * * @throws Exception * if the test fails. */ public void testBug69579() throws Exception { // Mock Server that accepts network connections and does nothing with them, for connection timeout testing. class MockServer implements Runnable { private ServerSocket serverSocket = null; int initialize() throws IOException { this.serverSocket = new ServerSocket(0); return this.serverSocket.getLocalPort(); } void releaseResources() { System.out.println("Start releasing mock server resources."); if (this.serverSocket != null) { try { this.serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } public void run() { if (this.serverSocket == null) { throw new Error("Mock server not initialized."); } Socket clientSocket = null; try { while ((clientSocket = this.serverSocket.accept()) != null) { System.out.println("Client socket accepted: [" + clientSocket.toString() + "]"); } } catch (IOException e) { System.out.println("Shutting down mock server."); } finally { if (clientSocket != null) { try { clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } } ExecutorService executor = Executors.newCachedThreadPool(); MockServer mockServer = new MockServer(); int serverPort = 0; try { serverPort = mockServer.initialize(); } catch (IOException e1) { fail("Failed to initialize a mock server."); } final String testURL = "jdbc:mysql://localhost:" + serverPort; Connection testConn = null; final int oldLoginTimeout = DriverManager.getLoginTimeout(); final int loginTimeout = 3; final int testTimeout = loginTimeout * 2; long timestamp = System.currentTimeMillis(); try { DriverManager.setLoginTimeout(loginTimeout); executor.execute(mockServer); Future<Connection> future = executor.submit(new Callable<Connection>() { @SuppressWarnings("synthetic-access") public Connection call() throws Exception { return getConnectionWithProps(testURL, ""); } }); testConn = future.get(testTimeout, TimeUnit.SECONDS); testConn.close(); fail("The connection attempt should have timed out."); } catch (InterruptedException e) { e.printStackTrace(); fail("Failed to establish a connection with mock server."); } catch (ExecutionException e) { if (e.getCause() instanceof SQLException) { e.printStackTrace(); assertTrue(e.getCause().getMessage().startsWith("Communications link failure") || e.getCause().getMessage().equals(Messages.getString("Connection.LoginTimeout"))); assertEquals("Login timeout should have occured in (secs.):", loginTimeout, (System.currentTimeMillis() - timestamp) / 1000); } else { fail("Failed to establish a connection with mock server."); } } catch (TimeoutException e) { fail("Time expired for connection attempt."); } finally { DriverManager.setLoginTimeout(oldLoginTimeout); mockServer.releaseResources(); executor.shutdownNow(); } } /** * Tests fix for Bug#71038, Add an option for custom collations detection * * @throws Exception */ public void testBug71038() throws Exception { long cnt0 = 0; long cnt1 = 0; for (int i = 0; i < 1000; i++) { cnt0 -= System.currentTimeMillis(); Connection c = getConnectionWithProps("detectCustomCollations=false"); cnt0 += System.currentTimeMillis(); c.close(); cnt1 -= System.currentTimeMillis(); c = getConnectionWithProps("detectCustomCollations=true"); cnt1 += System.currentTimeMillis(); c.close(); } System.out.println("detectCustomCollations=false: " + cnt0 + "\ndetectCustomCollations=true : " + cnt1); assertTrue(cnt0 < cnt1); } /** * Internal method for tests to get a replcation connection with a * single master host to the test URL. */ private ReplicationConnection getTestReplicationConnectionNoSlaves(String masterHost) throws Exception { Properties props = getPropertiesFromTestsuiteUrl(); List<String> masterHosts = new ArrayList<String>(); masterHosts.add(masterHost); List<String> slaveHosts = new ArrayList<String>(); // empty ReplicationConnection replConn = new ReplicationConnection(props, props, masterHosts, slaveHosts); return replConn; } /** * Test that we remain on the master when: * - the connection is not in read-only mode * - no slaves are configured * - a new slave is added */ public void testReplicationConnectionNoSlavesRemainOnMaster() throws Exception { Properties props = getPropertiesFromTestsuiteUrl(); String masterHost = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY) + ":" + props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); ReplicationConnection replConn = getTestReplicationConnectionNoSlaves(masterHost); Statement s = replConn.createStatement(); ResultSet rs1 = s.executeQuery("select CONNECTION_ID()"); assertTrue(rs1.next()); int masterConnectionId = rs1.getInt(1); rs1.close(); s.close(); // add a slave and make sure we are NOT on a new connection replConn.addSlaveHost(masterHost); s = replConn.createStatement(); rs1 = s.executeQuery("select CONNECTION_ID()"); assertTrue(rs1.next()); assertEquals(masterConnectionId, rs1.getInt(1)); assertFalse(replConn.isReadOnly()); rs1.close(); s.close(); } public void testReplicationConnectionNoSlavesBasics() throws Exception { // create a replication connection with only a master, get the // connection id for later use Properties props = getPropertiesFromTestsuiteUrl(); String masterHost = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY) + ":" + props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); ReplicationConnection replConn = getTestReplicationConnectionNoSlaves(masterHost); replConn.setAutoCommit(false); Statement s = replConn.createStatement(); ResultSet rs1 = s.executeQuery("select CONNECTION_ID()"); assertTrue(rs1.next()); int masterConnectionId = rs1.getInt(1); assertFalse(replConn.isReadOnly()); rs1.close(); s.close(); // make sure we are still on the same connection after going // to read-only mode. There are no slaves, so no other // connections are possible replConn.setReadOnly(true); assertTrue(replConn.isReadOnly()); assertTrue(replConn.getCurrentConnection().isReadOnly()); s = replConn.createStatement(); try { s.executeUpdate("truncate non_existing_table"); fail("executeUpdate should not be allowed in read-only mode"); } catch (SQLException ex) { assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, ex.getSQLState()); } try { s.execute("truncate non_existing_table"); fail("executeUpdate should not be allowed in read-only mode"); } catch (SQLException ex) { assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, ex.getSQLState()); } rs1 = s.executeQuery("select CONNECTION_ID()"); assertTrue(rs1.next()); assertEquals(masterConnectionId, rs1.getInt(1)); rs1.close(); s.close(); // add a slave and make sure we are on a new connection replConn.addSlaveHost(masterHost); s = replConn.createStatement(); rs1 = s.executeQuery("select CONNECTION_ID()"); assertTrue(rs1.next()); assertTrue(rs1.getInt(1) != masterConnectionId); rs1.close(); s.close(); // switch back to master replConn.setReadOnly(false); s = replConn.createStatement(); rs1 = s.executeQuery("select CONNECTION_ID()"); assertFalse(replConn.isReadOnly()); assertFalse(replConn.getCurrentConnection().isReadOnly()); assertTrue(rs1.next()); assertEquals(masterConnectionId, rs1.getInt(1)); rs1.close(); s.close(); // removing the slave should switch back to the master replConn.setReadOnly(true); replConn.removeSlave(masterHost); replConn.commit(); s = replConn.createStatement(); rs1 = s.executeQuery("select CONNECTION_ID()"); // should be maintained even though we're back on the master assertTrue(replConn.isReadOnly()); assertTrue(replConn.getCurrentConnection().isReadOnly()); assertTrue(rs1.next()); assertEquals(masterConnectionId, rs1.getInt(1)); rs1.close(); s.close(); } /** * Tests fix for Bug#71850 - init() is called twice on exception interceptors * * @throws Exception * if the test fails. */ public void testBug71850() throws Exception { assertThrows(Exception.class, "ExceptionInterceptor.init\\(\\) called 1 time\\(s\\)", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps("exceptionInterceptors=testsuite.regression.ConnectionRegressionTest$TestBug71850ExceptionInterceptor," + "user=unexistent_user"); return null; } }); } public static class TestBug71850ExceptionInterceptor implements ExceptionInterceptor { private int counter = 0; public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { this.counter++; } public void destroy() { } public SQLException interceptException(SQLException sqlEx, com.mysql.jdbc.Connection conn) { return new SQLException("ExceptionInterceptor.init() called " + this.counter + " time(s)"); } } /** * Tests fix for BUG#67803 - XA commands sent twice to MySQL server * * @throws Exception * if the test fails. */ public void testBug67803() throws Exception { MysqlXADataSource dataSource = new MysqlXADataSource(); dataSource.setUrl(dbUrl); dataSource.setUseCursorFetch(true); dataSource.setDefaultFetchSize(50); dataSource.setUseServerPrepStmts(true); dataSource.setExceptionInterceptors("testsuite.regression.ConnectionRegressionTest$TestBug67803ExceptionInterceptor"); XAConnection testXAConn1 = dataSource.getXAConnection(); testXAConn1.getXAResource().start(new MysqlXid("2".getBytes(), "2".getBytes(), 1), 0); } public static class TestBug67803ExceptionInterceptor implements ExceptionInterceptor { public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { } public void destroy() { } public SQLException interceptException(SQLException sqlEx, com.mysql.jdbc.Connection conn) { if (sqlEx.getErrorCode() == 1295 || sqlEx.getMessage().contains("This command is not supported in the prepared statement protocol yet")) { // SQLException will not be re-thrown if emulateUnsupportedPstmts=true, thus throw RuntimeException to fail the test throw new RuntimeException(sqlEx); } return sqlEx; } } /** * Test for Bug#72712 - SET NAMES issued unnecessarily. * * Using a statement interceptor, ensure that SET NAMES is not * called if the encoding requested by the client application * matches that of character_set_server. * * Also test that character_set_results is not set unnecessarily. */ public void testBug72712() throws Exception { // this test is only run when character_set_server=latin1 if (!((MySQLConnection) this.conn).getServerVariable("character_set_server").equals("latin1")) { return; } Properties p = new Properties(); p.setProperty("characterEncoding", "cp1252"); p.setProperty("characterSetResults", "cp1252"); p.setProperty("statementInterceptors", Bug72712StatementInterceptor.class.getName()); getConnectionWithProps(p); // exception will be thrown from the statement interceptor if any SET statements are issued } /** * Statement interceptor used to implement preceding test. */ public static class Bug72712StatementInterceptor extends BaseStatementInterceptor { @Override public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) throws SQLException { if (sql.contains("SET NAMES") || sql.contains("character_set_results") && !(sql.contains("SHOW VARIABLES") || sql.contains("SELECT @@"))) { throw new SQLException("Wrongt statement issued: " + sql); } return null; } } /** * Test for Bug#62577 - XA connection fails with ClassCastException */ public void testBug62577() throws Exception { Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); String hostSpec = host; if (!NonRegisteringDriver.isHostPropertiesList(host)) { hostSpec = host + ":" + port; } String database = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); removeHostRelatedProps(props); props.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY); StringBuilder configs = new StringBuilder(); for (@SuppressWarnings("rawtypes") Map.Entry entry : props.entrySet()) { configs.append(entry.getKey()); configs.append("="); configs.append(entry.getValue()); configs.append("&"); } String cfg1 = configs.toString(); configs.append("pinGlobalTxToPhysicalConnection"); configs.append("="); configs.append("true"); String cfg2 = configs.toString(); // load-balance testBug62577TestUrl(String.format("jdbc:mysql:loadbalance://%s,%s/%s?%s", hostSpec, hostSpec, database, cfg1)); testBug62577TestUrl(String.format("jdbc:mysql:loadbalance://%s,%s/%s?%s", hostSpec, hostSpec, database, cfg2)); // failover testBug62577TestUrl(String.format("jdbc:mysql://%s,%s/%s?%s", hostSpec, hostSpec, database, cfg1)); testBug62577TestUrl(String.format("jdbc:mysql://%s,%s/%s?%s", hostSpec, hostSpec, database, cfg2)); } private void testBug62577TestUrl(String url) throws Exception { MysqlXADataSource dataSource = new MysqlXADataSource(); dataSource.setUrl(url); XAConnection xaConn = dataSource.getXAConnection(); Statement st = xaConn.getConnection().createStatement(); st.executeQuery("SELECT 1;"); xaConn.close(); } /** * Test fix for Bug#18869381 - CHANGEUSER() FOR SHA USER RESULTS IN NULLPOINTEREXCEPTION * * This test requires additional server instance configured with * default-authentication-plugin=sha256_password and RSA encryption enabled. * * To run this test please add this variable to ant call: * -Dcom.mysql.jdbc.testsuite.url.sha256default=jdbc:mysql://localhost:3307/test?user=root&password=pwd * * @throws Exception */ public void testBug18869381() throws Exception { String sha256defaultDbUrl = System.getProperty("com.mysql.jdbc.testsuite.url.sha256default"); if (sha256defaultDbUrl != null && versionMeetsMinimum(5, 6, 6)) { Properties props = new Properties(); props.setProperty("allowPublicKeyRetrieval", "true"); // check that sha256_password plugin is available Connection con = DriverManager.getConnection(sha256defaultDbUrl, props); Statement st = con.createStatement(); if (!pluginIsActive(st, "sha256_password")) { fail("sha256_password required to run this test"); } try { st.executeUpdate("SET @current_old_passwords = @@global.old_passwords"); st.executeUpdate("grant all on *.* to 'bug18869381user1'@'%' identified WITH sha256_password"); st.executeUpdate("grant all on *.* to 'bug18869381user2'@'%' identified WITH sha256_password"); st.executeUpdate("grant all on *.* to 'bug18869381user3'@'%' identified WITH mysql_native_password"); st.executeUpdate(((MySQLConnection) con).versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'bug18869381user3'@'%' IDENTIFIED BY 'pwd3'" : "set password for 'bug18869381user3'@'%' = PASSWORD('pwd3')"); st.executeUpdate("SET GLOBAL old_passwords= 2"); st.executeUpdate("SET SESSION old_passwords= 2"); st.executeUpdate(((MySQLConnection) con).versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'bug18869381user1'@'%' IDENTIFIED BY 'LongLongLongLongLongLongLongLongLongLongLongLongPwd1'" : "set password for 'bug18869381user1'@'%' = PASSWORD('LongLongLongLongLongLongLongLongLongLongLongLongPwd1')"); st.executeUpdate(((MySQLConnection) con).versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'bug18869381user2'@'%' IDENTIFIED BY 'pwd2'" : "set password for 'bug18869381user2'@'%' = PASSWORD('pwd2')"); st.executeUpdate("flush privileges"); props.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.MysqlNativePasswordPlugin"); props.setProperty("useCompression", "false"); testBug18869381WithProperties(sha256defaultDbUrl, props); props.setProperty("useCompression", "true"); testBug18869381WithProperties(sha256defaultDbUrl, props); props.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); props.setProperty("useCompression", "false"); testBug18869381WithProperties(sha256defaultDbUrl, props); props.setProperty("useCompression", "true"); testBug18869381WithProperties(sha256defaultDbUrl, props); props.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); props.setProperty("useCompression", "false"); testBug18869381WithProperties(sha256defaultDbUrl, props); props.setProperty("useCompression", "true"); testBug18869381WithProperties(sha256defaultDbUrl, props); String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; System.setProperty("javax.net.ssl.keyStore", trustStorePath); System.setProperty("javax.net.ssl.keyStorePassword", "password"); System.setProperty("javax.net.ssl.trustStore", trustStorePath); System.setProperty("javax.net.ssl.trustStorePassword", "password"); props.setProperty("useSSL", "true"); props.setProperty("useCompression", "false"); testBug18869381WithProperties(sha256defaultDbUrl, props); props.setProperty("useCompression", "true"); testBug18869381WithProperties(sha256defaultDbUrl, props); } finally { st.executeUpdate("drop user 'bug18869381user1'@'%'"); st.executeUpdate("drop user 'bug18869381user2'@'%'"); st.executeUpdate("drop user 'bug18869381user3'@'%'"); st.executeUpdate("flush privileges"); st.executeUpdate("SET GLOBAL old_passwords = @current_old_passwords"); con.close(); } } } private void testBug18869381WithProperties(String sha256defaultDbUrl, Properties props) throws Exception { Connection testConn = null; Statement testSt = null; ResultSet testRs = null; try { testConn = getConnectionWithProps(sha256defaultDbUrl, props); ((MySQLConnection) testConn).changeUser("bug18869381user1", "LongLongLongLongLongLongLongLongLongLongLongLongPwd1"); testSt = testConn.createStatement(); testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); testRs.next(); assertEquals("bug18869381user1", testRs.getString(1).split("@")[0]); assertEquals("bug18869381user1", testRs.getString(2).split("@")[0]); testSt.close(); ((MySQLConnection) testConn).changeUser("bug18869381user2", "pwd2"); testSt = testConn.createStatement(); testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); testRs.next(); assertEquals("bug18869381user2", testRs.getString(1).split("@")[0]); assertEquals("bug18869381user2", testRs.getString(2).split("@")[0]); testSt.close(); ((MySQLConnection) testConn).changeUser("bug18869381user3", "pwd3"); testSt = testConn.createStatement(); testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); testRs.next(); assertEquals("bug18869381user3", testRs.getString(1).split("@")[0]); assertEquals("bug18869381user3", testRs.getString(2).split("@")[0]); } finally { if (testConn != null) { testConn.close(); } } } /** * Tests fix for BUG#73053 - Endless loop in MysqlIO.clearInputStream due to Linux kernel bug. * * @throws Exception * if the test fails. */ public void testBug73053() throws Exception { /* * Test reported issue using a Socket implementation that simulates the buggy behavior. */ try { Connection testConn = getConnectionWithProps("socketFactory=testsuite.regression.ConnectionRegressionTest$TestBug73053SocketFactory"); Statement testStmt = this.conn.createStatement(); testStmt.executeQuery("SELECT 1"); testStmt.close(); testConn.close(); } catch (SQLException e) { fail("No SQLException should be thrown."); } /* * Test the re-implementation of the method that was reported to fail - MysqlIO.clearInputStream() in a normal situation were there actually are bytes * to clear out. When running multi-queries with streaming results, if not all results are consumed then the socket has to be cleared out when closing * the statement, thus calling MysqlIO.clearInputStream() and effectively discard unread data. */ try { Connection testConn = getConnectionWithProps("allowMultiQueries=true"); Statement testStmt = testConn.createStatement(); testStmt.setFetchSize(Integer.MIN_VALUE); // set for streaming results ResultSet testRS = testStmt.executeQuery("SELECT 1; SELECT 2; SELECT 3; SELECT 4"); assertTrue(testRS.next()); assertEquals(1, testRS.getInt(1)); assertTrue(testStmt.getMoreResults()); testStmt.getResultSet(); testStmt.close(); testConn.close(); } catch (SQLException e) { fail("No SQLException should be thrown."); } /* * Test another scenario that may be able to reproduce the bug, as reported by some (never effectively verified though). */ try { final int timeout = 10000; final String query = "SELECT SLEEP(15)"; // 1. run a very slow query in a different thread Executors.newSingleThreadExecutor().execute(new Runnable() { public void run() { try { // set socketTimeout so this thread doesn't hang if no exception is thrown after killing the connection at server side @SuppressWarnings("synthetic-access") Connection testConn = getConnectionWithProps("socketTimeout=" + timeout); Statement testStmt = testConn.createStatement(); try { testStmt.execute(query); } catch (SQLException e) { assertEquals("Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.", e.getCause().getMessage()); } testStmt.close(); testConn.close(); } catch (SQLException e) { fail("No SQLException should be thrown."); } } }); // 2. kill the connection running the slow query, at server side, to make sure the driver doesn't hang after its killed final long timestamp = System.currentTimeMillis(); long elapsedTime = 0; boolean run = true; while (run) { this.rs = this.stmt.executeQuery("SHOW PROCESSLIST"); while (this.rs.next()) { if (query.equals(this.rs.getString(8))) { this.stmt.execute("KILL CONNECTION " + this.rs.getInt(1)); run = false; break; } } if (run) { Thread.sleep(250); } elapsedTime = System.currentTimeMillis() - timestamp; // allow it 10% more time to reach the socketTimeout threshold if (elapsedTime > timeout * 1.1) { fail("Failed to kill the connection at server side."); } } } catch (SQLException e) { fail("No SQLException should be thrown."); } } public static class TestBug73053SocketFactory extends StandardSocketFactory { Socket underlyingSocket; @Override public Socket connect(String hostname, int portNumber, Properties props) throws SocketException, IOException { return this.underlyingSocket = new ConnectionRegressionTest.TestBug73053SocketWrapper(super.connect(hostname, portNumber, props)); } @Override public Socket beforeHandshake() throws SocketException, IOException { super.beforeHandshake(); return this.underlyingSocket; } @Override public Socket afterHandshake() throws SocketException, IOException { super.afterHandshake(); return this.underlyingSocket; } } private static class TestBug73053SocketWrapper extends Socket { final Socket underlyingSocket; public TestBug73053SocketWrapper(Socket underlyingSocket) { this.underlyingSocket = underlyingSocket; try { this.underlyingSocket.setSoTimeout(100); } catch (SocketException e) { fail("Failed preparing custom Socket"); } } @Override public void connect(SocketAddress endpoint) throws IOException { this.underlyingSocket.connect(endpoint); } @Override public void connect(SocketAddress endpoint, int timeout) throws IOException { this.underlyingSocket.connect(endpoint, timeout); } @Override public void bind(SocketAddress bindpoint) throws IOException { this.underlyingSocket.bind(bindpoint); } @Override public InetAddress getInetAddress() { return this.underlyingSocket.getInetAddress(); } @Override public InetAddress getLocalAddress() { return this.underlyingSocket.getLocalAddress(); } @Override public int getPort() { return this.underlyingSocket.getPort(); } @Override public int getLocalPort() { return this.underlyingSocket.getLocalPort(); } @Override public SocketAddress getRemoteSocketAddress() { return this.underlyingSocket.getRemoteSocketAddress(); } @Override public SocketAddress getLocalSocketAddress() { return this.underlyingSocket.getLocalSocketAddress(); } @Override public SocketChannel getChannel() { return this.underlyingSocket.getChannel(); } @Override public InputStream getInputStream() throws IOException { return new ConnectionRegressionTest.TestBug73053InputStreamWrapper(this.underlyingSocket.getInputStream()); } @Override public OutputStream getOutputStream() throws IOException { return this.underlyingSocket.getOutputStream(); } @Override public void setTcpNoDelay(boolean on) throws SocketException { this.underlyingSocket.setTcpNoDelay(on); } @Override public boolean getTcpNoDelay() throws SocketException { return this.underlyingSocket.getTcpNoDelay(); } @Override public void setSoLinger(boolean on, int linger) throws SocketException { this.underlyingSocket.setSoLinger(on, linger); } @Override public int getSoLinger() throws SocketException { return this.underlyingSocket.getSoLinger(); } @Override public void sendUrgentData(int data) throws IOException { this.underlyingSocket.sendUrgentData(data); } @Override public void setOOBInline(boolean on) throws SocketException { this.underlyingSocket.setOOBInline(on); } @Override public boolean getOOBInline() throws SocketException { return this.underlyingSocket.getOOBInline(); } @Override public synchronized void setSoTimeout(int timeout) throws SocketException { this.underlyingSocket.setSoTimeout(timeout); } @Override public synchronized int getSoTimeout() throws SocketException { return this.underlyingSocket.getSoTimeout(); } @Override public synchronized void setSendBufferSize(int size) throws SocketException { this.underlyingSocket.setSendBufferSize(size); } @Override public synchronized int getSendBufferSize() throws SocketException { return this.underlyingSocket.getSendBufferSize(); } @Override public synchronized void setReceiveBufferSize(int size) throws SocketException { this.underlyingSocket.setReceiveBufferSize(size); } @Override public synchronized int getReceiveBufferSize() throws SocketException { return this.underlyingSocket.getReceiveBufferSize(); } @Override public void setKeepAlive(boolean on) throws SocketException { this.underlyingSocket.setKeepAlive(on); } @Override public boolean getKeepAlive() throws SocketException { return this.underlyingSocket.getKeepAlive(); } @Override public void setTrafficClass(int tc) throws SocketException { this.underlyingSocket.setTrafficClass(tc); } @Override public int getTrafficClass() throws SocketException { return this.underlyingSocket.getTrafficClass(); } @Override public void setReuseAddress(boolean on) throws SocketException { this.underlyingSocket.setReuseAddress(on); } @Override public boolean getReuseAddress() throws SocketException { return this.underlyingSocket.getReuseAddress(); } @Override public synchronized void close() throws IOException { this.underlyingSocket.close(); } @Override public void shutdownInput() throws IOException { this.underlyingSocket.shutdownInput(); } @Override public void shutdownOutput() throws IOException { this.underlyingSocket.shutdownOutput(); } @Override public String toString() { return this.underlyingSocket.toString(); } @Override public boolean isConnected() { return this.underlyingSocket.isConnected(); } @Override public boolean isBound() { return this.underlyingSocket.isBound(); } @Override public boolean isClosed() { return this.underlyingSocket.isClosed(); } @Override public boolean isInputShutdown() { return this.underlyingSocket.isInputShutdown(); } @Override public boolean isOutputShutdown() { return this.underlyingSocket.isOutputShutdown(); } @Override public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { this.underlyingSocket.setPerformancePreferences(connectionTime, latency, bandwidth); } @Override public int hashCode() { return this.underlyingSocket.hashCode(); } @Override public boolean equals(Object obj) { return this.underlyingSocket.equals(obj); } } private static class TestBug73053InputStreamWrapper extends InputStream { final InputStream underlyingInputStream; int loopCount = 0; public TestBug73053InputStreamWrapper(InputStream underlyingInputStream) { this.underlyingInputStream = underlyingInputStream; } @Override public int read() throws IOException { this.loopCount = 0; return this.underlyingInputStream.read(); } @Override public int read(byte[] b) throws IOException { this.loopCount = 0; return this.underlyingInputStream.read(b); } @Override public int read(byte[] b, int off, int len) throws IOException { try { int readCount = this.underlyingInputStream.read(b, off, len); this.loopCount = 0; return readCount; } catch (SocketTimeoutException e) { this.loopCount++; if (this.loopCount > 10) { fail("Probable infinite loop at MySQLIO.clearInputStream()."); } return -1; } } @Override public long skip(long n) throws IOException { return this.underlyingInputStream.skip(n); } @Override public int available() throws IOException { // In some older Linux kernels the underlying system call may return 1 when actually no bytes are available in a CLOSE_WAIT state socket, even if EOF // has been reached. int available = this.underlyingInputStream.available(); return available == 0 ? 1 : available; } @Override public void close() throws IOException { this.underlyingInputStream.close(); } @Override public synchronized void mark(int readlimit) { this.underlyingInputStream.mark(readlimit); } @Override public synchronized void reset() throws IOException { this.underlyingInputStream.reset(); } @Override public boolean markSupported() { return this.underlyingInputStream.markSupported(); } @Override public int hashCode() { return this.underlyingInputStream.hashCode(); } @Override public boolean equals(Object obj) { return this.underlyingInputStream.equals(obj); } @Override public String toString() { return this.underlyingInputStream.toString(); } } /** * Tests fix for BUG#19354014 - CHANGEUSER() CALL RESULTS IN "PACKETS OUT OF ORDER" ERROR * * @throws Exception */ public void testBug19354014() throws Exception { if (versionMeetsMinimum(5, 5, 7)) { Connection con = null; this.stmt.executeUpdate("grant all on *.* to 'bug19354014user'@'%' identified WITH mysql_native_password"); this.stmt.executeUpdate(versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'bug19354014user'@'%' IDENTIFIED BY 'pwd'" : "set password for 'bug19354014user'@'%' = PASSWORD('pwd')"); this.stmt.executeUpdate("flush privileges"); try { Properties props = new Properties(); props.setProperty("useCompression", "true"); con = getConnectionWithProps(props); ((MySQLConnection) con).changeUser("bug19354014user", "pwd"); } finally { this.stmt.executeUpdate("drop user 'bug19354014user'@'%'"); this.stmt.executeUpdate("flush privileges"); if (con != null) { con.close(); } } } } /** * Tests fix for Bug#75168 - loadBalanceExceptionChecker interface cannot work using JDBC4/JDK7 * * Bug observed only with JDBC4 classes. This test is a duplication of testsuite.regression.jdbc4.ConnectionRegressionTest#testBug75168(). * The two nested static classes, Bug75168LoadBalanceExceptionChecker and Bug75168StatementInterceptor are shared between the two tests. * * @throws Exception */ public void testBug75168() throws Exception { final Properties props = new Properties(); props.setProperty("loadBalanceExceptionChecker", "testsuite.regression.ConnectionRegressionTest$Bug75168LoadBalanceExceptionChecker"); props.setProperty("statementInterceptors", Bug75168StatementInterceptor.class.getName()); Connection connTest = getLoadBalancedConnection(2, null, props); // get a load balancing connection with two default servers for (int i = 0; i < 3; i++) { Statement stmtTest = null; try { stmtTest = connTest.createStatement(); stmtTest.execute("SELECT * FROM nonexistent_table"); fail("'Table doesn't exist' exception was expected."); } catch (SQLException e) { assertTrue("'Table doesn't exist' exception was expected.", e.getMessage().endsWith("nonexistent_table' doesn't exist")); } finally { if (stmtTest != null) { stmtTest.close(); } } } connTest.close(); boolean stop = false; do { connTest = getLoadBalancedConnection(2, null, props); // get a load balancing connection with two default servers for (int i = 0; i < 3; i++) { PreparedStatement pstmtTest = null; try { pstmtTest = connTest.prepareStatement("SELECT * FROM nonexistent_table"); pstmtTest.execute(); fail("'Table doesn't exist' exception was expected."); } catch (SQLException e) { assertTrue("'Table doesn't exist' exception was expected.", e.getMessage().endsWith("nonexistent_table' doesn't exist")); } finally { if (pstmtTest != null) { pstmtTest.close(); } } } connTest.close(); // do it again with server prepared statements props.setProperty("useServerPrepStmts", "true"); } while (stop = !stop); } public static class Bug75168LoadBalanceExceptionChecker implements LoadBalanceExceptionChecker { public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { } public void destroy() { } public boolean shouldExceptionTriggerFailover(SQLException ex) { return ex.getMessage().endsWith("nonexistent_table' doesn't exist"); } } public static class Bug75168StatementInterceptor extends BaseStatementInterceptor { static Connection previousConnection = null; @Override public void destroy() { if (previousConnection == null) { fail("Test testBug75168 didn't run as expected."); } } @Override public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) throws SQLException { if (sql == null) { sql = ""; } if (sql.length() == 0 && interceptedStatement instanceof com.mysql.jdbc.PreparedStatement) { sql = ((com.mysql.jdbc.PreparedStatement) interceptedStatement).asSql(); } if (sql.indexOf("nonexistent_table") >= 0) { assertTrue("Different connection expected.", !connection.equals(previousConnection)); previousConnection = connection; } return null; } } /** * Tests fix for BUG#71084 - Wrong java.sql.Date stored if client and server time zones differ * * This tests the behavior of the new connection property 'noTimezoneConversionForDateType' * * @throws Exception * if the test fails. */ public void testBug71084() throws Exception { createTable("testBug71084", "(id INT, dt DATE)"); Properties connProps = new Properties(); connProps.setProperty("cacheDefaultTimezone", "false"); /* * case 0: default settings (no conversions) */ testBug71084AssertCase(connProps, "GMT+2", "GMT+6", null, "1998-05-21", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-6", "GMT+2", null, "1998-05-21", "1998-05-21", "1998-05-21 0:00:00"); /* * case 1: connection property 'useLegacyDatetimeCode=false' */ connProps.setProperty("useLegacyDatetimeCode", "false"); // client 25 hours behind server testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-21 22:59:59", "1998-05-22", "1998-05-20 23:00:00"); testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-21 23:00:00", "1998-05-23", "1998-05-21 23:00:00"); testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-22 22:59:59", "1998-05-23", "1998-05-21 23:00:00"); testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-22 23:00:00", "1998-05-24", "1998-05-22 23:00:00"); // client 25 hours behind server, 24 hours behind target calendar testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-20 23:59:59", "1998-05-21", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-21 0:00:00", "1998-05-22", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-21 23:59:59", "1998-05-22", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-22 0:00:00", "1998-05-23", "1998-05-22 0:00:00"); // client 24 hours behind server testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-20 23:59:59", "1998-05-21", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-21 0:00:00", "1998-05-22", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-21 23:59:59", "1998-05-22", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-22 0:00:00", "1998-05-23", "1998-05-22 0:00:00"); // client 24 hours behind server, 25 hours behind target calendar testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-21 22:59:59", "1998-05-22", "1998-05-20 23:00:00"); testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-21 23:00:00", "1998-05-23", "1998-05-21 23:00:00"); testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-22 22:59:59", "1998-05-23", "1998-05-21 23:00:00"); testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-22 23:00:00", "1998-05-24", "1998-05-22 23:00:00"); // client 2 hours behind server testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-21 21:59:59", "1998-05-21", "1998-05-20 22:00:00"); testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-21 22:00:00", "1998-05-22", "1998-05-21 22:00:00"); testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-22 21:59:59", "1998-05-22", "1998-05-21 22:00:00"); testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-22 22:00:00", "1998-05-23", "1998-05-22 22:00:00"); // client 2 hours behind server, 2 hours ahead of target calendar testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-21 1:59:59", "1998-05-20", "1998-05-20 2:00:00"); testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-21 2:00:00", "1998-05-21", "1998-05-21 2:00:00"); testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-22 1:59:59", "1998-05-21", "1998-05-21 2:00:00"); testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-22 2:00:00", "1998-05-22", "1998-05-22 2:00:00"); // client and server in the same time zone testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); // client, server and target calendar in the same time zone testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); // client 2 hours ahead of server testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-21 1:59:59", "1998-05-20", "1998-05-20 2:00:00"); testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-21 2:00:00", "1998-05-21", "1998-05-21 2:00:00"); testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-22 1:59:59", "1998-05-21", "1998-05-21 2:00:00"); testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-22 2:00:00", "1998-05-22", "1998-05-22 2:00:00"); // client 2 hours ahead of server, 2 hours behind target calendar testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-21 21:59:59", "1998-05-21", "1998-05-20 22:00:00"); testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-21 22:00:00", "1998-05-22", "1998-05-21 22:00:00"); testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-22 21:59:59", "1998-05-22", "1998-05-21 22:00:00"); testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-22 22:00:00", "1998-05-23", "1998-05-22 22:00:00"); // client 24 hours ahead of server testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-20 23:59:59", "1998-05-19", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-21 0:00:00", "1998-05-20", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-21 23:59:59", "1998-05-20", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-22 0:00:00", "1998-05-21", "1998-05-22 0:00:00"); // client 24 hours ahead of server, 25 hours ahead of target calendar testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-21 0:59:59", "1998-05-19", "1998-05-20 1:00:00"); testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-21 1:00:00", "1998-05-20", "1998-05-21 1:00:00"); testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-22 0:59:59", "1998-05-20", "1998-05-21 1:00:00"); testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-22 1:00:00", "1998-05-21", "1998-05-22 1:00:00"); // client 25 hours ahead of server testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-21 0:59:59", "1998-05-19", "1998-05-20 1:00:00"); testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-21 1:00:00", "1998-05-20", "1998-05-21 1:00:00"); testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-22 0:59:59", "1998-05-20", "1998-05-21 1:00:00"); testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-22 1:00:00", "1998-05-21", "1998-05-22 1:00:00"); // client 25 hours ahead of server, 24 hours ahead of target calendar testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-20 23:59:59", "1998-05-19", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-21 0:00:00", "1998-05-20", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-21 23:59:59", "1998-05-20", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-22 0:00:00", "1998-05-21", "1998-05-22 0:00:00"); connProps.remove("useLegacyDatetimeCode"); /* * case 2: connection property 'useTimezone=true' */ connProps.setProperty("useTimezone", "true"); // client 25 hours behind server testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); // client 25 hours behind server, 24 hours behind target calendar testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-20 23:59:59", "1998-05-21", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-21 0:00:00", "1998-05-22", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-21 23:59:59", "1998-05-22", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-22 0:00:00", "1998-05-23", "1998-05-22 0:00:00"); // client 24 hours behind server testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); // client 24 hours behind server, 25 hours behind target calendar testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-21 22:59:59", "1998-05-22", "1998-05-20 23:00:00"); testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-21 23:00:00", "1998-05-23", "1998-05-21 23:00:00"); testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-22 22:59:59", "1998-05-23", "1998-05-21 23:00:00"); testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-22 23:00:00", "1998-05-24", "1998-05-22 23:00:00"); // client 2 hours behind server testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); // client 2 hours behind server, 2 hours ahead of target calendar testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-21 1:59:59", "1998-05-20", "1998-05-20 2:00:00"); testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-21 2:00:00", "1998-05-21", "1998-05-21 2:00:00"); testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-22 1:59:59", "1998-05-21", "1998-05-21 2:00:00"); testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-22 2:00:00", "1998-05-22", "1998-05-22 2:00:00"); // client and server in the same time zone testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); // client, server and target calendar in the same time zone testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); // client 2 hours ahead of server testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); // client 2 hours ahead of server, 2 hours behind target calendar testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-21 21:59:59", "1998-05-21", "1998-05-20 22:00:00"); testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-21 22:00:00", "1998-05-22", "1998-05-21 22:00:00"); testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-22 21:59:59", "1998-05-22", "1998-05-21 22:00:00"); testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-22 22:00:00", "1998-05-23", "1998-05-22 22:00:00"); // client 24 hours ahead of server testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); // client 24 hours ahead of server, 25 hours ahead of target calendar testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-21 0:59:59", "1998-05-19", "1998-05-20 1:00:00"); testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-21 1:00:00", "1998-05-20", "1998-05-21 1:00:00"); testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-22 0:59:59", "1998-05-20", "1998-05-21 1:00:00"); testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-22 1:00:00", "1998-05-21", "1998-05-22 1:00:00"); // client 25 hours ahead of server testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); // client 25 hours ahead of server, 24 hours ahead of target calendar testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-20 23:59:59", "1998-05-19", "1998-05-20 0:00:00"); testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-21 0:00:00", "1998-05-20", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-21 23:59:59", "1998-05-20", "1998-05-21 0:00:00"); testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-22 0:00:00", "1998-05-21", "1998-05-22 0:00:00"); connProps.remove("useTimezone"); } private void testBug71084AssertCase(Properties connProps, String clientTZ, String serverTZ, String targetTZ, String insertDate, String expectedStoredDate, String expectedRetrievedDate) throws Exception { final TimeZone defaultTZ = TimeZone.getDefault(); final boolean useTargetCal = targetTZ != null; final Properties testExtraProperties = new Properties(); testExtraProperties.setProperty("", ""); testExtraProperties.setProperty("useFastDateParsing", "false"); testExtraProperties.setProperty("useJDBCCompliantTimezoneShift", "true"); testExtraProperties.setProperty("useSSPSCompatibleTimezoneShift", "true"); this.stmt.execute("DELETE FROM testBug71084"); try { TimeZone.setDefault(TimeZone.getTimeZone(clientTZ)); SimpleDateFormat longDateFrmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); longDateFrmt.setTimeZone(TimeZone.getDefault()); SimpleDateFormat shortDateFrmt = new SimpleDateFormat("yyyy-MM-dd"); shortDateFrmt.setTimeZone(TimeZone.getDefault()); Calendar targetCal = null; String targetCalMsg = null; if (useTargetCal) { targetCal = Calendar.getInstance(TimeZone.getTimeZone(targetTZ)); targetCalMsg = " (Calendar methods)"; } else { targetCalMsg = " (non-Calendar methods)"; } Date dateIn = insertDate.length() == 10 ? shortDateFrmt.parse(insertDate) : longDateFrmt.parse(insertDate); String expectedDateInDB = expectedStoredDate; Date expectedDateInRS = longDateFrmt.parse(expectedRetrievedDate); String expectedDateInDBNoConv = shortDateFrmt.format(dateIn); Date expectedDateInRSNoConv = shortDateFrmt.parse(expectedDateInDBNoConv); int id = 0; for (Entry<Object, Object> prop : testExtraProperties.entrySet()) { id++; String key = (String) prop.getKey(); String value = (String) prop.getValue(); Properties connPropsLocal = new Properties(); String propsList = "..."; connPropsLocal.putAll(connProps); if (key.length() > 0) { connPropsLocal.setProperty(key, value); } for (Object k : connPropsLocal.keySet()) { if (!"cacheDefaultTimezone".equalsIgnoreCase((String) k)) { propsList += "," + (String) k; } } connPropsLocal.setProperty("serverTimezone", serverTZ); /* * Test using the property "noTimezoneConversionForDateType=false". Conversions should occur. */ connPropsLocal.setProperty("noTimezoneConversionForDateType", "false"); Connection testConn = getConnectionWithProps(connPropsLocal); PreparedStatement testPstmt = testConn.prepareStatement("INSERT INTO testBug71084 VALUES (?, ?)"); testPstmt.setInt(1, id); if (useTargetCal) { testPstmt.setDate(2, new java.sql.Date(dateIn.getTime()), targetCal); } else { testPstmt.setDate(2, new java.sql.Date(dateIn.getTime())); } testPstmt.execute(); testPstmt.close(); Statement testStmt = testConn.createStatement(); // Get date value from database: Column `dt` - allowing time zone conversion by returning it as is; Column `dtStr` - preventing time zone // conversion by returning it as String and invalidating the date format so that no automatic conversion can ever happen. ResultSet restRs = testStmt.executeQuery("SELECT dt, CONCAT('$', dt) AS dtStr FROM testBug71084 WHERE id = " + id); restRs.next(); java.sql.Date dateOut = useTargetCal ? restRs.getDate(1, targetCal) : restRs.getDate(1); String dateInDB = restRs.getString(2).substring(1); restRs.close(); testStmt.close(); testConn.close(); assertEquals(id + ". [" + propsList + "] Date stored" + targetCalMsg, expectedDateInDB, dateInDB); assertEquals(id + ". [" + propsList + "] Date retrieved" + targetCalMsg, longDateFrmt.format(expectedDateInRS), longDateFrmt.format(dateOut)); /* * Repeat the test using the property "noTimezoneConversionForDateType=true". No conversions should occur now. */ id++; propsList += ",noTimezoneConversionForDateType"; connPropsLocal.setProperty("noTimezoneConversionForDateType", "true"); testConn = getConnectionWithProps(connPropsLocal); testPstmt = testConn.prepareStatement("INSERT INTO testBug71084 VALUES (?, ?)"); testPstmt.setInt(1, id); if (useTargetCal) { testPstmt.setDate(2, new java.sql.Date(dateIn.getTime()), targetCal); } else { testPstmt.setDate(2, new java.sql.Date(dateIn.getTime())); } testPstmt.execute(); testPstmt.close(); testStmt = testConn.createStatement(); // Get date value from database: Column `dt` - allowing time zone conversion by returning it as is; Column `dtStr` - preventing time zone // conversion by returning it as String and invalidating the date format so that no automatic conversion can ever happen. restRs = testStmt.executeQuery("SELECT dt, CONCAT('$', dt) AS dtStr FROM testBug71084 WHERE id = " + id); restRs.next(); dateOut = useTargetCal ? restRs.getDate(1, targetCal) : restRs.getDate(1); dateInDB = restRs.getString(2).substring(1); restRs.close(); testStmt.close(); testConn.close(); if (useTargetCal) { assertEquals(id + ". [" + propsList + "] Date stored" + targetCalMsg, expectedDateInDB, dateInDB); assertEquals(id + ". [" + propsList + "] Date retrieved" + targetCalMsg, longDateFrmt.format(expectedDateInRS), longDateFrmt.format(dateOut)); } else { assertEquals(id + ". [" + propsList + "] Date stored" + targetCalMsg, expectedDateInDBNoConv, dateInDB); assertEquals(id + ". [" + propsList + "] Date retrieved" + targetCalMsg, longDateFrmt.format(expectedDateInRSNoConv), longDateFrmt.format(dateOut)); } } } finally { TimeZone.setDefault(defaultTZ); } } /** * Tests fix for BUG#20685022 - SSL CONNECTION TO MYSQL 5.7.6 COMMUNITY SERVER FAILS * * This test is duplicated in testuite.regression.ConnectionRegressionTest.jdbc4.testBug20685022(). * * @throws Exception * if the test fails. */ public void testBug20685022() throws Exception { final boolean sslCipherSuitesReqFor576 = Util.getJVMVersion() < 8 && versionMeetsMinimum(5, 7, 6) && isCommunityEdition(); final Properties props = new Properties(); final Callable<Void> callableInstance = new Callable<Void>() { public Void call() throws Exception { getConnectionWithProps(props); return null; } }; /* * case 1: non verifying server certificate */ props.clear(); props.setProperty("useSSL", "true"); props.setProperty("requireSSL", "true"); props.setProperty("verifyServerCertificate", "false"); if (sslCipherSuitesReqFor576) { assertThrows(SQLException.class, Messages.getString("CommunicationsException.incompatibleSSLCipherSuites"), callableInstance); props.setProperty("enabledSSLCipherSuites", SSL_CIPHERS_FOR_576); } getConnectionWithProps(props); /* * case 2: verifying server certificate using key store provided by connection properties */ props.clear(); props.setProperty("useSSL", "true"); props.setProperty("requireSSL", "true"); props.setProperty("verifyServerCertificate", "true"); props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/test-cert-store"); props.setProperty("trustCertificateKeyStoreType", "JKS"); props.setProperty("trustCertificateKeyStorePassword", "password"); if (sslCipherSuitesReqFor576) { assertThrows(SQLException.class, Messages.getString("CommunicationsException.incompatibleSSLCipherSuites"), callableInstance); props.setProperty("enabledSSLCipherSuites", SSL_CIPHERS_FOR_576); } getConnectionWithProps(props); /* * case 3: verifying server certificate using key store provided by system properties */ props.clear(); props.setProperty("useSSL", "true"); props.setProperty("requireSSL", "true"); props.setProperty("verifyServerCertificate", "true"); String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; System.setProperty("javax.net.ssl.keyStore", trustStorePath); System.setProperty("javax.net.ssl.keyStorePassword", "password"); System.setProperty("javax.net.ssl.trustStore", trustStorePath); System.setProperty("javax.net.ssl.trustStorePassword", "password"); if (sslCipherSuitesReqFor576) { assertThrows(SQLException.class, Messages.getString("CommunicationsException.incompatibleSSLCipherSuites"), callableInstance); props.setProperty("enabledSSLCipherSuites", SSL_CIPHERS_FOR_576); } getConnectionWithProps(props); } /** * Tests fix for BUG#75592 - "SHOW VARIABLES WHERE" is expensive. * * @throws Exception * if the test fails. */ public void testBug75592() throws Exception { if (versionMeetsMinimum(5, 0, 3)) { MySQLConnection con = (MySQLConnection) getConnectionWithProps("statementInterceptors=" + Bug75592StatementInterceptor.class.getName()); // reference values Map<String, String> serverVariables = new HashMap<String, String>(); this.rs = con.createStatement().executeQuery("SHOW VARIABLES"); while (this.rs.next()) { serverVariables.put(this.rs.getString(1), this.rs.getString(2)); } // check values from "select @@var..." assertEquals(serverVariables.get("auto_increment_increment"), con.getServerVariable("auto_increment_increment")); assertEquals(serverVariables.get("character_set_client"), con.getServerVariable("character_set_client")); assertEquals(serverVariables.get("character_set_connection"), con.getServerVariable("character_set_connection")); // we override character_set_results sometimes when configuring client charsets, thus need to check against actual value if (con.getServerVariable(ConnectionImpl.JDBC_LOCAL_CHARACTER_SET_RESULTS) == null) { assertEquals("", serverVariables.get("character_set_results")); } else { assertEquals(serverVariables.get("character_set_results"), con.getServerVariable(ConnectionImpl.JDBC_LOCAL_CHARACTER_SET_RESULTS)); } assertEquals(serverVariables.get("character_set_server"), con.getServerVariable("character_set_server")); assertEquals(serverVariables.get("init_connect"), con.getServerVariable("init_connect")); assertEquals(serverVariables.get("interactive_timeout"), con.getServerVariable("interactive_timeout")); assertEquals(serverVariables.get("license"), con.getServerVariable("license")); assertEquals(serverVariables.get("lower_case_table_names"), con.getServerVariable("lower_case_table_names")); assertEquals(serverVariables.get("max_allowed_packet"), con.getServerVariable("max_allowed_packet")); assertEquals(serverVariables.get("net_buffer_length"), con.getServerVariable("net_buffer_length")); assertEquals(serverVariables.get("net_write_timeout"), con.getServerVariable("net_write_timeout")); assertEquals(serverVariables.get("query_cache_size"), con.getServerVariable("query_cache_size")); assertEquals(serverVariables.get("query_cache_type"), con.getServerVariable("query_cache_type")); // not necessarily contains STRICT_TRANS_TABLES for (String sm : serverVariables.get("sql_mode").split(",")) { if (!sm.equals("STRICT_TRANS_TABLES")) { assertTrue(con.getServerVariable("sql_mode").contains(sm)); } } assertEquals(serverVariables.get("system_time_zone"), con.getServerVariable("system_time_zone")); assertEquals(serverVariables.get("time_zone"), con.getServerVariable("time_zone")); assertEquals(serverVariables.get("tx_isolation"), con.getServerVariable("tx_isolation")); assertEquals(serverVariables.get("wait_timeout"), con.getServerVariable("wait_timeout")); if (!versionMeetsMinimum(5, 5, 0)) { assertEquals(serverVariables.get("language"), con.getServerVariable("language")); } } } /** * Statement interceptor for preceding testBug75592(). */ public static class Bug75592StatementInterceptor extends BaseStatementInterceptor { @Override public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) throws SQLException { if (sql.contains("SHOW VARIABLES WHERE")) { throw new SQLException("'SHOW VARIABLES WHERE' statement issued: " + sql); } return null; } } /** * Tests fix for BUG#20825727 - CONNECT FAILURE WHEN TRY TO CONNECT SHA USER WITH DIFFERENT CHARSET. * * This test runs through all authentication plugins when one of the following server requirements is met: * 1. Default connection string points to a server configured with both SSL *and* RSA encryption. * or * 2. Default connection string points to a server configured with SSL enabled but no RSA encryption *and* the property * com.mysql.jdbc.testsuite.url.sha256default points to an additional server configured with * default-authentication-plugin=sha256_password and RSA encryption. * * If none of the servers has SSL and RSA encryption enabled then only 'mysql_native_password' and 'mysql_old_password' plugins are tested. * * @throws Exception * if the test fails. */ public void testBug20825727() throws Exception { if (!versionMeetsMinimum(5, 5, 7)) { return; } final String[] testDbUrls; final String sha256defaultDbUrl = System.getProperty("com.mysql.jdbc.testsuite.url.sha256default"); Properties props = new Properties(); props.setProperty("allowPublicKeyRetrieval", "true"); if (sha256defaultDbUrl != null) { com.mysql.jdbc.Connection testConn = (com.mysql.jdbc.Connection) getConnectionWithProps(sha256defaultDbUrl, props); if (testConn.versionMeetsMinimum(5, 5, 7)) { testDbUrls = new String[] { BaseTestCase.dbUrl, sha256defaultDbUrl }; } else { testDbUrls = new String[] { BaseTestCase.dbUrl }; } testConn.close(); } else { testDbUrls = new String[] { BaseTestCase.dbUrl }; } for (String testDbUrl : testDbUrls) { com.mysql.jdbc.Connection testConn = (com.mysql.jdbc.Connection) getConnectionWithProps(testDbUrl, props); Statement testStmt = testConn.createStatement(); this.rs = testStmt.executeQuery("SELECT @@GLOBAL.HAVE_SSL = 'YES' AS have_ssl"); final boolean sslEnabled = this.rs.next() && this.rs.getBoolean(1); this.rs = testStmt.executeQuery("SHOW STATUS LIKE '%Rsa_public_key%'"); final boolean rsaEnabled = this.rs.next() && this.rs.getString(1).length() > 0; System.out.println(); System.out.println("* Testing URL: " + testDbUrl + " [SSL enabled: " + sslEnabled + "] [RSA enabled: " + rsaEnabled + "]"); System.out.println("******************************************************************************************************************************" + "*************"); System.out.printf("%-25s : %-25s : %s : %-25s : %-18s : %-18s [%s]%n", "Connection Type", "Auth. Plugin", "pwd ", "Encoding Prop.", "Encoding Value", "Server Encoding", "TstRes"); System.out.println("------------------------------------------------------------------------------------------------------------------------------" + "-------------"); boolean clearTextPluginInstalled = false; boolean secureAuthChanged = false; try { String[] plugins; // install cleartext plugin if required this.rs = testStmt.executeQuery("SELECT (PLUGIN_LIBRARY LIKE 'auth_test_plugin%') FROM INFORMATION_SCHEMA.PLUGINS" + " WHERE PLUGIN_NAME='cleartext_plugin_server'"); if (!this.rs.next() || !this.rs.getBoolean(1)) { String ext = System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") > -1 ? ".dll" : ".so"; testStmt.execute("INSTALL PLUGIN cleartext_plugin_server SONAME 'auth_test_plugin" + ext + "'"); clearTextPluginInstalled = true; } if (testConn.versionMeetsMinimum(5, 7, 5)) { // mysql_old_password plugin not supported plugins = new String[] { "cleartext_plugin_server,-1", "mysql_native_password,0", "sha256_password,2" }; } else if (testConn.versionMeetsMinimum(5, 6, 6)) { plugins = new String[] { "cleartext_plugin_server,-1", "mysql_native_password,0", "mysql_old_password,1", "sha256_password,2" }; // temporarily disable --secure-auth mode to allow old format passwords testStmt.executeUpdate("SET @current_secure_auth = @@global.secure_auth"); testStmt.executeUpdate("SET @@global.secure_auth = off"); secureAuthChanged = true; } else { // sha256_password plugin not supported plugins = new String[] { "cleartext_plugin_server,-1", "mysql_native_password,0", "mysql_old_password,1" }; } final String simplePwd = "my\tpass word"; final String complexPwd = "my\tp\u00e4ss w\u263ard"; for (String encoding : new String[] { "", "UTF-8", "ISO-8859-1", "US-ASCII" }) { for (String plugin : plugins) { String pluginName = plugin.split(",")[0]; int pwdHashingMethod = Integer.parseInt(plugin.split(",")[1]); String testStep = ""; try { testStep = "create user"; testBug20825727CreateUser(testDbUrl, "testBug20825727", simplePwd, encoding, pluginName, pwdHashingMethod); testStep = "login with simple password"; testBug20825727TestLogin(testDbUrl, testConn.getEncoding(), sslEnabled, rsaEnabled, "testBug20825727", simplePwd, encoding, pluginName); testStep = "change password"; testBug20825727ChangePassword(testDbUrl, "testBug20825727", complexPwd, encoding, pluginName, pwdHashingMethod); testStep = "login with complex password"; testBug20825727TestLogin(testDbUrl, testConn.getEncoding(), sslEnabled, rsaEnabled, "testBug20825727", complexPwd, encoding, pluginName); } catch (SQLException e) { e.printStackTrace(); fail("Failed at '" + testStep + "' using encoding '" + encoding + "' and plugin '" + pluginName + "'. See also system output for more details."); } finally { try { testStmt.execute("DROP USER 'testBug20825727'@'%'"); } catch (Exception e) { } } } } } finally { if (clearTextPluginInstalled) { testStmt.executeUpdate("UNINSTALL PLUGIN cleartext_plugin_server"); } if (secureAuthChanged) { testStmt.executeUpdate("SET @@global.secure_auth = @current_secure_auth"); } testStmt.close(); testConn.close(); } } } private void testBug20825727CreateUser(String testDbUrl, String user, String password, String encoding, String pluginName, int pwdHashingMethod) throws SQLException { com.mysql.jdbc.Connection testConn = null; try { Properties props = new Properties(); props.setProperty("allowPublicKeyRetrieval", "true"); if (encoding.length() > 0) { props.setProperty("characterEncoding", encoding); } testConn = (com.mysql.jdbc.Connection) getConnectionWithProps(testDbUrl, props); Statement testStmt = testConn.createStatement(); if (testConn.versionMeetsMinimum(5, 7, 6)) { testStmt.execute("CREATE USER '" + user + "'@'%' IDENTIFIED WITH " + pluginName + " BY '" + password + "'"); } else if (pwdHashingMethod >= 0) { // for mysql_native_password, mysql_old_password and sha256_password plugins testStmt.execute("CREATE USER '" + user + "'@'%' IDENTIFIED WITH " + pluginName); testStmt.execute("SET @@session.old_passwords = " + pwdHashingMethod); testStmt.execute("SET PASSWORD FOR '" + user + "'@'%' = PASSWORD('" + password + "')"); testStmt.execute("SET @@session.old_passwords = @@global.old_passwords"); } else { // for cleartext_plugin_server plugin testStmt.execute("CREATE USER '" + user + "'@'%' IDENTIFIED WITH " + pluginName + " AS '" + password + "'"); } testStmt.execute("GRANT ALL ON *.* TO '" + user + "'@'%'"); testStmt.close(); } finally { if (testConn != null) { testConn.close(); } } } private void testBug20825727ChangePassword(String testDbUrl, String user, String password, String encoding, String pluginName, int pwdHashingMethod) throws SQLException { com.mysql.jdbc.Connection testConn = null; try { Properties props = new Properties(); props.setProperty("allowPublicKeyRetrieval", "true"); if (encoding.length() > 0) { props.setProperty("characterEncoding", encoding); } testConn = (com.mysql.jdbc.Connection) getConnectionWithProps(testDbUrl, props); Statement testStmt = testConn.createStatement(); if (testConn.versionMeetsMinimum(5, 7, 6)) { testStmt.execute("ALTER USER '" + user + "'@'%' IDENTIFIED BY '" + password + "'"); } else if (pwdHashingMethod >= 0) { // for mysql_native_password, mysql_old_password and sha256_password plugins testStmt.execute("SET @@session.old_passwords = " + pwdHashingMethod); testStmt.execute("SET PASSWORD FOR '" + user + "'@'%' = PASSWORD('" + password + "')"); testStmt.execute("SET @@session.old_passwords = @@global.old_passwords"); } else { // for cleartext_plugin_server plugin testStmt.execute("DROP USER '" + user + "'@'%'"); testStmt.execute("CREATE USER '" + user + "'@'%' IDENTIFIED WITH " + pluginName + " AS '" + password + "'"); testStmt.execute("GRANT ALL ON *.* TO '" + user + "'@'%'"); } testStmt.close(); } finally { if (testConn != null) { testConn.close(); } } } private void testBug20825727TestLogin(final String testDbUrl, String defaultServerEncoding, boolean sslEnabled, boolean rsaEnabled, String user, String password, String encoding, String pluginName) throws SQLException { final Properties props = new Properties(); props.setProperty("allowPublicKeyRetrieval", "true"); final com.mysql.jdbc.MySQLConnection testBaseConn = (com.mysql.jdbc.MySQLConnection) getConnectionWithProps(testDbUrl, props); final boolean pwdIsComplex = !Charset.forName("US-ASCII").newEncoder().canEncode(password); for (String encProp : encoding.length() == 0 ? new String[] { "*none*" } : new String[] { "characterEncoding", "passwordCharacterEncoding" }) { for (int testCase = 1; testCase <= 4; testCase++) { props.setProperty("user", user); props.setProperty("password", password); if (encoding.length() > 0) { props.setProperty(encProp, encoding); } String testCaseMsg = "*none*"; switch (testCase) { case 1: /* * Test with an SSL disabled connection. * Can't be used with plugins 'cleartext_plugin_server' and 'sha256_password'. */ if (pluginName.equals("cleartext_plugin_server") || pluginName.equals("sha256_password")) { continue; } props.setProperty("allowPublicKeyRetrieval", "true"); props.setProperty("useSSL", "false"); props.setProperty("requireSSL", "false"); testCaseMsg = "Non-SSL/Non-RSA"; break; case 2: /* * Test with an SSL enabled connection. */ if (!sslEnabled) { continue; } props.setProperty("allowPublicKeyRetrieval", "false"); props.setProperty("useSSL", "true"); props.setProperty("requireSSL", "true"); props.setProperty("verifyServerCertificate", "false"); if (Util.getJVMVersion() < 8 && testBaseConn.versionMeetsMinimum(5, 7, 6) && Util.isCommunityEdition(testBaseConn.getServerVersion())) { props.setProperty("enabledSSLCipherSuites", SSL_CIPHERS_FOR_576); } testCaseMsg = "SSL"; break; case 3: /* * Test with an RSA encryption enabled connection, using public key retrieved from server. * Requires additional server instance pointed by 'com.mysql.jdbc.testsuite.url.sha256default'. * Can't be used with plugin 'cleartext_plugin_server'. */ if (pluginName.equals("cleartext_plugin_server") || !rsaEnabled) { continue; } props.setProperty("allowPublicKeyRetrieval", "true"); testCaseMsg = "RSA [pubkey-retrieval]"; break; case 4: /* * Test with an RSA encryption enabled connection, using public key pointed by the property 'serverRSAPublicKeyFile'. * Requires additional server instance pointed by 'com.mysql.jdbc.testsuite.url.sha256default'. * Can't be used with plugin 'cleartext_plugin_server'. */ if (pluginName.equals("cleartext_plugin_server") || !rsaEnabled) { continue; } props.setProperty("allowPublicKeyRetrieval", "false"); props.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); testCaseMsg = "RSA [pubkey-file]"; break; } boolean testShouldPass = true; if (pwdIsComplex) { // if no encoding is specifically defined then our default password encoding ('UTF-8') and server's encoding must coincide testShouldPass = encoding.length() > 0 || defaultServerEncoding.equalsIgnoreCase("UTF-8"); if (!testBaseConn.versionMeetsMinimum(5, 7, 6) && pluginName.equals("cleartext_plugin_server")) { // 'cleartext_plugin_server' from servers below version 5.7.6 requires UTF-8 encoding testShouldPass = encoding.equals("UTF-8") || (encoding.length() == 0 && defaultServerEncoding.equals("UTF-8")); } } System.out.printf("%-25s : %-25s : %s : %-25s : %-18s : %-18s [%s]%n", testCaseMsg, pluginName, pwdIsComplex ? "cplx" : "smpl", encProp, encoding.length() == 0 ? "-" : encoding, defaultServerEncoding, testShouldPass); Connection testConn = null; try { if (testShouldPass) { testConn = getConnectionWithProps(testDbUrl, props); Statement testStmt = testConn.createStatement(); this.rs = testStmt.executeQuery("SELECT USER(), CURRENT_USER()"); assertTrue(this.rs.next()); if (!this.rs.getString(1).startsWith(user) || !this.rs.getString(2).startsWith(user)) { fail("Unexpected failure in test case '" + testCaseMsg + "' using encoding '" + encoding + "' in property '" + encProp + "'."); } this.rs.close(); testStmt.close(); } else { assertThrows(SQLException.class, "Access denied for user 'testBug20825727'@.*", new Callable<Void>() { @SuppressWarnings("synthetic-access") public Void call() throws Exception { getConnectionWithProps(testDbUrl, props); return null; } }); } } finally { if (testConn != null) { try { testConn.close(); } catch (SQLException e) { } } } } } testBaseConn.close(); } }