/*
Copyright (c) 2002, 2013, 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.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import java.net.SocketException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.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.Properties;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import testsuite.BaseTestCase;
import testsuite.UnreliableSocketFactory;
import com.mysql.jdbc.AuthenticationPlugin;
import com.mysql.jdbc.Buffer;
import com.mysql.jdbc.CharsetMapping;
import com.mysql.jdbc.ConnectionImpl;
import com.mysql.jdbc.Driver;
import com.mysql.jdbc.LoadBalancingConnectionProxy;
import com.mysql.jdbc.Messages;
import com.mysql.jdbc.MySQLConnection;
import com.mysql.jdbc.MysqlDataTruncation;
import com.mysql.jdbc.MysqlErrorNumbers;
import com.mysql.jdbc.NonRegisteringDriver;
import com.mysql.jdbc.RandomBalanceStrategy;
import com.mysql.jdbc.ReplicationConnection;
import com.mysql.jdbc.SQLError;
import com.mysql.jdbc.StandardSocketFactory;
import com.mysql.jdbc.StringUtils;
import com.mysql.jdbc.TimeUtil;
import com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException;
import com.mysql.jdbc.exceptions.MySQLNonTransientException;
import com.mysql.jdbc.exceptions.MySQLSyntaxErrorException;
import com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker;
import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import com.mysql.jdbc.jdbc2.optional.MysqlXid;
import com.mysql.jdbc.jdbc2.optional.SuspendableXAConnection;
import com.mysql.jdbc.log.StandardLogger;
/**
* Regression tests for Connections
*
* @author Mark Matthews
* @version $Id: ConnectionRegressionTest.java,v 1.1.2.1 2005/05/13 18:58:38
* mmatthews Exp $
*/
public class ConnectionRegressionTest extends BaseTestCase {
/**
* DOCUMENT ME!
*
* @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);
}
/**
* DOCUMENT ME!
*
* @throws Exception
* ...
*/
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);
}
}
/**
* DOCUMENT ME!
*
* @throws Exception
* ...
*/
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();
StringBuffer createTableCommand = new StringBuffer(
"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);
StringBuffer newUrlToTestPortNum = new StringBuffer(
"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
//
StringBuffer newUrlToTestFailover = new StringBuffer(
"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(Driver.HOST_PROPERTY_KEY);
String port = urlProps.getProperty(Driver.PORT_PROPERTY_KEY);
props.remove(Driver.HOST_PROPERTY_KEY);
props.remove(Driver.NUM_HOSTS_PROPERTY_KEY);
props.remove(Driver.HOST_PROPERTY_KEY + ".1");
props.remove(Driver.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");
}
((com.mysql.jdbc.Connection) failoverConnection)
.clearHasTriedMaster();
UnreliableSocketFactory.dontDownHost("master");
failoverConnection.setAutoCommit(true);
String newConnectionId = getSingleIndexedValueWithQuery(
failoverConnection, 1, "SELECT CONNECTION_ID()").toString();
assertTrue(((com.mysql.jdbc.Connection) failoverConnection)
.hasTriedMaster());
assertTrue(!newConnectionId.equals(originalConnectionId));
failoverConnection.createStatement().executeQuery("SELECT 1");
} finally {
UnreliableSocketFactory.flushAllHostLists();
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);
((com.mysql.jdbc.Connection) failoverConnection)
.setPreferSlaveDuringFailover(true);
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)
.setPreferSlaveDuringFailover(false);
((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)
.setPreferSlaveDuringFailover(false);
((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";
if (versionMeetsMinimum(5, 0, 3)
|| versionMeetsMinimum(4, 1, 11)) {
charsetToCheck = "windows-31j";
}
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);
StringBuffer queryBuf = new StringBuffer("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 {
StringBuffer urlBuf = new StringBuffer(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");
StringBuffer newHostBuf = new StringBuffer();
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;
StringBuffer logBuf = new StringBuffer();
StandardLogger.bufferedLog = logBuf;
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 = logBuf.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.bufferedLog = null;
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);
StringBuffer missingSettersBuf = new StringBuffer();
StringBuffer missingGettersBuf = new StringBuffer();
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;
}
StringBuffer mutatorName = new StringBuffer("set");
mutatorName.append(Character.toUpperCase(propertyName.charAt(0)));
mutatorName.append(propertyName.substring(1));
StringBuffer accessorName = new StringBuffer("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");
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 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";
_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 {
StringBuffer logBuf = new StringBuffer();
Properties props = new Properties();
props.setProperty("profileSQL", "true");
props.setProperty("logger", "StandardLogger");
StandardLogger.bufferedLog = logBuf;
Connection loggedConn = null;
try {
loggedConn = getConnectionWithProps(props);
loggedConn.getTransactionIsolation();
if (versionMeetsMinimum(4, 0, 3)) {
assertEquals(
-1,
logBuf.toString().indexOf(
"SHOW VARIABLES LIKE 'tx_isolation'"));
}
} finally {
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");
Connection failoverConn = null;
Statement failoverStmt = null;
try {
failoverConn = getConnectionWithProps(getMasterSlaveUrl(), props);
((com.mysql.jdbc.Connection) failoverConn)
.setPreferSlaveDuringFailover(true);
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());
((com.mysql.jdbc.Connection) failoverConn)
.setPreferSlaveDuringFailover(false);
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);
assertTrue(!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();
StringBuffer urlBuf = new StringBuffer();
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", 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 testBug43421() 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", conn2);
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.flushAllHostLists();
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", 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, false);
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, false);
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.flushAllHostLists();
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.flushAllHostLists();
}
}
public void testBug32216() throws Exception {
checkBug32216("www.mysql.com", "12345", "my_database");
checkBug32216("www.mysql.com", null, "my_database");
}
private void checkBug32216(String host, String port, String dbname)
throws SQLException {
NonRegisteringDriver driver = new NonRegisteringDriver();
StringBuffer url = new StringBuffer("jdbc:mysql://");
url.append(host);
if (port != null) {
url.append(':');
url.append(port);
}
url.append('/');
url.append(dbname);
Properties result = driver.parseURL(url.toString(), new Properties());
assertEquals("hostname not equal", host,
result.getProperty(Driver.HOST_PROPERTY_KEY));
if (port != null) {
assertEquals("port not equal", port,
result.getProperty(Driver.PORT_PROPERTY_KEY));
} else {
assertEquals("port default incorrect", "3306",
result.getProperty(Driver.PORT_PROPERTY_KEY));
}
assertEquals("dbname not equal", dbname,
result.getProperty(Driver.DBNAME_PROPERTY_KEY));
}
public void testBug44324() throws Exception {
createTable(
"bug44324",
"(Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, SomeVChar VARCHAR(10)) ENGINE=MyISAM;");
try {
this.stmt
.executeUpdate("INSERT INTO bug44324 values (null, 'Some text much longer than 10 characters')");
} catch (MysqlDataTruncation sqlEx) {
assertTrue(0 != sqlEx.getErrorCode());
}
}
public void testBug46925() throws Exception {
MysqlXADataSource xads1 = new MysqlXADataSource();
MysqlXADataSource xads2 = new MysqlXADataSource();
Xid txid = new MysqlXid(new byte[] { 0x1 }, new byte[] { 0xf }, 3306);
xads1.setPinGlobalTxToPhysicalConnection(true);
xads1.setUrl(dbUrl);
xads2.setPinGlobalTxToPhysicalConnection(true);
xads2.setUrl(dbUrl);
XAConnection c1 = xads1.getXAConnection();
assertTrue(c1 instanceof SuspendableXAConnection);
// start a transaction on one connection
c1.getXAResource().start(txid, XAResource.TMNOFLAGS);
c1.getXAResource().end(txid, XAResource.TMSUCCESS);
XAConnection c2 = xads2.getXAConnection();
assertTrue(c2 instanceof SuspendableXAConnection);
// prepare on another one. Since we are using a "pinned" connection
// we should have the same "currentXAConnection" for both
// SuspendableXAConnection
c2.getXAResource().prepare(txid); // this will fail without the fix.
c2.getXAResource().commit(txid, false);
}
public void testBug47494() throws Exception {
try {
getConnectionWithProps("jdbc:mysql://localhost:9999/test?socketFactory=testsuite.regression.ConnectionRegressionTest$PortNumberSocketFactory");
} catch (SQLException sqlEx) {
assertTrue(sqlEx.getCause() instanceof IOException);
}
try {
getConnectionWithProps("jdbc:mysql://:9999/test?socketFactory=testsuite.regression.ConnectionRegressionTest$PortNumberSocketFactory");
} catch (SQLException sqlEx) {
assertTrue(sqlEx.getCause() instanceof IOException);
}
try {
getConnectionWithProps("jdbc:mysql://:9999,:9999/test?socketFactory=testsuite.regression.ConnectionRegressionTest$PortNumberSocketFactory");
} catch (SQLException sqlEx) {
assertTrue(sqlEx.getCause() instanceof IOException);
}
try {
getConnectionWithProps("jdbc:mysql://localhost:9999,localhost:9999/test?socketFactory=testsuite.regression.ConnectionRegressionTest$PortNumberSocketFactory");
} catch (SQLException sqlEx) {
assertTrue(sqlEx.getCause() instanceof IOException);
}
}
public static class PortNumberSocketFactory extends StandardSocketFactory {
public PortNumberSocketFactory() {
}
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 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) {
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;
}
public void destroy() {
super.destroy();
}
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;
}
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);
}
public void destroy() {
super.destroy();
}
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(Driver.HOST_PROPERTY_KEY);
String port = urlProps.getProperty(Driver.PORT_PROPERTY_KEY);
props.remove(Driver.HOST_PROPERTY_KEY);
props.remove(Driver.NUM_HOSTS_PROPERTY_KEY);
props.remove(Driver.HOST_PROPERTY_KEY + ".1");
props.remove(Driver.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(Driver.HOST_PROPERTY_KEY);
String port = urlProps.getProperty(Driver.PORT_PROPERTY_KEY);
props.remove(Driver.HOST_PROPERTY_KEY);
props.remove(Driver.NUM_HOSTS_PROPERTY_KEY);
props.remove(Driver.HOST_PROPERTY_KEY + ".1");
props.remove(Driver.PORT_PROPERTY_KEY + ".1");
props.setProperty("queriesBeforeRetryMaster", "0");
props.setProperty("failOverReadOnly", "false");
props.setProperty("secondsBeforeRetryMaster", "1");
UnreliableSocketFactory.mapHost("master", host);
UnreliableSocketFactory.mapHost("slave", host);
Connection failoverConnection = null;
try {
failoverConnection = getConnectionWithProps("jdbc:mysql://master:"
+ port + ",slave:" + port + "/", props);
failoverConnection.setAutoCommit(false);
assertTrue(((com.mysql.jdbc.Connection)failoverConnection).isMasterConnection());
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);
assertTrue(!((com.mysql.jdbc.Connection)failoverConnection).isMasterConnection());
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");
assertTrue(((com.mysql.jdbc.Connection)failoverConnection).isMasterConnection());
failoverConnection.createStatement().executeQuery("SELECT 1");
} finally {
UnreliableSocketFactory.flushAllHostLists();
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();
for (int i = 0; i < 500; i++) {
((com.mysql.jdbc.Connection) this.conn).changeUser(props.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY), props.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY));
if (i % 10 == 0) {
try {
((com.mysql.jdbc.Connection) this.conn).changeUser("bubba", props.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY));
} catch (SQLException sqlEx) {
// expect
}
}
this.stmt.executeQuery("SELECT 1");
}
}
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(Driver.HOST_PROPERTY_KEY);
String port = urlProps.getProperty(Driver.PORT_PROPERTY_KEY);
props.remove(Driver.HOST_PROPERTY_KEY);
props.remove(Driver.NUM_HOSTS_PROPERTY_KEY);
props.remove(Driver.HOST_PROPERTY_KEY + ".1");
props.remove(Driver.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 (rs.next()) {
if (!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+"'");
}
String dbname = null;
this.rs = this.stmt.executeQuery("select database() as dbname");
if(this.rs.first()) {
dbname = this.rs.getString("dbname");
}
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");
Properties 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 (rs.next()) {
if (!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+"'");
}
String dbname = null;
this.rs = this.stmt.executeQuery("select database() as dbname");
if(this.rs.first()) {
dbname = this.rs.getString("dbname");
}
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");
Properties 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 (rs.next()) {
if (!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+"'");
}
String dbname = null;
this.rs = this.stmt.executeQuery("select database() as dbname");
if(this.rs.first()) {
dbname = this.rs.getString("dbname");
}
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");
Properties 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(counter>2 ? this.password : "wrongpassword"+counter));
toServer.add(bresp);
} else {
Buffer bresp = new Buffer(fromServer.getByteBuffer());
toServer.add(bresp);
}
return true;
}
}
public void testOldPasswordPlugin() throws Exception {
boolean secure_auth = false;
this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'secure_auth'");
while (this.rs.next()) {
secure_auth = "ON".equalsIgnoreCase(this.rs.getString(2));
}
if (versionMeetsMinimum(5, 5, 7) && !secure_auth) {
String dbname = null;
this.rs = this.stmt.executeQuery("select database() as dbname");
if(this.rs.first()) {
dbname = this.rs.getString("dbname");
}
if (dbname == null) assertTrue("No database selected", false);
Connection adminConn = null;
Statement adminStmt = null;
try {
testOldPasswordPlugin_createUsers(this.stmt, dbname);
} catch (Exception e) {
adminConn = getAdminConnection();
if (adminConn == null) {
assertTrue("Lack of grant permissions. Change default user or set com.mysql.jdbc.testsuite.admin-url property.", false);
} else {
adminStmt = adminConn.createStatement();
testOldPasswordPlugin_createUsers(adminStmt, dbname);
}
}
Properties props = new Properties();
props.setProperty("user", "bug64983user1");
props.setProperty("password", "pwd");
props.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.MysqlOldPasswordPlugin");
Connection testConn = null;
Statement testSt = null;
ResultSet testRs = null;
try {
testConn = getConnectionWithProps(props);
testSt = testConn.createStatement();
testRs = testSt.executeQuery("select USER()");
testRs.next();
assertEquals("bug64983user1", testRs.getString(1).split("@")[0]);
((MySQLConnection)testConn).changeUser("bug64983user2", "");
testRs = testSt.executeQuery("select USER()");
testRs.next();
assertEquals("bug64983user2", testRs.getString(1).split("@")[0]);
} finally {
if (testRs != null) {
testRs.close();
}
if (testSt != null) {
testSt.close();
}
if (testConn != null) {
testConn.close();
}
if (adminStmt != null) {
adminStmt.executeUpdate("drop user 'bug64983user1'@'%'");
adminStmt.executeUpdate("drop user 'bug64983user2'@'%'");
adminStmt.close();
} else {
this.stmt.executeUpdate("drop user 'bug64983user1'@'%'");
this.stmt.executeUpdate("drop user 'bug64983user2'@'%'");
}
if (adminConn != null) {
adminConn.close();
}
}
}
}
public void testOldPasswordPlugin_createUsers(Statement adminStmt, String dbname) throws Exception {
adminStmt.executeUpdate("grant usage on *.* to 'bug64983user1'@'%'");
adminStmt.executeUpdate("set password for 'bug64983user1'@'%' = OLD_PASSWORD('pwd')");
adminStmt.executeUpdate("delete from mysql.db where user='bug64983user1'");
adminStmt.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+"', 'bug64983user1', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')");
adminStmt.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', 'bug64983user1', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')");
adminStmt.executeUpdate("grant usage on *.* to 'bug64983user2'@'%'");
adminStmt.executeUpdate("set password for 'bug64983user2'@'%' = OLD_PASSWORD('')");
adminStmt.executeUpdate("delete from mysql.db where user='bug64983user2'");
adminStmt.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+"', 'bug64983user2', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')");
adminStmt.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', 'bug64983user2', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')");
adminStmt.executeUpdate("flush privileges");
}
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 (rs.next()) {
if (!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+"'");
}
String dbname = null;
this.rs = this.stmt.executeQuery("select database() as dbname");
if(this.rs.first()) {
dbname = this.rs.getString("dbname");
}
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");
Properties props = new Properties();
props.setProperty("user", "wl5735user");
props.setProperty("password", "");
Connection testConn = null;
Statement testSt = null;
ResultSet testRs = null;
try {
try {
testConn = getConnectionWithProps(props);
assertFalse("SQLException expected due to SSL connection is required", true);
} catch (SQLException e) {
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("requireSSL", "true");
testConn = getConnectionWithProps(props);
}
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");
}
}
}
}
public void testSha256PasswordPlugin() throws Exception {
if (versionMeetsMinimum(5, 6, 5)) {
// check that sha256_password plugin is available
boolean plugin_is_active = false;
this.rs = this.stmt.executeQuery("select (PLUGIN_STATUS='ACTIVE') as `TRUE` from INFORMATION_SCHEMA.PLUGINS where PLUGIN_NAME='sha256_password'");
if (rs.next()) {
plugin_is_active = rs.getBoolean(1);
}
if (plugin_is_active) {
try {
String dbname = null;
this.rs = this.stmt.executeQuery("select database() as dbname");
if(this.rs.first()) {
dbname = this.rs.getString("dbname");
}
if (dbname == null) assertTrue("No database selected", false);
// create proxy users
this.stmt.executeUpdate("SET @current_old_passwords = @@global.old_passwords");
this.stmt.executeUpdate("grant usage on *.* to 'wl5602user'@'%' identified WITH sha256_password");
this.stmt.executeUpdate("SET GLOBAL old_passwords= 2");
this.stmt.executeUpdate("SET SESSION old_passwords= 2");
this.stmt.executeUpdate("set password for 'wl5602user'@'%' = PASSWORD('pwd')");
this.stmt.executeUpdate("delete from mysql.db where user='wl5602user'");
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+"', 'wl5602user', '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', 'wl5602user', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')");
this.stmt.executeUpdate("flush privileges");
Properties props = new Properties();
props.setProperty("user", "wl5602user");
props.setProperty("password", "pwd");
Connection testConn = null;
Statement testSt = null;
ResultSet testRs = null;
try {
try {
testConn = getConnectionWithProps(props);
assertFalse("SQLException expected due to SSL connection is required", true);
} catch (SQLException e) {
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("requireSSL", "true");
testConn = getConnectionWithProps(props);
}
testSt = testConn.createStatement();
testRs = testSt.executeQuery("select USER(),CURRENT_USER()");
testRs.next();
assertEquals("wl5602user", testRs.getString(1).split("@")[0]);
assertEquals("wl5602user", 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 'wl5602user'@'%'");
this.stmt.executeUpdate("flush privileges");
this.stmt.executeUpdate("SET GLOBAL old_passwords = @current_old_passwords");
}
}
}
}
public void testBug36662() throws Exception {
try {
String tz1 = TimeUtil.getCanoncialTimezone("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.getCanoncialTimezone("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", "ISO8859-1");
try {
_conn = getConnectionWithProps(props);
assertTrue("This point should not be reached.", false);
} catch (Exception e) {
assertEquals(
"Can't map ISO8859-1 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)) {
String dbname = null;
this.rs = this.stmt.executeQuery("select database() as dbname");
if(this.rs.first()) {
dbname = this.rs.getString("dbname");
}
if (dbname == null) assertTrue("No database selected", false);
Properties 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+"`.`ほげほげ`");
} catch (SQLException e1) {
if (e1.getClass().getName().endsWith("MySQLSyntaxErrorException")) {
assertEquals("Table '"+dbname+".ほげほげ' 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+"`.`ほげほげ`");
} catch (SQLException e2) {
if (e2.getClass().getName().endsWith("MySQLSyntaxErrorException")) {
assertEquals("\u0422\u0430\u0431\u043b\u0438\u0446\u0430 '"+dbname+".ほげほげ' \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();
}
}
}
public void testIsLocal() throws Exception {
boolean normalState = ((ConnectionImpl) 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();
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))");
StringBuffer fileNameBuf = null;
if (File.separatorChar == '\\') {
fileNameBuf = new StringBuffer();
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 StringBuffer(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.getMysqlEncodingForJavaEncoding(((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(Driver.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("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("ALTER USER 'must_change1'@'%' PASSWORD EXPIRE, 'must_change2'@'%' PASSWORD EXPIRE");
this.pstmt.executeUpdate();
this.pstmt.close();
this.pstmt = testConn.prepareStatement("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("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("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 {
rs = failoverconnection[i].createStatement().executeQuery("SELECT 1");
} catch (Exception e1) {
try {
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");
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");
Connection conn2 = this.getUnreliableReplicationConnection(
new String[] { "master", "slave1", "slave2" }, props);
((ReplicationConnection) conn2).isMasterConnection();
assertTrue("Is not actually on master!", ((ReplicationConnection) 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.flushAllHostLists();
// peg connection to slave2:
ForcedLoadBalanceStrategy.forceFutureServer("slave2:" + portNumber, -1);
conn2.commit();
rs = conn2.createStatement().executeQuery("SELECT CONNECTION_ID()");
rs.next();
int slave2id = 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");
rs = conn2.createStatement().executeQuery("SELECT time FROM information_schema.processlist WHERE id = " + slave2id);
rs.next();
assertTrue("Processlist should be less than 2 seconds due to ping", 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.flushAllHostLists();
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");
// 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("/* ping */ 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 = c.createStatement();
this.pst1 = 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(c.getClass())) {
try {
if (this.num == 7 || this.num == 10) {
Proxy.getInvocationHandler(c).invoke(c, Connection.class.getMethod("close", new Class[]{}), null);
} else if (this.num == 8 || this.num == 11) {
Proxy.getInvocationHandler(c).invoke(c, MySQLConnection.class.getMethod("abortInternal", new Class[]{}), null);
} else if (this.num == 9 || this.num == 12) {
Proxy.getInvocationHandler(c).invoke(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(Driver.HOST_PROPERTY_KEY);
String port = urlProps.getProperty(Driver.PORT_PROPERTY_KEY);
baseprops.remove(Driver.HOST_PROPERTY_KEY);
baseprops.remove(Driver.NUM_HOSTS_PROPERTY_KEY);
baseprops.remove(Driver.HOST_PROPERTY_KEY + ".1");
baseprops.remove(Driver.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;
}
}