///*
// * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
// * and the EPL 1.0 (http://h2database.com/html/license.html).
// * Initial Developer: H2 Group
// */
//package my.test.mvstore;
//
//import java.sql.Connection;
//import java.sql.DriverManager;
//import java.sql.ResultSet;
//import java.sql.SQLException;
//import java.sql.Statement;
//import java.util.ArrayList;
//import java.util.Iterator;
//import java.util.List;
//import java.util.Random;
//import java.util.concurrent.atomic.AtomicInteger;
//
//import org.h2.mvstore.DataUtils;
//import org.h2.mvstore.MVMap;
//import org.h2.mvstore.MVStore;
//import org.h2.mvstore.db.TransactionStore;
//import org.h2.mvstore.db.TransactionStore.Change;
//import org.h2.mvstore.db.TransactionStore.Transaction;
//import org.h2.mvstore.db.TransactionStore.TransactionMap;
//import org.h2.store.fs.FileUtils;
//import org.h2.test.TestBase;
//import org.h2.util.New;
//import org.h2.util.Task;
//
///**
// * Test concurrent transactions.
// */
//public class TestTransactionStore extends TestBase {
//
// /**
// * Run just this test.
// *
// * @param a ignored
// */
// public static void main(String... a) throws Exception {
// TestBase.createCaller().init().test();
// }
//
// @Override
// public void test() throws Exception {
// FileUtils.createDirectories(getBaseDir());
//// testConcurrentAddRemove();
//// testConcurrentAdd();
//// testCountWithOpenTransactions();
//// testConcurrentUpdate();
//// testRepeatedChange();
//// testTransactionAge();
//// testStopWhileCommitting();
//// testGetModifiedMaps();
//// testKeyIterator();
//// testMultiStatement();
//// testTwoPhaseCommit();
// testSavepoint();
//// testConcurrentTransactionsReadCommitted();
//// testSingleConnection();
//// testCompareWithPostgreSQL();
// }
//
// private void testConcurrentAddRemove() throws InterruptedException {
// MVStore s = MVStore.open(null);
// int threadCount = 3;
// final int keyCount = 2;
// final TransactionStore ts = new TransactionStore(s);
// ts.init();
//
// final Random r = new Random(1);
//
// Task[] tasks = new Task[threadCount];
// for (int i = 0; i < threadCount; i++) {
// Task task = new Task() {
//
// @Override
// public void call() throws Exception {
// TransactionMap<Integer, Integer> map = null;
// while (!stop) {
// Transaction tx = ts.begin();
// map = tx.openMap("data");
// int k = r.nextInt(keyCount);
// try {
// map.remove(k);
// map.put(k, r.nextInt());
// } catch (IllegalStateException e) {
// // ignore and retry
// }
// tx.commit();
// }
// }
//
// };
// task.execute();
// tasks[i] = task;
// }
// Thread.sleep(1000);
// for (Task t : tasks) {
// t.get();
// }
// s.close();
// }
//
// private void testConcurrentAdd() {
// MVStore s;
// s = MVStore.open(null);
// final TransactionStore ts = new TransactionStore(s);
// ts.init();
//
// final Random r = new Random(1);
//
// final AtomicInteger key = new AtomicInteger();
// final AtomicInteger failCount = new AtomicInteger();
//
// Task task = new Task() {
//
// @Override
// public void call() throws Exception {
// Transaction tx = null;
// TransactionMap<Integer, Integer> map = null;
// while (!stop) {
// int k = key.get();
// tx = ts.begin();
// map = tx.openMap("data");
// try {
// map.put(k, r.nextInt());
// } catch (IllegalStateException e) {
// failCount.incrementAndGet();
// // ignore and retry
// }
// tx.commit();
// }
// }
//
// };
// task.execute();
// Transaction tx = null;
// int count = 10000;
// TransactionMap<Integer, Integer> map = null;
// for (int i = 0; i < count; i++) {
// int k = i;
// key.set(k);
// tx = ts.begin();
// map = tx.openMap("data");
// try {
// map.put(k, r.nextInt());
// } catch (IllegalStateException e) {
// failCount.incrementAndGet();
// // ignore and retry
// }
// tx.commit();
// }
// // we expect at least half the operations were successful
// assertTrue(failCount.toString(), failCount.get() < count / 2);
// // we expect at least a few failures
// assertTrue(failCount.toString(), failCount.get() > 0);
// s.close();
// }
//
// private void testCountWithOpenTransactions() {
// MVStore s;
// TransactionStore ts;
// s = MVStore.open(null);
// ts = new TransactionStore(s);
// ts.init();
//
// Transaction tx1 = ts.begin();
// TransactionMap<Integer, Integer> map1 = tx1.openMap("data");
// int size = 150;
// for (int i = 0; i < size; i++) {
// map1.put(i, i * 10);
// }
// tx1.commit();
// tx1 = ts.begin();
// map1 = tx1.openMap("data");
//
// Transaction tx2 = ts.begin();
// TransactionMap<Integer, Integer> map2 = tx2.openMap("data");
//
// Random r = new Random(1);
// for (int i = 0; i < size * 3; i++) {
// assertEquals("op: " + i, size, (int) map1.sizeAsLong());
// // keep the first 10%, and add 10%
// int k = size / 10 + r.nextInt(size);
// if (r.nextBoolean()) {
// map2.remove(k);
// } else {
// map2.put(k, i);
// }
// }
// s.close();
// }
//
// private void testConcurrentUpdate() {
// MVStore s;
// TransactionStore ts;
// s = MVStore.open(null);
// ts = new TransactionStore(s);
// ts.init();
//
// Transaction tx1 = ts.begin();
// TransactionMap<Integer, Integer> map1 = tx1.openMap("data");
// map1.put(1, 10);
//
// Transaction tx2 = ts.begin();
// TransactionMap<Integer, Integer> map2 = tx2.openMap("data");
// try {
// map2.put(1, 20);
// fail();
// } catch (IllegalStateException e) {
// assertEquals(DataUtils.ERROR_TRANSACTION_LOCKED,
// DataUtils.getErrorCode(e.getMessage()));
// }
// assertEquals(10, map1.get(1).intValue());
// assertNull(map2.get(1));
// tx1.commit();
// assertEquals(10, map2.get(1).intValue());
//
// s.close();
// }
//
// private void testRepeatedChange() {
// MVStore s;
// TransactionStore ts;
// s = MVStore.open(null);
// ts = new TransactionStore(s);
// ts.init();
//
// Transaction tx0 = ts.begin();
// TransactionMap<Integer, Integer> map0 = tx0.openMap("data");
// map0.put(1, -1);
// tx0.commit();
//
// Transaction tx = ts.begin();
// TransactionMap<Integer, Integer> map = tx.openMap("data");
// for (int i = 0; i < 2000; i++) {
// map.put(1, i);
// }
//
// Transaction tx2 = ts.begin();
// TransactionMap<Integer, Integer> map2 = tx2.openMap("data");
// assertEquals(-1, map2.get(1).intValue());
//
// s.close();
// }
//
// private void testTransactionAge() throws Exception {
// MVStore s;
// TransactionStore ts;
// s = MVStore.open(null);
// ts = new TransactionStore(s);
// ts.init();
// ts.setMaxTransactionId(16);
// ArrayList<Transaction> openList = new ArrayList<Transaction>();
// for (int i = 0, j = 1; i < 64; i++) {
// Transaction t = ts.begin();
// openList.add(t);
// assertEquals(j, t.getId());
// j++;
// if (j > 16) {
// j = 1;
// }
// if (openList.size() >= 16) {
// t = openList.remove(0);
// t.commit();
// }
// }
//
// s = MVStore.open(null);
// ts = new TransactionStore(s);
// ts.init();
// ts.setMaxTransactionId(16);
// ArrayList<Transaction> fifo = New.arrayList();
// int open = 0;
// for (int i = 0; i < 64; i++) {
// Transaction t = null;
// if (open >= 16) {
// try {
// t = ts.begin();
// fail();
// } catch (IllegalStateException e) {
// // expected - too many open
// }
// Transaction first = fifo.remove(0);
// first.commit();
// open--;
// }
// t = ts.begin();
// t.openMap("data").put(i, i);
// fifo.add(t);
// open++;
// }
// s.close();
// }
//
// private void testStopWhileCommitting() throws Exception {
// String fileName = getBaseDir() + "/testStopWhileCommitting.h3";
// FileUtils.delete(fileName);
// Random r = new Random(0);
//
// for (int i = 0; i < 10;) {
// MVStore s;
// TransactionStore ts;
// Transaction tx;
// TransactionMap<Integer, String> m;
//
// s = MVStore.open(fileName);
// ts = new TransactionStore(s);
// ts.init();
// tx = ts.begin();
// s.setReuseSpace(false);
// m = tx.openMap("test");
// final String value = "x" + i;
// for (int j = 0; j < 1000; j++) {
// m.put(j, value);
// }
// final AtomicInteger state = new AtomicInteger();
// final MVStore store = s;
// final MVMap<Integer, String> other = s.openMap("other");
// Task task = new Task() {
//
// @Override
// public void call() throws Exception {
// for (int i = 0; !stop; i++) {
// state.set(i);
// other.put(i, value);
// store.commit();
// }
// }
// };
// task.execute();
// // wait for the task to start
// while (state.get() < 1) {
// Thread.yield();
// }
// // commit while writing in the task
// tx.commit();
// // wait for the task to stop
// task.get();
// store.close();
// s = MVStore.open(fileName);
// // roll back a bit, until we have some undo log entries
// assertTrue(s.hasMap("undoLog"));
// for (int back = 0; back < 100; back++) {
// int minus = r.nextInt(10);
// s.rollbackTo(Math.max(0, s.getCurrentVersion() - minus));
// MVMap<?, ?> undo = s.openMap("undoLog");
// if (undo.size() > 0) {
// break;
// }
// }
// // re-open the store, because we have opened
// // the undoLog map with the wrong data type
// s.close();
// s = MVStore.open(fileName);
// ts = new TransactionStore(s);
// List<Transaction> list = ts.getOpenTransactions();
// if (list.size() != 0) {
// tx = list.get(0);
// if (tx.getStatus() == Transaction.STATUS_COMMITTING) {
// i++;
// }
// }
// s.close();
// FileUtils.delete(fileName);
// assertFalse(FileUtils.exists(fileName));
// }
// }
//
// private void testGetModifiedMaps() {
// MVStore s = MVStore.open(null);
// TransactionStore ts = new TransactionStore(s);
// ts.init();
// Transaction tx;
// TransactionMap<String, String> m1, m2, m3;
// long sp;
//
// tx = ts.begin();
// m1 = tx.openMap("m1");
// m2 = tx.openMap("m2");
// m3 = tx.openMap("m3");
// assertFalse(tx.getChanges(0).hasNext());
// tx.commit();
// //三个ts.begin()代表三个事务A、B、C
// //首先是A写了3条记录并提交,然后B删除key为2的记录并修改key为3的记录,新增一条key为4的记录,但是B事务不提交
// //然后C事务开始读,因为H2数据库只支持read committed事务隔离级别,所以C只能读到A的修改,
// //而B事务没提交,所以C不能看到,
// //但是B事务自身是能看到自己修改的结果的。
// tx = ts.begin();
// m1 = tx.openMap("m1");
// m2 = tx.openMap("m2");
// m3 = tx.openMap("m3");
// m1.put("1", "100");
// sp = tx.setSavepoint();
// m2.put("1", "100");
// m3.put("1", "100");
// Iterator<Change> it = tx.getChanges(sp);
// assertTrue(it.hasNext());
// Change c;
// c = it.next();
// assertEquals("m3", c.mapName);
// assertEquals("1", c.key.toString());
// assertNull(c.value);
// assertTrue(it.hasNext());
// c = it.next();
// assertEquals("m2", c.mapName);
// assertEquals("1", c.key.toString());
// assertNull(c.value);
// assertFalse(it.hasNext());
//
// it = tx.getChanges(0);
// assertTrue(it.hasNext());
// c = it.next();
// assertEquals("m3", c.mapName);
// assertEquals("1", c.key.toString());
// assertNull(c.value);
// assertTrue(it.hasNext());
// c = it.next();
// assertEquals("m2", c.mapName);
// assertEquals("1", c.key.toString());
// assertNull(c.value);
// assertTrue(it.hasNext());
// c = it.next();
// assertEquals("m1", c.mapName);
// assertEquals("1", c.key.toString());
// assertNull(c.value);
// assertFalse(it.hasNext());
//
// tx.rollbackToSavepoint(sp);
//
// it = tx.getChanges(0);
// assertTrue(it.hasNext());
// c = it.next();
// assertEquals("m1", c.mapName);
// assertEquals("1", c.key.toString());
// assertNull(c.value);
// assertFalse(it.hasNext());
//
// tx.commit();
//
// s.close();
// }
//
// private void testKeyIterator() {
// MVStore s = MVStore.open(null);
// TransactionStore ts = new TransactionStore(s);
// ts.init();
// Transaction tx, tx2;
// TransactionMap<String, String> m, m2;
// Iterator<String> it, it2;
//
// tx = ts.begin();
// m = tx.openMap("test");
// m.put("1", "Hello");
// m.put("2", "World");
// m.put("3", ".");
// tx.commit();
//
// tx2 = ts.begin();
// m2 = tx2.openMap("test");
// m2.remove("2");
// m2.put("3", "!");
// m2.put("4", "?");
//
// tx = ts.begin();
// m = tx.openMap("test");
// it = m.keyIterator(null);
// assertTrue(it.hasNext());
// assertEquals("1", it.next());
// assertTrue(it.hasNext());
// //因为tx2没有提交,所以还是能看到原来的值,也就是说新事务只能读到已提交的数据(哪怕tx2修改了,但它没提交)
// assertEquals("2", it.next());
// assertTrue(it.hasNext());
// assertEquals("3", it.next());
// assertFalse(it.hasNext());
//
// it2 = m2.keyIterator(null);
// assertTrue(it2.hasNext());
// assertEquals("1", it2.next());
// assertTrue(it2.hasNext());
// assertEquals("3", it2.next());
// assertTrue(it2.hasNext());
// assertEquals("4", it2.next());
// assertFalse(it2.hasNext());
//
// s.close();
// }
//
// /**
// * Tests behavior when used for a sequence of SQL statements. Each statement
// * uses a savepoint. Within a statement, changes by the statement itself are
// * not seen; the change is only seen when the statement finished.
// * <p>
// * Update statements that change the key of multiple rows may use delete/add
// * pairs to do so (they don't need to first delete all entries and then
// * re-add them). Trying to add multiple values for the same key is not
// * allowed (an update statement that would result in a duplicate key).
// */
// private void testMultiStatement() {
// MVStore s = MVStore.open(null);
// TransactionStore ts = new TransactionStore(s);
// ts.init();
//
// Transaction tx;
// TransactionMap<String, String> m;
// long startUpdate;
//
// tx = ts.begin();
//
// // start of statement
// // create table test
// startUpdate = tx.setSavepoint();
// m = tx.openMap("test");
// m.setSavepoint(startUpdate);
//
// // start of statement
// // insert into test(id, name) values(1, 'Hello'), (2, 'World')
// startUpdate = tx.setSavepoint();
// m.setSavepoint(startUpdate);
// assertTrue(m.trySet("1", "Hello", true));
// assertTrue(m.trySet("2", "World", true));
// //assertTrue(m.trySet("3", "World", true));
// // not seen yet (within the same statement)
// //因为前面设了 m.setSavepoint(startUpdate),所以m.logId很小
// //当调用trySet时,logId增加了,而下面的两个get还是读startUpdate时的值,所以读不到,就为null
// assertNull(m.get("1"));
// //assertNull(m.get("1",startUpdate+1));
// assertNull(m.get("2"));
//
// // start of statement
// startUpdate = tx.setSavepoint();
// // now we see the newest version
// m.setSavepoint(startUpdate); //logId变了,所以下面两个get返回的值不为null
// assertEquals("Hello", m.get("1"));
// assertEquals("World", m.get("2"));
// // update test set primaryKey = primaryKey + 1
// // (this is usually a tricky case)
// assertEquals("Hello", m.get("1"));
// assertTrue(m.trySet("1", null, true));
// assertTrue(m.trySet("2", "Hello", true));
// assertEquals("World", m.get("2"));
// // already updated by this statement, so it has no effect
// // but still returns true because it was changed by this transaction
// assertTrue(m.trySet("2", null, true));
//
// assertTrue(m.trySet("3", "World", true));
// // not seen within this statement
// assertEquals("Hello", m.get("1"));
// assertEquals("World", m.get("2"));
// assertNull(m.get("3"));
//
// // start of statement
// startUpdate = tx.setSavepoint();
// m.setSavepoint(startUpdate);
// // select * from test
// assertNull(m.get("1"));
// assertEquals("Hello", m.get("2"));
// assertEquals("World", m.get("3"));
//
// // start of statement
// startUpdate = tx.setSavepoint();
// m.setSavepoint(startUpdate);
// // update test set id = 1
// // should fail: duplicate key
// assertTrue(m.trySet("2", null, true));
// assertTrue(m.trySet("1", "Hello", true));
// assertTrue(m.trySet("3", null, true));
// assertFalse(m.trySet("1", "World", true));
// tx.rollbackToSavepoint(startUpdate);
//
// startUpdate = tx.setSavepoint();
// m.setSavepoint(startUpdate);
// assertNull(m.get("1"));
// assertEquals("Hello", m.get("2"));
// assertEquals("World", m.get("3"));
//
// tx.commit();
//
// ts.close();
// s.close();
// }
//
// private void testTwoPhaseCommit() {
// String fileName = getBaseDir() + "/testTwoPhaseCommit.h3";
// FileUtils.delete(fileName);
//
// MVStore s;
// TransactionStore ts;
// Transaction tx;
// Transaction txOld;
// TransactionMap<String, String> m;
// List<Transaction> list;
//
// s = MVStore.open(fileName);
// ts = new TransactionStore(s);
// ts.init();
// tx = ts.begin();
// assertEquals(null, tx.getName());
// tx.setName("first transaction");
// assertEquals("first transaction", tx.getName());
// assertEquals(1, tx.getId());
// assertEquals(Transaction.STATUS_OPEN, tx.getStatus());
// m = tx.openMap("test");
// m.put("1", "Hello");
// list = ts.getOpenTransactions();
// assertEquals(1, list.size());
// txOld = list.get(0);
// assertTrue(tx.getId() == txOld.getId());
// assertEquals("first transaction", txOld.getName());
// s.commit();
// ts.close();
// s.close();
//
// s = MVStore.open(fileName);
// ts = new TransactionStore(s);
// ts.init();
// tx = ts.begin();
// assertEquals(2, tx.getId());
// m = tx.openMap("test");
// assertEquals(null, m.get("1")); //因为前面的事务没有提交,如果调用tx.commit()就不会为null
// m.put("2", "Hello");
// list = ts.getOpenTransactions();
// assertEquals(2, list.size());
// txOld = list.get(0);
// assertEquals(1, txOld.getId());
// assertEquals(Transaction.STATUS_OPEN, txOld.getStatus());
// assertEquals("first transaction", txOld.getName());
// txOld.prepare();
// assertEquals(Transaction.STATUS_PREPARED, txOld.getStatus());
// txOld = list.get(1);
// txOld.commit();
// s.commit();
// s.close();
//
// s = MVStore.open(fileName);
// ts = new TransactionStore(s);
// ts.init();
// tx = ts.begin();
// m = tx.openMap("test");
// m.put("3", "Test");
// assertEquals(2, tx.getId());
// list = ts.getOpenTransactions();
// assertEquals(2, list.size());
// txOld = list.get(1);
// assertEquals(2, txOld.getId());
// assertEquals(Transaction.STATUS_OPEN, txOld.getStatus());
// assertEquals(null, txOld.getName());
// txOld.rollback();
// txOld = list.get(0);
// assertEquals(1, txOld.getId());
// assertEquals(Transaction.STATUS_PREPARED, txOld.getStatus());
// assertEquals("first transaction", txOld.getName());
// txOld.commit();
// assertEquals("Hello", m.get("1"));
// s.close();
//
// FileUtils.delete(fileName);
// }
//
// private void testSavepoint() {
// MVStore s = MVStore.open(null);
// TransactionStore ts = new TransactionStore(s);
// ts.init();
// Transaction tx;
// TransactionMap<String, String> m;
//
// tx = ts.begin();
// m = tx.openMap("test");
// m.put("1", "Hello");
// m.put("2", "World");
// m.put("1", "Hallo");
// m.remove("2");
// m.put("3", "!");
// long logId = tx.setSavepoint();
// m.put("1", "Hi");
// m.put("2", ".");
// m.remove("3");
// tx.rollbackToSavepoint(logId);
// //为什么事务未提交也在看到当前值,而testMultiStatement中的例子却看不到?
// //因为testMultiStatement中的例子不在一个事务中,未调用setSavepoint修改readLogId字段,它的值总是Long.MAX_VALUE
// assertEquals("Hallo", m.get("1"));
// assertNull(m.get("2"));
// assertEquals("!", m.get("3"));
// tx.rollback();
//
// tx = ts.begin();
// m = tx.openMap("test");
// assertNull(m.get("1"));
// assertNull(m.get("2"));
// assertNull(m.get("3"));
//
// ts.close();
// s.close();
// }
//
// private void testCompareWithPostgreSQL() throws Exception {
// ArrayList<Statement> statements = New.arrayList();
// ArrayList<Transaction> transactions = New.arrayList();
// ArrayList<TransactionMap<Integer, String>> maps = New.arrayList();
// int connectionCount = 3, opCount = 1000, rowCount = 10;
// try {
// Class.forName("org.postgresql.Driver");
// for (int i = 0; i < connectionCount; i++) {
// Connection conn = DriverManager.getConnection(
// "jdbc:postgresql:test", "sa", "sa");
// statements.add(conn.createStatement());
// }
// } catch (Exception e) {
// // database not installed - ok
// return;
// }
// statements.get(0).execute(
// "drop table if exists test cascade");
// statements.get(0).execute(
// "create table test(id int primary key, name varchar(255))");
//
// MVStore s = MVStore.open(null);
// TransactionStore ts = new TransactionStore(s);
// ts.init();
// for (int i = 0; i < connectionCount; i++) {
// Statement stat = statements.get(i);
// // 100 ms to avoid blocking (the test is single threaded)
// stat.execute("set statement_timeout to 100");
// Connection c = stat.getConnection();
// c.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
// c.setAutoCommit(false);
// Transaction transaction = ts.begin();
// transactions.add(transaction);
// TransactionMap<Integer, String> map;
// map = transaction.openMap("test");
// maps.add(map);
// }
// StringBuilder buff = new StringBuilder();
//
// Random r = new Random(1);
// try {
// for (int i = 0; i < opCount; i++) {
// int connIndex = r.nextInt(connectionCount);
// Statement stat = statements.get(connIndex);
// Transaction transaction = transactions.get(connIndex);
// TransactionMap<Integer, String> map = maps.get(connIndex);
// if (transaction == null) {
// transaction = ts.begin();
// map = transaction.openMap("test");
// transactions.set(connIndex, transaction);
// maps.set(connIndex, map);
//
// // read all data, to get a snapshot
// ResultSet rs = stat.executeQuery(
// "select * from test order by id");
// buff.append(i).append(": [" + connIndex + "]=");
// int size = 0;
// while (rs.next()) {
// buff.append(' ');
// int k = rs.getInt(1);
// String v = rs.getString(2);
// buff.append(k).append(':').append(v);
// assertEquals(v, map.get(k));
// size++;
// }
// buff.append('\n');
// if (size != map.sizeAsLong()) {
// assertEquals(size, map.sizeAsLong());
// }
// }
// int x = r.nextInt(rowCount);
// int y = r.nextInt(rowCount);
// buff.append(i).append(": [" + connIndex + "]: ");
// ResultSet rs = null;
// switch (r.nextInt(7)) {
// case 0:
// buff.append("commit");
// stat.getConnection().commit();
// transaction.commit();
// transactions.set(connIndex, null);
// break;
// case 1:
// buff.append("rollback");
// stat.getConnection().rollback();
// transaction.rollback();
// transactions.set(connIndex, null);
// break;
// case 2:
// // insert or update
// String old = map.get(x);
// if (old == null) {
// buff.append("insert " + x + "=" + y);
// if (map.tryPut(x, "" + y)) {
// stat.execute("insert into test values(" + x + ", '" + y + "')");
// } else {
// buff.append(" -> row was locked");
// // the statement would time out in PostgreSQL
// // TODO test sometimes if timeout occurs
// }
// } else {
// buff.append("update " + x + "=" + y + " (old:" + old + ")");
// if (map.tryPut(x, "" + y)) {
// int c = stat.executeUpdate("update test set name = '" + y
// + "' where id = " + x);
// assertEquals(1, c);
// } else {
// buff.append(" -> row was locked");
// // the statement would time out in PostgreSQL
// // TODO test sometimes if timeout occurs
// }
// }
// break;
// case 3:
// buff.append("delete " + x);
// try {
// int c = stat.executeUpdate("delete from test where id = " + x);
// if (c == 1) {
// map.remove(x);
// } else {
// assertNull(map.get(x));
// }
// } catch (SQLException e) {
// assertTrue(map.get(x) != null);
// assertFalse(map.tryRemove(x));
// // PostgreSQL needs to rollback
// buff.append(" -> rollback");
// stat.getConnection().rollback();
// transaction.rollback();
// transactions.set(connIndex, null);
// }
// break;
// case 4:
// case 5:
// case 6:
// rs = stat.executeQuery("select * from test where id = " + x);
// String expected = rs.next() ? rs.getString(2) : null;
// buff.append("select " + x + "=" + expected);
// assertEquals("i:" + i, expected, map.get(x));
// break;
// }
// buff.append('\n');
// }
// } catch (Exception e) {
// e.printStackTrace();
// fail(buff.toString());
// }
// for (Statement stat : statements) {
// stat.getConnection().close();
// }
// ts.close();
// s.close();
// }
//
// private void testConcurrentTransactionsReadCommitted() {
// MVStore s = MVStore.open(null);
//
// TransactionStore ts = new TransactionStore(s);
// ts.init();
//
// Transaction tx1, tx2;
// TransactionMap<String, String> m1, m2;
//
// tx1 = ts.begin();
// m1 = tx1.openMap("test");
// m1.put("1", "Hi");
// m1.put("3", ".");
// tx1.commit();
//
// tx1 = ts.begin();
// m1 = tx1.openMap("test");
// m1.put("1", "Hello");
// m1.put("2", "World");
// m1.remove("3");
// tx1.commit();
//
// // start new transaction to read old data
// tx2 = ts.begin();
// m2 = tx2.openMap("test");
//
// // start transaction tx1, update/delete/add
// tx1 = ts.begin();
// m1 = tx1.openMap("test");
// m1.put("1", "Hallo");
// m1.remove("2");
// m1.put("3", "!");
//
// assertEquals("Hello", m2.get("1"));
// assertEquals("World", m2.get("2"));
// assertNull(m2.get("3"));
//
// tx1.commit();
//
// assertEquals("Hallo", m2.get("1"));
// assertNull(m2.get("2"));
// assertEquals("!", m2.get("3"));
//
// tx1 = ts.begin();
// m1 = tx1.openMap("test");
// m1.put("2", "World");
//
// assertNull(m2.get("2"));
// assertFalse(m2.tryRemove("2"));
// //tx1事务更改了key "2",但是它没提交,所以tryRemove、tryPut返回false
// assertFalse(m2.tryPut("2", "Welt"));
//
// tx2 = ts.begin();
// m2 = tx2.openMap("test");
// assertNull(m2.get("2"));
// m1.remove("2");
// assertNull(m2.get("2"));
// tx1.commit();
//
// tx1 = ts.begin();
// m1 = tx1.openMap("test");
// assertNull(m1.get("2"));
// m1.put("2", "World");
// m1.put("2", "Welt");
// tx1.rollback();
//
// tx1 = ts.begin();
// m1 = tx1.openMap("test");
// assertNull(m1.get("2"));
//
// ts.close();
// s.close();
// }
//
// private void testSingleConnection() {
// MVStore s = MVStore.open(null);
//
// TransactionStore ts = new TransactionStore(s);
// ts.init();
//
// Transaction tx;
// TransactionMap<String, String> m;
//
// // add, rollback
// tx = ts.begin();
// m = tx.openMap("test");
// m.put("1", "Hello");
// assertEquals("Hello", m.get("1"));
// m.put("2", "World");
// assertEquals("World", m.get("2"));
// tx.rollback();
// tx = ts.begin();
// m = tx.openMap("test");
// assertNull(m.get("1"));
// assertNull(m.get("2"));
//
// // add, commit
// tx = ts.begin();
// m = tx.openMap("test");
// m.put("1", "Hello");
// m.put("2", "World");
// assertEquals("Hello", m.get("1"));
// assertEquals("World", m.get("2"));
// tx.commit();
// tx = ts.begin();
// m = tx.openMap("test");
// assertEquals("Hello", m.get("1"));
// assertEquals("World", m.get("2"));
//
// // update+delete+insert, rollback
// tx = ts.begin();
// m = tx.openMap("test");
// m.put("1", "Hallo");
// m.remove("2");
// m.put("3", "!");
// assertEquals("Hallo", m.get("1"));
// assertNull(m.get("2"));
// assertEquals("!", m.get("3"));
// tx.rollback();
// tx = ts.begin();
// m = tx.openMap("test");
// assertEquals("Hello", m.get("1"));
// assertEquals("World", m.get("2"));
// assertNull(m.get("3"));
//
// // update+delete+insert, commit
// tx = ts.begin();
// m = tx.openMap("test");
// m.put("1", "Hallo");
// m.remove("2");
// m.put("3", "!");
// assertEquals("Hallo", m.get("1"));
// assertNull(m.get("2"));
// assertEquals("!", m.get("3"));
// tx.commit();
// tx = ts.begin();
// m = tx.openMap("test");
// assertEquals("Hallo", m.get("1"));
// assertNull(m.get("2"));
// assertEquals("!", m.get("3"));
//
// ts.close();
// s.close();
// }
//
//}
//