提交 f0beaad4 authored 作者: Thomas Mueller's avatar Thomas Mueller

A persistent multi-version map: explicit map creation; change map factory to…

A persistent multi-version map: explicit map creation; change map factory to become just a factory for data types again
上级 8d7a5a8d
......@@ -15,6 +15,8 @@ import org.h2.dev.store.btree.DataUtils;
*/
class IntegerType implements DataType {
public static final IntegerType INSTANCE = new IntegerType();
public int compare(Object a, Object b) {
return ((Integer) a).compareTo((Integer) b);
}
......
......@@ -12,25 +12,26 @@ import org.h2.dev.store.btree.Cursor;
import org.h2.dev.store.btree.CursorPos;
import org.h2.dev.store.btree.DataType;
import org.h2.dev.store.btree.MVMap;
import org.h2.dev.store.btree.MVStore;
import org.h2.dev.store.btree.Page;
import org.h2.util.New;
/**
* An r-tree implementation. It uses the quadratic split algorithm.
*
* @param <K> the key class
* @param <V> the value class
*/
public class MVRTreeMap<K, V> extends MVMap<K, V> {
public class MVRTreeMap<V> extends MVMap<SpatialKey, V> {
final SpatialType keyType;
private boolean quadraticSplit;
MVRTreeMap(MVStore store, int id, String name, DataType keyType,
DataType valueType, long createVersion) {
super(store, id, name, keyType, valueType, createVersion);
this.keyType = (SpatialType) keyType;
public MVRTreeMap(int dimensions, DataType valueType) {
super(new SpatialType(dimensions), valueType);
this.keyType = (SpatialType) getKeyType();
}
public static <V> MVRTreeMap<V> create(int dimensions, DataType valueType) {
return new MVRTreeMap<V>(dimensions, valueType);
}
@SuppressWarnings("unchecked")
......@@ -45,12 +46,10 @@ public class MVRTreeMap<K, V> extends MVMap<K, V> {
* @param x the rectangle
* @return the iterator
*/
public Iterator<K> findIntersectingKeys(K x) {
public Iterator<SpatialKey> findIntersectingKeys(SpatialKey x) {
checkOpen();
return new RTreeCursor<K>(this, root, x) {
protected boolean check(K key, K test) {
System.out.println("key " + key + " contains " + test + " = " + keyType.contains(key, test));
return new RTreeCursor(this, root, x) {
protected boolean check(boolean leaf, SpatialKey key, SpatialKey test) {
return keyType.contains(key, test);
}
};
......@@ -62,12 +61,14 @@ public class MVRTreeMap<K, V> extends MVMap<K, V> {
* @param x the rectangle
* @return the iterator
*/
public Iterator<K> findContainedKeys(K x) {
public Iterator<SpatialKey> findContainedKeys(SpatialKey x) {
checkOpen();
return new RTreeCursor<K>(this, root, x) {
protected boolean check(K key, K test) {
System.out.println("key " + key + " isInside " + test + " = " + keyType.contains(test, key));
return keyType.isInside(test, key);
return new RTreeCursor(this, root, x) {
protected boolean check(boolean leaf, SpatialKey key, SpatialKey test) {
if (leaf) {
return keyType.isInside(key, test);
}
return keyType.isOverlap(key, test);
}
};
}
......@@ -103,7 +104,7 @@ public class MVRTreeMap<K, V> extends MVMap<K, V> {
return null;
}
protected Page getPage(K key) {
protected Page getPage(SpatialKey key) {
return getPage(root, key);
}
......@@ -179,7 +180,7 @@ public class MVRTreeMap<K, V> extends MVMap<K, V> {
}
@SuppressWarnings("unchecked")
public V put(K key, V value) {
public V put(SpatialKey key, V value) {
return (V) putOrAdd(key, value, false);
}
......@@ -190,11 +191,11 @@ public class MVRTreeMap<K, V> extends MVMap<K, V> {
* @param key the key
* @param value the value
*/
public void add(K key, V value) {
public void add(SpatialKey key, V value) {
putOrAdd(key, value, true);
}
private Object putOrAdd(K key, V value, boolean alwaysAdd) {
private Object putOrAdd(SpatialKey key, V value, boolean alwaysAdd) {
checkWrite();
long writeVersion = store.getCurrentVersion();
Page p = root.copyOnWrite(writeVersion);
......@@ -221,7 +222,7 @@ public class MVRTreeMap<K, V> extends MVMap<K, V> {
} else {
result = set(p, writeVersion, key, value);
}
setRoot(p);
newRoot(p);
return result;
}
......@@ -425,11 +426,10 @@ public class MVRTreeMap<K, V> extends MVMap<K, V> {
* @param list the list
* @param p the root page
*/
@SuppressWarnings("unchecked")
public void addNodeKeys(ArrayList<K> list, Page p) {
public void addNodeKeys(ArrayList<SpatialKey> list, Page p) {
if (p != null && !p.isLeaf()) {
for (int i = 0; i < p.getKeyCount(); i++) {
list.add((K) p.getKey(i));
list.add((SpatialKey) p.getKey(i));
addNodeKeys(list, p.getChildPage(i));
}
}
......@@ -449,12 +449,10 @@ public class MVRTreeMap<K, V> extends MVMap<K, V> {
/**
* A cursor to iterate over a subset of the keys.
*
* @param <K> the key class
*/
static class RTreeCursor<K> extends Cursor<K> {
static class RTreeCursor extends Cursor<SpatialKey> {
protected RTreeCursor(MVRTreeMap<K, ?> map, Page root, K from) {
protected RTreeCursor(MVRTreeMap<?> map, Page root, SpatialKey from) {
super(map, root, from);
}
......@@ -470,31 +468,24 @@ public class MVRTreeMap<K, V> extends MVMap<K, V> {
/**
* Check a given key.
*
* @param leaf if the key is from a leaf page
* @param key the stored key
* @param test the user-supplied test key
* @return true if there is a match
*/
protected boolean check(K key, K test) {
protected boolean check(boolean leaf, SpatialKey key, SpatialKey test) {
return true;
}
@SuppressWarnings("unchecked")
protected void min(Page p, K x) {
protected void min(Page p, SpatialKey x) {
while (true) {
if (p.isLeaf()) {
pos = new CursorPos(p, 0, pos);
return;
// for (int i = 0; i < p.getKeyCount(); i++) {
// if (check((K) p.getKey(i), x)) {
// pos = new CursorPos(p, i, pos);
// return;
// }
// }
// break;
}
boolean found = false;
for (int i = 0; i < p.getKeyCount(); i++) {
if (check((K) p.getKey(i), x)) {
if (check(false, (SpatialKey) p.getKey(i), x)) {
pos = new CursorPos(p, i + 1, pos);
p = p.getChildPage(i);
found = true;
......@@ -508,12 +499,11 @@ public class MVRTreeMap<K, V> extends MVMap<K, V> {
fetchNext();
}
@SuppressWarnings("unchecked")
protected void fetchNext() {
while (pos != null) {
while (pos.index < pos.page.getKeyCount()) {
K k = (K) pos.page.getKey(pos.index++);
if (check(k, from)) {
SpatialKey k = (SpatialKey) pos.page.getKey(pos.index++);
if (check(true, k, from)) {
current = k;
return;
}
......@@ -522,7 +512,7 @@ public class MVRTreeMap<K, V> extends MVMap<K, V> {
if (pos == null) {
break;
}
MVRTreeMap<K, ?> m = (MVRTreeMap<K, ?>) map;
MVRTreeMap<?> m = (MVRTreeMap<?>) map;
if (pos.index < m.getChildPageCount(pos.page)) {
min(pos.page.getChildPage(pos.index++), from);
}
......@@ -531,4 +521,8 @@ public class MVRTreeMap<K, V> extends MVMap<K, V> {
}
}
public String getType() {
return "rtree";
}
}
......@@ -12,7 +12,6 @@ import java.nio.ByteBuffer;
import java.util.UUID;
import org.h2.dev.store.btree.DataType;
import org.h2.dev.store.btree.DataUtils;
import org.h2.dev.store.btree.MVStore;
import org.h2.util.Utils;
/**
......@@ -1065,13 +1064,13 @@ public class ObjectType implements DataType {
if (!(obj instanceof String)) {
return super.getMemory(obj);
}
return MVStore.STRING_TYPE.getMemory(obj);
return 24 + 2 * obj.toString().length();
}
@Override
public int compare(Object aObj, Object bObj) {
if (aObj instanceof String && bObj instanceof String) {
return MVStore.STRING_TYPE.compare(aObj, bObj);
return aObj.toString().compareTo(bObj.toString());
}
return super.compare(aObj, bObj);
}
......@@ -1081,7 +1080,7 @@ public class ObjectType implements DataType {
if (!(obj instanceof String)) {
return super.getMaxLength(obj);
}
return 1 + MVStore.STRING_TYPE.getMaxLength(obj);
return 1 + DataUtils.MAX_VAR_INT_LEN + 3 * obj.toString().length();
}
@Override
......
......@@ -9,7 +9,7 @@ package org.h2.test.store;
import java.nio.ByteBuffer;
import org.h2.dev.store.btree.DataType;
import org.h2.dev.store.btree.DataUtils;
import org.h2.dev.store.btree.MapFactory;
import org.h2.dev.store.btree.DataTypeFactory;
import org.h2.util.StringUtils;
/**
......@@ -19,7 +19,7 @@ public class RowType implements DataType {
private final DataType[] types;
private RowType(DataType[] types) {
RowType(DataType[] types) {
this.types = types;
}
......@@ -105,7 +105,7 @@ public class RowType implements DataType {
* @param factory the data type factory
* @return the row type
*/
static RowType fromString(String t, MapFactory factory) {
static RowType fromString(String t, DataTypeFactory factory) {
if (!t.startsWith("r(") || !t.endsWith(")")) {
throw new RuntimeException("Unknown type: " + t);
}
......
......@@ -5,29 +5,14 @@
*/
package org.h2.test.store;
import org.h2.dev.store.btree.MVMap;
import org.h2.dev.store.btree.MVStore;
import org.h2.dev.store.btree.DataType;
import org.h2.dev.store.btree.MapFactory;
import org.h2.dev.store.btree.DataTypeFactory;
import org.h2.dev.store.btree.StringType;
/**
* A data type factory.
*/
public class TestMapFactory implements MapFactory {
@Override
public <K, V> MVMap<K, V> buildMap(String mapType, MVStore store,
int id, String name, DataType keyType, DataType valueType,
long createVersion) {
if (mapType.equals("s")) {
return new SequenceMap<K, V>(store, id, name, keyType, valueType, createVersion);
} else if (mapType.equals("r")) {
return new MVRTreeMap<K, V>(store, id, name, keyType, valueType, createVersion);
} else {
throw new RuntimeException("Unsupported map type " + mapType);
}
}
public class SampleDataTypeFactory implements DataTypeFactory {
@Override
public DataType buildDataType(String s) {
......
......@@ -7,19 +7,16 @@
package org.h2.test.store;
import java.util.AbstractSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import org.h2.dev.store.btree.MVMap;
import org.h2.dev.store.btree.MVStore;
import org.h2.dev.store.btree.DataType;
/**
* A custom map returning the values 1 .. 10.
*
* @param <K> the key type
* @param <V> the key type
* A custom map returning the keys and values values 1 .. 10.
*/
public class SequenceMap<K, V> extends MVMap<K, V> {
public class SequenceMap extends MVMap<Integer, String> {
/**
* The minimum value.
......@@ -31,18 +28,21 @@ public class SequenceMap<K, V> extends MVMap<K, V> {
*/
int max = 10;
SequenceMap(MVStore store, int id, String name, DataType keyType,
DataType valueType, long createVersion) {
super(store, id, name, keyType, valueType, createVersion);
public SequenceMap() {
super(IntegerType.INSTANCE, IntegerType.INSTANCE);
}
public void open(MVStore store, HashMap<String, String> config) {
super.open(store, config);
setReadOnly(true);
}
public Set<K> keySet() {
return new AbstractSet<K>() {
public Set<Integer> keySet() {
return new AbstractSet<Integer>() {
@Override
public Iterator<K> iterator() {
return new Iterator<K>() {
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
int x = min;
......@@ -51,10 +51,9 @@ public class SequenceMap<K, V> extends MVMap<K, V> {
return x <= max;
}
@SuppressWarnings("unchecked")
@Override
public K next() {
return (K) Integer.valueOf(x++);
public Integer next() {
return Integer.valueOf(x++);
}
@Override
......
......@@ -8,7 +8,6 @@ package org.h2.test.store;
import java.util.Arrays;
/**
* A unique spatial key.
*/
......
......@@ -20,7 +20,7 @@ public class SpatialType implements DataType {
private final int dimensions;
private SpatialType(int dimensions) {
public SpatialType(int dimensions) {
if (dimensions <= 0 || dimensions > 255) {
throw new IllegalArgumentException("Dimensions: " + dimensions);
}
......@@ -208,7 +208,7 @@ public class SpatialType implements DataType {
}
/**
* Check whether a given object is completely inside and does not touch the
* Check whether a is completely inside b and does not touch the
* given bound.
*
* @param objA the object to check
......
......@@ -64,7 +64,7 @@ public class TestConcurrent extends TestMVStore {
long len = s.getFile().size();
if (len > 1024 * 100) {
// slow down writing
Thread.sleep(10);
Thread.sleep(20);
}
}
}
......@@ -131,7 +131,8 @@ public class TestConcurrent extends TestMVStore {
}
private void testConcurrentIterate() {
MVStore s = MVStoreBuilder.inMemory().with(new TestMapFactory()).open();
MVStore s = MVStoreBuilder.inMemory().
with(new SampleDataTypeFactory()).open();
s.setMaxPageSize(3);
final MVMap<Integer, Integer> map = s.openMap("test");
final int len = 10;
......
......@@ -21,6 +21,7 @@ import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import org.h2.dev.store.btree.MVStore;
import org.h2.dev.store.btree.StringType;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
import org.h2.util.New;
......@@ -52,7 +53,8 @@ public class TestMVRTree extends TestMVStore {
MVStore s;
s = openStore(fileName);
// s.setMaxPageSize(50);
MVRTreeMap<SpatialKey, String> r = s.openMap("data", "r", "s2", "");
MVRTreeMap<String> r = MVRTreeMap.create(2, StringType.INSTANCE);
r = s.openMap("data", r);
// r.setQuadraticSplit(true);
Random rand = new Random(1);
int len = 1000;
......@@ -76,7 +78,8 @@ public class TestMVRTree extends TestMVStore {
s.store();
s.close();
s = openStore(fileName);
r = s.openMap("data", "r", "s2", "");
r = MVRTreeMap.create(2, StringType.INSTANCE);
r = s.openMap("data", r);
// t = System.currentTimeMillis();
rand = new Random(1);
for (int i = 0; i < len; i++) {
......@@ -114,7 +117,8 @@ public class TestMVRTree extends TestMVStore {
FileUtils.delete(fileName);
MVStore s;
s = openStore(fileName);
MVRTreeMap<SpatialKey, String> r = s.openMap("data", "r", "s2", "");
MVRTreeMap<String> r = MVRTreeMap.create(2, StringType.INSTANCE);
r = s.openMap("data", r);
add(r, "Bern", key(0, 46.57, 7.27, 124381));
add(r, "Basel", key(1, 47.34, 7.36, 170903));
add(r, "Zurich", key(2, 47.22, 8.33, 376008));
......@@ -147,8 +151,7 @@ public class TestMVRTree extends TestMVStore {
list.add(r.get(it.next()));
}
Collections.sort(list);
assertEquals("[Basel]",
list.toString());
assertEquals("[Basel]", list.toString());
// contains
list.clear();
......@@ -157,17 +160,16 @@ public class TestMVRTree extends TestMVStore {
list.add(r.get(it.next()));
}
assertEquals(0, list.size());
k = key(0, 47.34, 7.36, 170903);
Collections.sort(list);
assertEquals("[Bern]",
list.toString());
k = key(0, 47.34, 7.36, 171000);
for (Iterator<SpatialKey> it = r.findContainedKeys(k); it.hasNext();) {
list.add(r.get(it.next()));
}
assertEquals("[Basel]", list.toString());
s.close();
}
private static void add(MVRTreeMap<SpatialKey, String> r, String name, SpatialKey k) {
private static void add(MVRTreeMap<String> r, String name, SpatialKey k) {
r.put(k, name);
}
......@@ -179,7 +181,7 @@ public class TestMVRTree extends TestMVStore {
return k;
}
private static void render(MVRTreeMap<SpatialKey, String> r, String fileName) {
private static void render(MVRTreeMap<String> r, String fileName) {
int width = 1000, height = 500;
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) img.getGraphics();
......@@ -235,7 +237,8 @@ public class TestMVRTree extends TestMVStore {
String fileName = getBaseDir() + "/testRandom.h3";
FileUtils.delete(fileName);
MVStore s = openStore(fileName);
MVRTreeMap<SpatialKey, String> m = s.openMap("data", "r", "s2", "");
MVRTreeMap<String> m = MVRTreeMap.create(2, StringType.INSTANCE);
m = s.openMap("data", m);
HashMap<SpatialKey, String> map = new HashMap<SpatialKey, String>();
Random rand = new Random(1);
int operationCount = 1000;
......@@ -278,7 +281,8 @@ public class TestMVRTree extends TestMVStore {
FileUtils.delete(fileName);
MVStore s;
s = openStore(fileName);
SequenceMap<Integer, String> seq = s.openMap("data", "s", "i", "");
SequenceMap seq = new SequenceMap();
seq = s.openMap("data", seq);
StringBuilder buff = new StringBuilder();
for (int x : seq.keySet()) {
buff.append(x).append(';');
......
......@@ -14,9 +14,11 @@ import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import org.h2.dev.store.btree.Cursor;
import org.h2.dev.store.btree.DataType;
import org.h2.dev.store.btree.MVMap;
import org.h2.dev.store.btree.MVStore;
import org.h2.dev.store.btree.MVStoreBuilder;
import org.h2.dev.store.btree.StringType;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
......@@ -38,6 +40,7 @@ public class TestMVStore extends TestBase {
public void test() throws Exception {
FileUtils.deleteRecursive(getBaseDir(), true);
testCacheSize();
testConcurrentOpen();
testFileHeader();
testFileHeaderCorruption();
......@@ -67,6 +70,39 @@ public class TestMVStore extends TestBase {
testSimple();
}
private void testCacheSize() {
String fileName = getBaseDir() + "/testCacheSize.h3";
MVStore s;
MVMap<Integer, String> map;
s = MVStoreBuilder.fileBased(fileName).
with(new SampleDataTypeFactory()).open();
map = s.openMap("test");
// add 10 MB of data
for (int i = 0; i < 1024; i++) {
map.put(i, new String(new char[10240]));
}
s.store();
s.close();
int[] expectedReadsForCacheSize = {
3419, 2590, 1924, 1440, 1127, 956, 918
};
for (int cacheSize = 0; cacheSize <= 6; cacheSize += 4) {
s = MVStoreBuilder.fileBased(fileName).
cacheSizeMB(1 + 3 * cacheSize).
with(new SampleDataTypeFactory()).open();
map = s.openMap("test");
for (int i = 0; i < 1024; i += 128) {
for (int j = 0; j < i; j++) {
map.get(j);
}
}
assertEquals(expectedReadsForCacheSize[cacheSize],
s.getFileReadCount());
s.close();
}
}
private void testConcurrentOpen() {
String fileName = getBaseDir() + "/testConcurrentOpen.h3";
MVStore s = MVStoreBuilder.fileBased(fileName).open();
......@@ -273,7 +309,8 @@ public class TestMVStore extends TestBase {
private void testIterateOldVersion() {
MVStore s;
Map<Integer, Integer> map;
s = MVStoreBuilder.inMemory().with(new TestMapFactory()).open();
s = MVStoreBuilder.inMemory().
with(new SampleDataTypeFactory()).open();
map = s.openMap("test");
int len = 100;
for (int i = 0; i < len; i++) {
......@@ -298,14 +335,16 @@ public class TestMVStore extends TestBase {
FileUtils.delete(fileName);
MVStore s;
Map<Object, Object> map;
s = MVStoreBuilder.fileBased(fileName).with(new TestMapFactory()).open();
s = MVStoreBuilder.fileBased(fileName).
with(new SampleDataTypeFactory()).open();
map = s.openMap("test");
map.put(1, "Hello");
map.put("2", 200);
map.put(new Object[1], new Object[]{1, "2"});
s.store();
s.close();
s = MVStoreBuilder.fileBased(fileName).with(new TestMapFactory()).open();
s = MVStoreBuilder.fileBased(fileName).
with(new SampleDataTypeFactory()).open();
map = s.openMap("test");
assertEquals("Hello", map.get(1).toString());
assertEquals(200, ((Integer) map.get("2")).intValue());
......@@ -506,7 +545,7 @@ public class TestMVStore extends TestBase {
assertEquals(i + 1, m.size());
}
assertEquals(1000, m.size());
assertEquals(281, s.getUnsavedPageCount());
assertEquals(279, s.getUnsavedPageCount());
s.store();
assertEquals(3, s.getFileWriteCount());
s.close();
......@@ -517,7 +556,7 @@ public class TestMVStore extends TestBase {
assertEquals(0, m.size());
s.store();
// ensure only nodes are read, but not leaves
assertEquals(34, s.getFileReadCount());
assertEquals(36, s.getFileReadCount());
assertEquals(2, s.getFileWriteCount());
s.close();
}
......@@ -660,11 +699,13 @@ public class TestMVStore extends TestBase {
data.put("1", "Hello");
data.put("2", "World");
s.store();
assertEquals("1/0///", m.get("map.data"));
assertEquals("id:1,name:data,type:btree,createVersion:0,key:,value:",
m.get("map.data"));
assertTrue(m.containsKey("chunk.1"));
assertEquals("Hello", data.put("1", "Hallo"));
s.store();
assertEquals("1/0///", m.get("map.data"));
assertEquals("id:1,name:data,type:btree,createVersion:0,key:,value:",
m.get("map.data"));
assertTrue(m.get("root.1").length() > 0);
assertTrue(m.containsKey("chunk.1"));
assertTrue(m.containsKey("chunk.2"));
......@@ -713,7 +754,16 @@ public class TestMVStore extends TestBase {
MVStore s = openStore(fileName);
// s.setCompressor(null);
s.setMaxPageSize(40);
MVMap<Integer, Object[]> m = s.openMap("data", "", "i", "r(i,,)");
MVMap<Integer, Object[]> m = new MVMap<Integer, Object[]>(
IntegerType.INSTANCE,
new RowType(new DataType[] {
IntegerType.INSTANCE,
StringType.INSTANCE,
StringType.INSTANCE
})
);
m = s.openMap("data", m);
// Profiler prof = new Profiler();
// prof.startCollecting();
// long t = System.currentTimeMillis();
......@@ -824,7 +874,7 @@ public class TestMVStore extends TestBase {
// System.out.println("len2: " + len);
}
private void testReuseSpace() {
private void testReuseSpace() throws Exception {
String fileName = getBaseDir() + "/testReuseSpace.h3";
FileUtils.delete(fileName);
long initialLength = 0;
......@@ -845,7 +895,8 @@ public class TestMVStore extends TestBase {
if (initialLength == 0) {
initialLength = len;
} else {
assertTrue(len <= initialLength * 2);
assertTrue("len: " + len + " initial: " + initialLength + " j: " + j,
len <= initialLength * 2);
}
}
}
......@@ -1038,7 +1089,7 @@ public class TestMVStore extends TestBase {
*/
protected static MVStore openStore(String fileName) {
MVStore store = MVStoreBuilder.fileBased(fileName).
with(new TestMapFactory()).open();
with(new SampleDataTypeFactory()).open();
store.setMaxPageSize(1000);
return store;
}
......
......@@ -83,12 +83,14 @@ public class Chunk {
int chunkId = buff.getInt();
int pageCount = buff.getInt();
long metaRootPos = buff.getLong();
long maxLength = buff.getLong();
long maxLengthLive = buff.getLong();
Chunk c = new Chunk(chunkId);
c.length = length;
c.pageCount = pageCount;
c.start = start;
c.metaRootPos = metaRootPos;
c.maxLength = maxLength;
c.maxLengthLive = maxLengthLive;
return c;
}
......@@ -99,6 +101,7 @@ public class Chunk {
buff.putInt(id);
buff.putInt(pageCount);
buff.putLong(metaRootPos);
buff.putLong(maxLength);
buff.putLong(maxLengthLive);
}
......@@ -134,7 +137,7 @@ public class Chunk {
return o instanceof Chunk && ((Chunk) o).id == id;
}
public String toString() {
public String asString() {
return
"id:" + id + "," +
"start:" + start + "," +
......
......@@ -10,23 +10,7 @@ package org.h2.dev.store.btree;
/**
* A factory for maps and data types.
*/
public interface MapFactory {
/**
* Build a map.
*
* @param mapType the map type and type specific meta data
* @param store the store
* @param id the unique map id
* @param name the map name
* @param keyType the key type
* @param valueType the value type
* @param createVersion when the map was created
* @return the map
*/
<K, V> MVMap<K, V> buildMap(
String mapType, MVStore store, int id, String name,
DataType keyType, DataType valueType, long createVersion);
public interface DataTypeFactory {
/**
* Parse the data type.
......
......@@ -16,6 +16,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import org.h2.upgrade.v1_1.util.New;
/**
* A stored map.
......@@ -29,33 +30,34 @@ public class MVMap<K, V> extends AbstractMap<K, V>
/**
* The store.
*/
protected final MVStore store;
protected MVStore store;
/**
* The current root page (may not be null).
*/
protected volatile Page root;
private final int id;
private final String name;
private int id;
private String name;
private long createVersion;
private final DataType keyType;
private final DataType valueType;
private final long createVersion;
private ArrayList<Page> oldRoots = new ArrayList<Page>();
private boolean closed;
private boolean readOnly;
protected MVMap(MVStore store, int id, String name,
DataType keyType, DataType valueType, long createVersion) {
this.store = store;
this.id = id;
this.name = name;
public MVMap(DataType keyType, DataType valueType) {
this.keyType = keyType;
this.valueType = valueType;
this.createVersion = createVersion;
this.root = Page.createEmpty(this, createVersion - 1);
store.registerUnsavedPage();
}
public void open(MVStore store, HashMap<String, String> config) {
this.store = store;
this.id = Integer.parseInt(config.get("id"));
this.name = config.get("name");
this.createVersion = Long.parseLong(config.get("createVersion"));
}
/**
......@@ -85,7 +87,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
// now p is a node; insert continues
}
Object result = put(p, writeVersion, key, value);
setRoot(p);
newRoot(p);
return (V) result;
}
......@@ -440,7 +442,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
public void clear() {
checkWrite();
root.removeAllRecursive();
setRoot(Page.createEmpty(this, store.getCurrentVersion()));
newRoot(Page.createEmpty(this, store.getCurrentVersion()));
}
/**
......@@ -481,7 +483,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
Page p = root.copyOnWrite(writeVersion);
@SuppressWarnings("unchecked")
V result = (V) remove(p, writeVersion, key);
setRoot(p);
newRoot(p);
return result;
}
......@@ -596,7 +598,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
return result;
}
protected void setRoot(Page newRoot) {
protected void newRoot(Page newRoot) {
if (root != newRoot) {
removeUnusedOldVersions();
if (root.getVersion() != newRoot.getVersion()) {
......@@ -640,7 +642,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*
* @return the key type
*/
DataType getKeyType() {
protected DataType getKeyType() {
return keyType;
}
......@@ -649,7 +651,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*
* @return the value type
*/
DataType getValueType() {
protected DataType getValueType() {
return valueType;
}
......@@ -838,18 +840,6 @@ public class MVMap<K, V> extends AbstractMap<K, V>
}
}
public String toString() {
StringBuilder buff = new StringBuilder();
buff.append("map:").append(name);
if (readOnly) {
buff.append(" readOnly");
}
if (closed) {
buff.append(" closed");
}
return buff.toString();
}
public int hashCode() {
return id;
}
......@@ -905,18 +895,29 @@ public class MVMap<K, V> extends AbstractMap<K, V>
// not found
if (i == -1) {
// smaller than all in-memory versions
return store.openMapVersion(version, name);
return store.openMapVersion(version, name, this);
}
i = -i - 2;
}
newest = oldRoots.get(i);
}
MVMap<K, V> m = new MVMap<K, V>(store, id, name, keyType, valueType, createVersion);
m.readOnly = true;
MVMap<K, V> m = openReadOnly();
m.root = newest;
return m;
}
protected MVMap<K, V> openReadOnly() {
MVMap<K, V> m = new MVMap<K, V>(keyType, valueType);
m.readOnly = true;
HashMap<String, String> config = New.hashMap();
config.put("id", String.valueOf(id));
config.put("name", name);
config.put("createVersion", String.valueOf(createVersion));
m.open(store, config);
m.root = root;
return m;
}
private int searchRoot(long version) {
int low = 0, high = oldRoots.size() - 1;
while (low <= high) {
......@@ -941,4 +942,24 @@ public class MVMap<K, V> extends AbstractMap<K, V>
return p.getChildPageCount();
}
/**
* Get the map type. When opening an existing map, the map type must match.
*
* @return the map type
*/
public String getType() {
return "btree";
}
public String asString() {
StringBuilder buff = new StringBuilder();
DataUtils.appendMap(buff, "id", id);
DataUtils.appendMap(buff, "name", name);
DataUtils.appendMap(buff, "type", getType());
DataUtils.appendMap(buff, "createVersion", createVersion);
DataUtils.appendMap(buff, "key", keyType.asString());
DataUtils.appendMap(buff, "value", valueType.asString());
return buff.toString();
}
}
......@@ -64,9 +64,18 @@ public class MVStoreBuilder {
return set("openMode", "r");
}
public MVStoreBuilder with(MapFactory mapFactory) {
int todoRemove;
return set("mapFactory", mapFactory);
/**
* Set the read cache size in MB. The default is 16 MB.
*
* @param mb the cache size
* @return this
*/
public MVStoreBuilder cacheSizeMB(int mb) {
return set("cacheSize", Integer.toString(mb));
}
public MVStoreBuilder with(DataTypeFactory factory) {
return set("dataTypeFactory", factory);
}
/**
......
......@@ -65,7 +65,7 @@ public class MVStoreUtils {
writer.println("file " + fileName);
writer.println(" length " + fileLength);
writer.println(" " + prop);
ByteBuffer block = ByteBuffer.allocate(32);
ByteBuffer block = ByteBuffer.allocate(40);
for (long pos = 0; pos < fileLength;) {
block.rewind();
DataUtils.readFully(file, pos, block);
......@@ -78,12 +78,14 @@ public class MVStoreUtils {
int chunkId = block.getInt();
int pageCount = block.getInt();
long metaRootPos = block.getLong();
long maxLength = block.getLong();
long maxLengthLive = block.getLong();
writer.println(" chunk " + chunkId +
" at " + pos +
" length " + chunkLength +
" pageCount " + pageCount +
" root " + metaRootPos +
" maxLength " + maxLength +
" maxLengthLive " + maxLengthLive);
ByteBuffer chunk = ByteBuffer.allocate(chunkLength);
DataUtils.readFully(file, pos, chunk);
......@@ -102,7 +104,7 @@ public class MVStoreUtils {
boolean node = (type & 1) != 0;
writer.println(" map " + mapId + " at " + p + " " +
(node ? "node" : "leaf") + " " +
(compressed ? "compressed" : "") + " " +
(compressed ? "compressed " : "") +
"len: " + pageLength + " entries: " + len);
p += pageLength;
chunkLength -= pageLength;
......
......@@ -13,6 +13,8 @@ import java.nio.ByteBuffer;
*/
public class StringType implements DataType {
public static final StringType INSTANCE = new StringType();
public int compare(Object a, Object b) {
return a.toString().compareTo(b.toString());
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论