Skip to content

Commit af0aa94

Browse files
committed
Fix for Bug#113413 (Bug#36107426), Connection.changeUser cannot be done after DriverManager.loginTimeout elapses.
Change-Id: I0ec337027dcfc5eab25cdb19917192ee1a4d2a29
1 parent e9dfbe8 commit af0aa94

8 files changed

Lines changed: 76 additions & 52 deletions

File tree

CHANGES

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
Version 9.6.0
55

6+
- Fix for Bug#113413 (Bug#36107426), Connection.changeUser cannot be done after DriverManager.loginTimeout elapses.
7+
Thanks to Kazuhisa Kawashima for his contribution.
8+
69
- Fix for Bug#119271 (Bug#38599496), Connector/J fails to accept legacy value zeroDateTimeBehavior=convertToNull on multi-host URLs (failover).
710

811
- Fix for Bug#119245 (Bug#38599240), Select into fix breaks queries with 'into' in them.

src/main/core-api/java/com/mysql/cj/protocol/Protocol.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,6 @@ public interface Protocol<M extends Message> {
104104

105105
void afterHandshake();
106106

107-
void changeDatabase(String database);
108-
109107
/**
110108
* Re-authenticates as the given user and password
111109
*

src/main/protocol-impl/java/com/mysql/cj/protocol/a/DebugBufferingPacketReader.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ private NativePacketHeader readHeaderLocal(byte prevPacketSeq, NativePacketHeade
7878
throw new IOException(Messages.getString("PacketReader.9", new Object[] { "-1", currPacketSeq }));
7979
}
8080

81-
if (currPacketSeq != -128 && prevPacketSeq != -1 && currPacketSeq != prevPacketSeq + 1) {
82-
throw new IOException(Messages.getString("PacketReader.9", new Object[] { prevPacketSeq + 1, currPacketSeq }));
81+
if (currPacketSeq != -128 && prevPacketSeq != -1 && (currPacketSeq < prevPacketSeq + 1 || currPacketSeq > prevPacketSeq + 2)) {
82+
throw new IOException(Messages.getString("PacketReader.9", new Object[] { prevPacketSeq + 1, prevPacketSeq + 2, currPacketSeq }));
8383
}
8484

8585
} else {

src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeAuthenticationProvider.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -524,12 +524,6 @@ private void proceedHandshakeWithPluggableAuthentication(final NativePacketPaylo
524524
throw ExceptionFactory.createException(WrongArgumentException.class,
525525
Messages.getString("CommunicationsException.TooManyAuthenticationPluginNegotiations"), getExceptionInterceptor());
526526
}
527-
528-
this.protocol.afterHandshake();
529-
530-
if (!this.useConnectWithDb) {
531-
this.protocol.changeDatabase(this.database);
532-
}
533527
}
534528

535529
private String getNthFactorPassword(int nthFactor) {
@@ -653,9 +647,9 @@ private NativePacketPayload createChangeUserPacket(ServerSession serverSession,
653647
String enc = serverSession.getCharsetSettings().getPasswordCharacterEncoding();
654648

655649
NativePacketPayload last_sent = new NativePacketPayload(AUTH_411_OVERHEAD + 7 //
656-
+ 48 // passwordLength
657-
+ 3 * this.username.length() // userLength
658-
+ (this.useConnectWithDb ? 3 * this.database.length() : 1) // databaseLength
650+
+ 48 // passwordLength
651+
+ 3 * this.username.length() // userLength
652+
+ (this.useConnectWithDb ? 3 * this.database.length() : 1) // databaseLength
659653
+ 1);
660654
last_sent.writeInteger(IntegerDataType.INT1, NativeConstants.COM_CHANGE_USER);
661655

src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeProtocol.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -527,8 +527,7 @@ public NativeServerSession getServerSession() {
527527
return this.serverSession;
528528
}
529529

530-
@Override
531-
public void changeDatabase(String database) {
530+
private void changeDatabase(String database) {
532531
if (database == null || database.length() == 0) {
533532
return;
534533
}
@@ -1280,10 +1279,13 @@ private void calculateSlowQueryThreshold() {
12801279
@Override
12811280
public void changeUser(String user, String password, String database) {
12821281
this.packetSequence = -1;
1283-
this.packetSender = this.packetSender.undecorateAll();
1284-
this.packetReader = this.packetReader.undecorateAll();
1282+
this.packetReader.resetMessageSequence();
12851283

12861284
this.authProvider.changeUser(user, password, database);
1285+
1286+
if (this.propertySet.getBooleanProperty(PropertyKey.createDatabaseIfNotExist).getValue()) {
1287+
changeDatabase(database);
1288+
}
12871289
}
12881290

12891291
protected boolean useNanosForElapsedTime() {
@@ -1330,6 +1332,12 @@ public void connect(String user, String password, String database) {
13301332
beforeHandshake();
13311333

13321334
this.authProvider.connect(user, password, database);
1335+
1336+
afterHandshake();
1337+
1338+
if (this.propertySet.getBooleanProperty(PropertyKey.createDatabaseIfNotExist).getValue()) {
1339+
changeDatabase(database);
1340+
}
13331341
}
13341342

13351343
protected boolean isDataAvailable() {

src/main/protocol-impl/java/com/mysql/cj/protocol/x/XProtocol.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -972,12 +972,6 @@ public ExceptionInterceptor getExceptionInterceptor() {
972972
throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported");
973973
}
974974

975-
@Override
976-
public void changeDatabase(String database) {
977-
throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported");
978-
// TODO: Figure out how this is relevant for X Protocol client Session
979-
}
980-
981975
@Override
982976
public boolean versionMeetsMinimum(int major, int minor, int subminor) {
983977
//TODO: expose this via ServerVersion so calls look like x.getServerVersion().meetsMinimum(major, minor, subminor)

src/main/resources/com/mysql/cj/LocalizedErrorMessages.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ PacketReader.5=reuseAndReadPacket() payload:\n
361361
PacketReader.6=readPacket() payload:\n
362362
PacketReader.7=\n\nLarge packet dump truncated at
363363
PacketReader.8=\ bytes.
364-
PacketReader.9=Packets out of order, expected packet # {0}, but received packet # {1}
364+
PacketReader.9=Packets out of order, expected packet # {0} or # {1}, but received packet # {2}
365365
PacketReader.10=Packets received out of order
366366

367367
PreparedQuery.0=SQL String cannot be NULL

src/test/java/testsuite/regression/ConnectionRegressionTest.java

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -635,14 +635,14 @@ public void testBug7952() throws Exception {
635635
assertTrue("08S01".equals(sqlEx.getSQLState()));
636636
}
637637

638-
((com.mysql.cj.jdbc.JdbcConnection) failoverConnection).setFailedOver(true);
638+
((JdbcConnection) failoverConnection).setFailedOver(true);
639639

640640
failoverConnection.setAutoCommit(true);
641641

642642
String failedConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString();
643643
System.out.println("Failed over connection id: " + failedConnectionId);
644644

645-
((com.mysql.cj.jdbc.JdbcConnection) failoverConnection).setFailedOver(true);
645+
((JdbcConnection) failoverConnection).setFailedOver(true);
646646

647647
for (int i = 0; i < 30; i++) {
648648
failoverConnection.setAutoCommit(true);
@@ -689,13 +689,13 @@ public void testBug10144() throws Exception {
689689

690690
Connection bareConn = getConnectionWithProps(props);
691691

692-
int currentOpenStatements = ((com.mysql.cj.jdbc.JdbcConnection) bareConn).getActiveStatementCount();
692+
int currentOpenStatements = ((JdbcConnection) bareConn).getActiveStatementCount();
693693

694694
try {
695695
bareConn.prepareStatement("Boo!");
696696
fail("Should not've been able to prepare that one!");
697697
} catch (SQLException sqlEx) {
698-
assertEquals(currentOpenStatements, ((com.mysql.cj.jdbc.JdbcConnection) bareConn).getActiveStatementCount());
698+
assertEquals(currentOpenStatements, ((JdbcConnection) bareConn).getActiveStatementCount());
699699
} finally {
700700
bareConn.close();
701701
}
@@ -2251,14 +2251,14 @@ public void testBug48442() throws Exception {
22512251
public void testBug45171() throws Exception {
22522252
List<Statement> statementsToTest = new LinkedList<>();
22532253
statementsToTest.add(this.conn.createStatement());
2254-
statementsToTest.add(((com.mysql.cj.jdbc.JdbcConnection) this.conn).clientPrepareStatement("SELECT 1"));
2255-
statementsToTest.add(((com.mysql.cj.jdbc.JdbcConnection) this.conn).clientPrepareStatement("SELECT 1", Statement.RETURN_GENERATED_KEYS));
2256-
statementsToTest.add(((com.mysql.cj.jdbc.JdbcConnection) this.conn).clientPrepareStatement("SELECT 1", new int[0]));
2257-
statementsToTest.add(((com.mysql.cj.jdbc.JdbcConnection) this.conn).clientPrepareStatement("SELECT 1", new String[0]));
2258-
statementsToTest.add(((com.mysql.cj.jdbc.JdbcConnection) this.conn).serverPrepareStatement("SELECT 1"));
2259-
statementsToTest.add(((com.mysql.cj.jdbc.JdbcConnection) this.conn).serverPrepareStatement("SELECT 1", Statement.RETURN_GENERATED_KEYS));
2260-
statementsToTest.add(((com.mysql.cj.jdbc.JdbcConnection) this.conn).serverPrepareStatement("SELECT 1", new int[0]));
2261-
statementsToTest.add(((com.mysql.cj.jdbc.JdbcConnection) this.conn).serverPrepareStatement("SELECT 1", new String[0]));
2254+
statementsToTest.add(((JdbcConnection) this.conn).clientPrepareStatement("SELECT 1"));
2255+
statementsToTest.add(((JdbcConnection) this.conn).clientPrepareStatement("SELECT 1", Statement.RETURN_GENERATED_KEYS));
2256+
statementsToTest.add(((JdbcConnection) this.conn).clientPrepareStatement("SELECT 1", new int[0]));
2257+
statementsToTest.add(((JdbcConnection) this.conn).clientPrepareStatement("SELECT 1", new String[0]));
2258+
statementsToTest.add(((JdbcConnection) this.conn).serverPrepareStatement("SELECT 1"));
2259+
statementsToTest.add(((JdbcConnection) this.conn).serverPrepareStatement("SELECT 1", Statement.RETURN_GENERATED_KEYS));
2260+
statementsToTest.add(((JdbcConnection) this.conn).serverPrepareStatement("SELECT 1", new int[0]));
2261+
statementsToTest.add(((JdbcConnection) this.conn).serverPrepareStatement("SELECT 1", new String[0]));
22622262

22632263
for (Statement toTest : statementsToTest) {
22642264
assertEquals(toTest.getResultSetType(), ResultSet.TYPE_FORWARD_ONLY);
@@ -2550,7 +2550,7 @@ public void testBug49700() throws Exception {
25502550
props.setProperty(PropertyKey.sessionVariables.getKeyName(), "@foo='bar'");
25512551
Connection c = getConnectionWithProps(props);
25522552
assertEquals("bar", getSingleIndexedValueWithQuery(c, 1, "SELECT @foo"));
2553-
((com.mysql.cj.jdbc.JdbcConnection) c).resetServerState();
2553+
((JdbcConnection) c).resetServerState();
25542554
assertEquals("bar", getSingleIndexedValueWithQuery(c, 1, "SELECT @foo"));
25552555
}
25562556

@@ -2930,7 +2930,7 @@ public void testStatementComment() throws Exception {
29302930
PrintStream printStream = new PrintStream(bOut);
29312931
System.setErr(printStream);
29322932

2933-
((com.mysql.cj.jdbc.JdbcConnection) c).setStatementComment("Hi there");
2933+
((JdbcConnection) c).setStatementComment("Hi there");
29342934
c.setAutoCommit(false);
29352935

29362936
c.createStatement().execute("SELECT 1");
@@ -3004,12 +3004,11 @@ public void testChangeUser() throws Exception {
30043004
Statement testStmt = testConn.createStatement();
30053005

30063006
for (int i = 0; i < 500; i++) {
3007-
((com.mysql.cj.jdbc.JdbcConnection) testConn).changeUser(props.getProperty(PropertyKey.USER.getKeyName()),
3008-
props.getProperty(PropertyKey.PASSWORD.getKeyName()));
3007+
((JdbcConnection) testConn).changeUser(props.getProperty(PropertyKey.USER.getKeyName()), props.getProperty(PropertyKey.PASSWORD.getKeyName()));
30093008

30103009
if (i % 10 == 0) {
30113010
try {
3012-
((com.mysql.cj.jdbc.JdbcConnection) testConn).changeUser("bubba", props.getProperty(PropertyKey.PASSWORD.getKeyName()));
3011+
((JdbcConnection) testConn).changeUser("bubba", props.getProperty(PropertyKey.PASSWORD.getKeyName()));
30133012
} catch (SQLException sqlEx) {
30143013
sqlEx.printStackTrace();
30153014
assertTrue(testConn.isClosed());
@@ -3037,14 +3036,13 @@ public void testChangeUserNoDb() throws Exception {
30373036

30383037
Connection con = getConnectionWithProps(props);
30393038

3040-
this.rs = this.stmt.executeQuery("show databases like '" + databaseName + "'");
3039+
this.rs = this.stmt.executeQuery("SHOW DATABASES LIKE '" + databaseName + "'");
30413040
assertTrue(this.rs.next(), "Database " + databaseName + " is not found.");
30423041
assertEquals(databaseName, this.rs.getString(1));
30433042

3044-
((com.mysql.cj.jdbc.JdbcConnection) con).changeUser(props.getProperty(PropertyKey.USER.getKeyName()),
3045-
props.getProperty(PropertyKey.PASSWORD.getKeyName()));
3043+
((JdbcConnection) con).changeUser(props.getProperty(PropertyKey.USER.getKeyName()), props.getProperty(PropertyKey.PASSWORD.getKeyName()));
30463044

3047-
this.rs = con.createStatement().executeQuery("select DATABASE()");
3045+
this.rs = con.createStatement().executeQuery("SELECT DATABASE()");
30483046
assertTrue(this.rs.next());
30493047
assertEquals(databaseName, this.rs.getString(1));
30503048

@@ -3063,8 +3061,7 @@ public void testChangeUserClosedConn() throws Exception {
30633061

30643062
try {
30653063
newConn.close();
3066-
((com.mysql.cj.jdbc.JdbcConnection) newConn).changeUser(props.getProperty(PropertyKey.USER.getKeyName()),
3067-
props.getProperty(PropertyKey.PASSWORD.getKeyName()));
3064+
((JdbcConnection) newConn).changeUser(props.getProperty(PropertyKey.USER.getKeyName()), props.getProperty(PropertyKey.PASSWORD.getKeyName()));
30683065
fail("Expected SQL Exception");
30693066
} catch (SQLException ex) {
30703067
// expected
@@ -3103,7 +3100,7 @@ public void testBug63284() throws Exception {
31033100

31043101
failoverConnection2 = getConnectionWithProps("jdbc:mysql://source:" + port + ",replica:" + port + "/", props);
31053102

3106-
assertTrue(((com.mysql.cj.jdbc.JdbcConnection) failoverConnection1).isSourceConnection());
3103+
assertTrue(((JdbcConnection) failoverConnection1).isSourceConnection());
31073104

31083105
// Two different Connection objects should not equal each other:
31093106
assertFalse(failoverConnection1.equals(failoverConnection2));
@@ -3120,7 +3117,7 @@ public void testBug63284() throws Exception {
31203117
}
31213118
}
31223119
// ensure we're now connected to the replica
3123-
assertFalse(((com.mysql.cj.jdbc.JdbcConnection) failoverConnection1).isSourceConnection());
3120+
assertFalse(((JdbcConnection) failoverConnection1).isSourceConnection());
31243121

31253122
// ensure that hashCode() result is persistent across failover events when proxy state changes
31263123
assertEquals(hc, failoverConnection1.hashCode());
@@ -4896,7 +4893,7 @@ private void testMemLeakBatch(Properties props, Set<?> connectionTrackingSet, Fi
48964893
if (finType == 1) {
48974894
connection.close();
48984895
} else if (finType == 2) {
4899-
((com.mysql.cj.jdbc.JdbcConnection) connection).abortInternal();
4896+
((JdbcConnection) connection).abortInternal();
49004897
}
49014898
connection = null;
49024899
}
@@ -4925,7 +4922,7 @@ private void testMemLeakBatch(Properties props, Set<?> connectionTrackingSet, Fi
49254922
private int countTestConnections(Set<?> connectionTrackingSet, Field referentField, boolean show, String attributValue) throws Exception {
49264923
int connectionNumber = 0;
49274924
for (Object o1 : connectionTrackingSet) {
4928-
com.mysql.cj.jdbc.JdbcConnection ctmp = (com.mysql.cj.jdbc.JdbcConnection) referentField.get(o1);
4925+
JdbcConnection ctmp = (JdbcConnection) referentField.get(o1);
49294926
String atts = null;
49304927
try {
49314928
if (ctmp != null) {
@@ -5259,7 +5256,7 @@ public void testBug69452() throws Exception {
52595256
props.setProperty(PropertyKey.blobSendChunkSize.getKeyName(), String.format("1.2%1$s", testMemUnits[i][j]));
52605257
props.setProperty(PropertyKey.largeRowSizeThreshold.getKeyName(), String.format("1.4%1$s", testMemUnits[i][j]));
52615258
props.setProperty(PropertyKey.locatorFetchBufferSize.getKeyName(), String.format("1.6%1$s", testMemUnits[i][j]));
5262-
connWithMemProps = (com.mysql.cj.jdbc.JdbcConnection) getConnectionWithProps(props);
5259+
connWithMemProps = (JdbcConnection) getConnectionWithProps(props);
52635260

52645261
// test values of property 'blobSendChunkSize'
52655262
assertEquals((int) (memMultiplier[i] * 1.2),
@@ -12635,4 +12632,34 @@ void testBug119271() throws Exception {
1263512632
assertDoesNotThrow(() -> getSourceReplicaReplicationConnection(props).close());
1263612633
}
1263712634

12635+
/**
12636+
* Tests fix for Bug#113413 (Bug#36107426), Connection.changeUser cannot be done after DriverManager.loginTimeout elapses.
12637+
*
12638+
* @throws Exception
12639+
*/
12640+
@Test
12641+
public void testBug113413() throws Exception {
12642+
Properties props = getPropertiesFromTestsuiteUrl();
12643+
String username = props.getProperty(PropertyKey.USER.getKeyName());
12644+
String password = props.getProperty(PropertyKey.PASSWORD.getKeyName());
12645+
12646+
int originalLoginTimeout = DriverManager.getLoginTimeout();
12647+
DriverManager.setLoginTimeout(3);
12648+
try (Connection testConn = getNewConnection()) {
12649+
Statement testStmt = testConn.createStatement();
12650+
12651+
testConn.unwrap(JdbcConnection.class).changeUser(username, password);
12652+
this.rs = testStmt.executeQuery("SELECT 1");
12653+
assertTrue(this.rs.next());
12654+
12655+
TimeUnit.MILLISECONDS.sleep(3001);
12656+
12657+
testConn.unwrap(JdbcConnection.class).changeUser(username, password);
12658+
this.rs = testStmt.executeQuery("SELECT 1");
12659+
assertTrue(this.rs.next());
12660+
} finally {
12661+
DriverManager.setLoginTimeout(originalLoginTimeout);
12662+
}
12663+
}
12664+
1263812665
}

0 commit comments

Comments
 (0)