提交 606aa497 authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore: maps can now be renamed.

上级 8d221f87
......@@ -62,7 +62,8 @@ public class MVMap<K, V> extends AbstractMap<K, V>
public void open(MVStore store, HashMap<String, String> config) {
this.store = store;
this.id = Integer.parseInt(config.get("id"));
this.createVersion = Long.parseLong(config.get("createVersion"));
String x = config.get("createVersion");
this.createVersion = x == null ? 0 : Long.parseLong(x);
}
/**
......@@ -799,7 +800,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
checkWrite();
removeUnusedOldVersions();
if (version <= createVersion) {
removeMap();
// the map is removed later
} else if (root.getVersion() >= version) {
// iterating in descending order -
// this is not terribly efficient if there are many versions
......@@ -997,24 +998,38 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* @return the map type
*/
public String getType() {
return "btree";
return null;
}
/**
* Get the map metadata as a string.
*
* @param name the map name (or null)
* @return the string
*/
public String asString() {
public String asString(String name) {
StringBuilder buff = new StringBuilder();
DataUtils.appendMap(buff, "id", id);
DataUtils.appendMap(buff, "type", getType());
if (name != null) {
DataUtils.appendMap(buff, "name", name);
}
if (createVersion != 0) {
DataUtils.appendMap(buff, "createVersion", createVersion);
}
String type = getType();
if (type != null) {
DataUtils.appendMap(buff, "type", type);
}
if (keyType != null) {
DataUtils.appendMap(buff, "key", keyType.asString());
String k = keyType.asString();
if (k.length() > 0) {
DataUtils.appendMap(buff, "key", k);
}
}
if (valueType != null) {
DataUtils.appendMap(buff, "value", valueType.asString());
String v = valueType.asString();
if (v.length() > 0) {
DataUtils.appendMap(buff, "value", v);
}
}
return buff.toString();
}
......@@ -1024,13 +1039,13 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*
* @param newMapName the name name
*/
public void rename(String newMapName) {
public void renameMap(String newMapName) {
checkWrite();
store.renameMap(this, newMapName);
}
public String toString() {
return asString();
return asString(null);
}
}
......@@ -44,9 +44,7 @@ H:3,...
TODO:
- after rollback: is a regular save ok?
- cache: change API to better match guava / Android
- rename a map
- MVStore: improved API thanks to Simo Tripodi
- implement table engine for H2
- automated 'kill process' and 'power failure' test
......@@ -154,8 +152,7 @@ public class MVStore {
private final HashMap<Long, HashMap<Integer, Chunk>> freedChunks = New.hashMap();
private MVMap<String, String> meta;
private final HashMap<String, MVMap<?, ?>> maps = New.hashMap();
private final HashMap<Integer, String> mapIdName = New.hashMap();
private final HashMap<Integer, MVMap<?, ?>> maps = New.hashMap();
/**
* The set of maps with potentially unsaved changes.
......@@ -291,34 +288,37 @@ public class MVStore {
@SuppressWarnings("unchecked")
public <T extends MVMap<K, V>, K, V> T openMap(String name, T template) {
checkOpen();
MVMap<K, V> m = (MVMap<K, V>) maps.get(name);
MVMap<K, V> m;
String x = meta.get("name." + name);
int id;
long root;
HashMap<String, String> c;
if (x != null) {
id = Integer.parseInt(x);
m = (MVMap<K, V>) maps.get(id);
if (m != null) {
return (T) m;
}
m = template;
String config = meta.get("map." + name);
long root;
int id;
HashMap<String, String> c;
if (config == null) {
String config = meta.get("map." + x);
c = DataUtils.parseMap(config);
c.put("id", x);
m.open(this, c);
String r = meta.get("root." + id);
root = r == null ? 0 : Long.parseLong(r);
} else {
c = New.hashMap();
id = ++lastMapId;
c.put("id", Integer.toString(id));
c.put("name", name);
c.put("createVersion", Long.toString(currentVersion));
m = template;
m.open(this, c);
meta.put("map." + name, m.asString());
meta.put("map." + id, m.asString(name));
meta.put("name." + name, Integer.toString(id));
root = 0;
} else {
c = DataUtils.parseMap(config);
id = Integer.parseInt(c.get("id"));
String r = meta.get("root." + id);
root = r == null ? 0 : Long.parseLong(r);
}
m.open(this, c);
m.setRootPos(root, -1);
mapIdName.put(id, name);
maps.put(name, m);
maps.put(id, m);
return (T) m;
}
......@@ -370,12 +370,12 @@ public class MVStore {
* @param id the map id
*/
void removeMap(int id) {
String name = mapIdName.get(id);
meta.remove("map." + name);
String name = getMapName(id);
meta.remove("map." + id);
meta.remove("name." + name);
meta.remove("root." + id);
mapsChanged.remove(id);
mapIdName.remove(id);
maps.remove(name);
maps.remove(id);
}
private DataType getDataType(Class<?> clazz) {
......@@ -402,7 +402,6 @@ public class MVStore {
meta = new MVMap<String, String>(StringDataType.INSTANCE, StringDataType.INSTANCE);
HashMap<String, String> c = New.hashMap();
c.put("id", "0");
c.put("name", "meta");
c.put("createVersion", Long.toString(currentVersion));
meta.open(this, c);
if (fileName == null) {
......@@ -572,7 +571,6 @@ public class MVStore {
chunks.clear();
cache.clear();
maps.clear();
mapIdName.clear();
mapsChanged.clear();
} catch (Exception e) {
throw DataUtils.illegalStateException("Closing failed for file " + fileName, e);
......@@ -1004,7 +1002,7 @@ public class MVStore {
if (mapId == 0) {
return meta;
}
return maps.get(mapIdName.get(mapId));
return maps.get(mapId);
}
/**
......@@ -1250,7 +1248,6 @@ public class MVStore {
if (!isKnownVersion(version)) {
throw DataUtils.illegalArgumentException("Unknown version: " + version);
}
// TODO could remove newer temporary pages on rollback
for (MVMap<?, ?> m : mapsChanged.values()) {
m.rollbackTo(version);
}
......@@ -1286,12 +1283,11 @@ public class MVStore {
readMeta();
}
}
int todoRollbackMapNames;
for (MVMap<?, ?> m : maps.values()) {
for (MVMap<?, ?> m : New.arrayList(maps.values())) {
int id = m.getId();
if (m.getCreateVersion() >= version) {
m.close();
removeMap(id);
maps.remove(id);
} else {
if (loadFromFile) {
String r = meta.get("root." + id);
......@@ -1389,20 +1385,20 @@ public class MVStore {
if (map.getName().equals(newName)) {
return;
}
if (meta.containsKey("map." + newName)) {
if (meta.containsKey("name." + newName)) {
throw DataUtils.illegalArgumentException("A map named " + newName + " already exists");
}
int id = map.getId();
String oldName = mapIdName.remove(id);
maps.remove(oldName);
String value = meta.remove("map." + oldName);
meta.put("map." + newName, value);
maps.put(newName, map);
mapIdName.put(id, newName);
String oldName = getMapName(id);
meta.remove("map." + id);
meta.remove("name." + oldName);
meta.put("map." + id, map.asString(newName));
meta.put("name." + newName, Integer.toString(id));
}
String getMapName(int id) {
return mapIdName.get(id);
String m = meta.get("map." + id);
return DataUtils.parseMap(m).get("name");
}
}
......@@ -55,7 +55,7 @@ public class MVPrimaryIndex extends BaseIndex {
public void renameTable(String newName) {
rename(newName + "_DATA");
map.rename(newName + "_DATA_" + getId());
map.renameMap(newName + "_DATA_" + getId());
}
public String getCreateSQL() {
......
......@@ -73,7 +73,7 @@ public class MVSecondaryIndex extends BaseIndex {
}
public void rename(String newName) {
map.rename(newName + "_" + getId());
map.renameMap(newName + "_" + getId());
super.rename(newName);
}
......
......@@ -42,6 +42,7 @@ public class TestMVStore extends TestBase {
public void test() throws Exception {
FileUtils.deleteRecursive(getBaseDir(), true);
testRenameMapRollback();
testCustomMapType();
testCacheSize();
testConcurrentOpen();
......@@ -73,11 +74,24 @@ public class TestMVStore extends TestBase {
testSimple();
}
private void testRenameMapRollback() {
MVStore s = openStore(null);
MVMap<Integer, Integer> map;
map = s.openMap("hello");
map.put(1, 10);
long old = s.incrementVersion();
map.renameMap("world");
map.put(2, 20);
assertEquals("world", map.getName());
s.rollbackTo(old);
assertEquals("hello", map.getName());
s.close();
}
private void testCustomMapType() {
String fileName = getBaseDir() + "/testMapType.h3";
FileUtils.delete(fileName);
MVStore s;
s = openStore(fileName);
MVStore s = openStore(fileName);
SequenceMap seq = new SequenceMap();
seq = s.openMap("data", seq);
StringBuilder buff = new StringBuilder();
......@@ -634,7 +648,7 @@ public class TestMVStore extends TestBase {
assertTrue(s.hasUnsavedChanges());
s.rollbackTo(v2);
assertFalse(s.hasUnsavedChanges());
assertNull(meta.get("map.data1"));
assertNull(meta.get("name.data1"));
assertNull(m0.get("1"));
assertEquals("Hello", m.get("1"));
assertEquals(2, s.store());
......@@ -643,9 +657,9 @@ public class TestMVStore extends TestBase {
s = openStore(fileName);
assertEquals(2, s.getCurrentVersion());
meta = s.getMetaMap();
assertTrue(meta.get("map.data") != null);
assertTrue(meta.get("map.data0") != null);
assertNull(meta.get("map.data1"));
assertTrue(meta.get("name.data") != null);
assertTrue(meta.get("name.data0") != null);
assertNull(meta.get("name.data1"));
m = s.openMap("data", String.class, String.class);
m0 = s.openMap("data0", String.class, String.class);
assertNull(m0.get("1"));
......@@ -735,17 +749,16 @@ public class TestMVStore extends TestBase {
assertTrue(m.containsKey("chunk.1"));
assertFalse(m.containsKey("chunk.2"));
assertEquals("id:1,type:btree,createVersion:0,key:,value:",
m.get("map.data"));
String id = s.getMetaMap().get("name.data");
assertEquals("name:data", m.get("map." + id));
assertTrue(m.containsKey("chunk.1"));
assertEquals("Hello", data.put("1", "Hallo"));
s.store();
assertEquals("id:1,type:btree,createVersion:0,key:,value:",
m.get("map.data"));
assertEquals("name:data", m.get("map." + id));
assertTrue(m.get("root.1").length() > 0);
assertTrue(m.containsKey("chunk.1"));
assertEquals("id:1,length:271,maxLength:288,maxLengthLive:0," +
"metaRoot:274877910924,pageCount:2," +
assertEquals("id:1,length:246,maxLength:224,maxLengthLive:0," +
"metaRoot:274877910922,pageCount:2," +
"start:8192,time:0,version:1", m.get("chunk.1"));
assertTrue(m.containsKey("chunk.2"));
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论