diff --git a/CHANGES b/CHANGES index 6bfb4037b..2fee4698c 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,8 @@ Version 8.2.0 + - Fix for Bug#35811592, Missing implementation for Connection.releaseSavepoint(). + - Fix for Bug#91351 (Bug#28225464), MysqlConnectionPoolDataSource - autocommit status lost if global autocommit = 0. - WL#15197, Support WebauthN in fido authentication plugin. 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 5b8219568..06ef01ffc 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 @@ -1741,8 +1741,21 @@ public void registerStatement(JdbcStatement stmt) { } @Override - public void releaseSavepoint(Savepoint arg0) throws SQLException { - // this is a no-op + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + StringBuilder releaseSavepointQuery = new StringBuilder("RELEASE SAVEPOINT "); + releaseSavepointQuery + .append(StringUtils.quoteIdentifier(savepoint.getSavepointName(), this.session.getIdentifierQuoteString(), this.pedantic.getValue())); + java.sql.Statement stmt = null; + try { + stmt = getMetadataSafeStatement(); + stmt.executeUpdate(releaseSavepointQuery.toString()); + } finally { + closeStatement(stmt); + } + } } @Override @@ -1836,25 +1849,18 @@ void forEach(ConnectionLifecycleInterceptor each) throws SQLException { } StringBuilder rollbackQuery = new StringBuilder("ROLLBACK TO SAVEPOINT "); - rollbackQuery.append('`'); - rollbackQuery.append(savepoint.getSavepointName()); - rollbackQuery.append('`'); - + rollbackQuery + .append(StringUtils.quoteIdentifier(savepoint.getSavepointName(), this.session.getIdentifierQuoteString(), this.pedantic.getValue())); java.sql.Statement stmt = null; - try { stmt = getMetadataSafeStatement(); - stmt.executeUpdate(rollbackQuery.toString()); } catch (SQLException sqlEx) { int errno = sqlEx.getErrorCode(); - if (errno == 1181) { String msg = sqlEx.getMessage(); - if (msg != null) { int indexOfError153 = msg.indexOf("153"); - if (indexOfError153 != -1) { throw SQLError.createSQLException(Messages.getString("Connection.22", new Object[] { savepoint.getSavepointName() }), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, errno, getExceptionInterceptor()); @@ -2064,10 +2070,6 @@ void forEach(ConnectionLifecycleInterceptor each) throws SQLException { String quotedId = this.session.getIdentifierQuoteString(); - if (quotedId == null || quotedId.equals(" ")) { - quotedId = ""; - } - StringBuilder query = new StringBuilder("USE "); query.append(StringUtils.quoteIdentifier(db, quotedId, this.pedantic.getValue())); @@ -2122,9 +2124,7 @@ public void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException { @Override public java.sql.Savepoint setSavepoint() throws SQLException { MysqlSavepoint savepoint = new MysqlSavepoint(getExceptionInterceptor()); - setSavepoint(savepoint); - return savepoint; } @@ -2133,15 +2133,10 @@ private void setSavepoint(MysqlSavepoint savepoint) throws SQLException { checkClosed(); StringBuilder savePointQuery = new StringBuilder("SAVEPOINT "); - savePointQuery.append('`'); - savePointQuery.append(savepoint.getSavepointName()); - savePointQuery.append('`'); - + savePointQuery.append(StringUtils.quoteIdentifier(savepoint.getSavepointName(), this.session.getIdentifierQuoteString(), this.pedantic.getValue())); java.sql.Statement stmt = null; - try { stmt = getMetadataSafeStatement(); - stmt.executeUpdate(savePointQuery.toString()); } finally { closeStatement(stmt); @@ -2153,9 +2148,7 @@ private void setSavepoint(MysqlSavepoint savepoint) throws SQLException { public java.sql.Savepoint setSavepoint(String name) throws SQLException { synchronized (getConnectionMutex()) { MysqlSavepoint savepoint = new MysqlSavepoint(name, getExceptionInterceptor()); - setSavepoint(savepoint); - return savepoint; } } diff --git a/src/test/java/testsuite/simple/ConnectionTest.java b/src/test/java/testsuite/simple/ConnectionTest.java index 53698bcd7..26b723652 100644 --- a/src/test/java/testsuite/simple/ConnectionTest.java +++ b/src/test/java/testsuite/simple/ConnectionTest.java @@ -322,58 +322,57 @@ public void testIsolationLevel() throws Exception { */ @Test public void testSavepoint() throws Exception { - DatabaseMetaData dbmd = this.conn.getMetaData(); + assumeTrue(this.conn.getMetaData().supportsSavepoints(), "Savepoints not supported"); - if (dbmd.supportsSavepoints()) { - System.out.println("Testing SAVEPOINTs"); + try { + this.conn.setAutoCommit(true); - try { - this.conn.setAutoCommit(true); - - createTable("testSavepoints", "(field1 int)", "InnoDB"); - - // Try with named save points - this.conn.setAutoCommit(false); - this.stmt.executeUpdate("INSERT INTO testSavepoints VALUES (1)"); - - Savepoint afterInsert = this.conn.setSavepoint("afterInsert"); - this.stmt.executeUpdate("UPDATE testSavepoints SET field1=2"); - - Savepoint afterUpdate = this.conn.setSavepoint("afterUpdate"); - this.stmt.executeUpdate("DELETE FROM testSavepoints"); - - assertTrue(getRowCount("testSavepoints") == 0, "Row count should be 0"); - this.conn.rollback(afterUpdate); - assertTrue(getRowCount("testSavepoints") == 1, "Row count should be 1"); - assertTrue("2".equals(getSingleValue("testSavepoints", "field1", null).toString()), "Value should be 2"); - this.conn.rollback(afterInsert); - assertTrue("1".equals(getSingleValue("testSavepoints", "field1", null).toString()), "Value should be 1"); - this.conn.rollback(); - assertTrue(getRowCount("testSavepoints") == 0, "Row count should be 0"); - - // Try with 'anonymous' save points - this.conn.rollback(); - - this.stmt.executeUpdate("INSERT INTO testSavepoints VALUES (1)"); - afterInsert = this.conn.setSavepoint(); - this.stmt.executeUpdate("UPDATE testSavepoints SET field1=2"); - afterUpdate = this.conn.setSavepoint(); - this.stmt.executeUpdate("DELETE FROM testSavepoints"); - - assertTrue(getRowCount("testSavepoints") == 0, "Row count should be 0"); - this.conn.rollback(afterUpdate); - assertTrue(getRowCount("testSavepoints") == 1, "Row count should be 1"); - assertTrue("2".equals(getSingleValue("testSavepoints", "field1", null).toString()), "Value should be 2"); - this.conn.rollback(afterInsert); - assertTrue("1".equals(getSingleValue("testSavepoints", "field1", null).toString()), "Value should be 1"); - this.conn.rollback(); - - this.conn.releaseSavepoint(this.conn.setSavepoint()); - } finally { - this.conn.setAutoCommit(true); - } - } else { - System.out.println("MySQL version does not support SAVEPOINTs"); + createTable("testSavepoints", "(field1 int)", "InnoDB"); + + // Try with named save points + this.conn.setAutoCommit(false); + this.stmt.executeUpdate("INSERT INTO testSavepoints VALUES (1)"); + + Savepoint afterInsert = this.conn.setSavepoint("afterInsert"); + this.stmt.executeUpdate("UPDATE testSavepoints SET field1=2"); + + Savepoint afterUpdate = this.conn.setSavepoint("afterUpdate"); + this.stmt.executeUpdate("DELETE FROM testSavepoints"); + + assertEquals(0, getRowCount("testSavepoints"), "Row count should be 0"); + this.conn.rollback(afterUpdate); + assertEquals(1, getRowCount("testSavepoints"), "Row count should be 1"); + assertEquals("2", getSingleValue("testSavepoints", "field1", null).toString(), "Value should be 2"); + this.conn.rollback(afterInsert); + assertEquals("1", getSingleValue("testSavepoints", "field1", null).toString(), "Value should be 1"); + this.conn.rollback(); + assertEquals(0, getRowCount("testSavepoints"), "Row count should be 0"); + + // Try with 'anonymous' save points + this.conn.rollback(); + + this.stmt.executeUpdate("INSERT INTO testSavepoints VALUES (1)"); + afterInsert = this.conn.setSavepoint(); + this.stmt.executeUpdate("UPDATE testSavepoints SET field1=2"); + afterUpdate = this.conn.setSavepoint(); + this.stmt.executeUpdate("DELETE FROM testSavepoints"); + + assertEquals(0, getRowCount("testSavepoints"), "Row count should be 0"); + this.conn.rollback(afterUpdate); + assertEquals(1, getRowCount("testSavepoints"), "Row count should be 1"); + assertEquals("2", getSingleValue("testSavepoints", "field1", null).toString(), "Value should be 2"); + this.conn.rollback(afterInsert); + assertEquals("1", getSingleValue("testSavepoints", "field1", null).toString(), "Value should be 1"); + this.conn.rollback(); + + Savepoint savepoint = this.conn.setSavepoint(); + this.conn.releaseSavepoint(savepoint); + assertThrows(SQLException.class, "SAVEPOINT .* does not exist", () -> { + this.conn.rollback(savepoint); + return null; + }); + } finally { + this.conn.setAutoCommit(true); } }