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