提交 831e6937 authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore: the file format was changed slightly.

上级 b9726376
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.
...@@ -262,7 +262,8 @@ public class Comparison extends Condition { ...@@ -262,7 +262,8 @@ public class Comparison extends Condition {
* @param l the first value * @param l the first value
* @param r the second value * @param r the second value
* @param compareType the compare type * @param compareType the compare type
* @return true if the comparison indicated by the comparison type evaluates to true * @return true if the comparison indicated by the comparison type evaluates
* to true
*/ */
static boolean compareNotNull(Database database, Value l, Value r, int compareType) { static boolean compareNotNull(Database database, Value l, Value r, int compareType) {
boolean result; boolean result;
......
...@@ -16,6 +16,7 @@ import java.util.Map.Entry; ...@@ -16,6 +16,7 @@ import java.util.Map.Entry;
import java.util.Properties; import java.util.Properties;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.engine.Constants;
import org.h2.jdbc.JdbcSQLException; import org.h2.jdbc.JdbcSQLException;
import org.h2.util.SortedProperties; import org.h2.util.SortedProperties;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -46,7 +47,7 @@ public class DbException extends RuntimeException { ...@@ -46,7 +47,7 @@ public class DbException extends RuntimeException {
// message: translated message + english // message: translated message + english
// (otherwise certain applications don't work) // (otherwise certain applications don't work)
if (translations != null) { if (translations != null) {
Properties p = SortedProperties.fromLines(new String(translations, "UTF-8")); Properties p = SortedProperties.fromLines(new String(translations, Constants.UTF8));
for (Entry<Object, Object> e : p.entrySet()) { for (Entry<Object, Object> e : p.entrySet()) {
String key = (String) e.getKey(); String key = (String) e.getKey();
String translation = (String) e.getValue(); String translation = (String) e.getValue();
......
...@@ -30,7 +30,8 @@ public class Chunk { ...@@ -30,7 +30,8 @@ public class Chunk {
/** /**
* The length of the chunk footer. The longest footer is: * The length of the chunk footer. The longest footer is:
* chunk:ffffffff,block:ffffffffffffffff,version:ffffffffffffffff,fletcher:ffffffff * chunk:ffffffff,block:ffffffffffffffff,
* version:ffffffffffffffff,fletcher:ffffffff
*/ */
static final int FOOTER_LENGTH = 128; static final int FOOTER_LENGTH = 128;
...@@ -62,7 +63,7 @@ public class Chunk { ...@@ -62,7 +63,7 @@ public class Chunk {
/** /**
* The sum of the max length of all pages. * The sum of the max length of all pages.
*/ */
public long maxLength; public long maxLen;
/** /**
* The sum of the max length of all pages that are in use. * The sum of the max length of all pages that are in use.
...@@ -98,7 +99,6 @@ public class Chunk { ...@@ -98,7 +99,6 @@ public class Chunk {
* The predicted position of the next chunk. * The predicted position of the next chunk.
*/ */
public long next; public long next;
public long nextSize;
Chunk(int id) { Chunk(int id) {
this.id = id; this.id = id;
...@@ -136,6 +136,7 @@ public class Chunk { ...@@ -136,6 +136,7 @@ public class Chunk {
* Write the chunk header. * Write the chunk header.
* *
* @param buff the target buffer * @param buff the target buffer
* @param minLength the minimum length
*/ */
void writeChunkHeader(WriteBuffer buff, int minLength) { void writeChunkHeader(WriteBuffer buff, int minLength) {
long pos = buff.position(); long pos = buff.position();
...@@ -146,6 +147,12 @@ public class Chunk { ...@@ -146,6 +147,12 @@ public class Chunk {
buff.put((byte) '\n'); buff.put((byte) '\n');
} }
/**
* Get the metadata key for the given chunk id.
*
* @param chunkId the chunk id
* @return the metadata key
*/
static String getMetaKey(int chunkId) { static String getMetaKey(int chunkId) {
return "chunk." + Integer.toHexString(chunkId); return "chunk." + Integer.toHexString(chunkId);
} }
...@@ -165,8 +172,8 @@ public class Chunk { ...@@ -165,8 +172,8 @@ public class Chunk {
c.pageCount = DataUtils.readHexInt(map, "pages", 0); c.pageCount = DataUtils.readHexInt(map, "pages", 0);
c.pageCountLive = DataUtils.readHexInt(map, "livePages", c.pageCount); c.pageCountLive = DataUtils.readHexInt(map, "livePages", c.pageCount);
c.mapId = DataUtils.readHexInt(map, "map", 0); c.mapId = DataUtils.readHexInt(map, "map", 0);
c.maxLength = DataUtils.readHexLong(map, "max", 0); c.maxLen = DataUtils.readHexLong(map, "max", 0);
c.maxLenLive = DataUtils.readHexLong(map, "liveMax", c.maxLength); c.maxLenLive = DataUtils.readHexLong(map, "liveMax", c.maxLen);
c.metaRootPos = DataUtils.readHexLong(map, "root", 0); c.metaRootPos = DataUtils.readHexLong(map, "root", 0);
c.time = DataUtils.readHexLong(map, "time", 0); c.time = DataUtils.readHexLong(map, "time", 0);
c.version = DataUtils.readHexLong(map, "version", id); c.version = DataUtils.readHexLong(map, "version", id);
...@@ -175,7 +182,7 @@ public class Chunk { ...@@ -175,7 +182,7 @@ public class Chunk {
} }
public int getFillRate() { public int getFillRate() {
return (int) (maxLength == 0 ? 0 : 100 * maxLenLive / maxLength); return (int) (maxLen == 0 ? 0 : 100 * maxLenLive / maxLen);
} }
@Override @Override
...@@ -198,14 +205,14 @@ public class Chunk { ...@@ -198,14 +205,14 @@ public class Chunk {
DataUtils.appendMap(buff, "chunk", id); DataUtils.appendMap(buff, "chunk", id);
DataUtils.appendMap(buff, "block", block); DataUtils.appendMap(buff, "block", block);
DataUtils.appendMap(buff, "len", len); DataUtils.appendMap(buff, "len", len);
if (maxLength != maxLenLive) { if (maxLen != maxLenLive) {
DataUtils.appendMap(buff, "liveMax", maxLenLive); DataUtils.appendMap(buff, "liveMax", maxLenLive);
} }
if (pageCount != pageCountLive) { if (pageCount != pageCountLive) {
DataUtils.appendMap(buff, "livePages", pageCountLive); DataUtils.appendMap(buff, "livePages", pageCountLive);
} }
DataUtils.appendMap(buff, "map", mapId); DataUtils.appendMap(buff, "map", mapId);
DataUtils.appendMap(buff, "max", maxLength); DataUtils.appendMap(buff, "max", maxLen);
if (next != 0) { if (next != 0) {
DataUtils.appendMap(buff, "next", next); DataUtils.appendMap(buff, "next", next);
} }
......
...@@ -130,6 +130,11 @@ public class DataUtils { ...@@ -130,6 +130,11 @@ public class DataUtils {
*/ */
public static final int PAGE_MEMORY_CHILD = 16; public static final int PAGE_MEMORY_CHILD = 16;
/**
* The marker size of a very large page.
*/
public static final int PAGE_LARGE = 2 * 1024 * 1024;
/** /**
* The UTF-8 character encoding format. * The UTF-8 character encoding format.
*/ */
...@@ -480,7 +485,7 @@ public class DataUtils { ...@@ -480,7 +485,7 @@ public class DataUtils {
/** /**
* Get the maximum length for the given code. * Get the maximum length for the given code.
* For the code 31, Integer.MAX_VALUE is returned. * For the code 31, PAGE_LARGE is returned.
* *
* @param pos the position * @param pos the position
* @return the maximum length * @return the maximum length
...@@ -488,7 +493,7 @@ public class DataUtils { ...@@ -488,7 +493,7 @@ public class DataUtils {
public static int getPageMaxLength(long pos) { public static int getPageMaxLength(long pos) {
int code = (int) ((pos >> 1) & 31); int code = (int) ((pos >> 1) & 31);
if (code == 31) { if (code == 31) {
return Integer.MAX_VALUE; return PAGE_LARGE;
} }
return (2 + (code & 1)) << ((code >> 1) + 4); return (2 + (code & 1)) << ((code >> 1) + 4);
} }
......
...@@ -29,20 +29,6 @@ import org.h2.util.New; ...@@ -29,20 +29,6 @@ import org.h2.util.New;
/* /*
File format:
store header: (blockSize) bytes
store header: (blockSize) bytes
[ chunk ] *
(there are two headers for security at the beginning of the file,
and there is a store header at the end of each chunk)
H:2,block:0,blockSize:1000,chunk:0,created:143fd8e5767,format:1,fletcher:a3acedfb
chunk:1,block:2,len:1,map:6,max:1c0,pages:2,root:4000004c8c,time:20a,version:1
chunk:2,block:3,fletcher:ca8cb347
maybe split chunk metadata into static and variable
TODO: TODO:
Documentation Documentation
...@@ -66,10 +52,10 @@ TransactionStore: ...@@ -66,10 +52,10 @@ TransactionStore:
MVStore: MVStore:
- page format: for nodes, maybe store child pointers first,
so we can dump them in the MVStoreTool even if the dataType is unknown
- ensure data is overwritten eventually if the system doesn't have a - ensure data is overwritten eventually if the system doesn't have a
real-time clock (Raspberry Pi) and if there are few writes per startup real-time clock (Raspberry Pi) and if there are few writes per startup
- when opening, verify the footer of the chunk (also when following next pointers)
- test max length sum with length code 31 (which is Integer.MAX_VALUE)
- maybe change the length code to have lower gaps - maybe change the length code to have lower gaps
- test chunk id rollover - test chunk id rollover
- document and review the file format - document and review the file format
...@@ -129,6 +115,9 @@ MVStore: ...@@ -129,6 +115,9 @@ MVStore:
"if the sum of the past 8196 bytes divides by 4096 with zero remainder" "if the sum of the past 8196 bytes divides by 4096 with zero remainder"
- Compression: try using a bloom filter (64 bit) before trying to match - Compression: try using a bloom filter (64 bit) before trying to match
- LIRS cache: maybe remove 'mask' field, and dynamically grow the arrays - LIRS cache: maybe remove 'mask' field, and dynamically grow the arrays
- chunk metadata: maybe split into static and variable,
or use a small page size for metadata
- data type "string": maybe use prefix compression for keys
*/ */
...@@ -521,7 +510,7 @@ public class MVStore { ...@@ -521,7 +510,7 @@ public class MVStore {
boolean validHeader = false; boolean validHeader = false;
// we don't know yet which chunk and version are the newest // we don't know yet which chunk and version are the newest
long newestVersion = -1; long newestVersion = -1;
long newestChunkBlock = -1; long chunkBlock = -1;
// read the first two blocks // read the first two blocks
ByteBuffer fileHeaderBlocks = fileStore.readFully(0, 2 * BLOCK_SIZE); ByteBuffer fileHeaderBlocks = fileStore.readFully(0, 2 * BLOCK_SIZE);
byte[] buff = new byte[BLOCK_SIZE]; byte[] buff = new byte[BLOCK_SIZE];
...@@ -550,7 +539,7 @@ public class MVStore { ...@@ -550,7 +539,7 @@ public class MVStore {
if (version > newestVersion) { if (version > newestVersion) {
newestVersion = version; newestVersion = version;
fileHeader.putAll(m); fileHeader.putAll(m);
newestChunkBlock = DataUtils.readHexLong(m, "block", 0); chunkBlock = DataUtils.readHexLong(m, "block", 0);
creationTime = DataUtils.readHexLong(m, "created", 0); creationTime = DataUtils.readHexLong(m, "created", 0);
validHeader = true; validHeader = true;
} }
...@@ -579,78 +568,60 @@ public class MVStore { ...@@ -579,78 +568,60 @@ public class MVStore {
format, FORMAT_READ); format, FORMAT_READ);
} }
lastStoredVersion = -1; lastStoredVersion = -1;
chunks.clear();
// read the chunk footer of the last block of the file Chunk footer = readChunkFooter(fileStore.size());
ByteBuffer lastBlock = fileStore.readFully( if (footer != null) {
fileStore.size() - Chunk.FOOTER_LENGTH, Chunk.FOOTER_LENGTH); if (footer.version > newestVersion) {
buff = new byte[Chunk.FOOTER_LENGTH]; newestVersion = footer.version;
lastBlock.get(buff); chunkBlock = footer.block;
// the following can fail for various reasons
try {
String s = new String(buff, DataUtils.LATIN).trim();
HashMap<String, String> m = DataUtils.parseMap(s);
int check = DataUtils.readHexInt(m, "fletcher", 0);
m.remove("fletcher");
s = s.substring(0, s.lastIndexOf("fletcher") - 1);
byte[] bytes = s.getBytes(DataUtils.LATIN);
int checksum = DataUtils.getFletcher32(bytes, bytes.length / 2 * 2);
if (check == checksum) {
int chunk = DataUtils.readHexInt(m, "chunk", 0);
long version = DataUtils.readHexLong(m, "version", chunk);
if (version > newestVersion) {
newestVersion = version;
newestChunkBlock = DataUtils.readHexLong(m, "block", 0);
validHeader = true;
} }
} }
} catch (Exception e) { if (chunkBlock <= 0) {
// ignore // no chunk
return;
} }
// follow the chain of next chunks // read the chunk header and footer,
long testChunkBlock = newestChunkBlock; // and follow the chain of next chunks
lastChunk = null;
while (true) { while (true) {
Chunk header; Chunk header;
try { try {
header = readChunkHeader(testChunkBlock); header = readChunkHeader(chunkBlock);
} catch (Exception e) { } catch (Exception e) {
// ignore the exception, but exit the loop // invalid chunk header: ignore, but stop
break; break;
} }
if (header.version < newestVersion) { if (header.version < newestVersion) {
// we have reached the end // we have reached the end
break; break;
} }
newestChunkBlock = testChunkBlock; footer = readChunkFooter((chunkBlock + header.len) * BLOCK_SIZE);
if (footer == null || footer.id != header.id) {
// invalid chunk footer, or the wrong one
break;
}
lastChunk = header;
newestVersion = header.version; newestVersion = header.version;
if (header.next == 0 || header.next >= fileStore.size() / BLOCK_SIZE) { if (header.next == 0 || header.next >= fileStore.size() / BLOCK_SIZE) {
// no (valid) next
break; break;
} }
testChunkBlock = header.next; chunkBlock = header.next;
} }
if (lastChunk == null) {
if (newestChunkBlock > 0) { // no valid chunk
readMeta(newestChunkBlock); return;
}
}
private void readMeta(long chunkBlock) {
chunks.clear();
Chunk header = readChunkHeader(chunkBlock);
if (header.block == Long.MAX_VALUE) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_FILE_CORRUPT,
"Chunk {0} is invalid", header.id);
} }
lastChunk = header; lastMapId = lastChunk.mapId;
lastMapId = header.mapId; currentVersion = lastChunk.version;
currentVersion = header.version;
setWriteVersion(currentVersion); setWriteVersion(currentVersion);
chunks.put(header.id, header); chunks.put(lastChunk.id, lastChunk);
meta.setRootPos(header.metaRootPos, -1); meta.setRootPos(lastChunk.metaRootPos, -1);
// we can load the chunk in any order,
// because loading chunk metadata // load the chunk metadata: we can load in any order,
// might recursively load another chunk // because loading chunk metadata might recursively load another chunk
for (Iterator<String> it = meta.keyIterator("chunk."); it.hasNext();) { for (Iterator<String> it = meta.keyIterator("chunk."); it.hasNext();) {
String s = it.next(); String s = it.next();
if (!s.startsWith("chunk.")) { if (!s.startsWith("chunk.")) {
...@@ -679,6 +650,40 @@ public class MVStore { ...@@ -679,6 +650,40 @@ public class MVStore {
} }
} }
/**
* Try to read a chunk footer.
*
* @param end the end of the chunk
* @return the chunk, or null if not successful
*/
private Chunk readChunkFooter(long end) {
// read the chunk footer of the last block of the file
ByteBuffer lastBlock = fileStore.readFully(
end - Chunk.FOOTER_LENGTH, Chunk.FOOTER_LENGTH);
byte[] buff = new byte[Chunk.FOOTER_LENGTH];
lastBlock.get(buff);
// the following can fail for various reasons
try {
String s = new String(buff, DataUtils.LATIN).trim();
HashMap<String, String> m = DataUtils.parseMap(s);
int check = DataUtils.readHexInt(m, "fletcher", 0);
m.remove("fletcher");
s = s.substring(0, s.lastIndexOf("fletcher") - 1);
byte[] bytes = s.getBytes(DataUtils.LATIN);
int checksum = DataUtils.getFletcher32(bytes, bytes.length / 2 * 2);
if (check == checksum) {
int chunk = DataUtils.readHexInt(m, "chunk", 0);
Chunk c = new Chunk(chunk);
c.version = DataUtils.readHexLong(m, "version", 0);
c.block = DataUtils.readHexLong(m, "block", 0);
return c;
}
} catch (Exception e) {
// ignore
}
return null;
}
private void writeFileHeader() { private void writeFileHeader() {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
if (lastChunk != null) { if (lastChunk != null) {
...@@ -904,7 +909,7 @@ public class MVStore { ...@@ -904,7 +909,7 @@ public class MVStore {
c.pageCount = Integer.MAX_VALUE; c.pageCount = Integer.MAX_VALUE;
c.pageCountLive = Integer.MAX_VALUE; c.pageCountLive = Integer.MAX_VALUE;
c.maxLength = Long.MAX_VALUE; c.maxLen = Long.MAX_VALUE;
c.maxLenLive = Long.MAX_VALUE; c.maxLenLive = Long.MAX_VALUE;
c.metaRootPos = Long.MAX_VALUE; c.metaRootPos = Long.MAX_VALUE;
c.block = Long.MAX_VALUE; c.block = Long.MAX_VALUE;
...@@ -950,7 +955,7 @@ public class MVStore { ...@@ -950,7 +955,7 @@ public class MVStore {
int headerLength = buff.position(); int headerLength = buff.position();
c.pageCount = 0; c.pageCount = 0;
c.pageCountLive = 0; c.pageCountLive = 0;
c.maxLength = 0; c.maxLen = 0;
c.maxLenLive = 0; c.maxLenLive = 0;
for (MVMap<?, ?> m : changed) { for (MVMap<?, ?> m : changed) {
Page p = m.getRoot(); Page p = m.getRoot();
...@@ -1007,7 +1012,6 @@ public class MVStore { ...@@ -1007,7 +1012,6 @@ public class MVStore {
// calculate and set the likely next position // calculate and set the likely next position
if (reuseSpace) { if (reuseSpace) {
int predictBlocks = c.len; int predictBlocks = c.len;
c.nextSize = predictBlocks;
long predictedNextStart = fileStore.allocate(predictBlocks * BLOCK_SIZE); long predictedNextStart = fileStore.allocate(predictBlocks * BLOCK_SIZE);
fileStore.free(predictedNextStart, predictBlocks * BLOCK_SIZE); fileStore.free(predictedNextStart, predictBlocks * BLOCK_SIZE);
c.next = predictedNextStart / BLOCK_SIZE; c.next = predictedNextStart / BLOCK_SIZE;
...@@ -1417,7 +1421,7 @@ public class MVStore { ...@@ -1417,7 +1421,7 @@ public class MVStore {
long maxLengthSum = 0; long maxLengthSum = 0;
long maxLengthLiveSum = 0; long maxLengthLiveSum = 0;
for (Chunk c : chunks.values()) { for (Chunk c : chunks.values()) {
maxLengthSum += c.maxLength; maxLengthSum += c.maxLen;
maxLengthLiveSum += c.maxLenLive; maxLengthLiveSum += c.maxLenLive;
} }
// the fill rate of all chunks combined // the fill rate of all chunks combined
...@@ -1459,7 +1463,7 @@ public class MVStore { ...@@ -1459,7 +1463,7 @@ public class MVStore {
long saved = 0; long saved = 0;
Chunk move = null; Chunk move = null;
for (Chunk c : old) { for (Chunk c : old) {
long save = c.maxLength - c.maxLenLive; long save = c.maxLen - c.maxLenLive;
if (move != null) { if (move != null) {
if (saved > minSaving) { if (saved > minSaving) {
break; break;
......
...@@ -65,18 +65,18 @@ public class MVStoreTool { ...@@ -65,18 +65,18 @@ public class MVStoreTool {
int blockSize = MVStore.BLOCK_SIZE; int blockSize = MVStore.BLOCK_SIZE;
try { try {
file = FilePath.get(fileName).open("r"); file = FilePath.get(fileName).open("r");
long fileLength = file.size(); long fileSize = file.size();
pw.println("file " + fileName); int len = Long.toHexString(fileSize).length();
pw.println(" length " + Long.toHexString(fileLength));
ByteBuffer block = ByteBuffer.allocate(4096); ByteBuffer block = ByteBuffer.allocate(4096);
for (long pos = 0; pos < fileLength;) { for (long pos = 0; pos < fileSize;) {
block.rewind(); block.rewind();
DataUtils.readFully(file, pos, block); DataUtils.readFully(file, pos, block);
block.rewind(); block.rewind();
int headerType = block.get(); int headerType = block.get();
if (headerType == 'H') { if (headerType == 'H') {
pw.println(" store header at " + Long.toHexString(pos)); pw.printf("%0" + len + "x fileHeader %s%n",
pw.println(" " + new String(block.array(), "UTF-8").trim()); pos,
new String(block.array(), DataUtils.LATIN).trim());
pos += blockSize; pos += blockSize;
continue; continue;
} }
...@@ -87,7 +87,7 @@ public class MVStoreTool { ...@@ -87,7 +87,7 @@ public class MVStoreTool {
block.position(0); block.position(0);
Chunk c = Chunk.readChunkHeader(block, pos); Chunk c = Chunk.readChunkHeader(block, pos);
int length = c.len * MVStore.BLOCK_SIZE; int length = c.len * MVStore.BLOCK_SIZE;
pw.println(" " + c.toString()); pw.printf("%n%0" + len + "x chunkHeader %s%n", pos, c.toString());
ByteBuffer chunk = ByteBuffer.allocate(length); ByteBuffer chunk = ByteBuffer.allocate(length);
DataUtils.readFully(file, pos, chunk); DataUtils.readFully(file, pos, chunk);
int p = block.position(); int p = block.position();
...@@ -95,62 +95,94 @@ public class MVStoreTool { ...@@ -95,62 +95,94 @@ public class MVStoreTool {
int remaining = c.pageCount; int remaining = c.pageCount;
while (remaining > 0) { while (remaining > 0) {
chunk.position(p); chunk.position(p);
int pageLength = chunk.getInt(); int pageSize = chunk.getInt();
// check value (ignored) // check value (ignored)
chunk.getShort(); chunk.getShort();
int mapId = DataUtils.readVarInt(chunk); int mapId = DataUtils.readVarInt(chunk);
int len = DataUtils.readVarInt(chunk); int entries = DataUtils.readVarInt(chunk);
int type = chunk.get(); int type = chunk.get();
boolean compressed = (type & 2) != 0; boolean compressed = (type & 2) != 0;
boolean node = (type & 1) != 0; boolean node = (type & 1) != 0;
pw.println( pw.printf(
" map " + Integer.toHexString(mapId) + "+%0" + len + "x %s, map %x, %d entries, %d bytes%n",
" at " + Long.toHexString(p) + " " + p,
(node ? " node" : " leaf") + (node ? "node" : "leaf") +
(compressed ? " compressed" : "") + (compressed ? " compressed" : ""),
" len: " + Integer.toHexString(pageLength) + mapId,
" entries: " + Integer.toHexString(len)); node ? entries + 1 : entries,
p += pageLength; pageSize);
p += pageSize;
remaining--; remaining--;
if (mapId == 0 && !compressed) { if (compressed) {
String[] keys = new String[len]; continue;
for (int i = 0; i < len; i++) {
String k = StringDataType.INSTANCE.read(chunk);
keys[i] = k;
} }
String[] keys = new String[entries];
long[] children = null;
long[] counts = null;
if (node) { if (node) {
long[] children = new long[len + 1]; children = new long[entries + 1];
for (int i = 0; i <= len; i++) { for (int i = 0; i <= entries; i++) {
children[i] = chunk.getLong(); children[i] = chunk.getLong();
} }
long[] counts = new long[len + 1]; counts = new long[entries + 1];
for (int i = 0; i <= len; i++) { for (int i = 0; i <= entries; i++) {
long s = DataUtils.readVarLong(chunk); long s = DataUtils.readVarLong(chunk);
counts[i] = s; counts[i] = s;
} }
for (int i = 0; i < len; i++) {
pw.println(" < " + keys[i] + ": " +
counts[i] + " -> " + getPosString(children[i]));
} }
pw.println(" >= : " + if (mapId == 0) {
counts[len] + " -> " + getPosString(children[len])); for (int i = 0; i < entries; i++) {
String k = StringDataType.INSTANCE.read(chunk);
keys[i] = k;
}
if (node) {
// meta map node
for (int i = 0; i < entries; i++) {
long cp = children[i];
pw.printf(" %d children < %s @ chunk %x +%0" + len + "x%n",
counts[i],
keys[i],
DataUtils.getPageChunkId(cp),
DataUtils.getPageOffset(cp));
}
long cp = children[entries];
pw.printf(" %d children >= %s @ chunk %x +%0" + len + "x%n",
counts[entries],
keys[entries],
DataUtils.getPageChunkId(cp),
DataUtils.getPageOffset(cp));
} else { } else {
// meta map leaf // meta map leaf
String[] values = new String[len]; String[] values = new String[entries];
for (int i = 0; i < len; i++) { for (int i = 0; i < entries; i++) {
String v = StringDataType.INSTANCE.read(chunk); String v = StringDataType.INSTANCE.read(chunk);
values[i] = v; values[i] = v;
} }
for (int i = 0; i < len; i++) { for (int i = 0; i < entries; i++) {
pw.println(" " + keys[i] + "=" + values[i]); pw.println(" " + keys[i] + " = " + values[i]);
}
}
} else {
if (node) {
for (int i = 0; i <= entries; i++) {
long cp = children[i];
pw.printf(" %d children @ chunk %x +%0" + len + "x%n",
counts[i],
DataUtils.getPageChunkId(cp),
DataUtils.getPageOffset(cp));
} }
} }
} }
} }
chunk.position(chunk.limit() - Chunk.FOOTER_LENGTH); int footerPos = chunk.limit() - Chunk.FOOTER_LENGTH;
pw.println(" chunk footer"); chunk.position(footerPos);
pw.println(" " + new String(chunk.array(), chunk.position(), Chunk.FOOTER_LENGTH, "UTF-8").trim()); pw.printf(
"+%0" + len + "x chunkFooter %s%n",
footerPos,
new String(chunk.array(), chunk.position(),
Chunk.FOOTER_LENGTH, DataUtils.LATIN).trim());
} }
pw.printf("%n%0" + len + "x eof%n", fileSize);
} catch (IOException e) { } catch (IOException e) {
pw.println("ERROR: " + e); pw.println("ERROR: " + e);
e.printStackTrace(pw); e.printStackTrace(pw);
...@@ -163,15 +195,7 @@ public class MVStoreTool { ...@@ -163,15 +195,7 @@ public class MVStoreTool {
} }
} }
} }
pw.println();
pw.flush(); pw.flush();
} }
private static String getPosString(long pos) {
return "pos " + Long.toHexString(pos) +
", chunk " + Integer.toHexString(DataUtils.getPageChunkId(pos)) +
", offset " + Integer.toHexString(DataUtils.getPageOffset(pos));
}
} }
...@@ -178,7 +178,7 @@ public class Page { ...@@ -178,7 +178,7 @@ public class Page {
long pos, long filePos, long fileSize) { long pos, long filePos, long fileSize) {
ByteBuffer buff; ByteBuffer buff;
int maxLength = DataUtils.getPageMaxLength(pos); int maxLength = DataUtils.getPageMaxLength(pos);
if (maxLength == Integer.MAX_VALUE) { if (maxLength == DataUtils.PAGE_LARGE) {
buff = fileStore.readFully(filePos, 128); buff = fileStore.readFully(filePos, 128);
maxLength = buff.getInt(); maxLength = buff.getInt();
// read the first bytes again // read the first bytes again
...@@ -758,7 +758,6 @@ public class Page { ...@@ -758,7 +758,6 @@ public class Page {
buff = ByteBuffer.allocate(l); buff = ByteBuffer.allocate(l);
compressor.expand(comp, 0, compLen, buff.array(), buff.arrayOffset(), l); compressor.expand(comp, 0, compLen, buff.array(), buff.arrayOffset(), l);
} }
map.getKeyType().read(buff, keys, len, true);
if (node) { if (node) {
childCount = len + 1; childCount = len + 1;
children = new long[len + 1]; children = new long[len + 1];
...@@ -774,7 +773,9 @@ public class Page { ...@@ -774,7 +773,9 @@ public class Page {
counts[i] = s; counts[i] = s;
} }
totalCount = total; totalCount = total;
} else { }
map.getKeyType().read(buff, keys, len, true);
if (!node) {
values = new Object[len]; values = new Object[len];
map.getValueType().read(buff, values, len, false); map.getValueType().read(buff, values, len, false);
totalCount = len; totalCount = len;
...@@ -799,7 +800,6 @@ public class Page { ...@@ -799,7 +800,6 @@ public class Page {
putVarInt(len). putVarInt(len).
put((byte) type); put((byte) type);
int compressStart = buff.position(); int compressStart = buff.position();
map.getKeyType().write(buff, keys, len, true);
if (type == DataUtils.PAGE_TYPE_NODE) { if (type == DataUtils.PAGE_TYPE_NODE) {
for (int i = 0; i <= len; i++) { for (int i = 0; i <= len; i++) {
buff.putLong(children[i]); buff.putLong(children[i]);
...@@ -807,7 +807,9 @@ public class Page { ...@@ -807,7 +807,9 @@ public class Page {
for (int i = 0; i <= len; i++) { for (int i = 0; i <= len; i++) {
buff.putVarLong(counts[i]); buff.putVarLong(counts[i]);
} }
} else { }
map.getKeyType().write(buff, keys, len, true);
if (type == DataUtils.PAGE_TYPE_LEAF) {
map.getValueType().write(buff, values, len, false); map.getValueType().write(buff, values, len, false);
} }
MVStore store = map.getStore(); MVStore store = map.getStore();
...@@ -840,7 +842,7 @@ public class Page { ...@@ -840,7 +842,7 @@ public class Page {
pos = DataUtils.getPagePos(chunkId, start, pageLength, type); pos = DataUtils.getPagePos(chunkId, start, pageLength, type);
store.cachePage(pos, this, getMemory()); store.cachePage(pos, this, getMemory());
long max = DataUtils.getPageMaxLength(pos); long max = DataUtils.getPageMaxLength(pos);
chunk.maxLength += max; chunk.maxLen += max;
chunk.maxLenLive += max; chunk.maxLenLive += max;
chunk.pageCount++; chunk.pageCount++;
chunk.pageCountLive++; chunk.pageCountLive++;
......
...@@ -393,7 +393,7 @@ public class WebApp { ...@@ -393,7 +393,7 @@ public class WebApp {
try { try {
tool.runTool(argList); tool.runTool(argList);
out.flush(); out.flush();
String o = new String(outBuff.toByteArray(), "UTF-8"); String o = new String(outBuff.toByteArray(), Constants.UTF8);
String result = PageParser.escapeHtml(o); String result = PageParser.escapeHtml(o);
session.put("toolResult", result); session.put("toolResult", result);
} catch (Exception e) { } catch (Exception e) {
......
...@@ -450,7 +450,7 @@ public class WebServer implements Service { ...@@ -450,7 +450,7 @@ public class WebServer implements Service {
trace("translation: "+language); trace("translation: "+language);
byte[] trans = getFile("_text_"+language+".prop"); byte[] trans = getFile("_text_"+language+".prop");
trace(" "+new String(trans)); trace(" "+new String(trans));
text = SortedProperties.fromLines(new String(trans, "UTF-8")); text = SortedProperties.fromLines(new String(trans, Constants.UTF8));
// remove starting # (if not translated yet) // remove starting # (if not translated yet)
for (Entry<Object, Object> entry : text.entrySet()) { for (Entry<Object, Object> entry : text.entrySet()) {
String value = (String) entry.getValue(); String value = (String) entry.getValue();
......
...@@ -197,7 +197,7 @@ public class Recover extends Tool implements DataHandler { ...@@ -197,7 +197,7 @@ public class Recover extends Tool implements DataHandler {
* INTERNAL * INTERNAL
*/ */
public static Reader readClob(String fileName) throws IOException { public static Reader readClob(String fileName) throws IOException {
return new BufferedReader(new InputStreamReader(readBlob(fileName), "UTF-8")); return new BufferedReader(new InputStreamReader(readBlob(fileName), Constants.UTF8));
} }
/** /**
...@@ -273,7 +273,7 @@ public class Recover extends Tool implements DataHandler { ...@@ -273,7 +273,7 @@ public class Recover extends Tool implements DataHandler {
*/ */
public static Reader readClobMap(Connection conn, long lobId, long precision) throws Exception { public static Reader readClobMap(Connection conn, long lobId, long precision) throws Exception {
InputStream in = readBlobMap(conn, lobId, precision); InputStream in = readBlobMap(conn, lobId, precision);
return new BufferedReader(new InputStreamReader(in, "UTF-8")); return new BufferedReader(new InputStreamReader(in, Constants.UTF8));
} }
private void trace(String message) { private void trace(String message) {
......
...@@ -25,6 +25,7 @@ import java.util.ArrayList; ...@@ -25,6 +25,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
...@@ -312,7 +313,7 @@ public class SourceCompiler { ...@@ -312,7 +313,7 @@ public class SourceCompiler {
copyInThread(p.getInputStream(), buff); copyInThread(p.getInputStream(), buff);
copyInThread(p.getErrorStream(), buff); copyInThread(p.getErrorStream(), buff);
p.waitFor(); p.waitFor();
String err = new String(buff.toByteArray(), "UTF-8"); String err = new String(buff.toByteArray(), Constants.UTF8);
throwSyntaxError(err); throwSyntaxError(err);
return p.exitValue(); return p.exitValue();
} catch (Exception e) { } catch (Exception e) {
...@@ -343,7 +344,7 @@ public class SourceCompiler { ...@@ -343,7 +344,7 @@ public class SourceCompiler {
"-d", COMPILE_DIR, "-d", COMPILE_DIR,
"-encoding", "UTF-8", "-encoding", "UTF-8",
javaFile.getAbsolutePath() }); javaFile.getAbsolutePath() });
String err = new String(buff.toByteArray(), "UTF-8"); String err = new String(buff.toByteArray(), Constants.UTF8);
throwSyntaxError(err); throwSyntaxError(err);
} catch (Exception e) { } catch (Exception e) {
throw DbException.convert(e); throw DbException.convert(e);
......
...@@ -633,7 +633,7 @@ public class Transfer { ...@@ -633,7 +633,7 @@ public class Transfer {
if (magic != LOB_MAGIC) { if (magic != LOB_MAGIC) {
throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "magic=" + magic); throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "magic=" + magic);
} }
byte[] small = new String(buff).getBytes("UTF-8"); byte[] small = new String(buff).getBytes(Constants.UTF8);
return ValueLobDb.createSmallLob(Value.CLOB, small, length); return ValueLobDb.createSmallLob(Value.CLOB, small, length);
} }
Value v = session.getDataHandler().getLobStorage().createClob(new DataReader(in), length); Value v = session.getDataHandler().getLobStorage().createClob(new DataReader(in), length);
......
...@@ -252,7 +252,7 @@ public class TestDataUtils extends TestBase { ...@@ -252,7 +252,7 @@ public class TestDataUtils extends TestBase {
assertEquals(max, DataUtils.parseHexLong(hex)); assertEquals(max, DataUtils.parseHexLong(hex));
assertEquals(Chunk.MAX_ID, DataUtils.getPageChunkId(max)); assertEquals(Chunk.MAX_ID, DataUtils.getPageChunkId(max));
assertEquals(Integer.MAX_VALUE, DataUtils.getPageOffset(max)); assertEquals(Integer.MAX_VALUE, DataUtils.getPageOffset(max));
assertEquals(Integer.MAX_VALUE, DataUtils.getPageMaxLength(max)); assertEquals(DataUtils.PAGE_LARGE, DataUtils.getPageMaxLength(max));
assertEquals(DataUtils.PAGE_TYPE_NODE, DataUtils.getPageType(max)); assertEquals(DataUtils.PAGE_TYPE_NODE, DataUtils.getPageType(max));
long overflow = DataUtils.getPagePos(Chunk.MAX_ID + 1, long overflow = DataUtils.getPagePos(Chunk.MAX_ID + 1,
......
...@@ -14,6 +14,7 @@ import java.util.Random; ...@@ -14,6 +14,7 @@ import java.util.Random;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import org.h2.mvstore.Chunk;
import org.h2.mvstore.Cursor; import org.h2.mvstore.Cursor;
import org.h2.mvstore.DataUtils; import org.h2.mvstore.DataUtils;
import org.h2.mvstore.FileStore; import org.h2.mvstore.FileStore;
...@@ -48,6 +49,8 @@ public class TestMVStore extends TestBase { ...@@ -48,6 +49,8 @@ public class TestMVStore extends TestBase {
public void test() throws Exception { public void test() throws Exception {
FileUtils.deleteRecursive(getBaseDir(), true); FileUtils.deleteRecursive(getBaseDir(), true);
FileUtils.createDirectories(getBaseDir()); FileUtils.createDirectories(getBaseDir());
testFileFormatExample();
testMaxChunkLength();
testCacheInfo(); testCacheInfo();
testRollback(); testRollback();
testVersionsToKeep(); testVersionsToKeep();
...@@ -100,6 +103,37 @@ public class TestMVStore extends TestBase { ...@@ -100,6 +103,37 @@ public class TestMVStore extends TestBase {
testLargerThan2G(); testLargerThan2G();
} }
private void testFileFormatExample() {
String fileName = getBaseDir() + "/testFileFormatExample.h3";
MVStore s = MVStore.open(fileName);
MVMap<Integer, String> map = s.openMap("data");
for (int i = 0; i < 400; i++) {
map.put(i, "Hello");
}
s.commit();
for (int i = 0; i < 100; i++) {
map.put(0, "Hi");
}
s.commit();
s.close();
// MVStoreTool.dump(fileName);
}
private void testMaxChunkLength() {
String fileName = getBaseDir() + "/testMaxChunkLength.h3";
MVStore s = new MVStore.Builder().fileName(fileName).open();
MVMap<Integer, byte[]> map = s.openMap("data");
map.put(0, new byte[2 * 1024 * 1024]);
s.commit();
map.put(1, new byte[10 * 1024]);
s.commit();
MVMap<String, String> meta = s.getMetaMap();
Chunk c = Chunk.fromString(meta.get("chunk.1"));
assertTrue(c.maxLen < Integer.MAX_VALUE);
assertTrue(c.maxLenLive < Integer.MAX_VALUE);
s.close();
}
private void testCacheInfo() { private void testCacheInfo() {
String fileName = getBaseDir() + "/testCloseMap.h3"; String fileName = getBaseDir() + "/testCloseMap.h3";
MVStore s = new MVStore.Builder().fileName(fileName).cacheSize(2).open(); MVStore s = new MVStore.Builder().fileName(fileName).cacheSize(2).open();
......
...@@ -751,3 +751,5 @@ sameorigin nobuffer francois hikari duske phromros thailand kritchai mendonca ...@@ -751,3 +751,5 @@ sameorigin nobuffer francois hikari duske phromros thailand kritchai mendonca
maginatics jdbclint lint lsm unmappable adams douglas definer invoker maginatics jdbclint lint lsm unmappable adams douglas definer invoker
fmrn fmxxx fmday fml syyyy tzd nov iyy iyyy fmc fmb fmxx tzr btc yyfxyy scc syear fmrn fmxxx fmday fml syyyy tzd nov iyy iyyy fmc fmb fmxx tzr btc yyfxyy scc syear
overwrote though randomize readability datagram rsync mongodb divides crypto overwrote though randomize readability datagram rsync mongodb divides crypto
predicted prediction wojtek hops jurczyk cbtree predict vast assumption upside
adjusted lastly sgtatham
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论