提交 7f21dd1e authored 作者: Thomas Mueller's avatar Thomas Mueller

Prepare release.

上级 775b1b00
...@@ -18,6 +18,10 @@ Change Log ...@@ -18,6 +18,10 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>-
</li></ul>
<h2>Version 1.3.165 (2012-03-18)</h2>
<ul><li>Better string representation for decimal values (for example 0.00000000 instead of 0E-26). <ul><li>Better string representation for decimal values (for example 0.00000000 instead of 0E-26).
</li><li>Prepared statements could only be re-used if the same data types were used </li><li>Prepared statements could only be re-used if the same data types were used
the second time they were executed. the second time they were executed.
...@@ -560,108 +564,5 @@ Change Log ...@@ -560,108 +564,5 @@ Change Log
</li><li>Deleting rows in the trigger table inside a trigger could result in a NullPointerException. </li><li>Deleting rows in the trigger table inside a trigger could result in a NullPointerException.
</li></ul> </li></ul>
<h2>Version 1.3.151 Beta (2011-02-12)</h2>
<ul><li>Server: it was possible to open a database outside of the base URL
(if one was set). The test case was broken.
</li><li>The H2 Console tool now supports a database URL as a command line option.
</li><li>Clob.getSubString and Blob.getBytes are now up to 3 times faster.
</li><li>New database setting DEFAULT_CONNECTION (disabled by default)
to support DriverManager.getConnection("jdbc:default:connection").
Please note the Oracle JDBC driver will try to resolve this database URL if it is loaded before the H2 driver.
</li><li>JaQu: the static map Db.TOKENS was not synchronized.
</li><li>DatabaseMetaData.getProcedureColumns returned the wrong data
(it also returned the Connection parameter if there was any).
</li><li>Issue 284: If the query cache was used (enabled by default in version 1.3.x),
and multiple threads used the same connection,
and the same query but different prepared statements,
parameters of one prepared statement could be overwritten by another.
</li><li>If the query cache was used (enabled by default in version 1.3.x),
parameters of re-used queries were not reset in prepared statements.
</li><li>CallableStatement: the first row of the result set was skipped when using CallableStatement.execute().
</li><li>Batch update exceptions now include the root cause and all chained exceptions (getNextException).
</li><li>Reading a large result set with a BLOB or CLOB column could throw a NullPointerException.
</li><li>Support for the MS SQL Server syntax VARCHAR(MAX).
</li><li>The Recover tool could print "/ by zero" on an empty database.
</li><li>For tables without a single column primary key of type INT or LONG,
the unique row id had gaps, which didn't look nice.
</li><li>Each table now has a pseudo-column "_ROWID_" to get the unique row id (only enabled for version 1.3.x).
</li><li>User defined functions can now have parameters of any class. Values of type
OTHER (or OBJECT or JAVA_OBJECT) are automatically de-serialized in that case.
</li><li>Linked tables: for NUMERIC column, Oracle reports precision 0 and scale -127.
A workaround has been implemented (the same as for DECIMAL columns in version 1.3.150).
</li><li>Issue 279: Auto-Server mode: unclear error message when trying to connect using AUTO_SERVER
if the database is already open without the flag (Connection is broken: "null").
</li><li>Issue 238: Can drop a column that has a single-column constraint.
</li></ul>
<h2>Version 1.3.150 Beta (2011-01-28)</h2>
<ul><li>CSVREAD / CSVWRITE: instead of setting the options one by one,
all options can be combined into a space separated key-value pairs.
</li><li>CSVREAD / CSV tool: there is a new option "lineCommentCharacter" to set or disable line comments.
For H2 version 1.2, the default is '#' (as before). For H2 version 1.3, line comments are disabled by default.
</li><li>Issue 277: JaQu didn't correctly convert a CLOB column to a String.
</li><li>./build.sh testNetwork could block if there was a network configuration problem.
</li><li>Reading input streams from the classpath is now supported. Example:
RUNSCRIPT FROM 'classpath:org/h2/samples/newsfeed.sql'.
</li><li>PreparedStatement.toString() now includes the parameter values in a human readable format.
</li><li>New database setting OPTIMIZE_IN_SELECT (enabled by default for version 1.3.x).
If enabled, IN(SELECT...) conditions are faster if there are many rows in the table or subquery.
</li><li>Linked tables: for DECIMAL column, Oracle reports precision 0 and scale -127.
A workaround has been implemented.
</li><li>TCP Server: a base directory with a prefix with a database name with prefix didn't work as expected
(this only applies to non-disk and wrapped file systems).
</li><li>Version 1.3: when re-running the same query so that the previous result was re-used,
and if the result set was large so that it was stored externally (in a temp file or in a temp table),
then reading rows from both result sets resulted in an exception or wrong behavior.
</li><li>A new sample application that shows how to create
a read-only database in a zip file where the database file is split into multiple smaller parts.
</li><li>The Backup tool now supports compressing all files in a given directory.
</li><li>H2 Console: the special syntax @list and @meta can not be combined.
</li><li>The wrong error message was thrown when trying to open a database where the database file
could not be read for some reason (for example because the split file system was used, and the file
was split at the wrong position).
</li><li>There was a memory leak in the trace system. Opening and closing many connections could run out of memory.
</li><li>The scan-resistant cache type "TQ" (two queue) is again available.
To use it, append ;CACHE_TYPE=TQ to the database URL.
</li></ul>
<h2>Version 1.3.149 Beta (2011-01-07)</h2>
<ul><li>Lob in database: after the process was killed while adding a lob,
inserting a lob could throw an exception (primary key violation in LOB_MAP).
</li><li>Server tool: the tcpShutdown feature now also stops other servers (web server and PostgreSQL server).
</li><li>Server tool: the tcpShutdown url must now end with the port if the port is used.
</li><li>Server tool: when starting one service fails, the started services are now stopped.
</li><li>Multi-version concurrency: SELECT ... FOR UPDATE threw a "concurrent update" exception immediately
instead of a "lock timeout" exception after the set lock timeout if the row was already locked by another connection.
</li><li>Translation: the H2 Console and error messages have been translated to Czech by Hannibal (http://hannibal.cestiny.cz/). Thanks a lot!
</li><li>When the system property h2.lobInDatabase is set, reading a BLOB is a bit faster
because the length is not read.
</li><li>The SimpleResultSet now has a feature to not close the result set after reading the last row.
</li><li>Native fulltext search: the ignore list doesn't need to be all uppercase now.
</li><li>Improved statistics output in the Recover tool.
</li><li>Issue 269: GROUP BY queries with a column or having clause that contains IN(SELECT ...)
could return the wrong values. Example: SELECT X IN(SELECT Y) FROM Z GROUP BY X.
</li><li>After closing a database, the writer thread will stop almost immediately (instead of after at most 100 ms).
This should avoid error messages in the Tomcat log file after a shutdown.
</li><li>Issue 271: ResultSet.getConcurrency() did not correctly detect that the result set is not updatable.
</li><li>The SLF4J 1.6 API is now used by default (this should not have any user visible effect).
</li><li>The org.h2.tools.Console no longer calls System.exit on shutdown
(this should not have any user visible effect, but should allow to integrate the tool easier into other applications).
</li><li>Improved date / time arithmetics. Adding and subtracting a
floating point value from a date or timestamp is now supported.
</li><li>When creating a BLOB with in InputStream or a CLOB with a Reader, and the InputStream or Reader
threw an non-IOException, then the LOB storage was broken when storing LOBs in the database.
</li><li>EXPLAIN SELECT didn't include the sample size if one was set.
</li><li>Improved EXPLAIN plan formatting and documentation.
</li><li>Issue 266: Domains: a NOT NULL clause in a domain definition ignored.
</li><li>More accurate calculation of variance and standard deviation for large number of samples with low variance
(now using Welford's method).
</li><li>Server: CLOB data with unicode characters between character code 0xd800 and 0xdfff
were not transferred correctly. In many cases, the thread was stuck afterwards.
</li><li>Issue 264: the Hibernate dialect in src/tools was removed because it is outdated.
</li><li>Cache size limit could be exceeded for certain queries, leading to an OutOfMemoryError
in some cases. Fixed.
</li></ul>
<!-- [close] { --></div></td></tr></table><!-- } --><!-- analytics --></body></html> <!-- [close] { --></div></td></tr></table><!-- } --><!-- analytics --></body></html>
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -74,6 +74,7 @@ public interface DatabaseEventListener extends EventListener { ...@@ -74,6 +74,7 @@ public interface DatabaseEventListener extends EventListener {
* <p> * <p>
* More states might be added in future versions, therefore implementations * More states might be added in future versions, therefore implementations
* should silently ignore states that they don't understand. * should silently ignore states that they don't understand.
* </p>
* *
* @param state the state * @param state the state
* @param name the object name * @param name the object name
......
...@@ -312,11 +312,10 @@ public class DbSettings extends SettingsBase { ...@@ -312,11 +312,10 @@ public class DbSettings extends SettingsBase {
public final boolean shareLinkedConnections = get("SHARE_LINKED_CONNECTIONS", true); public final boolean shareLinkedConnections = get("SHARE_LINKED_CONNECTIONS", true);
/** /**
* Database setting <code>LOG_SIZE_LIMIT</code> * Database setting <code>LOG_SIZE_LIMIT</code> (default: 0).<br />
* (default: 0).<br /> * The maximum size of the transaction log (in MB). If the transaction log
* The maximum size of the transaction log (in MB). If the transaction log is bigger * is bigger than this value the oldest session is rolled back. The value 0
* than this value the oldest session is rolled back. The value 0 means the * means the sessions are never rolled back.
* sessions are never rolled back.
*/ */
public long logSizeLimit = get("LOG_SIZE_LIMIT", 0); public long logSizeLimit = get("LOG_SIZE_LIMIT", 0);
......
...@@ -16,22 +16,22 @@ public class Constants { ...@@ -16,22 +16,22 @@ public class Constants {
/** /**
* The build date is updated for each public release. * The build date is updated for each public release.
*/ */
public static final String BUILD_DATE = "2012-02-03"; public static final String BUILD_DATE = "2012-03-18";
/** /**
* The build date is updated for each public release. * The build date is updated for each public release.
*/ */
public static final String BUILD_DATE_STABLE = "2011-12-30"; public static final String BUILD_DATE_STABLE = "2012-02-03";
/** /**
* The build id is incremented for each public release. * The build id is incremented for each public release.
*/ */
public static final int BUILD_ID = 164; public static final int BUILD_ID = 165;
/** /**
* The build id of the last stable release. * The build id of the last stable release.
*/ */
public static final int BUILD_ID_STABLE = 163; public static final int BUILD_ID_STABLE = 164;
/** /**
* If H2 is compiled to be included in a product, this should be set to * If H2 is compiled to be included in a product, this should be set to
......
...@@ -78,8 +78,9 @@ ALTER SEQUENCE sequenceName [ RESTART WITH long ] [ INCREMENT BY long ] ...@@ -78,8 +78,9 @@ ALTER SEQUENCE sequenceName [ RESTART WITH long ] [ INCREMENT BY long ]
"," ","
Changes the next value and the increment of a sequence." Changes the next value and the increment of a sequence."
"Commands (DDL)","ALTER TABLE ADD"," "Commands (DDL)","ALTER TABLE ADD","
ALTER TABLE tableName ADD [ IF NOT EXISTS ] name dataType [ DEFAULT expression ] ALTER TABLE tableName ADD [ COLUMN ]
[ [ NOT ] NULL ] [ AUTO_INCREMENT | IDENTITY ] [ BEFORE columnName ] { [ IF NOT EXISTS ] columnDefinition [ BEFORE columnName ]
| ( { columnDefinition } [,...] ) }
"," ","
Adds a new column to a table." Adds a new column to a table."
"Commands (DDL)","ALTER TABLE ADD CONSTRAINT"," "Commands (DDL)","ALTER TABLE ADD CONSTRAINT","
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
CREATE TABLE VERSION(ID INT PRIMARY KEY, VERSION VARCHAR, CREATED VARCHAR); CREATE TABLE VERSION(ID INT PRIMARY KEY, VERSION VARCHAR, CREATED VARCHAR);
INSERT INTO VERSION VALUES INSERT INTO VERSION VALUES
(115, '1.3.165', '2012-03-18'),
(114, '1.3.164', '2012-01-03'), (114, '1.3.164', '2012-01-03'),
(113, '1.3.163', '2011-12-30'), (113, '1.3.163', '2011-12-30'),
(112, '1.3.162', '2011-11-26'), (112, '1.3.162', '2011-11-26'),
......
...@@ -104,10 +104,10 @@ public class TestTreeMapStore extends TestBase { ...@@ -104,10 +104,10 @@ public class TestTreeMapStore extends TestBase {
map.remove(k); map.remove(k);
} }
Iterator<Integer> it = m.keyIterator(null); Iterator<Integer> it = m.keyIterator(null);
Iterator<Integer> itm = map.keySet().iterator(); Iterator<Integer> it2 = map.keySet().iterator();
while (itm.hasNext()) { while (it2.hasNext()) {
assertTrue(it.hasNext()); assertTrue(it.hasNext());
assertEquals(itm.next(), it.next()); assertEquals(it2.next(), it.next());
} }
assertFalse(it.hasNext()); assertFalse(it.hasNext());
} }
......
...@@ -696,4 +696,6 @@ pst patadia summertime jalpesh scheme compilable ski takanori dsts kawashima ...@@ -696,4 +696,6 @@ pst patadia summertime jalpesh scheme compilable ski takanori dsts kawashima
kokoci seldom jaros ciphers srcs invectorate noah nfontes fontes recoding kokoci seldom jaros ciphers srcs invectorate noah nfontes fontes recoding
minecraft videos youtube dataflyer bukkit alessio adamo jacopo angel leon frost minecraft videos youtube dataflyer bukkit alessio adamo jacopo angel leon frost
deserializing eckenfelder daniel serodio dirname semicolons whose stretch deserializing eckenfelder daniel serodio dirname semicolons whose stretch
stabilize succeeded widening optimise deprecate increasing stabilize succeeded widening optimise deprecate increasing leaning rotate git
hub rewind spawn shimizu fumiyuki nelson github laird rollover millions
ljnelson edugility
\ No newline at end of file
...@@ -29,6 +29,14 @@ class Node { ...@@ -29,6 +29,14 @@ class Node {
this.map = map; this.map = map;
} }
/**
* Create a new node.
*
* @param map the map
* @param key the key
* @param data the value
* @return the node
*/
static Node create(StoredMap<?, ?> map, Object key, Object data) { static Node create(StoredMap<?, ?> map, Object key, Object data) {
Node n = new Node(map); Node n = new Node(map);
n.key = key; n.key = key;
...@@ -38,6 +46,14 @@ class Node { ...@@ -38,6 +46,14 @@ class Node {
return n; return n;
} }
/**
* Read a node.
*
* @param map the map
* @param id the node id
* @param buff the source buffer
* @return the node
*/
static Node read(StoredMap<?, ?> map, long id, ByteBuffer buff) { static Node read(StoredMap<?, ?> map, long id, ByteBuffer buff) {
Node n = new Node(map); Node n = new Node(map);
n.id = id; n.id = id;
...@@ -45,6 +61,11 @@ class Node { ...@@ -45,6 +61,11 @@ class Node {
return n; return n;
} }
/**
* Get the left child.
*
* @return the left child
*/
Node getLeft() { Node getLeft() {
if (left == null && leftId != 0) { if (left == null && leftId != 0) {
return map.readNode(leftId); return map.readNode(leftId);
...@@ -52,6 +73,11 @@ class Node { ...@@ -52,6 +73,11 @@ class Node {
return left; return left;
} }
/**
* Get the right child.
*
* @return the right child
*/
Node getRight() { Node getRight() {
if (right == null && rightId != 0) { if (right == null && rightId != 0) {
return map.readNode(rightId); return map.readNode(rightId);
...@@ -59,19 +85,39 @@ class Node { ...@@ -59,19 +85,39 @@ class Node {
return right; return right;
} }
/**
* Get the node id of the left child.
*
* @return the node id
*/
long getLeftId() { long getLeftId() {
return leftId; return leftId;
} }
/**
* Set the node id of the left child.
*
* @param leftId the node id
*/
void setLeftId(long leftId) { void setLeftId(long leftId) {
this.leftId = leftId; this.leftId = leftId;
left = null; left = null;
} }
/**
* Get the node id of the right child.
*
* @return the node id
*/
long getRightId() { long getRightId() {
return rightId; return rightId;
} }
/**
* Set the node id of the right child.
*
* @param rightId the node id
*/
void setRightId(long rightId) { void setRightId(long rightId) {
this.rightId = rightId; this.rightId = rightId;
left = null; left = null;
...@@ -130,19 +176,39 @@ class Node { ...@@ -130,19 +176,39 @@ class Node {
getRight().flags = getRight().flags ^ FLAG_BLACK; getRight().flags = getRight().flags ^ FLAG_BLACK;
} }
public long getId() { /**
* Get the node id.
*
* @return the node id
*/
long getId() {
return id; return id;
} }
public void setId(long id) { /**
* Set the node id.
*
* @param id the new id
*/
void setId(long id) {
this.id = id; this.id = id;
} }
public Object getKey() { /**
* Get the key.
*
* @return the key
*/
Object getKey() {
return key; return key;
} }
public Object getData() { /**
* Get the value.
*
* @return the value
*/
Object getData() {
return data; return data;
} }
...@@ -208,6 +274,13 @@ class Node { ...@@ -208,6 +274,13 @@ class Node {
return n.fixUp(); return n.fixUp();
} }
/**
* Remove the node.
*
* @param n the root node
* @param key the key
* @return the new root node
*/
static Node remove(Node n, Object key) { static Node remove(Node n, Object key) {
if (getNode(n, key) == null) { if (getNode(n, key) == null) {
return n; return n;
...@@ -215,6 +288,13 @@ class Node { ...@@ -215,6 +288,13 @@ class Node {
return n.remove(key); return n.remove(key);
} }
/**
* Compare the key with the key of this node.
*
* @param key the key
* @return -1 if the key is smaller than this nodes key, 1 if bigger, and 0
* if equal
*/
int compare(Object key) { int compare(Object key) {
return map.compare(key, this.key); return map.compare(key, this.key);
} }
...@@ -249,6 +329,13 @@ class Node { ...@@ -249,6 +329,13 @@ class Node {
return n.fixUp(); return n.fixUp();
} }
/**
* Get the node.
*
* @param n the root
* @param key the key
* @return the node, or null
*/
static Node getNode(Node n, Object key) { static Node getNode(Node n, Object key) {
while (n != null) { while (n != null) {
int compare = n.compare(key); int compare = n.compare(key);
...@@ -263,6 +350,15 @@ class Node { ...@@ -263,6 +350,15 @@ class Node {
return null; return null;
} }
/**
* Put the node in the map.
*
* @param map the map
* @param n the node
* @param key the key
* @param data the value
* @return the root node
*/
static Node put(StoredMap<?, ?> map, Node n, Object key, Object data) { static Node put(StoredMap<?, ?> map, Node n, Object key, Object data) {
if (n == null) { if (n == null) {
n = Node.create(map, key, data); n = Node.create(map, key, data);
...@@ -306,6 +402,11 @@ class Node { ...@@ -306,6 +402,11 @@ class Node {
data = map.getValueType().read(buff); data = map.getValueType().read(buff);
} }
/**
* Store the node.
*
* @param buff the target buffer
*/
void write(ByteBuffer buff) { void write(ByteBuffer buff) {
buff.put((byte) flags); buff.put((byte) flags);
buff.putLong(leftId); buff.putLong(leftId);
...@@ -314,6 +415,11 @@ class Node { ...@@ -314,6 +415,11 @@ class Node {
map.getValueType().write(buff, data); map.getValueType().write(buff, data);
} }
/**
* Get the length in bytes.
*
* @return the length
*/
int length() { int length() {
return map.getKeyType().length(key) + return map.getKeyType().length(key) +
map.getValueType().length(data) + 17; map.getValueType().length(data) + 17;
......
...@@ -43,6 +43,12 @@ public class StoredMap<K, V> { ...@@ -43,6 +43,12 @@ public class StoredMap<K, V> {
} }
} }
/**
* Get the class with the given tag name.
*
* @param name the tag name
* @return the class
*/
static Class<?> getClass(String name) { static Class<?> getClass(String name) {
if (name.equals("i")) { if (name.equals("i")) {
return Integer.class; return Integer.class;
...@@ -52,34 +58,73 @@ public class StoredMap<K, V> { ...@@ -52,34 +58,73 @@ public class StoredMap<K, V> {
throw new RuntimeException("Unknown class name " + name); throw new RuntimeException("Unknown class name " + name);
} }
/**
* Open a map.
*
* @param <K> the key type
* @param <V> the value type
* @param store the tree store
* @param name the name of the map
* @param keyClass the key class
* @param valueClass the value class
* @return the map
*/
static <K, V> StoredMap<K, V> open(TreeMapStore store, String name, Class<K> keyClass, Class<V> valueClass) { static <K, V> StoredMap<K, V> open(TreeMapStore store, String name, Class<K> keyClass, Class<V> valueClass) {
return new StoredMap<K, V>(store, name, keyClass, valueClass); return new StoredMap<K, V>(store, name, keyClass, valueClass);
} }
/**
* Store a key-value pair.
*
* @param key the key
* @param data the value
*/
public void put(K key, V data) { public void put(K key, V data) {
if (!isChanged()) { if (!isChanged()) {
store.changed(name, this); store.markChanged(name, this);
} }
root = Node.put(this, root, key, data); root = Node.put(this, root, key, data);
} }
/**
* Get a value.
*
* @param key the key
* @return the value
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public V get(K key) { public V get(K key) {
Node n = Node.getNode(root, key); Node n = Node.getNode(root, key);
return (V) (n == null ? null : n.getData()); return (V) (n == null ? null : n.getData());
} }
/**
* Get the node with the given key.
*
* @param key the key
* @return the node
*/
Node getNode(Object key) { Node getNode(Object key) {
return Node.getNode(root, key); return Node.getNode(root, key);
} }
/**
* Remove a key-value pair.
*
* @param key the key
*/
public void remove(K key) { public void remove(K key) {
if (!isChanged()) { if (!isChanged()) {
store.changed(name, this); store.markChanged(name, this);
} }
root = Node.remove(root, key); root = Node.remove(root, key);
} }
/**
* Was this map changed.
*
* @return true if yes
*/
boolean isChanged() { boolean isChanged() {
return root != null && root.getId() < 0; return root != null && root.getId() < 0;
} }
...@@ -88,19 +133,62 @@ public class StoredMap<K, V> { ...@@ -88,19 +133,62 @@ public class StoredMap<K, V> {
* A value type. * A value type.
*/ */
static interface ValueType { static interface ValueType {
/**
* Get the length in bytes.
*
* @param obj the object
* @return the length
*/
int length(Object obj); int length(Object obj);
/**
* Write the object.
*
* @param buff the target buffer
* @param x the value
*/
void write(ByteBuffer buff, Object x); void write(ByteBuffer buff, Object x);
/**
* Read an object.
*
* @param buff the source buffer
* @return the object
*/
Object read(ByteBuffer buff); Object read(ByteBuffer buff);
/**
* Get the tag name of the class.
*
* @return the tag name
*/
String getName(); String getName();
} }
/** /**
* A key type. * A key type.
*/ */
static interface KeyType extends ValueType { static interface KeyType extends ValueType {
/**
* Compare two keys.
*
* @param a the first key
* @param b the second key
* @return -1 if the first key is smaller, 1 if larger, and 0 if equal
*/
int compare(Object a, Object b); int compare(Object a, Object b);
} }
/**
* Compare two keys.
*
* @param a the first key
* @param b the second key
* @return -1 if the first key is smaller, 1 if bigger, 0 if equal
*/
int compare(Object a, Object b) { int compare(Object a, Object b) {
return keyType.compare(a, b); return keyType.compare(a, b);
} }
...@@ -115,15 +203,15 @@ public class StoredMap<K, V> { ...@@ -115,15 +203,15 @@ public class StoredMap<K, V> {
} }
public int length(Object obj) { public int length(Object obj) {
return TreeMapStore.getVarIntLen((Integer) obj); return getVarIntLen((Integer) obj);
} }
public Integer read(ByteBuffer buff) { public Integer read(ByteBuffer buff) {
return TreeMapStore.readVarInt(buff); return readVarInt(buff);
} }
public void write(ByteBuffer buff, Object x) { public void write(ByteBuffer buff, Object x) {
TreeMapStore.writeVarInt(buff, (Integer) x); writeVarInt(buff, (Integer) x);
} }
public String getName() { public String getName() {
...@@ -144,14 +232,14 @@ public class StoredMap<K, V> { ...@@ -144,14 +232,14 @@ public class StoredMap<K, V> {
public int length(Object obj) { public int length(Object obj) {
try { try {
byte[] bytes = obj.toString().getBytes("UTF-8"); byte[] bytes = obj.toString().getBytes("UTF-8");
return TreeMapStore.getVarIntLen(bytes.length) + bytes.length; return getVarIntLen(bytes.length) + bytes.length;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public String read(ByteBuffer buff) { public String read(ByteBuffer buff) {
int len = TreeMapStore.readVarInt(buff); int len = readVarInt(buff);
byte[] bytes = new byte[len]; byte[] bytes = new byte[len];
buff.get(bytes); buff.get(bytes);
try { try {
...@@ -164,7 +252,7 @@ public class StoredMap<K, V> { ...@@ -164,7 +252,7 @@ public class StoredMap<K, V> {
public void write(ByteBuffer buff, Object x) { public void write(ByteBuffer buff, Object x) {
try { try {
byte[] bytes = x.toString().getBytes("UTF-8"); byte[] bytes = x.toString().getBytes("UTF-8");
TreeMapStore.writeVarInt(buff, bytes.length); writeVarInt(buff, bytes.length);
buff.put(bytes); buff.put(bytes);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
...@@ -177,10 +265,20 @@ public class StoredMap<K, V> { ...@@ -177,10 +265,20 @@ public class StoredMap<K, V> {
} }
/**
* Get the key type.
*
* @return the key type
*/
KeyType getKeyType() { KeyType getKeyType() {
return keyType; return keyType;
} }
/**
* Get the value type.
*
* @return the value type
*/
ValueType getValueType() { ValueType getValueType() {
return valueType; return valueType;
} }
...@@ -189,22 +287,49 @@ public class StoredMap<K, V> { ...@@ -189,22 +287,49 @@ public class StoredMap<K, V> {
return store.getTransaction(); return store.getTransaction();
} }
/**
* Get the next temporary node id.
*
* @return the node id
*/
long nextTempNodeId() { long nextTempNodeId() {
return store.nextTempNodeId(); return store.nextTempNodeId();
} }
public Node readNode(long id) { /**
* Read a node.
*
* @param id the node id
* @return the node
*/
Node readNode(long id) {
return store.readNode(this, id); return store.readNode(this, id);
} }
/**
* Remove a node.
*
* @param id the node id
*/
void removeNode(long id) { void removeNode(long id) {
store.removeNode(id); store.removeNode(id);
} }
/**
* Set the position of the root node.
*
* @param rootPos the position
*/
void setRoot(long rootPos) { void setRoot(long rootPos) {
root = readNode(rootPos); root = readNode(rootPos);
} }
/**
* Iterate over all keys.
*
* @param from the first key to return
* @return the iterator
*/
public Iterator<K> keyIterator(K from) { public Iterator<K> keyIterator(K from) {
return new Cursor(root, from); return new Cursor(root, from);
} }
...@@ -271,12 +396,91 @@ public class StoredMap<K, V> { ...@@ -271,12 +396,91 @@ public class StoredMap<K, V> {
} }
} }
/**
* Get the root node.
*
* @return the root node
*/
Node getRoot() { Node getRoot() {
return root; return root;
} }
/**
* Get the map name.
*
* @return the name
*/
String getName() { String getName() {
return name; return name;
} }
/**
* Read a variable size int.
*
* @param buff the source buffer
* @return the value
*/
static int readVarInt(ByteBuffer buff) {
int b = buff.get();
if (b >= 0) {
return b;
}
// a separate function so that this one can be inlined
return readVarIntRest(buff, b);
}
private static int readVarIntRest(ByteBuffer buff, int b) {
int x = b & 0x7f;
b = buff.get();
if (b >= 0) {
return x | (b << 7);
}
x |= (b & 0x7f) << 7;
b = buff.get();
if (b >= 0) {
return x | (b << 14);
}
x |= (b & 0x7f) << 14;
b = buff.get();
if (b >= 0) {
return x | b << 21;
}
x |= ((b & 0x7f) << 21) | (buff.get() << 28);
return x;
}
/**
* Get the length of the variable size int.
*
* @param x the value
* @return the length in bytes
*/
static int getVarIntLen(int x) {
if ((x & (-1 << 7)) == 0) {
return 1;
} else if ((x & (-1 << 14)) == 0) {
return 2;
} else if ((x & (-1 << 21)) == 0) {
return 3;
} else if ((x & (-1 << 28)) == 0) {
return 4;
}
return 5;
}
/**
* Write a variable size int.
*
* @param buff the target buffer
* @param x the value
*/
static void writeVarInt(ByteBuffer buff, int x) {
while ((x & ~0x7f) != 0) {
buff.put((byte) (0x80 | (x & 0x7f)));
x >>>= 7;
}
buff.put((byte) x);
}
} }
...@@ -57,7 +57,7 @@ todo: ...@@ -57,7 +57,7 @@ todo:
for each page, store chunk id and offset to root for each page, store chunk id and offset to root
for each chunk, store position of expected next chunks for each chunk, store position of expected next chunks
*/ */
/** /**
* A persistent storage for tree maps. * A persistent storage for tree maps.
...@@ -86,12 +86,28 @@ public class TreeMapStore { ...@@ -86,12 +86,28 @@ public class TreeMapStore {
this.fileName = fileName; this.fileName = fileName;
} }
/**
* Open a tree store.
*
* @param fileName the file name
* @return the store
*/
public static TreeMapStore open(String fileName) { public static TreeMapStore open(String fileName) {
TreeMapStore s = new TreeMapStore(fileName); TreeMapStore s = new TreeMapStore(fileName);
s.open(); s.open();
return s; return s;
} }
/**
* Open a map.
*
* @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
* @return the map
*/
public <K, V> StoredMap<K, V> openMap(String name, Class<K> keyClass, Class<V> valueClass) { public <K, V> StoredMap<K, V> openMap(String name, Class<K> keyClass, Class<V> valueClass) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
StoredMap<K, V> m = (StoredMap<K, V>) maps.get(name); StoredMap<K, V> m = (StoredMap<K, V>) maps.get(name);
...@@ -109,13 +125,19 @@ public class TreeMapStore { ...@@ -109,13 +125,19 @@ public class TreeMapStore {
return m; return m;
} }
void changed(String name, StoredMap<?, ?> map) { /**
* Mark a map as changed.
*
* @param name the map name
* @param the map
*/
void markChanged(String name, StoredMap<?, ?> map) {
if (map != meta) { if (map != meta) {
mapsChanged.put(name, map); mapsChanged.put(name, map);
} }
} }
void open() { private void open() {
meta = StoredMap.open(this, "meta", String.class, String.class); meta = StoredMap.open(this, "meta", String.class, String.class);
new File(fileName).getParentFile().mkdirs(); new File(fileName).getParentFile().mkdirs();
try { try {
...@@ -186,6 +208,9 @@ public class TreeMapStore { ...@@ -186,6 +208,9 @@ public class TreeMapStore {
throw new RuntimeException("Exception: " + e, e); throw new RuntimeException("Exception: " + e, e);
} }
/**
* Close the file.
*/
public void close() { public void close() {
store(); store();
if (file != null) { if (file != null) {
...@@ -272,6 +297,9 @@ public class TreeMapStore { ...@@ -272,6 +297,9 @@ public class TreeMapStore {
return ((long) blockId << 32) | offset; return ((long) blockId << 32) | offset;
} }
/**
* Persist all changes to disk.
*/
public void store() { public void store() {
if (!meta.isChanged() && mapsChanged.size() == 0) { if (!meta.isChanged() && mapsChanged.size() == 0) {
// TODO truncate file if empty // TODO truncate file if empty
...@@ -395,14 +423,29 @@ public class TreeMapStore { ...@@ -395,14 +423,29 @@ public class TreeMapStore {
return set.size() * pageSize; return set.size() * pageSize;
} }
/**
* Get the current transaction number.
*
* @return the transaction number
*/
long getTransaction() { long getTransaction() {
return transaction; return transaction;
} }
/**
* Get the next temporary node id.
*
* @return the node id
*/
long nextTempNodeId() { long nextTempNodeId() {
return -(++tempNodeId); return -(++tempNodeId);
} }
/**
* Commit the current transaction.
*
* @return the transaction id
*/
public long commit() { public long commit() {
return ++transaction; return ++transaction;
} }
...@@ -422,6 +465,9 @@ public class TreeMapStore { ...@@ -422,6 +465,9 @@ public class TreeMapStore {
} }
} }
/**
* Try to reduce the file size.
*/
public void compact() { public void compact() {
if (blocks.size() <= 1) { if (blocks.size() <= 1) {
return; return;
...@@ -514,10 +560,12 @@ public class TreeMapStore { ...@@ -514,10 +560,12 @@ public class TreeMapStore {
} }
// //
// meta = StoredMap.open(this, "meta", String.class, String.class); // meta = StoredMap.open(this, "meta",
// String.class, String.class);
// new File(fileName).getParentFile().mkdirs(); // new File(fileName).getParentFile().mkdirs();
// try { // try {
// file = FilePathCache.wrap(FilePath.get(fileName).open("rw")); // file = FilePathCache.
// wrap(FilePath.get(fileName).open("rw"));
// if (file.size() == 0) { // if (file.size() == 0) {
// writeHeader(); // writeHeader();
// } else { // } else {
...@@ -540,6 +588,13 @@ public class TreeMapStore { ...@@ -540,6 +588,13 @@ public class TreeMapStore {
} }
/**
* Read a node.
*
* @param map the map
* @param id the node id
* @return the node
*/
Node readNode(StoredMap<?, ?> map, long id) { Node readNode(StoredMap<?, ?> map, long id) {
Node n = cache.get(id); Node n = cache.get(id);
if (n == null) { if (n == null) {
...@@ -563,56 +618,11 @@ public class TreeMapStore { ...@@ -563,56 +618,11 @@ public class TreeMapStore {
return n; return n;
} }
static int getVarIntLen(int x) { /**
if ((x & (-1 << 7)) == 0) { * Remove a node.
return 1; *
} else if ((x & (-1 << 14)) == 0) { * @param id the node id
return 2; */
} else if ((x & (-1 << 21)) == 0) {
return 3;
} else if ((x & (-1 << 28)) == 0) {
return 4;
}
return 5;
}
static void writeVarInt(ByteBuffer buff, int x) {
while ((x & ~0x7f) != 0) {
buff.put((byte) (0x80 | (x & 0x7f)));
x >>>= 7;
}
buff.put((byte) x);
}
static int readVarInt(ByteBuffer buff) {
int b = buff.get();
if (b >= 0) {
return b;
}
// a separate function so that this one can be inlined
return readVarIntRest(buff, b);
}
static int readVarIntRest(ByteBuffer buff, int b) {
int x = b & 0x7f;
b = buff.get();
if (b >= 0) {
return x | (b << 7);
}
x |= (b & 0x7f) << 7;
b = buff.get();
if (b >= 0) {
return x | (b << 14);
}
x |= (b & 0x7f) << 14;
b = buff.get();
if (b >= 0) {
return x | b << 21;
}
x |= ((b & 0x7f) << 21) | (buff.get() << 28);
return x;
}
void removeNode(long id) { void removeNode(long id) {
if (id > 0) { if (id > 0) {
if (getBlock(id).liveCount == 0) { if (getBlock(id).liveCount == 0) {
...@@ -641,7 +651,13 @@ public class TreeMapStore { ...@@ -641,7 +651,13 @@ public class TreeMapStore {
this.id = id; this.id = id;
} }
public static Block fromString(String s) { /**
* Build a block from the given string.
*
* @param s the string
* @return the block
*/
static Block fromString(String s) {
Block b = new Block(0); Block b = new Block(0);
Properties prop = new Properties(); Properties prop = new Properties();
try { try {
......
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version 1.0,
and under the Eclipse Public License, Version 1.0
(http://h2database.com/html/license.html).
Initial Developer: H2 Group
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /><title>
Javadoc package documentation
</title></head><body style="font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"><p>
A persistent storage for tree maps.
</p></body></html>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论