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

MVTableEngine

上级 36f3c9ef
......@@ -79,9 +79,12 @@ public class MVSecondaryIndex extends BaseIndex {
public void add(Session session, Row row) {
TransactionMap<Value, Value> map = getMap(session);
ValueArray array = getKey(row);
ValueArray unique = null;
if (indexType.isUnique()) {
array.getList()[keyColumns - 1] = ValueLong.get(Long.MIN_VALUE);
ValueArray key = (ValueArray) map.getLatestCeilingKey(array);
// this will detect committed entries only
unique = getKey(row);
unique.getList()[keyColumns - 1] = ValueLong.get(Long.MIN_VALUE);
ValueArray key = (ValueArray) map.getLatestCeilingKey(unique);
if (key != null) {
SearchRow r2 = getRow(key.getList());
if (compareRows(row, r2) == 0) {
......@@ -91,12 +94,35 @@ public class MVSecondaryIndex extends BaseIndex {
}
}
}
array.getList()[keyColumns - 1] = ValueLong.get(row.getKey());
try {
map.put(array, ValueLong.get(0));
} catch (IllegalStateException e) {
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
......
......@@ -397,7 +397,7 @@ public class MVTable extends TableBase {
index = new MVDelegateIndex(this, indexId,
indexName, primaryIndex, indexType);
} else if (indexType.isSpatial()) {
index = new SpatialTreeIndex(this, indexId, indexName, cols,
index = new SpatialTreeIndex(this, indexId, indexName, cols,
indexType, true, create, session);
} else {
index = new MVSecondaryIndex(session.getDatabase(),
......@@ -700,6 +700,10 @@ public class MVTable extends TableBase {
return true;
}
/**
* Mark the transaction as committed, so that the modification counter of
* the database is incremented.
*/
public void commit() {
if (database != null) {
lastModificationId = database.getNextModificationDataId();
......
......@@ -8,6 +8,7 @@ package org.h2.mvstore.db;
import java.io.InputStream;
import java.lang.Thread.UncaughtExceptionHandler;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
......@@ -233,7 +234,11 @@ public class MVTableEngine implements TableEngine {
}
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 {
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) {
VersionedValue data = map.get(key);
while (true) {
......@@ -1182,7 +1198,7 @@ public class TransactionStore {
* @return the result
*/
public K lowerKey(K key) {
// TODO Auto-generated method stub
// TODO transactional lowerKey
return map.lowerKey(key);
}
......@@ -1193,6 +1209,17 @@ public class TransactionStore {
* @return the iterator
*/
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>() {
private final Cursor<K> cursor = map.keyIterator(from);
private K current;
......@@ -1204,6 +1231,9 @@ public class TransactionStore {
private void fetchNext() {
while (cursor.hasNext()) {
current = cursor.next();
if (includeUncommitted) {
return;
}
if (containsKey(current)) {
return;
}
......
......@@ -987,7 +987,7 @@ public class TestMetaData extends TestBase {
conn.close();
deleteDb("metaData");
}
private void testSessionsUncommitted() throws SQLException {
if (config.mvcc || config.memory) {
return;
......
......@@ -93,7 +93,7 @@ public class TestMVStore extends TestBase {
// longer running tests
testLargerThan2G();
}
private void testOffHeapStorage() throws Exception {
OffHeapStore offHeap = new OffHeapStore();
MVStore s = new MVStore.Builder().
......@@ -106,7 +106,7 @@ public class TestMVStore extends TestBase {
}
assertTrue(1000 < offHeap.getWriteCount());
// s.close();
s = new MVStore.Builder().
fileStore(offHeap).
open();
......@@ -116,7 +116,7 @@ public class TestMVStore extends TestBase {
}
s.close();
}
private void testNewerWriteVersion() throws Exception {
String fileName = getBaseDir() + "/testNewerWriteVersion.h3";
FileUtils.delete(fileName);
......@@ -132,7 +132,7 @@ public class TestMVStore extends TestBase {
m.put(0, "Hello World");
s.store();
s.close();
try {
s = new MVStore.Builder().
encryptionKey("007".toCharArray()).
......@@ -140,7 +140,7 @@ public class TestMVStore extends TestBase {
open();
fail();
} catch (IllegalStateException e) {
assertEquals(DataUtils.ERROR_UNSUPPORTED_FORMAT,
assertEquals(DataUtils.ERROR_UNSUPPORTED_FORMAT,
DataUtils.getErrorCode(e.getMessage()));
}
s = new MVStore.Builder().
......@@ -151,8 +151,8 @@ public class TestMVStore extends TestBase {
assertTrue(s.getFileStore().isReadOnly());
m = s.openMap("data");
assertEquals("Hello World", m.get(0));
s.close();
s.close();
FileUtils.setReadOnly(fileName);
s = new MVStore.Builder().
encryptionKey("007".toCharArray()).
......@@ -161,7 +161,7 @@ public class TestMVStore extends TestBase {
assertTrue(s.getFileStore().isReadOnly());
m = s.openMap("data");
assertEquals("Hello World", m.get(0));
s.close();
s.close();
}
......@@ -197,7 +197,7 @@ public class TestMVStore extends TestBase {
s = new MVStore.Builder().
fileName(fileName).
backgroundExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
exRef.set(e);
......@@ -416,7 +416,7 @@ public class TestMVStore extends TestBase {
m = s.openMap("test");
assertEquals("Hello", m.get(1));
s.close();
FileUtils.setReadOnly(fileName);
passwordChars = "007".toCharArray();
s = new MVStore.Builder().
......@@ -424,7 +424,7 @@ public class TestMVStore extends TestBase {
encryptionKey(passwordChars).open();
assertTrue(s.getFileStore().isReadOnly());
s.close();
FileUtils.delete(fileName);
assertFalse(FileUtils.exists(fileName));
}
......
......@@ -43,7 +43,8 @@ public class TestStreamStore extends TestBase {
FileUtils.deleteRecursive(getBaseDir(), true);
FileUtils.createDirectories(getBaseDir());
testVeryLarge();
testReadCount();
testLarge();
testDetectIllegalId();
testTreeStructure();
testFormat();
......@@ -51,8 +52,54 @@ public class TestStreamStore extends TestBase {
testWithFullMap();
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";
FileUtils.delete(fileName);
final MVStore s = new MVStore.Builder().
......@@ -101,7 +148,7 @@ public class TestStreamStore extends TestBase {
}
len = (int) Math.min(size - pos, 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
// also good: x * 9 + 1, shift 6; x * 11 + 1, shift 7
while (off < end) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论