提交 0d3a6cc7 authored 作者: Thomas Mueller's avatar Thomas Mueller

Page store: shrink the log

上级 94484549
...@@ -451,6 +451,7 @@ See also <a href="build.html#providing_patches">Providing Patches</a>. ...@@ -451,6 +451,7 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
</li><li>Issue 107: Prefer using the ORDER BY index if LIMIT is used. </li><li>Issue 107: Prefer using the ORDER BY index if LIMIT is used.
</li><li>Support reading sequences using DatabaseMetaData.getTables(null, null, null, new String[]{"SEQUENCE"}). </li><li>Support reading sequences using DatabaseMetaData.getTables(null, null, null, new String[]{"SEQUENCE"}).
See PostgreSQL. See PostgreSQL.
</li><li>Add option to enable TCP_NODELAY using Socket.setTcpNoDelay(true).
</li></ul> </li></ul>
<h2>Not Planned</h2> <h2>Not Planned</h2>
......
/*
* Copyright 2004-2009 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
*/
package org.h2.store;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import org.h2.util.IOUtils;
/**
* This class is backed by an input stream and supports reading values and
* variable size data.
*/
public class DataReader {
private static final EOFException EOF = new EOFException();
private InputStream in;
/**
* Create a new data reader.
*
* @param in the input stream
*/
public DataReader(InputStream in) {
this.in = in;
}
/**
* Read a byte.
*
* @return the byte
*/
public byte read() throws IOException {
int x = in.read();
if (x < 0) {
throw EOF;
}
return (byte) x;
}
/**
* Read a variable size integer.
*
* @return the value
*/
public int readVarInt() throws IOException {
int b = read();
if (b >= 0) {
return b;
}
int x = b & 0x7f;
b = read();
if (b >= 0) {
return x | (b << 7);
}
x |= (b & 0x7f) << 7;
b = read();
if (b >= 0) {
return x | (b << 14);
}
x |= (b & 0x7f) << 14;
b = read();
if (b >= 0) {
return x | b << 21;
}
return x | ((b & 0x7f) << 21) | (read() << 28);
}
/**
* Read a variable size long.
*
* @return the value
*/
public long readVarLong() throws IOException {
long x = read();
if (x >= 0) {
return x;
}
x &= 0x7f;
for (int s = 7;; s += 7) {
long b = read();
x |= (b & 0x7f) << s;
if (b >= 0) {
return x;
}
}
}
/**
* Read an integer.
*
* @return the value
*/
public int readInt() throws IOException {
return (read() << 24) + ((read() & 0xff) << 16) + ((read() & 0xff) << 8) + (read() & 0xff);
}
/**
* Read a long.
*
* @return the value
*/
public long readLong() throws IOException {
return ((long) (readInt()) << 32) + (readInt() & 0xffffffffL);
}
/**
* Read a number of bytes.
*
* @param buff the target buffer
* @param offset the offset within the target buffer
* @param len the number of bytes to read
*/
public void readFully(byte[] buff, int offset, int len) throws IOException {
int got = IOUtils.readFully(in, buff, offset, len);
if (got < len) {
throw EOF;
}
}
/**
* Read a string from the stream.
*
* @return the string
*/
public String readString() throws IOException {
int len = readVarInt();
return readString(len);
}
private String readString(int len) throws IOException {
char[] chars = new char[len];
for (int i = 0; i < len; i++) {
int x = read() & 0xff;
if (x < 0x80) {
chars[i] = (char) x;
} else if (x >= 0xe0) {
chars[i] = (char) (((x & 0xf) << 12) + ((read() & 0x3f) << 6) + (read() & 0x3f));
} else {
chars[i] = (char) (((x & 0x1f) << 6) + (read() & 0x3f));
}
}
return new String(chars);
}
}
...@@ -79,16 +79,15 @@ import org.h2.value.ValueString; ...@@ -79,16 +79,15 @@ import org.h2.value.ValueString;
*/ */
public class PageStore implements CacheWriter { public class PageStore implements CacheWriter {
// TODO fix page format of .. // TODO check commit delay
// TODO log: use varInt / varLong
// TODO record: replace getPos() with long getKey() in page store
// TODO long primary keys don't use delegating index yet (setPos(): int)
// TODO update: only log the key and changed values
// TODO implement checksum; 0 for empty pages // TODO implement checksum; 0 for empty pages
// TODO undo log: (option) fully compress empty pages
// TODO undo log: don't store empty space between head and data // TODO undo log: don't store empty space between head and data
// TODO undo log: lzf compression // TODO update: only log the key and changed values
// TODO long primary keys don't use delegating index yet (setPos(): int) // TODO maybe remove some parent pointers
// TODO maybe remove parent pointer
// TODO index creation: use less space (ordered, split at insertion point) // TODO index creation: use less space (ordered, split at insertion point)
// TODO test running out of disk space (using a special file system) // TODO test running out of disk space (using a special file system)
......
...@@ -36,6 +36,7 @@ import org.h2.security.SHA256; ...@@ -36,6 +36,7 @@ import org.h2.security.SHA256;
import org.h2.store.Data; import org.h2.store.Data;
import org.h2.store.DataHandler; import org.h2.store.DataHandler;
import org.h2.store.DataPage; import org.h2.store.DataPage;
import org.h2.store.DataReader;
import org.h2.store.DiskFile; import org.h2.store.DiskFile;
import org.h2.store.FileLister; import org.h2.store.FileLister;
import org.h2.store.FileStore; import org.h2.store.FileStore;
...@@ -56,7 +57,6 @@ import org.h2.util.ObjectArray; ...@@ -56,7 +57,6 @@ import org.h2.util.ObjectArray;
import org.h2.util.RandomUtils; import org.h2.util.RandomUtils;
import org.h2.util.SmallLRUCache; import org.h2.util.SmallLRUCache;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.util.TempFileDeleter; import org.h2.util.TempFileDeleter;
import org.h2.util.Tool; import org.h2.util.Tool;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -899,7 +899,7 @@ public class Recover extends Tool implements DataHandler { ...@@ -899,7 +899,7 @@ public class Recover extends Tool implements DataHandler {
private void dumpPageLogStream(PrintWriter writer, int logFirstTrunkPage, int logFirstDataPage) throws IOException, SQLException { private void dumpPageLogStream(PrintWriter writer, int logFirstTrunkPage, int logFirstDataPage) throws IOException, SQLException {
Data s = Data.create(this, pageSize); Data s = Data.create(this, pageSize);
DataInputStream in = new DataInputStream( DataReader in = new DataReader(
new PageInputStream(writer, this, store, logFirstTrunkPage, logFirstDataPage, pageSize) new PageInputStream(writer, this, store, logFirstTrunkPage, logFirstDataPage, pageSize)
); );
while (true) { while (true) {
...@@ -908,44 +908,41 @@ public class Recover extends Tool implements DataHandler { ...@@ -908,44 +908,41 @@ public class Recover extends Tool implements DataHandler {
break; break;
} }
if (x == PageLog.UNDO) { if (x == PageLog.UNDO) {
int pageId = in.readInt(); int pageId = in.readVarInt();
in.readFully(new byte[pageSize]); in.readFully(new byte[pageSize], 0, pageSize);
writer.println("-- undo page " + pageId); writer.println("-- undo page " + pageId);
} else if (x == PageLog.ADD || x == PageLog.REMOVE) { } else if (x == PageLog.ADD || x == PageLog.REMOVE) {
int sessionId = in.readInt(); int sessionId = in.readVarInt();
setStorage(in.readInt()); setStorage(in.readVarInt());
Row row = PageLog.readRow(in, s); Row row = PageLog.readRow(in, s);
writer.println("-- session " + sessionId + writer.println("-- session " + sessionId +
" table " + storageId + " table " + storageId +
" " + (x == PageLog.ADD ? "add" : "remove") + " " + row.toString()); " " + (x == PageLog.ADD ? "add" : "remove") + " " + row.toString());
} else if (x == PageLog.TRUNCATE) { } else if (x == PageLog.TRUNCATE) {
int sessionId = in.readInt(); int sessionId = in.readVarInt();
setStorage(in.readInt()); setStorage(in.readVarInt());
writer.println("-- session " + sessionId + writer.println("-- session " + sessionId +
" table " + storageId + " table " + storageId +
" truncate"); " truncate");
} else if (x == PageLog.COMMIT) { } else if (x == PageLog.COMMIT) {
int sessionId = in.readInt(); int sessionId = in.readVarInt();
writer.println("-- commit " + sessionId); writer.println("-- commit " + sessionId);
} else if (x == PageLog.ROLLBACK) { } else if (x == PageLog.ROLLBACK) {
int sessionId = in.readInt(); int sessionId = in.readVarInt();
writer.println("-- rollback " + sessionId); writer.println("-- rollback " + sessionId);
} else if (x == PageLog.PREPARE_COMMIT) { } else if (x == PageLog.PREPARE_COMMIT) {
int sessionId = in.readInt(); int sessionId = in.readVarInt();
int len = in.readInt(); String transaction = in.readString();
byte[] t = new byte[len];
in.readFully(t);
String transaction = StringUtils.utf8Decode(t);
writer.println("-- prepare commit " + sessionId + " " + transaction); writer.println("-- prepare commit " + sessionId + " " + transaction);
} else if (x == PageLog.NOOP) { } else if (x == PageLog.NOOP) {
// nothing to do // nothing to do
} else if (x == PageLog.CHECKPOINT) { } else if (x == PageLog.CHECKPOINT) {
writer.println("-- checkpoint"); writer.println("-- checkpoint");
} else if (x == PageLog.FREE_LOG) { } else if (x == PageLog.FREE_LOG) {
int size = in.readInt(); int size = in.readVarInt();
StringBuilder buff = new StringBuilder("-- free"); StringBuilder buff = new StringBuilder("-- free");
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
buff.append(' ').append(in.readInt()); buff.append(' ').append(in.readVarInt());
} }
writer.println(buff); writer.println(buff);
} else { } else {
......
...@@ -43,9 +43,32 @@ public class TestTwoPhaseCommit extends TestBase { ...@@ -43,9 +43,32 @@ public class TestTwoPhaseCommit extends TestBase {
prepare(); prepare();
openWith(false); openWith(false);
test(false); test(false);
testLargeTransactionName();
deleteDb("twoPhaseCommit"); deleteDb("twoPhaseCommit");
} }
private void testLargeTransactionName() throws SQLException {
if (!config.pageStore) {
return;
}
Connection conn = getConnection("twoPhaseCommit");
Statement stat = conn.createStatement();
conn.setAutoCommit(false);
stat.execute("CREATE TABLE TEST2(ID INT)");
String name = "tx12345678";
try {
for (int i = 0;; i++) {
stat.execute("INSERT INTO TEST2 VALUES(1)");
name += "x";
stat.execute("PREPARE COMMIT " + name);
}
} catch (SQLException e) {
assertKnownException(e);
}
conn.close();
}
private void test(boolean rolledBack) throws SQLException { private void test(boolean rolledBack) throws SQLException {
Connection conn = getConnection("twoPhaseCommit"); Connection conn = getConnection("twoPhaseCommit");
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论