private void testSavepoint() {
    MVStore s = MVStore.open(null);
    TransactionStore ts = new TransactionStore(s);
    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);
    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();
  }
  /**
   * 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);
    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));
    // not seen yet (within the same statement)
    assertNull(m.get("1"));
    assertNull(m.get("2"));

    // start of statement
    startUpdate = tx.setSavepoint();
    // now we see the newest version
    m.setSavepoint(startUpdate);
    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 testGetModifiedMaps() {
    MVStore s = MVStore.open(null);
    TransactionStore ts = new TransactionStore(s);
    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();

    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();
  }