提交 582ca70b authored 作者: Thomas Mueller's avatar Thomas Mueller

A persistent tree map (work in progress).

上级 cc743041
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package org.h2.dev.store.btree; package org.h2.dev.store.btree;
import java.util.Iterator; import java.util.Iterator;
import java.util.TreeMap;
/** /**
* A stored map. * A stored map.
...@@ -16,35 +17,23 @@ import java.util.Iterator; ...@@ -16,35 +17,23 @@ import java.util.Iterator;
*/ */
public class BtreeMap<K, V> { public class BtreeMap<K, V> {
private final BtreeMapStore store;
private final int id; private final int id;
private final String name; private final String name;
private final DataType keyType; private final DataType keyType;
private final DataType valueType; private final DataType valueType;
private final long createVersion;
private final TreeMap<Long, Page> oldRoots = new TreeMap<Long, Page>();
private BtreeMapStore store;
private Page root; private Page root;
private boolean readOnly;
private BtreeMap(BtreeMapStore store, int id, String name, DataType keyType, DataType valueType) { BtreeMap(BtreeMapStore store, int id, String name, DataType keyType, DataType valueType, long createVersion) {
this.store = store; this.store = store;
this.id = id; this.id = id;
this.name = name; this.name = name;
this.keyType = keyType; this.keyType = keyType;
this.valueType = valueType; this.valueType = valueType;
} this.createVersion = createVersion;
/**
* Open a map.
*
* @param <K> the key type
* @param <V> the value type
* @param store the tree store
* @param id the map id
* @param name the name of the map
* @param keyClass the key class
* @param valueClass the value class
* @return the map
*/
static <K, V> BtreeMap<K, V> open(BtreeMapStore store, int id, String name, DataType keyType, DataType valueType) {
return new BtreeMap<K, V>(store, id, name, keyType, valueType);
} }
/** /**
...@@ -54,8 +43,10 @@ public class BtreeMap<K, V> { ...@@ -54,8 +43,10 @@ public class BtreeMap<K, V> {
* @param data the value * @param data the value
*/ */
public void put(K key, V data) { public void put(K key, V data) {
markChanged(); checkWrite();
root = Page.put(this, root, key, data); Page oldRoot = root;
root = Page.put(this, root, store.getCurrentVersion(), key, data);
markChanged(oldRoot);
} }
/** /**
...@@ -66,12 +57,17 @@ public class BtreeMap<K, V> { ...@@ -66,12 +57,17 @@ public class BtreeMap<K, V> {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public V get(K key) { public V get(K key) {
checkOpen();
if (root == null) { if (root == null) {
return null; return null;
} }
return (V) root.find(key); return (V) root.find(key);
} }
public boolean containsKey(K key) {
return get(key) != null;
}
/** /**
* Get the page for the given value. * Get the page for the given value.
* *
...@@ -89,19 +85,32 @@ public class BtreeMap<K, V> { ...@@ -89,19 +85,32 @@ public class BtreeMap<K, V> {
* Remove all entries. * Remove all entries.
*/ */
public void clear() { public void clear() {
checkWrite();
if (root != null) { if (root != null) {
markChanged(); Page oldRoot = root;
root.removeAllRecursive(); root.removeAllRecursive();
root = null; root = null;
markChanged(oldRoot);
} }
} }
/** /**
* Remove all entries, and remove the map. * Remove all entries, and remove the map. The map becomes invalid.
*/ */
public void remove() { public void remove() {
clear(); checkWrite();
if (root != null) {
root.removeAllRecursive();
}
store.removeMap(id); store.removeMap(id);
oldRoots.clear();
root = null;
store = null;
readOnly = true;
}
public boolean isClosed() {
return store == null;
} }
/** /**
...@@ -110,25 +119,23 @@ public class BtreeMap<K, V> { ...@@ -110,25 +119,23 @@ public class BtreeMap<K, V> {
* @param key the key * @param key the key
*/ */
public void remove(K key) { public void remove(K key) {
checkWrite();
if (root != null) { if (root != null) {
markChanged(); Page oldRoot = root;
root = Page.remove(root, key); root = Page.remove(root, store.getCurrentVersion(), key);
markChanged(oldRoot);
} }
} }
/** private void markChanged(Page oldRoot) {
* Was this map changed. if (oldRoot != root) {
* oldRoots.put(store.getCurrentVersion(), oldRoot);
* @return true if yes store.markChanged(this);
*/ }
boolean isChanged() {
return root != null && root.getPos() < 0;
} }
private void markChanged() { public boolean hasUnsavedChanges() {
if (!isChanged()) { return oldRoots.size() > 0;
store.markChanged(name, this);
}
} }
/** /**
...@@ -160,20 +167,6 @@ public class BtreeMap<K, V> { ...@@ -160,20 +167,6 @@ public class BtreeMap<K, V> {
return valueType; return valueType;
} }
long getTransaction() {
return store.getTransaction();
}
/**
* Register a page and get the next temporary page id.
*
* @param p the new page
* @return the page id
*/
long registerTempPage(Page p) {
return store.registerTempPage(p);
}
/** /**
* Read a page. * Read a page.
* *
...@@ -184,22 +177,13 @@ public class BtreeMap<K, V> { ...@@ -184,22 +177,13 @@ public class BtreeMap<K, V> {
return store.readPage(this, pos); return store.readPage(this, pos);
} }
/**
* Remove a page.
*
* @param pos the position of the page
*/
void removePage(long pos) {
store.removePage(pos);
}
/** /**
* Set the position of the root page. * Set the position of the root page.
* *
* @param rootPos the position * @param rootPos the position, 0 for empty
*/ */
void setRootPos(long rootPos) { void setRootPos(long rootPos) {
root = readPage(rootPos); root = rootPos == 0 ? null : readPage(rootPos);
} }
/** /**
...@@ -209,6 +193,7 @@ public class BtreeMap<K, V> { ...@@ -209,6 +193,7 @@ public class BtreeMap<K, V> {
* @return the iterator * @return the iterator
*/ */
public Iterator<K> keyIterator(K from) { public Iterator<K> keyIterator(K from) {
checkOpen();
return new Cursor<K>(root, from); return new Cursor<K>(root, from);
} }
...@@ -238,4 +223,78 @@ public class BtreeMap<K, V> { ...@@ -238,4 +223,78 @@ public class BtreeMap<K, V> {
return id; return id;
} }
void rollbackTo(long version) {
checkWrite();
if (version <= createVersion) {
remove();
} else {
// iterating in ascending order, and pick the last version -
// this is not terribly efficient if there are many versions
// but it is a simple algorithm
Long newestOldVersion = null;
for (Iterator<Long> it = oldRoots.keySet().iterator(); it.hasNext();) {
Long x = it.next();
if (x >= version) {
if (newestOldVersion == null) {
newestOldVersion = x;
root = oldRoots.get(x);
}
it.remove();
}
}
}
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public boolean isReadOnly() {
return readOnly;
}
private void checkOpen() {
if (store == null) {
throw new IllegalStateException("This map is closed");
}
}
private void checkWrite() {
if (readOnly) {
checkOpen();
throw new IllegalStateException("This map is read-only");
}
}
public String toString() {
StringBuilder buff = new StringBuilder();
buff.append("map:").append(name);
if (readOnly) {
buff.append(" readOnly");
}
if (store == null) {
buff.append(" closed");
}
return buff.toString();
}
public void close() {
readOnly = true;
store = null;
oldRoots.clear();
root = null;
}
public int hashCode() {
return id;
}
public boolean equals(Object o) {
return this == o;
}
long getCreatedVersion() {
return createVersion;
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论