提交 ce8a983b authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore: improved API thanks to Simo Tripodi.

上级 a9025788
......@@ -53,7 +53,8 @@ public class ChangeCursor<K, V> implements Iterator<K> {
}
public void remove() {
throw DataUtils.unsupportedOperationException("Removing is not supported");
throw DataUtils.newUnsupportedOperationException(
"Removing is not supported");
}
private void fetchNext() {
......
......@@ -89,7 +89,8 @@ public class Chunk {
*/
static Chunk fromHeader(ByteBuffer buff, long start) {
if (buff.get() != 'c') {
throw DataUtils.illegalStateException("File corrupt reading chunk at position " + start);
throw DataUtils.newIllegalStateException(
"File corrupt reading chunk at position {0}", start);
}
int length = buff.getInt();
int chunkId = buff.getInt();
......
......@@ -67,7 +67,8 @@ public class Cursor<K> implements Iterator<K> {
}
public void remove() {
throw DataUtils.unsupportedOperationException("Removing is not supported");
throw DataUtils.newUnsupportedOperationException(
"Removing is not supported");
}
/**
......
......@@ -11,11 +11,13 @@ import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import org.h2.engine.Constants;
import org.h2.util.New;
import org.h2.util.StringUtils;
/**
* Utility methods
......@@ -316,7 +318,9 @@ public class DataUtils {
} while (dst.remaining() > 0);
dst.rewind();
} catch (IOException e) {
throw illegalStateException("Reading from " + file + " failed; length " + dst.remaining() + " at " + pos, e);
throw newIllegalStateException(
"Reading from {0} failed; length {1} at {2}",
file, dst.remaining(), pos, e);
}
}
......@@ -335,7 +339,9 @@ public class DataUtils {
off += len;
} while (src.remaining() > 0);
} catch (IOException e) {
throw illegalStateException("Writing to " + file + " failed; length " + src.remaining() + " at " + pos, e);
throw newIllegalStateException(
"Writing to {0} failed; length {1} at {2}",
file, src.remaining(), pos, e);
}
}
......@@ -553,27 +559,51 @@ public class DataUtils {
return (s2 << 16) | s1;
}
public static IllegalStateException illegalStateException(String message) {
return new IllegalStateException(message + version());
public static void checkArgument(boolean test, String message, Object... arguments) {
if (!test) {
throw newIllegalArgumentException(message, arguments);
}
}
public static IllegalArgumentException newIllegalArgumentException(
String message, Object... arguments) {
return initCause(new IllegalArgumentException(
formatMessage(message, arguments)), arguments);
}
public static IllegalStateException illegalStateException(String message, Exception cause) {
return new IllegalStateException(message + version(), cause);
public static UnsupportedOperationException newUnsupportedOperationException(
String message) {
return new UnsupportedOperationException(message + getVersion());
}
public static IllegalArgumentException illegalArgumentException(String message) {
return new IllegalArgumentException(message + version());
public static IllegalStateException newIllegalStateException(
String message, Object... arguments) {
return initCause(new IllegalStateException(
formatMessage(message, arguments)), arguments);
}
public static IllegalArgumentException illegalArgumentException(String message, Exception cause) {
return new IllegalArgumentException(message + version(), cause);
private static <T extends Exception> T initCause(T e, Object... arguments) {
int size = arguments.length;
if (size > 0) {
Object o = arguments[size - 1];
if (o instanceof Exception) {
e.initCause((Exception) o);
}
}
return e;
}
public static UnsupportedOperationException unsupportedOperationException(String message) {
return new UnsupportedOperationException(message + version());
private static String formatMessage(String pattern, Object... arguments) {
for (int i = 0, size = arguments.length; i < size; i++) {
Object o = arguments[i];
String s = o == null ? "null" : o instanceof String ? StringUtils
.quoteIdentifier(o.toString()) : o.toString();
arguments[i] = s;
}
return MessageFormat.format(pattern, arguments) + getVersion();
}
private static String version() {
private static String getVersion() {
return " [" + Constants.VERSION_MAJOR + "." +
Constants.VERSION_MINOR + "." + Constants.BUILD_ID + "]";
}
......
......@@ -60,7 +60,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* @param store the store
* @param config the configuration
*/
public void open(MVStore store, HashMap<String, String> config) {
protected void init(MVStore store, HashMap<String, String> config) {
this.store = store;
this.id = Integer.parseInt(config.get("id"));
String x = config.get("createVersion");
......@@ -861,7 +861,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*/
protected void checkOpen() {
if (closed) {
throw DataUtils.illegalStateException("This map is closed");
throw DataUtils.newIllegalStateException("This map is closed");
}
}
......@@ -873,7 +873,8 @@ public class MVMap<K, V> extends AbstractMap<K, V>
protected void checkWrite() {
if (readOnly) {
checkOpen();
throw DataUtils.unsupportedOperationException("This map is read-only");
throw DataUtils.newUnsupportedOperationException(
"This map is read-only");
}
}
......@@ -916,12 +917,12 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*/
public MVMap<K, V> openVersion(long version) {
if (readOnly) {
throw DataUtils.unsupportedOperationException(
throw DataUtils.newUnsupportedOperationException(
"This map is read-only - need to call the method on the writable map");
}
if (version < createVersion) {
throw DataUtils.illegalArgumentException("Unknown version");
}
DataUtils.checkArgument(version >= createVersion,
"Unknown version {0}; this map was created in version is {1}",
version, createVersion);
Page newest = null;
// need to copy because it can change
Page r = root;
......@@ -956,7 +957,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
HashMap<String, String> config = New.hashMap();
config.put("id", String.valueOf(id));
config.put("createVersion", String.valueOf(createVersion));
m.open(store, config);
m.init(store, config);
m.root = root;
return m;
}
......@@ -1052,7 +1053,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*
* @return the map
*/
public M create();
M create();
}
......
......@@ -41,8 +41,7 @@ H:3,...
TODO:
- improve exception factory (fluent api)
- implement table engine for H2
- update checkstyle
- automated 'kill process' and 'power failure' test
- maybe split database into multiple files, to speed up compact
- auto-compact from time to time and on close
......@@ -272,7 +271,7 @@ public class MVStore {
String config = meta.get("map." + x);
c = DataUtils.parseMap(config);
c.put("id", x);
map.open(this, c);
map.init(this, c);
String r = meta.get("root." + id);
root = r == null ? 0 : Long.parseLong(r);
} else {
......@@ -281,9 +280,10 @@ public class MVStore {
c.put("id", Integer.toString(id));
c.put("createVersion", Long.toString(currentVersion));
map = builder.create();
map.open(this, c);
map.init(this, c);
meta.put("map." + id, map.asString(name));
meta.put("name." + name, Integer.toString(id));
markMetaChanged();
root = 0;
}
map.setRootPos(root, -1);
......@@ -313,9 +313,7 @@ public class MVStore {
private MVMap<String, String> getMetaMap(long version) {
Chunk c = getChunkForVersion(version);
if (c == null) {
throw DataUtils.illegalArgumentException("Unknown version: " + version);
}
DataUtils.checkArgument(c != null, "Unknown version {}", version);
c = readChunkHeader(c.start);
MVMap<String, String> oldMeta = meta.openReadOnly();
oldMeta.setRootPos(c.metaRootPos, version);
......@@ -340,6 +338,7 @@ public class MVStore {
*/
void removeMap(int id) {
String name = getMapName(id);
markMetaChanged();
meta.remove("map." + id);
meta.remove("name." + name);
meta.remove("root." + id);
......@@ -356,6 +355,12 @@ public class MVStore {
mapsChanged.put(map.getId(), map);
}
private void markMetaChanged() {
// changes in the metadata alone are usually not detected, as the meta
// map is changed after storing
markChanged(meta);
}
/**
* Open the store.
*/
......@@ -364,7 +369,7 @@ public class MVStore {
HashMap<String, String> c = New.hashMap();
c.put("id", "0");
c.put("createVersion", Long.toString(currentVersion));
meta.open(this, c);
meta.init(this, c);
if (fileName == null) {
return;
}
......@@ -408,7 +413,8 @@ public class MVStore {
} catch (Exception e2) {
// ignore
}
throw DataUtils.illegalStateException("Could not open " + fileName, e);
throw DataUtils.newIllegalStateException(
"Could not open file {0}", fileName, e);
}
}
......@@ -479,7 +485,7 @@ public class MVStore {
}
}
if (currentVersion < 0) {
throw DataUtils.illegalStateException("File header is corrupt");
throw DataUtils.newIllegalStateException("File header is corrupt");
}
}
......@@ -493,9 +499,8 @@ public class MVStore {
int checksum = DataUtils.getFletcher32(bytes, bytes.length / 2 * 2);
DataUtils.appendMap(buff, "fletcher", Integer.toHexString(checksum));
bytes = StringUtils.utf8Encode(buff.toString());
if (bytes.length > BLOCK_SIZE) {
throw DataUtils.illegalArgumentException("File header too large: " + buff);
}
DataUtils.checkArgument(bytes.length <= BLOCK_SIZE,
"File header too large: {}", buff);
return bytes;
}
......@@ -534,7 +539,8 @@ public class MVStore {
maps.clear();
mapsChanged.clear();
} catch (Exception e) {
throw DataUtils.illegalStateException("Closing failed for file " + fileName, e);
throw DataUtils.newIllegalStateException(
"Closing failed for file {0}", fileName, e);
} finally {
file = null;
}
......@@ -660,7 +666,7 @@ public class MVStore {
if (ASSERT) {
if (freedChunks.size() > 0) {
throw DataUtils.illegalStateException("Temporary freed chunks");
throw DataUtils.newIllegalStateException("Temporary freed chunks");
}
}
......@@ -729,7 +735,8 @@ public class MVStore {
Chunk c = chunks.get(f.id);
c.maxLengthLive += f.maxLengthLive;
if (c.maxLengthLive < 0) {
throw DataUtils.illegalStateException("Corrupt max length: " + c.maxLengthLive);
throw DataUtils.newIllegalStateException(
"Corrupt max length {0}", c.maxLengthLive);
}
}
}
......@@ -757,7 +764,9 @@ public class MVStore {
try {
file.truncate(used);
} catch (IOException e) {
throw DataUtils.illegalStateException("Could not truncate to size " + used, e);
throw DataUtils.newIllegalStateException(
"Could not truncate file {0} to size {1}",
fileName, used, e);
}
fileSize = used;
}
......@@ -921,9 +930,7 @@ public class MVStore {
DataUtils.readFully(file, chunk.start, buff);
Chunk.fromHeader(buff, chunk.start);
int chunkLength = chunk.length;
// mark a change, even if it doesn't look like there was a change
// as changes in the metadata alone are not detected
markChanged(meta);
markMetaChanged();
while (buff.position() < chunkLength) {
int start = buff.position();
int pageLength = buff.getInt();
......@@ -978,7 +985,9 @@ public class MVStore {
if (p == null) {
Chunk c = getChunk(pos);
if (c == null) {
throw DataUtils.illegalStateException("Chunk " + DataUtils.getPageChunkId(pos) + " not found");
throw DataUtils.newIllegalStateException(
"Chunk {0} not found",
DataUtils.getPageChunkId(pos));
}
long filePos = c.start;
filePos += DataUtils.getPageOffset(pos);
......@@ -1193,6 +1202,7 @@ public class MVStore {
*/
public void setStoreVersion(int version) {
checkOpen();
markMetaChanged();
meta.put("setting.storeVersion", Integer.toString(version));
}
......@@ -1206,9 +1216,9 @@ public class MVStore {
*/
public void rollbackTo(long version) {
checkOpen();
if (!isKnownVersion(version)) {
throw DataUtils.illegalArgumentException("Unknown version: " + version);
}
DataUtils.checkArgument(
isKnownVersion(version),
"Unknown version {0}", version);
for (MVMap<?, ?> m : mapsChanged.values()) {
m.rollbackTo(version);
}
......@@ -1330,23 +1340,23 @@ public class MVStore {
private void checkOpen() {
if (closed) {
throw DataUtils.illegalStateException("This store is closed");
throw DataUtils.newIllegalStateException("This store is closed");
}
}
void renameMap(MVMap<?, ?> map, String newName) {
checkOpen();
if (map == meta) {
throw DataUtils.unsupportedOperationException("Renaming the meta map is not allowed");
}
DataUtils.checkArgument(map != meta,
"Renaming the meta map is not allowed");
if (map.getName().equals(newName)) {
return;
}
if (meta.containsKey("name." + newName)) {
throw DataUtils.illegalArgumentException("A map named " + newName + " already exists");
}
DataUtils.checkArgument(
!meta.containsKey("name." + newName),
"A map named {0} already exists", newName);
int id = map.getId();
String oldName = getMapName(id);
markMetaChanged();
meta.remove("map." + id);
meta.remove("name." + oldName);
meta.put("map." + id, map.asString(newName));
......@@ -1366,9 +1376,6 @@ public class MVStore {
private final HashMap<String, Object> config = New.hashMap();
private Builder set(String key, Object value) {
if (config.containsKey(key)) {
throw DataUtils.illegalArgumentException("Parameter " + config.get(key) + " is already set");
}
config.put(key, value);
return this;
}
......
......@@ -426,8 +426,8 @@ public class Page {
}
}
if (check != totalCount) {
throw DataUtils.illegalStateException("Expected: " + check + " got: "
+ totalCount);
throw DataUtils.newIllegalStateException(
"Expected: {0} got: {1}", check, totalCount);
}
}
return totalCount;
......@@ -689,21 +689,24 @@ public class Page {
int start = buff.position();
int pageLength = buff.getInt();
if (pageLength > maxLength) {
throw DataUtils.illegalStateException("File corrupted, expected length =< "
+ maxLength + " got " + pageLength);
throw DataUtils.newIllegalStateException(
"File corrupted, expected length =< {0}, got {1}",
maxLength, pageLength);
}
short check = buff.getShort();
int mapId = DataUtils.readVarInt(buff);
if (mapId != map.getId()) {
throw DataUtils.illegalStateException("File corrupted, expected map id "
+ map.getId() + " got " + mapId);
throw DataUtils.newIllegalStateException(
"File corrupted, expected map id {0}, got {1}",
map.getId(), mapId);
}
int checkTest = DataUtils.getCheckValue(chunkId)
^ DataUtils.getCheckValue(offset)
^ DataUtils.getCheckValue(pageLength);
if (check != (short) checkTest) {
throw DataUtils.illegalStateException("File corrupted, expected check value "
+ checkTest + " got " + check);
throw DataUtils.newIllegalStateException(
"File corrupted, expected check value {0}, got {1}",
checkTest, check);
}
int len = DataUtils.readVarInt(buff);
keys = new Object[len];
......@@ -901,7 +904,7 @@ public class Page {
public int getMemory() {
if (MVStore.ASSERT) {
if (memory != calculateMemory()) {
throw DataUtils.illegalStateException("Memory calculation error");
throw DataUtils.newIllegalStateException("Memory calculation error");
}
}
return memory;
......
......@@ -208,7 +208,8 @@ public class StreamStore {
map.remove(k2);
break;
default:
throw DataUtils.illegalArgumentException("Unsupported id " + StringUtils.convertBytesToHex(id));
throw DataUtils.newIllegalArgumentException(
"Unsupported id {0}", StringUtils.convertBytesToHex(id));
}
}
}
......@@ -239,7 +240,8 @@ public class StreamStore {
DataUtils.readVarLong(idBuffer);
break;
default:
throw DataUtils.illegalArgumentException("Unsupported id " + StringUtils.convertBytesToHex(id));
throw DataUtils.newIllegalArgumentException(
"Unsupported id {0}", StringUtils.convertBytesToHex(id));
}
}
return length;
......@@ -402,8 +404,8 @@ public class StreamStore {
return nextBuffer();
}
default:
throw DataUtils.illegalArgumentException("Unsupported id " +
StringUtils.convertBytesToHex(idBuffer.array()));
throw DataUtils.newIllegalArgumentException(
"Unsupported id {0}", StringUtils.convertBytesToHex(idBuffer.array()));
}
}
return null;
......
......@@ -85,9 +85,9 @@ public class CacheLongKeyLIRS<V> {
public CacheLongKeyLIRS(long maxMemory, int averageMemory, int segmentCount, int stackMoveDistance) {
setMaxMemory(maxMemory);
setAverageMemory(averageMemory);
if (Integer.bitCount(segmentCount) != 1) {
throw DataUtils.illegalArgumentException("The segment count must be a power of 2, is " + segmentCount);
}
DataUtils.checkArgument(
Integer.bitCount(segmentCount) == 1,
"The segment count must be a power of 2, is {0}", segmentCount);
this.segmentCount = segmentCount;
this.segmentMask = segmentCount - 1;
this.stackMoveDistance = stackMoveDistance;
......@@ -252,9 +252,9 @@ public class CacheLongKeyLIRS<V> {
* @param maxMemory the maximum size (1 or larger)
*/
public void setMaxMemory(long maxMemory) {
if (maxMemory <= 0) {
throw DataUtils.illegalArgumentException("Max memory must be larger than 0");
}
DataUtils.checkArgument(
maxMemory > 0,
"Max memory must be larger than 0, is {0}", maxMemory);
this.maxMemory = maxMemory;
if (segments != null) {
long max = 1 + maxMemory / segments.length;
......@@ -271,9 +271,9 @@ public class CacheLongKeyLIRS<V> {
* @param averageMemory the average memory used (1 or larger)
*/
public void setAverageMemory(int averageMemory) {
if (averageMemory <= 0) {
throw DataUtils.illegalArgumentException("Average memory must be larger than 0");
}
DataUtils.checkArgument(
averageMemory > 0,
"Average memory must be larger than 0, is {0}", averageMemory);
this.averageMemory = averageMemory;
if (segments != null) {
for (Segment<V> s : segments) {
......@@ -687,7 +687,8 @@ public class CacheLongKeyLIRS<V> {
*/
synchronized V put(long key, int hash, V value, int memory) {
if (value == null) {
throw DataUtils.illegalArgumentException("The value may not be null");
throw DataUtils.newIllegalArgumentException(
"The value may not be null");
}
V old;
Entry<V> e = find(key, hash);
......@@ -951,9 +952,6 @@ public class CacheLongKeyLIRS<V> {
* @param maxMemory the maximum size (1 or larger)
*/
void setMaxMemory(long maxMemory) {
if (maxMemory <= 0) {
throw DataUtils.illegalArgumentException("Max memory must be larger than 0");
}
this.maxMemory = maxMemory;
}
......@@ -964,9 +962,6 @@ public class CacheLongKeyLIRS<V> {
* @param averageMemory the average memory used (1 or larger)
*/
void setAverageMemory(int averageMemory) {
if (averageMemory <= 0) {
throw DataUtils.illegalArgumentException("Average memory must be larger than 0");
}
this.averageMemory = averageMemory;
}
......
......@@ -368,12 +368,6 @@ public class MVTable extends TableBase {
// if (isPersistIndexes() && indexType.isPersistent()) {
int mainIndexColumn;
mainIndexColumn = getMainIndexColumn(indexType, cols);
// if (database.isStarting()) {
// mainIndexColumn = -1;
// } else if (!database.isStarting() && primaryIndex.getRowCount(session) != 0) {
// mainIndexColumn = -1;
// } else {
// }
if (!database.isStarting() && primaryIndex.getRowCount(session) != 0) {
mainIndexColumn = -1;
}
......@@ -386,9 +380,6 @@ public class MVTable extends TableBase {
this, indexId,
indexName, cols, indexType);
}
// } else {
// index = new TreeIndex(this, indexId, indexName, cols, indexType);
// }
if (index.needRebuild() && rowCount > 0) {
try {
Index scan = getScanIndex(session);
......@@ -674,7 +665,7 @@ public class MVTable extends TableBase {
private void storeIfRequired() {
if (store.getUnsavedPageCount() > 1000) {
store.store();
MVTableEngine.store(store);
}
}
......@@ -690,4 +681,8 @@ public class MVTable extends TableBase {
return rowIdColumn;
}
public String toString() {
return getSQL();
}
}
......@@ -7,6 +7,7 @@
package org.h2.mvstore.db;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.WeakHashMap;
import org.h2.api.TableEngine;
......@@ -37,10 +38,14 @@ public class MVTableEngine implements TableEngine {
return;
}
// TODO this stores uncommitted transactions as well
store.store.store();
store(store.store);
}
}
public static Collection<Store> getStores() {
return STORES.values();
}
@Override
public TableBase createTable(CreateTableData data) {
Database db = data.session.getDatabase();
......@@ -73,7 +78,7 @@ public class MVTableEngine implements TableEngine {
if (store != null) {
store.openTables.remove(table);
if (store.openTables.size() == 0) {
store.store.store();
store(store.store);
store.store.close();
STORES.remove(storeName);
}
......@@ -81,10 +86,15 @@ public class MVTableEngine implements TableEngine {
}
}
static void store(MVStore store) {
store.compact(50);
store.store();
}
/**
* A store with open tables.
*/
static class Store {
public static class Store {
final Database db;
final MVStore store;
......@@ -95,6 +105,10 @@ public class MVTableEngine implements TableEngine {
this.store = store;
}
public MVStore getStore() {
return store;
}
}
}
......@@ -501,26 +501,6 @@ public class ValueArrayDataType implements DataType {
}
}
// private void writeStringWithoutLength(char[] chars, int len) {
// int p = pos;
// byte[] buff = data;
// for (int i = 0; i < len; i++) {
// int c = chars[i];
// if (c < 0x80) {
// buff[p++] = (byte) c;
// } else if (c >= 0x800) {
// buff[p++] = (byte) (0xe0 | (c >> 12));
// buff[p++] = (byte) (((c >> 6) & 0x3f));
// buff[p++] = (byte) (c & 0x3f);
// } else {
// buff[p++] = (byte) (0xc0 | (c >> 6));
// buff[p++] = (byte) (c & 0x3f);
// }
// }
// pos = p;
// }
/**
* Read a value.
*
......
......@@ -21,9 +21,9 @@ public class SpatialDataType implements DataType {
private final int dimensions;
public SpatialDataType(int dimensions) {
if (dimensions <= 0 || dimensions > 255) {
throw DataUtils.illegalArgumentException("Dimensions: " + dimensions);
}
DataUtils.checkArgument(
dimensions > 1 && dimensions < 256,
"Dimensions must be between 2 and 255, is {0}", dimensions);
this.dimensions = dimensions;
}
......
......@@ -139,7 +139,7 @@ public class ObjectDataType implements DataType {
case TYPE_SERIALIZED_OBJECT:
return new SerializedObjectType(this);
}
throw DataUtils.illegalStateException("Unsupported type: " + typeId);
throw DataUtils.newIllegalStateException("Unsupported type {0}", typeId);
}
@Override
......@@ -192,7 +192,7 @@ public class ObjectDataType implements DataType {
} else if (tag >= TAG_BYTE_ARRAY_0_15 && tag <= TAG_BYTE_ARRAY_0_15 + 15) {
typeId = TYPE_BYTE_ARRAY;
} else {
throw DataUtils.illegalStateException("Unknown tag: " + tag);
throw DataUtils.newIllegalStateException("Unknown tag {0}", tag);
}
}
}
......@@ -241,7 +241,8 @@ public class ObjectDataType implements DataType {
return TYPE_CHARACTER;
}
if (obj == null) {
throw DataUtils.illegalArgumentException("Null is not supported");
throw DataUtils.newIllegalArgumentException(
"Null is not supported");
}
return TYPE_SERIALIZED_OBJECT;
}
......@@ -368,7 +369,7 @@ public class ObjectDataType implements DataType {
@Override
public final Object read(ByteBuffer buff) {
throw DataUtils.illegalStateException("Internal error");
throw DataUtils.newIllegalStateException("Internal error");
}
/**
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论