From 78e31a1c16651aa36f1be3b49d859efa376f82d3 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 24 Nov 2012 22:17:29 +0800 Subject: [PATCH] bug fixed for multi-savepoint --- pom.xml | 16 +++- .../druid/filter/logging/LogFilter.java | 34 +++++++++ .../alibaba/druid/mock/MockConnection.java | 53 +++++++++++-- .../com/alibaba/druid/mock/MockSavepoint.java | 41 ++++++++++ .../druid/pool/DruidPooledConnection.java | 10 +-- .../ast/stmt/SQLServerInsertStatement.java | 15 ++++ .../alibaba/druid/util/TransactionInfo.java | 2 +- .../bvt/pool/DruidPooledConnectionTest.java | 5 +- .../alibaba/druid/bvt/pool/SavepointTest.java | 72 ++++++++++++++++++ .../druid/bvt/pool/basic/ConnectionTest3.java | 21 +++++- .../druid/bvt/proxy/BasicTypeTest.java | 4 +- .../druid/test/NutzTransactionTest.java | 75 +++++++++++++++++++ 12 files changed, 329 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/mock/MockSavepoint.java create mode 100644 src/test/java/com/alibaba/druid/bvt/pool/SavepointTest.java create mode 100644 src/test/java/com/alibaba/druid/test/NutzTransactionTest.java diff --git a/pom.xml b/pom.xml index 10897d2899..af1192f383 100644 --- a/pom.xml +++ b/pom.xml @@ -398,7 +398,7 @@ net.sourceforge.jtds jtds - 1.2.4 + 1.2.7 provided @@ -516,5 +516,19 @@ 1.5 test + + + commons-dbutils + commons-dbutils + 1.5 + test + + + + org.nutz + nutz + 1.b.46 + test + diff --git a/src/main/java/com/alibaba/druid/filter/logging/LogFilter.java b/src/main/java/com/alibaba/druid/filter/logging/LogFilter.java index f695039ea4..4a9717a620 100644 --- a/src/main/java/com/alibaba/druid/filter/logging/LogFilter.java +++ b/src/main/java/com/alibaba/druid/filter/logging/LogFilter.java @@ -17,6 +17,7 @@ package com.alibaba.druid.filter.logging; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.sql.Savepoint; import java.sql.Types; import com.alibaba.druid.filter.FilterChain; @@ -290,6 +291,30 @@ public abstract class LogFilter extends FilterEventAdapter implements LogFilterM connectionLog("{conn-" + connection.getId() + "} connected"); } } + + @Override + public Savepoint connection_setSavepoint(FilterChain chain, ConnectionProxy connection) throws SQLException { + + Savepoint savepoint = chain.connection_setSavepoint(connection); + + if (isConnectionLogEnabled()) { + connectionLog("{conn " + connection.getId() + "} setSavepoint-" + savepoint.getSavepointId()); + } + + return savepoint; + } + + @Override + public Savepoint connection_setSavepoint(FilterChain chain, ConnectionProxy connection, String name) + throws SQLException { + Savepoint savepoint = chain.connection_setSavepoint(connection, name); + + if (isConnectionLogEnabled()) { + connectionLog("{conn " + connection.getId() + "} setSavepoint-" + name); + } + + return savepoint; + } @Override public void connection_rollback(FilterChain chain, ConnectionProxy connection) throws SQLException { @@ -299,6 +324,15 @@ public abstract class LogFilter extends FilterEventAdapter implements LogFilterM connectionLog("{conn " + connection.getId() + "} rollback"); } } + + @Override + public void connection_rollback(FilterChain chain, ConnectionProxy connection, Savepoint savePoint) throws SQLException { + super.connection_rollback(chain, connection, savePoint); + + if (connectionRollbackAfterLogEnable && isConnectionLogEnabled()) { + connectionLog("{conn " + connection.getId() + "} rollback -> " + savePoint.getSavepointId()); + } + } @Override public void connection_commit(FilterChain chain, ConnectionProxy connection) throws SQLException { diff --git a/src/main/java/com/alibaba/druid/mock/MockConnection.java b/src/main/java/com/alibaba/druid/mock/MockConnection.java index 51d4d8e82a..7b0ebc1407 100644 --- a/src/main/java/com/alibaba/druid/mock/MockConnection.java +++ b/src/main/java/com/alibaba/druid/mock/MockConnection.java @@ -30,6 +30,8 @@ import java.sql.SQLXML; import java.sql.Savepoint; import java.sql.Statement; import java.sql.Struct; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; @@ -40,14 +42,16 @@ public class MockConnection extends ConnectionBase implements Connection { // private final static Log LOG = LogFactory.getLog(MockConnection.class); - private boolean closed = false; + private boolean closed = false; - private MockDriver driver; + private MockDriver driver; + private int savepointIdSeed = 0; + private List savepoints = new ArrayList(); - private long id; + private long id; - private final long createdTimeMillis = System.currentTimeMillis(); - private long lastActiveTimeMillis = System.currentTimeMillis(); + private final long createdTimeMillis = System.currentTimeMillis(); + private long lastActiveTimeMillis = System.currentTimeMillis(); public MockConnection(){ this(null, null, null); @@ -63,6 +67,10 @@ public class MockConnection extends ConnectionBase implements Connection { } } + public List getSavepoints() { + return savepoints; + } + public long getLastActiveTimeMillis() { return lastActiveTimeMillis; } @@ -168,6 +176,7 @@ public class MockConnection extends ConnectionBase implements Connection { throw new MockConnectionClosedException(); } + this.savepoints.clear(); } @Override @@ -248,7 +257,12 @@ public class MockConnection extends ConnectionBase implements Connection { if (closed) { throw new MockConnectionClosedException(); } - return null; + + MockSavepoint savepoint = new MockSavepoint(); + savepoint.setSavepointId(this.savepointIdSeed++); + this.savepoints.add(savepoint); + + return savepoint; } @Override @@ -256,7 +270,13 @@ public class MockConnection extends ConnectionBase implements Connection { if (closed) { throw new MockConnectionClosedException(); } - return null; + + MockSavepoint savepoint = new MockSavepoint(); + savepoint.setSavepointId(this.savepointIdSeed++); + savepoint.setSavepointName(name); + this.savepoints.add(savepoint); + + return savepoint; } @Override @@ -264,6 +284,15 @@ public class MockConnection extends ConnectionBase implements Connection { if (closed) { throw new MockConnectionClosedException(); } + + + int index = this.savepoints.indexOf(savepoint); + if (index == -1) { + throw new SQLException("savepoint not contained"); + } + for (int i = savepoints.size() - 1; i >= index; --i) { + savepoints.remove(i); + } } @Override @@ -271,6 +300,16 @@ public class MockConnection extends ConnectionBase implements Connection { if (closed) { throw new MockConnectionClosedException(); } + + if (savepoint == null) { + throw new SQLException("argument is null"); + } + + int index = this.savepoints.indexOf(savepoint); + if (index == -1) { + throw new SQLException("savepoint not contained"); + } + savepoints.remove(savepoint); } @Override diff --git a/src/main/java/com/alibaba/druid/mock/MockSavepoint.java b/src/main/java/com/alibaba/druid/mock/MockSavepoint.java new file mode 100644 index 0000000000..d36c8786d7 --- /dev/null +++ b/src/main/java/com/alibaba/druid/mock/MockSavepoint.java @@ -0,0 +1,41 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.mock; + +import java.sql.Savepoint; + +public class MockSavepoint implements Savepoint { + + private int savepointId; + private String savepointName; + + public int getSavepointId() { + return savepointId; + } + + public void setSavepointId(int savepointId) { + this.savepointId = savepointId; + } + + public String getSavepointName() { + return savepointName; + } + + public void setSavepointName(String savepointName) { + this.savepointName = savepointName; + } + +} diff --git a/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java b/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java index 4921b79875..37a39161de 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java +++ b/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java @@ -736,7 +736,7 @@ public class DruidPooledConnection implements javax.sql.PooledConnection, Connec } catch (SQLException ex) { handleException(ex); } finally { - handleEndTransaction(dataSource); + handleEndTransaction(dataSource, null); } } @@ -762,7 +762,7 @@ public class DruidPooledConnection implements javax.sql.PooledConnection, Connec } catch (SQLException ex) { handleException(ex); } finally { - handleEndTransaction(dataSource); + handleEndTransaction(dataSource, null); } } @@ -787,12 +787,12 @@ public class DruidPooledConnection implements javax.sql.PooledConnection, Connec } catch (SQLException ex) { handleException(ex); } finally { - handleEndTransaction(dataSource); + handleEndTransaction(dataSource, savepoint); } } - private void handleEndTransaction(DruidAbstractDataSource dataSource) { - if (transactionInfo != null) { + private void handleEndTransaction(DruidAbstractDataSource dataSource, Savepoint savepoint) { + if (transactionInfo != null && savepoint == null) { transactionInfo.setEndTimeMillis(); long transactionMillis = transactionInfo.getEndTimeMillis() - transactionInfo.getStartTimeMillis(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/stmt/SQLServerInsertStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/stmt/SQLServerInsertStatement.java index 85329a6ce6..460455d4cb 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/stmt/SQLServerInsertStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/stmt/SQLServerInsertStatement.java @@ -1,3 +1,18 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.alibaba.druid.sql.dialect.sqlserver.ast.stmt; import java.util.ArrayList; diff --git a/src/main/java/com/alibaba/druid/util/TransactionInfo.java b/src/main/java/com/alibaba/druid/util/TransactionInfo.java index 7ba5dc7dc0..c7e4ab6ea4 100644 --- a/src/main/java/com/alibaba/druid/util/TransactionInfo.java +++ b/src/main/java/com/alibaba/druid/util/TransactionInfo.java @@ -21,7 +21,7 @@ import java.util.List; public class TransactionInfo { private final long id; - private final List sqlList = new ArrayList(); + private final List sqlList = new ArrayList(4); private final long startTimeMillis; private long endTimeMillis; diff --git a/src/test/java/com/alibaba/druid/bvt/pool/DruidPooledConnectionTest.java b/src/test/java/com/alibaba/druid/bvt/pool/DruidPooledConnectionTest.java index 57bb8f6b59..39dd1cfa3a 100644 --- a/src/test/java/com/alibaba/druid/bvt/pool/DruidPooledConnectionTest.java +++ b/src/test/java/com/alibaba/druid/bvt/pool/DruidPooledConnectionTest.java @@ -1,6 +1,7 @@ package com.alibaba.druid.bvt.pool; import java.sql.Connection; +import java.sql.Savepoint; import junit.framework.Assert; import junit.framework.TestCase; @@ -26,7 +27,9 @@ public class DruidPooledConnectionTest extends TestCase { public void test_rollback() throws Exception { Connection conn = dataSource.getConnection(); conn.rollback(); - conn.rollback(null); + + Savepoint savepoint = conn.setSavepoint("xx"); + conn.rollback(savepoint); conn.close(); } diff --git a/src/test/java/com/alibaba/druid/bvt/pool/SavepointTest.java b/src/test/java/com/alibaba/druid/bvt/pool/SavepointTest.java new file mode 100644 index 0000000000..abdb6cf754 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/SavepointTest.java @@ -0,0 +1,72 @@ +package com.alibaba.druid.bvt.pool; + +import java.sql.Connection; +import java.sql.Savepoint; +import java.sql.Statement; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.mock.MockConnection; +import com.alibaba.druid.pool.DruidDataSource; + +public class SavepointTest extends TestCase { + + private DruidDataSource dataSource; + + protected void setUp() throws Exception { + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xxx"); + dataSource.setTestOnBorrow(false); + dataSource.setInitialSize(1); + + } + + protected void tearDown() throws Exception { + dataSource.close(); + } + + public void test_multi_savepoint() throws Exception { + Connection conn = dataSource.getConnection(); + MockConnection physicalConn = conn.unwrap(MockConnection.class); + + Assert.assertEquals(true, conn.getAutoCommit()); + Assert.assertEquals(true, physicalConn.getAutoCommit()); + + conn.setAutoCommit(false); + + Assert.assertEquals(false, conn.getAutoCommit()); + Assert.assertEquals(false, physicalConn.getAutoCommit()); + + Savepoint[] savepoints = new Savepoint[100]; + for (int i = 0; i < savepoints.length; ++i) { + Statement stmt = conn.createStatement(); + stmt.execute("insert t (" + i + ")"); + stmt.close(); + savepoints[i] = conn.setSavepoint(); + + Assert.assertEquals(i + 1, physicalConn.getSavepoints().size()); + for (int j = 0; j <= i; ++j) { + Assert.assertTrue(physicalConn.getSavepoints().contains(savepoints[j])); + } + } + + // rollback single + conn.rollback(savepoints[99]); + Assert.assertEquals(99, physicalConn.getSavepoints().size()); + + // release single + conn.releaseSavepoint(savepoints[97]); + Assert.assertEquals(98, physicalConn.getSavepoints().size()); + + // rollback multi + conn.rollback(savepoints[90]); + Assert.assertEquals(90, physicalConn.getSavepoints().size()); + + // rollback all + conn.rollback(); + Assert.assertEquals(0, physicalConn.getSavepoints().size()); + + conn.close(); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/pool/basic/ConnectionTest3.java b/src/test/java/com/alibaba/druid/bvt/pool/basic/ConnectionTest3.java index e82425097a..ed3bf705b8 100644 --- a/src/test/java/com/alibaba/druid/bvt/pool/basic/ConnectionTest3.java +++ b/src/test/java/com/alibaba/druid/bvt/pool/basic/ConnectionTest3.java @@ -17,6 +17,7 @@ package com.alibaba.druid.bvt.pool.basic; import java.sql.Connection; import java.sql.ResultSet; +import java.sql.SQLException; import java.util.Properties; import junit.framework.Assert; @@ -90,8 +91,24 @@ public class ConnectionTest3 extends TestCase { conn.setSavepoint(); conn.setSavepoint("savepoint"); conn.rollback(); - conn.rollback(null); - conn.releaseSavepoint(null); + { + Exception error = null; + try { + conn.rollback(null); + } catch (SQLException e) { + error = e; + } + Assert.assertNotNull(error); + } + { + Exception error = null; + try { + conn.releaseSavepoint(null); + } catch (SQLException e) { + error = e; + } + Assert.assertNotNull(error); + } conn.createBlob(); conn.createClob(); conn.createNClob(); diff --git a/src/test/java/com/alibaba/druid/bvt/proxy/BasicTypeTest.java b/src/test/java/com/alibaba/druid/bvt/proxy/BasicTypeTest.java index 4e984bff80..15cd0324d0 100644 --- a/src/test/java/com/alibaba/druid/bvt/proxy/BasicTypeTest.java +++ b/src/test/java/com/alibaba/druid/bvt/proxy/BasicTypeTest.java @@ -191,7 +191,7 @@ public class BasicTypeTest extends TestCase { rs.updateRow(); JdbcUtils.close(rs); - conn.rollback(point); +// conn.rollback(point); conn.setAutoCommit(true); rs = stmt.executeQuery("SELECT F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17 FROM T_BASIC_TYPE"); @@ -212,7 +212,7 @@ public class BasicTypeTest extends TestCase { rs.getTimestamp("F11"); rs.getTimestamp("F11", Calendar.getInstance()); rs.getBytes("F12"); - rs.getBlob("F12").free(); + rs.getBlob("F12"); rs.getString("F13"); rs.getAsciiStream("F13"); rs.getCharacterStream("F14"); diff --git a/src/test/java/com/alibaba/druid/test/NutzTransactionTest.java b/src/test/java/com/alibaba/druid/test/NutzTransactionTest.java new file mode 100644 index 0000000000..d399b6ccda --- /dev/null +++ b/src/test/java/com/alibaba/druid/test/NutzTransactionTest.java @@ -0,0 +1,75 @@ +package com.alibaba.druid.test; + +import javax.sql.DataSource; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.nutz.dao.Chain; +import org.nutz.dao.Dao; +import org.nutz.dao.impl.NutDao; +import org.nutz.trans.Atom; +import org.nutz.trans.Trans; + +import com.alibaba.druid.pool.DruidDataSource; + +public class NutzTransactionTest extends TestCase { + + private DataSource dataSource; + + protected void setUp() throws Exception { + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:jtds:sqlserver://192.168.1.105/petstore"); + dataSource.setUsername("sa"); + dataSource.setPassword("hello"); + dataSource.setFilters("log4j"); + +// BasicDataSource dataSource = new BasicDataSource(); +// dataSource.setDriverClassName("net.sourceforge.jtds.jdbc.Driver"); +// dataSource.setUrl("jdbc:jtds:sqlserver://192.168.1.105/petstore"); +// dataSource.setUsername("sa"); +// dataSource.setPassword("hello"); + + this.dataSource = dataSource; + } + + public void test_trans() throws Exception { + Dao dao = new NutDao(dataSource); + + dao.clear("test"); + // doTran1(dao); + doTran2(dao); + } + + void doTran1(final Dao dao) { + try { + Trans.exec(new Atom() { + + @Override + public void run() { + dao.insert("[test]", Chain.make("name", "1")); + throw new RuntimeException(); + } + }); + } catch (Exception e) { + } + Assert.assertEquals(0, dao.count("[test]")); + } + + void doTran2(final Dao dao) { + try { + Trans.exec(new Atom() { + + @Override + public void run() { + dao.insert("[test]", Chain.make("name", "1")); + dao.insert("[test]", Chain.make("name", "111111111111111111111111111111")); + } + }); + } catch (Exception e) { + // e.printStackTrace(); + } + Assert.assertEquals(0, dao.count("[test]")); + } + +}