提交 1397fe53 authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore: improved API thanks to Simo Tripodi.

上级 54d90c04
......@@ -31,6 +31,7 @@ Change Log
</li><li>New connection setting "DEFAULT_TABLE_ENGINE" to use a specific
table engine if none is set explicitly. This is to simplify testing
the MVStore table engine.
</li><li>MVStore: improved API thanks to Simo Tripodi.
</li><li>MVStore: maps can now be renamed.
</li><li>MVStore: store the file header also at the end of each chunk,
which results in a further reduced number of write operations.
......
......@@ -557,14 +557,18 @@ public class DataUtils {
return new IllegalStateException(message + version());
}
public static IllegalStateException illegalStateException(String message, Exception e) {
return new IllegalStateException(message + version(), e);
public static IllegalStateException illegalStateException(String message, Exception cause) {
return new IllegalStateException(message + version(), cause);
}
public static IllegalArgumentException illegalArgumentException(String message) {
return new IllegalArgumentException(message + version());
}
public static IllegalArgumentException illegalArgumentException(String message, Exception cause) {
return new IllegalArgumentException(message + version(), cause);
}
public static UnsupportedOperationException unsupportedOperationException(String message) {
return new UnsupportedOperationException(message + version());
}
......
......@@ -17,6 +17,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import org.h2.mvstore.type.DataType;
import org.h2.mvstore.type.ObjectDataType;
import org.h2.util.New;
/**
......@@ -47,7 +48,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
private boolean closed;
private boolean readOnly;
public MVMap(DataType keyType, DataType valueType) {
protected MVMap(DataType keyType, DataType valueType) {
this.keyType = keyType;
this.valueType = valueType;
this.root = Page.createEmpty(this, -1);
......@@ -1048,4 +1049,76 @@ public class MVMap<K, V> extends AbstractMap<K, V>
return asString(null);
}
/**
* A builder for maps.
*
* @param <M> the map type
* @param <K> the key type
* @param <V> the value type
*/
public interface MapBuilder<M extends MVMap<K, V>, K, V> {
/**
* Create a new map of the given type.
*
* @return the map
*/
public M create();
}
/**
* A builder for this class.
*
* @param <K> the key type
* @param <V> the value type
*/
public static class Builder<K, V> implements MapBuilder<MVMap<K, V>, K, V> {
protected DataType keyType;
protected DataType valueType;
/**
* Create a new builder with the default key and value data types.
*/
public Builder() {
// ignore
}
/**
* Set the key data type.
*
* @param keyType the key type
* @return this
*/
public Builder<K, V> keyType(DataType keyType) {
this.keyType = keyType;
return this;
}
/**
* Set the key data type.
*
* @param valueType the key type
* @return this
*/
public Builder<K, V> valueType(DataType valueType) {
this.valueType = valueType;
return this;
}
@Override
public MVMap<K, V> create() {
if (keyType == null) {
keyType = new ObjectDataType();
}
if (valueType == null) {
valueType = new ObjectDataType();
}
return new MVMap<K, V>(keyType, valueType);
}
}
}
......@@ -69,4 +69,57 @@ public class MVMapConcurrent<K, V> extends MVMap<K, V> {
return result;
}
/**
* A builder for this class.
*
* @param <K> the key type
* @param <V> the value type
*/
public static class Builder<K, V> implements MapBuilder<MVMapConcurrent<K, V>, K, V> {
protected DataType keyType;
protected DataType valueType;
/**
* Create a new builder with the default key and value data types.
*/
public Builder() {
// ignore
}
/**
* Set the key data type.
*
* @param keyType the key type
* @return this
*/
public Builder<K, V> keyType(DataType keyType) {
this.keyType = keyType;
return this;
}
/**
* Set the key data type.
*
* @param valueType the key type
* @return this
*/
public Builder<K, V> valueType(DataType valueType) {
this.valueType = valueType;
return this;
}
@Override
public MVMapConcurrent<K, V> create() {
if (keyType == null) {
keyType = new ObjectDataType();
}
if (valueType == null) {
valueType = new ObjectDataType();
}
return new MVMapConcurrent<K, V>(keyType, valueType);
}
}
}
......@@ -21,7 +21,6 @@ import org.h2.compress.CompressLZF;
import org.h2.compress.Compressor;
import org.h2.mvstore.cache.CacheLongKeyLIRS;
import org.h2.mvstore.cache.FilePathCache;
import org.h2.mvstore.type.DataType;
import org.h2.mvstore.type.DataTypeFactory;
import org.h2.mvstore.type.ObjectDataTypeFactory;
import org.h2.mvstore.type.StringDataType;
......@@ -44,7 +43,8 @@ H:3,...
TODO:
- MVStore: improved API thanks to Simo Tripodi
- fix MVStore documentation, example code
- improve exception factory (fluent api)
- implement table engine for H2
- automated 'kill process' and 'power failure' test
- maybe split database into multiple files, to speed up compact
......@@ -121,8 +121,6 @@ public class MVStore {
*/
static final int BLOCK_SIZE = 4 * 1024;
private final HashMap<String, Object> config;
private final String fileName;
private final DataTypeFactory dataTypeFactory;
......@@ -187,7 +185,6 @@ public class MVStore {
private boolean closed;
MVStore(HashMap<String, Object> config) {
this.config = config;
this.fileName = (String) config.get("fileName");
DataTypeFactory parent = new ObjectDataTypeFactory();
DataTypeFactory f = (DataTypeFactory) config.get("dataTypeFactory");
......@@ -242,67 +239,49 @@ public class MVStore {
}
/**
* Open a map with the previous key and value type (if the map already
* exists), or Object if not.
* Open a map with the default settings. The map is automatically create if
* it does not yet exist. If a map with this name is already open, this map
* is returned.
*
* @param <K> the key type
* @param <V> the value type
* @param name the name of the map
* @return the map
*/
@SuppressWarnings("unchecked")
public <K, V> MVMap<K, V> openMap(String name) {
return (MVMap<K, V>) openMap(name, Object.class, Object.class);
return openMap(name, new MVMap.Builder<K, V>());
}
/**
* Open a map.
* Open a map with the given builder. The map is automatically create if it
* does not yet exist. If a map with this name is already open, this map is
* returned.
*
* @param <K> the key type
* @param <V> the value type
* @param name the name of the map
* @param keyClass the key class
* @param valueClass the value class
* @param builder the map builder
* @return the map
*/
public <K, V> MVMap<K, V> openMap(String name, Class<K> keyClass, Class<V> valueClass) {
checkOpen();
DataType keyType = getDataType(keyClass);
DataType valueType = getDataType(valueClass);
MVMap<K, V> m = new MVMap<K, V>(keyType, valueType);
return openMap(name, m);
}
/**
* Open a map using the given template. The returned map is of the same type
* as the template, and contains the same key and value types. If a map with
* this name is already open, this map is returned. If it is not open,
* the template object is opened with the applicable configuration.
*
* @param <T> the map type
* @param name the name of the map
* @param template the template map
* @return the opened map
*/
@SuppressWarnings("unchecked")
public <T extends MVMap<K, V>, K, V> T openMap(String name, T template) {
public <M extends MVMap<K, V>, K, V> M openMap(String name, MVMap.MapBuilder<M, K, V> builder) {
checkOpen();
MVMap<K, V> m;
String x = meta.get("name." + name);
int id;
long root;
HashMap<String, String> c;
M map;
if (x != null) {
id = Integer.parseInt(x);
m = (MVMap<K, V>) maps.get(id);
if (m != null) {
return (T) m;
@SuppressWarnings("unchecked")
M old = (M) maps.get(id);
if (old != null) {
return old;
}
m = template;
map = builder.create();
String config = meta.get("map." + x);
c = DataUtils.parseMap(config);
c.put("id", x);
m.open(this, c);
map.open(this, c);
String r = meta.get("root." + id);
root = r == null ? 0 : Long.parseLong(r);
} else {
......@@ -310,15 +289,15 @@ public class MVStore {
id = ++lastMapId;
c.put("id", Integer.toString(id));
c.put("createVersion", Long.toString(currentVersion));
m = template;
m.open(this, c);
meta.put("map." + id, m.asString(name));
map = builder.create();
map.open(this, c);
meta.put("map." + id, map.asString(name));
meta.put("name." + name, Integer.toString(id));
root = 0;
}
m.setRootPos(root, -1);
maps.put(id, m);
return (T) m;
map.setRootPos(root, -1);
maps.put(id, map);
return map;
}
/**
......@@ -377,14 +356,6 @@ public class MVStore {
maps.remove(id);
}
private DataType getDataType(Class<?> clazz) {
if (clazz == String.class) {
return StringDataType.INSTANCE;
}
String s = dataTypeFactory.getDataType(clazz);
return dataTypeFactory.buildDataType(s);
}
/**
* Mark a map as changed (containing unsaved changes).
*
......@@ -1372,10 +1343,6 @@ public class MVStore {
}
}
public String toString() {
return DataUtils.appendMap(new StringBuilder(), config).toString();
}
void renameMap(MVMap<?, ?> map, String newName) {
checkOpen();
if (map == meta) {
......
......@@ -18,7 +18,6 @@ import org.h2.index.Cursor;
import org.h2.index.IndexType;
import org.h2.message.DbException;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.type.ObjectDataType;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
......@@ -47,8 +46,8 @@ public class MVPrimaryIndex extends BaseIndex {
}
ValueArrayDataType t = new ValueArrayDataType(
db.getCompareMode(), db, sortTypes);
map = new MVMap<Long, Value[]>(new ObjectDataType(), t);
map = table.getStore().openMap(getName() + "_" + getId(), map);
map = table.getStore().openMap(getName() + "_" + getId(),
new MVMap.Builder<Long, Value[]>().valueType(t));
Long k = map.lastKey();
lastKey = k == null ? 0 : k;
}
......
......@@ -17,7 +17,6 @@ import org.h2.index.Cursor;
import org.h2.index.IndexType;
import org.h2.message.DbException;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.type.ObjectDataType;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
......@@ -52,10 +51,12 @@ public class MVSecondaryIndex extends BaseIndex {
sortTypes[i] = columns[i].sortType;
}
sortTypes[keyColumns - 1] = SortOrder.ASCENDING;
String name = getName() + "_" + getId();
ValueArrayDataType t = new ValueArrayDataType(
db.getCompareMode(), db, sortTypes);
map = new MVMap<Value[], Long>(t, new ObjectDataType());
map = table.getStore().openMap(getName() + "_" + getId(), map);
map = table.getStore().openMap(name,
new MVMap.Builder<Value[], Long>().keyType(t));
}
private static void checkIndexColumnTypes(IndexColumn[] columns) {
......
......@@ -37,9 +37,4 @@ public class ValueDataTypeFactory implements DataTypeFactory {
return parent.buildDataType(s);
}
@Override
public String getDataType(Class<?> objectClass) {
return parent.getDataType(objectClass);
}
}
......@@ -13,6 +13,8 @@ import org.h2.mvstore.CursorPos;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.Page;
import org.h2.mvstore.type.DataType;
import org.h2.mvstore.type.ObjectDataType;
import org.h2.mvstore.type.StringDataType;
import org.h2.util.New;
/**
......@@ -541,4 +543,53 @@ public class MVRTreeMap<V> extends MVMap<SpatialKey, V> {
return "rtree";
}
/**
* A builder for this class.
*
* @param <V> the value type
*/
public static class Builder<V> implements
MVMap.MapBuilder<MVRTreeMap<V>, SpatialKey, V> {
private int dimensions = 2;
private DataType valueType;
/**
* Create a new builder for maps with 2 dimensions.
*/
public Builder() {
// default
}
/**
* Set the dimensions.
*
* @param dimensions the dimensions to use
* @return this
*/
public Builder<V> dimensions(int dimensions) {
this.dimensions = dimensions;
return this;
}
/**
* Set the key data type.
*
* @param valueType the key type
* @return this
*/
public Builder<V> valueType(StringDataType valueType) {
this.valueType = valueType;
return this;
}
public MVRTreeMap<V> create() {
if (valueType == null) {
valueType = new ObjectDataType();
}
return new MVRTreeMap<V>(dimensions, valueType);
}
}
}
......@@ -26,15 +26,4 @@ public interface DataTypeFactory {
*/
DataType buildDataType(String dataType);
/**
* Get the data type identifier for the given class.
* <p>
* To avoid conflict with the default factory, the returned string should
* start with the package name of the type factory.
*
* @param objectClass the class
* @return the data type identifier, or null if not supported
*/
String getDataType(Class<?> objectClass);
}
......@@ -27,12 +27,4 @@ public class ObjectDataTypeFactory implements DataTypeFactory {
return null;
}
@Override
public String getDataType(Class<?> objectClass) {
if (objectClass == SpatialDataType.class) {
return "s";
}
return "o";
}
}
......@@ -267,7 +267,7 @@ public abstract class TestBase {
url = name;
}
int test;
// url = addOption(url, "DEFAULT_TABLE_ENGINE", MVTableEngine.class.getName());
url = addOption(url, "DEFAULT_TABLE_ENGINE", MVTableEngine.class.getName());
if (!config.memory) {
if (config.smallLog && admin) {
url = addOption(url, "MAX_LOG_SIZE", "1");
......
......@@ -27,9 +27,4 @@ public class SampleTypeFactory implements DataTypeFactory {
return parent.buildDataType(s);
}
@Override
public String getDataType(Class<?> objectClass) {
return parent.getDataType(objectClass);
}
}
......@@ -71,4 +71,23 @@ public class SequenceMap extends MVMap<Long, Long> {
};
}
/**
* A builder for this class.
*/
public static class Builder implements MapBuilder<SequenceMap, Long, Long> {
/**
* Create a new builder.
*/
public Builder() {
// ignore
}
@Override
public SequenceMap create() {
return new SequenceMap();
}
}
}
......@@ -50,8 +50,8 @@ public class TestConcurrent extends TestMVStore {
*/
private void testConcurrentMap() throws InterruptedException {
final MVStore s = openStore(null);
final MVMap<Integer, Integer> cm = MVMapConcurrent.create();
final MVMap<Integer, Integer> m = s.openMap("data", cm);
final MVMap<Integer, Integer> m = s.openMap("data",
new MVMapConcurrent.Builder<Integer, Integer>());
final int size = 20;
final Random rand = new Random(1);
Task task = new Task() {
......@@ -210,7 +210,7 @@ public class TestConcurrent extends TestMVStore {
*/
private void testConcurrentWrite() throws InterruptedException {
final MVStore s = openStore(null);
final MVMap<Integer, Integer> m = s.openMap("data", Integer.class, Integer.class);
final MVMap<Integer, Integer> m = s.openMap("data");
final int size = 20;
final Random rand = new Random(1);
Task task = new Task() {
......@@ -265,7 +265,7 @@ public class TestConcurrent extends TestMVStore {
private void testConcurrentRead() throws InterruptedException {
final MVStore s = openStore(null);
final MVMap<Integer, Integer> m = s.openMap("data", Integer.class, Integer.class);
final MVMap<Integer, Integer> m = s.openMap("data");
final int size = 3;
int x = (int) s.getCurrentVersion();
for (int i = 0; i < size; i++) {
......
......@@ -23,7 +23,6 @@ import javax.imageio.stream.FileImageOutputStream;
import org.h2.mvstore.MVStore;
import org.h2.mvstore.rtree.MVRTreeMap;
import org.h2.mvstore.rtree.SpatialKey;
import org.h2.mvstore.type.ObjectDataType;
import org.h2.mvstore.type.StringDataType;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
......@@ -54,12 +53,9 @@ public class TestMVRTree extends TestMVStore {
// create an in-memory store
MVStore s = MVStore.open(null);
// create an R-tree map
// the key has 2 dimensions, the value is a string
MVRTreeMap<String> r = MVRTreeMap.create(2, new ObjectDataType());
// open the map
r = s.openMap("data", r);
// open an R-tree map
MVRTreeMap<String> r = s.openMap("data",
new MVRTreeMap.Builder<String>());
// add two key-value pairs
// the first value is the key id (to make the key unique)
......@@ -84,8 +80,9 @@ public class TestMVRTree extends TestMVStore {
MVStore s;
s = openStore(fileName);
// s.setMaxPageSize(50);
MVRTreeMap<String> r = MVRTreeMap.create(2, StringDataType.INSTANCE);
r = s.openMap("data", r);
MVRTreeMap<String> r = s.openMap("data",
new MVRTreeMap.Builder<String>().dimensions(2).
valueType(StringDataType.INSTANCE));
// r.setQuadraticSplit(true);
Random rand = new Random(1);
int len = 1000;
......@@ -109,8 +106,9 @@ public class TestMVRTree extends TestMVStore {
s.store();
s.close();
s = openStore(fileName);
r = MVRTreeMap.create(2, StringDataType.INSTANCE);
r = s.openMap("data", r);
r = s.openMap("data",
new MVRTreeMap.Builder<String>().dimensions(2).
valueType(StringDataType.INSTANCE));
// t = System.currentTimeMillis();
rand = new Random(1);
for (int i = 0; i < len; i++) {
......@@ -148,8 +146,10 @@ public class TestMVRTree extends TestMVStore {
FileUtils.delete(fileName);
MVStore s;
s = openStore(fileName);
MVRTreeMap<String> r = MVRTreeMap.create(2, StringDataType.INSTANCE);
r = s.openMap("data", r);
MVRTreeMap<String> r = s.openMap("data",
new MVRTreeMap.Builder<String>().dimensions(2).
valueType(StringDataType.INSTANCE));
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));
......@@ -273,9 +273,11 @@ public class TestMVRTree extends TestMVStore {
String fileName = getBaseDir() + "/testRandom.h3";
FileUtils.delete(fileName);
MVStore s = openStore(fileName);
MVRTreeMap<String> m = MVRTreeMap.create(2, StringDataType.INSTANCE);
MVRTreeMap<String> m = s.openMap("data",
new MVRTreeMap.Builder<String>());
m.setQuadraticSplit(quadraticSplit);
m = s.openMap("data", m);
HashMap<SpatialKey, String> map = new HashMap<SpatialKey, String>();
Random rand = new Random(1);
int operationCount = 1000;
......
......@@ -28,7 +28,7 @@ public class TestMVTableEngine extends TestBase {
}
public void test() throws Exception {
// testCase();
testCase();
testSimple();
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论