diff --git a/CHANGES b/CHANGES index c9eb5bde8..e4f6b0b6e 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,9 @@ Version 8.0.28 + - Fix for Bug#104067 (33054827), No reset autoCommit after unknown issue occurs. + Thanks to Tingyu Wei for his contribution. + - Fix for Bug#85223 (25656020), MYSQLSQLXML SETSTRING CRASH. - Fix for Bug#84365 (33425867), INSERT..VALUE with VALUES function lead to a StringIndexOutOfBoundsException. diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionImpl.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionImpl.java index 1b9d7d8d0..2a149e0a4 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionImpl.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ConnectionImpl.java @@ -70,6 +70,7 @@ import com.mysql.cj.conf.PropertyDefinitions.DatabaseTerm; import com.mysql.cj.conf.PropertyKey; import com.mysql.cj.conf.RuntimeProperty; +import com.mysql.cj.exceptions.CJCommunicationsException; import com.mysql.cj.exceptions.CJException; import com.mysql.cj.exceptions.ExceptionFactory; import com.mysql.cj.exceptions.ExceptionInterceptor; @@ -2017,10 +2018,10 @@ void forEach(ConnectionLifecycleInterceptor each) throws SQLException { this.autoReconnect.setValue(true); } + boolean isAutocommit = this.session.getServerSession().isAutocommit(); try { boolean needsSetOnServer = true; - - if (this.useLocalSessionState.getValue() && this.session.getServerSession().isAutoCommit() == autoCommitFlag) { + if (this.useLocalSessionState.getValue() && isAutocommit == autoCommitFlag) { needsSetOnServer = false; } else if (!this.autoReconnect.getValue()) { needsSetOnServer = getSession().isSetNeededForAutoCommitMode(autoCommitFlag); @@ -2035,6 +2036,13 @@ void forEach(ConnectionLifecycleInterceptor each) throws SQLException { this.session.execSQL(null, autoCommitFlag ? "SET autocommit=1" : "SET autocommit=0", -1, null, false, this.nullStatementResultSetFactory, null, false); } + } catch (CJCommunicationsException e) { + throw e; + } catch (CJException e) { + // Reset to current autocommit value in case of an exception different than a communication exception occurs. + this.session.getServerSession().setAutoCommit(isAutocommit); + // Update the stacktrace. + throw SQLError.createSQLException(e.getMessage(), e.getSQLState(), e.getVendorCode(), e.isTransient(), e, getExceptionInterceptor()); } finally { if (this.autoReconnectForPools.getValue()) { this.autoReconnect.setValue(false); diff --git a/src/test/java/testsuite/regression/ConnectionRegressionTest.java b/src/test/java/testsuite/regression/ConnectionRegressionTest.java index 3dfc1fe92..14ca22d8d 100644 --- a/src/test/java/testsuite/regression/ConnectionRegressionTest.java +++ b/src/test/java/testsuite/regression/ConnectionRegressionTest.java @@ -11562,4 +11562,69 @@ public void testBug28725534() throws Exception { assertTrue(end - start < 250, ".equals() took too long to exectute, the method is being locked by a synchronized block."); } + + /** + * Tests fix for Bug#104067 (33054827), No reset autoCommit after unknown issue occurs. + * + * @throws Exception + */ + @Test + public void testBug104067() throws Exception { + Properties props = new Properties(); + props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name()); + props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true"); + props.setProperty(PropertyKey.queryInterceptors.getKeyName(), Bug104067QueryInterceptor.class.getName()); + Connection testConn = getConnectionWithProps(props); + Statement testStmt = testConn.createStatement(); + + // Connection vs session autocommit value: + // 1. Default value. + assertTrue(testConn.getAutoCommit()); + this.rs = testStmt.executeQuery("SHOW SESSION VARIABLES LIKE 'autocommit'"); + assertTrue(this.rs.next()); + assertTrue(this.rs.getString(2).equalsIgnoreCase("ON")); + + // 2. After Connection.setAutcommit(true). + try { + testConn.setAutoCommit(true); + } catch (SQLException e) { + fail("Exception not expected.", e); + } + assertTrue(testConn.getAutoCommit()); + this.rs = testStmt.executeQuery("SHOW SESSION VARIABLES LIKE 'autocommit'"); + assertTrue(this.rs.next()); + assertTrue(this.rs.getString(2).equalsIgnoreCase("ON")); + + // 3. After Connection.setAutcommit(false). + assertThrows(SQLException.class, () -> { + testConn.setAutoCommit(false); + return null; + }); + assertTrue(testConn.getAutoCommit()); + this.rs = testStmt.executeQuery("SHOW SESSION VARIABLES LIKE 'autocommit'"); + this.rs.next(); + assertTrue(this.rs.getString(2).equalsIgnoreCase("ON")); + + // 4. After Connection.setAutcommit(true). + try { + testConn.setAutoCommit(true); + } catch (SQLException e) { + fail("Exception not expected.", e); + } + assertTrue(testConn.getAutoCommit()); + this.rs = testStmt.executeQuery("SHOW SESSION VARIABLES LIKE 'autocommit'"); + assertTrue(this.rs.next()); + assertTrue(this.rs.getString(2).equalsIgnoreCase("ON")); + } + + public static class Bug104067QueryInterceptor extends BaseQueryInterceptor { + @Override + public T preProcess(Supplier str, Query interceptedQuery) { + String sql = str.get(); + if (sql.equalsIgnoreCase("SET autocommit=0")) { + throw ExceptionFactory.createException("Artificial non-connection related exception while executing \"" + sql + "\""); + } + return super.preProcess(str, interceptedQuery); + } + } }