提交 203f2a1c authored 作者: Thomas Mueller's avatar Thomas Mueller

MVTableEngine

上级 36f3c9ef
...@@ -79,9 +79,12 @@ public class MVSecondaryIndex extends BaseIndex { ...@@ -79,9 +79,12 @@ public class MVSecondaryIndex extends BaseIndex {
public void add(Session session, Row row) { public void add(Session session, Row row) {
TransactionMap<Value, Value> map = getMap(session); TransactionMap<Value, Value> map = getMap(session);
ValueArray array = getKey(row); ValueArray array = getKey(row);
ValueArray unique = null;
if (indexType.isUnique()) { if (indexType.isUnique()) {
array.getList()[keyColumns - 1] = ValueLong.get(Long.MIN_VALUE); // this will detect committed entries only
ValueArray key = (ValueArray) map.getLatestCeilingKey(array); unique = getKey(row);
unique.getList()[keyColumns - 1] = ValueLong.get(Long.MIN_VALUE);
ValueArray key = (ValueArray) map.getLatestCeilingKey(unique);
if (key != null) { if (key != null) {
SearchRow r2 = getRow(key.getList()); SearchRow r2 = getRow(key.getList());
if (compareRows(row, r2) == 0) { if (compareRows(row, r2) == 0) {
...@@ -91,12 +94,35 @@ public class MVSecondaryIndex extends BaseIndex { ...@@ -91,12 +94,35 @@ public class MVSecondaryIndex extends BaseIndex {
} }
} }
} }
array.getList()[keyColumns - 1] = ValueLong.get(row.getKey());
try { try {
map.put(array, ValueLong.get(0)); map.put(array, ValueLong.get(0));
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName()); throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName());
} }
if (indexType.isUnique()) {
// check if there is another (uncommitted) entry
Iterator<Value> it = map.keyIterator(unique, true);
while (it.hasNext()) {
ValueArray k = (ValueArray) it.next();
SearchRow r2 = getRow(k.getList());
if (compareRows(row, r2) != 0) {
break;
}
if (containsNullAndAllowMultipleNull(r2)) {
// this is allowed
continue;
}
if (map.isSameTransaction(k)) {
continue;
}
map.remove(array);
if (map.get(k) != null) {
// committed
throw getDuplicateKeyException(k.toString());
}
throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName());
}
}
} }
@Override @Override
......
...@@ -397,7 +397,7 @@ public class MVTable extends TableBase { ...@@ -397,7 +397,7 @@ public class MVTable extends TableBase {
index = new MVDelegateIndex(this, indexId, index = new MVDelegateIndex(this, indexId,
indexName, primaryIndex, indexType); indexName, primaryIndex, indexType);
} else if (indexType.isSpatial()) { } else if (indexType.isSpatial()) {
index = new SpatialTreeIndex(this, indexId, indexName, cols, index = new SpatialTreeIndex(this, indexId, indexName, cols,
indexType, true, create, session); indexType, true, create, session);
} else { } else {
index = new MVSecondaryIndex(session.getDatabase(), index = new MVSecondaryIndex(session.getDatabase(),
...@@ -700,6 +700,10 @@ public class MVTable extends TableBase { ...@@ -700,6 +700,10 @@ public class MVTable extends TableBase {
return true; return true;
} }
/**
* Mark the transaction as committed, so that the modification counter of
* the database is incremented.
*/
public void commit() { public void commit() {
if (database != null) { if (database != null) {
lastModificationId = database.getNextModificationDataId(); lastModificationId = database.getNextModificationDataId();
......
...@@ -8,6 +8,7 @@ package org.h2.mvstore.db; ...@@ -8,6 +8,7 @@ package org.h2.mvstore.db;
import java.io.InputStream; import java.io.InputStream;
import java.lang.Thread.UncaughtExceptionHandler; import java.lang.Thread.UncaughtExceptionHandler;
import java.nio.channels.FileChannel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -233,7 +234,11 @@ public class MVTableEngine implements TableEngine { ...@@ -233,7 +234,11 @@ public class MVTableEngine implements TableEngine {
} }
public InputStream getInputStream() { public InputStream getInputStream() {
return new FileChannelInputStream(store.getFileStore().getFile(), false); FileChannel fc = store.getFileStore().getEncryptedFile();
if (fc == null) {
fc = store.getFileStore().getFile();
}
return new FileChannelInputStream(fc, false);
} }
/** /**
......
...@@ -1032,6 +1032,22 @@ public class TransactionStore { ...@@ -1032,6 +1032,22 @@ public class TransactionStore {
return data == null ? null : (V) data.value; return data == null ? null : (V) data.value;
} }
/**
* Whether the entry for this key was added or removed from this session.
*
* @param key the key
* @return true if yes
*/
public boolean isSameTransaction(K key) {
VersionedValue data = map.get(key);
if (data == null) {
// doesn't exist or deleted by a committed transaction
return false;
}
long tx = data.transactionId;
return tx == transaction.transactionId;
}
private VersionedValue getValue(K key, long maxLog) { private VersionedValue getValue(K key, long maxLog) {
VersionedValue data = map.get(key); VersionedValue data = map.get(key);
while (true) { while (true) {
...@@ -1182,7 +1198,7 @@ public class TransactionStore { ...@@ -1182,7 +1198,7 @@ public class TransactionStore {
* @return the result * @return the result
*/ */
public K lowerKey(K key) { public K lowerKey(K key) {
// TODO Auto-generated method stub // TODO transactional lowerKey
return map.lowerKey(key); return map.lowerKey(key);
} }
...@@ -1193,6 +1209,17 @@ public class TransactionStore { ...@@ -1193,6 +1209,17 @@ public class TransactionStore {
* @return the iterator * @return the iterator
*/ */
public Iterator<K> keyIterator(final K from) { public Iterator<K> keyIterator(final K from) {
return keyIterator(from, false);
}
/**
* Iterate over all keys.
*
* @param from the first key to return
* @param includeUncommitted whether uncommitted entries should be included
* @return the iterator
*/
public Iterator<K> keyIterator(final K from, final boolean includeUncommitted) {
return new Iterator<K>() { return new Iterator<K>() {
private final Cursor<K> cursor = map.keyIterator(from); private final Cursor<K> cursor = map.keyIterator(from);
private K current; private K current;
...@@ -1204,6 +1231,9 @@ public class TransactionStore { ...@@ -1204,6 +1231,9 @@ public class TransactionStore {
private void fetchNext() { private void fetchNext() {
while (cursor.hasNext()) { while (cursor.hasNext()) {
current = cursor.next(); current = cursor.next();
if (includeUncommitted) {
return;
}
if (containsKey(current)) { if (containsKey(current)) {
return; return;
} }
......
...@@ -987,7 +987,7 @@ public class TestMetaData extends TestBase { ...@@ -987,7 +987,7 @@ public class TestMetaData extends TestBase {
conn.close(); conn.close();
deleteDb("metaData"); deleteDb("metaData");
} }
private void testSessionsUncommitted() throws SQLException { private void testSessionsUncommitted() throws SQLException {
if (config.mvcc || config.memory) { if (config.mvcc || config.memory) {
return; return;
......
...@@ -93,7 +93,7 @@ public class TestMVStore extends TestBase { ...@@ -93,7 +93,7 @@ public class TestMVStore extends TestBase {
// longer running tests // longer running tests
testLargerThan2G(); testLargerThan2G();
} }
private void testOffHeapStorage() throws Exception { private void testOffHeapStorage() throws Exception {
OffHeapStore offHeap = new OffHeapStore(); OffHeapStore offHeap = new OffHeapStore();
MVStore s = new MVStore.Builder(). MVStore s = new MVStore.Builder().
...@@ -106,7 +106,7 @@ public class TestMVStore extends TestBase { ...@@ -106,7 +106,7 @@ public class TestMVStore extends TestBase {
} }
assertTrue(1000 < offHeap.getWriteCount()); assertTrue(1000 < offHeap.getWriteCount());
// s.close(); // s.close();
s = new MVStore.Builder(). s = new MVStore.Builder().
fileStore(offHeap). fileStore(offHeap).
open(); open();
...@@ -116,7 +116,7 @@ public class TestMVStore extends TestBase { ...@@ -116,7 +116,7 @@ public class TestMVStore extends TestBase {
} }
s.close(); s.close();
} }
private void testNewerWriteVersion() throws Exception { private void testNewerWriteVersion() throws Exception {
String fileName = getBaseDir() + "/testNewerWriteVersion.h3"; String fileName = getBaseDir() + "/testNewerWriteVersion.h3";
FileUtils.delete(fileName); FileUtils.delete(fileName);
...@@ -132,7 +132,7 @@ public class TestMVStore extends TestBase { ...@@ -132,7 +132,7 @@ public class TestMVStore extends TestBase {
m.put(0, "Hello World"); m.put(0, "Hello World");
s.store(); s.store();
s.close(); s.close();
try { try {
s = new MVStore.Builder(). s = new MVStore.Builder().
encryptionKey("007".toCharArray()). encryptionKey("007".toCharArray()).
...@@ -140,7 +140,7 @@ public class TestMVStore extends TestBase { ...@@ -140,7 +140,7 @@ public class TestMVStore extends TestBase {
open(); open();
fail(); fail();
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
assertEquals(DataUtils.ERROR_UNSUPPORTED_FORMAT, assertEquals(DataUtils.ERROR_UNSUPPORTED_FORMAT,
DataUtils.getErrorCode(e.getMessage())); DataUtils.getErrorCode(e.getMessage()));
} }
s = new MVStore.Builder(). s = new MVStore.Builder().
...@@ -151,8 +151,8 @@ public class TestMVStore extends TestBase { ...@@ -151,8 +151,8 @@ public class TestMVStore extends TestBase {
assertTrue(s.getFileStore().isReadOnly()); assertTrue(s.getFileStore().isReadOnly());
m = s.openMap("data"); m = s.openMap("data");
assertEquals("Hello World", m.get(0)); assertEquals("Hello World", m.get(0));
s.close(); s.close();
FileUtils.setReadOnly(fileName); FileUtils.setReadOnly(fileName);
s = new MVStore.Builder(). s = new MVStore.Builder().
encryptionKey("007".toCharArray()). encryptionKey("007".toCharArray()).
...@@ -161,7 +161,7 @@ public class TestMVStore extends TestBase { ...@@ -161,7 +161,7 @@ public class TestMVStore extends TestBase {
assertTrue(s.getFileStore().isReadOnly()); assertTrue(s.getFileStore().isReadOnly());
m = s.openMap("data"); m = s.openMap("data");
assertEquals("Hello World", m.get(0)); assertEquals("Hello World", m.get(0));
s.close(); s.close();
} }
...@@ -197,7 +197,7 @@ public class TestMVStore extends TestBase { ...@@ -197,7 +197,7 @@ public class TestMVStore extends TestBase {
s = new MVStore.Builder(). s = new MVStore.Builder().
fileName(fileName). fileName(fileName).
backgroundExceptionHandler(new UncaughtExceptionHandler() { backgroundExceptionHandler(new UncaughtExceptionHandler() {
@Override @Override
public void uncaughtException(Thread t, Throwable e) { public void uncaughtException(Thread t, Throwable e) {
exRef.set(e); exRef.set(e);
...@@ -416,7 +416,7 @@ public class TestMVStore extends TestBase { ...@@ -416,7 +416,7 @@ public class TestMVStore extends TestBase {
m = s.openMap("test"); m = s.openMap("test");
assertEquals("Hello", m.get(1)); assertEquals("Hello", m.get(1));
s.close(); s.close();
FileUtils.setReadOnly(fileName); FileUtils.setReadOnly(fileName);
passwordChars = "007".toCharArray(); passwordChars = "007".toCharArray();
s = new MVStore.Builder(). s = new MVStore.Builder().
...@@ -424,7 +424,7 @@ public class TestMVStore extends TestBase { ...@@ -424,7 +424,7 @@ public class TestMVStore extends TestBase {
encryptionKey(passwordChars).open(); encryptionKey(passwordChars).open();
assertTrue(s.getFileStore().isReadOnly()); assertTrue(s.getFileStore().isReadOnly());
s.close(); s.close();
FileUtils.delete(fileName); FileUtils.delete(fileName);
assertFalse(FileUtils.exists(fileName)); assertFalse(FileUtils.exists(fileName));
} }
......
...@@ -43,7 +43,8 @@ public class TestStreamStore extends TestBase { ...@@ -43,7 +43,8 @@ public class TestStreamStore extends TestBase {
FileUtils.deleteRecursive(getBaseDir(), true); FileUtils.deleteRecursive(getBaseDir(), true);
FileUtils.createDirectories(getBaseDir()); FileUtils.createDirectories(getBaseDir());
testVeryLarge(); testReadCount();
testLarge();
testDetectIllegalId(); testDetectIllegalId();
testTreeStructure(); testTreeStructure();
testFormat(); testFormat();
...@@ -51,8 +52,54 @@ public class TestStreamStore extends TestBase { ...@@ -51,8 +52,54 @@ public class TestStreamStore extends TestBase {
testWithFullMap(); testWithFullMap();
testLoop(); testLoop();
} }
private void testReadCount() throws IOException {
String fileName = getBaseDir() + "/testReadCount.h3";
FileUtils.delete(fileName);
MVStore s = new MVStore.Builder().
fileName(fileName).
open();
s.setCacheSize(1);
StreamStore streamStore = getAutoCommitStreamStore(s);
long size = s.getPageSplitSize() * 2;
for (int i = 0; i < 100; i++) {
streamStore.put(new RandomStream(size, i));
}
s.store();
MVMap<Long, byte[]> map = s.openMap("data");
assertTrue("size: " + map.size(), map.sizeAsLong() >= 100);
s.close();
s = new MVStore.Builder().
fileName(fileName).
open();
streamStore = getAutoCommitStreamStore(s);
for (int i = 0; i < 100; i++) {
streamStore.put(new RandomStream(size, -i));
}
s.store();
long readCount = s.getFileStore().getReadCount();
// the read count should be low because new blocks
// are appended at the end (not between existing blocks)
assertTrue("rc: " + readCount, readCount < 10);
map = s.openMap("data");
assertTrue("size: " + map.size(), map.sizeAsLong() >= 200);
s.close();
}
private static StreamStore getAutoCommitStreamStore(final MVStore s) {
MVMap<Long, byte[]> map = s.openMap("data");
return new StreamStore(map) {
@Override
protected void onStore(int len) {
if (s.getUnsavedPageCount() > s.getUnsavedPageCountMax() / 2) {
s.commit();
}
}
};
}
private void testVeryLarge() throws IOException { private void testLarge() throws IOException {
String fileName = getBaseDir() + "/testVeryLarge.h3"; String fileName = getBaseDir() + "/testVeryLarge.h3";
FileUtils.delete(fileName); FileUtils.delete(fileName);
final MVStore s = new MVStore.Builder(). final MVStore s = new MVStore.Builder().
...@@ -101,7 +148,7 @@ public class TestStreamStore extends TestBase { ...@@ -101,7 +148,7 @@ public class TestStreamStore extends TestBase {
} }
len = (int) Math.min(size - pos, len); len = (int) Math.min(size - pos, len);
int x = seed, end = off + len; int x = seed, end = off + len;
// a very simple pseudo-random number generator // a fast and very simple pseudo-random number generator
// with a period length of 4 GB // with a period length of 4 GB
// also good: x * 9 + 1, shift 6; x * 11 + 1, shift 7 // also good: x * 9 + 1, shift 6; x * 11 + 1, shift 7
while (off < end) { while (off < end) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论