提交 675317bb authored 作者: Thomas Mueller's avatar Thomas Mueller

MVCC: concurrently updating a row could result in the row to appear deleted in…

MVCC: concurrently updating a row could result in the row to appear deleted in the second connection, if there are multiple unique indexes (or a primary key and at least one unique index). Thanks a lot to Teruo for the patch!
上级 f54bc593
......@@ -419,6 +419,8 @@ public abstract class Table extends SchemaObjectBase {
* new row,...
*/
public void updateRows(Prepared prepared, Session session, RowList rows) {
// in case we need to undo the update
int rollback = session.getUndoLogPos();
// remove the old rows
int rowScanCount = 0;
for (rows.reset(); rows.hasNext();) {
......@@ -437,7 +439,14 @@ public abstract class Table extends SchemaObjectBase {
}
rows.next();
Row n = rows.next();
addRow(session, n);
try {
addRow(session, n);
} catch (DbException e) {
if (e.getErrorCode() == ErrorCode.CONCURRENT_UPDATE_1) {
session.rollbackTo(rollback, false);
}
throw e;
}
session.log(this, UndoLogRecord.INSERT, n);
}
}
......
......@@ -24,10 +24,13 @@ public class TestMvcc3 extends TestBase {
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
TestBase test = TestBase.createCaller().init();
test.config.mvcc = true;
test.test();
}
public void test() throws SQLException {
testConcurrentUpdate();
testInsertUpdateRollback();
testCreateTableAsSelect();
testSequence();
......@@ -36,6 +39,50 @@ public class TestMvcc3 extends TestBase {
deleteDb("mvcc3");
}
private void testConcurrentUpdate() throws SQLException {
if (!config.mvcc) {
return;
}
deleteDb("mvcc3");
Connection c1 = getConnection("mvcc3");
c1.setAutoCommit(false);
Statement s1 = c1.createStatement();
Connection c2 = getConnection("mvcc3");
c2.setAutoCommit(false);
Statement s2 = c2.createStatement();
s1.execute("create table test(id int primary key, name varchar) as select x, x from system_range(1, 2)");
s1.execute("create unique index on test(name)");
s1.executeUpdate("update test set name = 100 where id = 1");
try {
s2.executeUpdate("update test set name = 100 where id = 2");
fail();
} catch (SQLException e) {
// expected
}
ResultSet rs = s1.executeQuery("select * from test order by id");
assertTrue(rs.next());
assertEquals(1, rs.getInt(1));
assertEquals("100", rs.getString(2));
assertTrue(rs.next());
assertEquals(2, rs.getInt(1));
assertEquals("2", rs.getString(2));
assertFalse(rs.next());
rs = s2.executeQuery("select * from test order by id");
assertTrue(rs.next());
assertEquals(1, rs.getInt(1));
assertEquals("1", rs.getString(2));
assertTrue(rs.next());
assertEquals(2, rs.getInt(1));
assertEquals("2", rs.getString(2));
assertFalse(rs.next());
c1.close();
c2.close();
}
private void testInsertUpdateRollback() throws SQLException {
if (!config.mvcc) {
return;
......@@ -188,6 +235,7 @@ public class TestMvcc3 extends TestBase {
assertEquals(1, rs.getInt(1));
conn.close();
}
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论