提交 42d8b5e6 authored 作者: Thomas Mueller's avatar Thomas Mueller

New experimental page store.

上级 c1a0982e
...@@ -114,9 +114,6 @@ abstract class PageBtree extends Record { ...@@ -114,9 +114,6 @@ abstract class PageBtree extends Record {
l = i + 1; l = i + 1;
} }
} }
if (bigger && comp < 0) {
l++;
}
return l; return l;
} }
...@@ -210,4 +207,13 @@ abstract class PageBtree extends Record { ...@@ -210,4 +207,13 @@ abstract class PageBtree extends Record {
*/ */
abstract boolean remove(SearchRow row) throws SQLException; abstract boolean remove(SearchRow row) throws SQLException;
/**
* Ensure all rows are read in memory.
*/
protected void readAllRows() throws SQLException {
for (int i = 0; i < entryCount; i++) {
getRow(i);
}
}
} }
...@@ -76,6 +76,7 @@ class PageBtreeLeaf extends PageBtree { ...@@ -76,6 +76,7 @@ class PageBtreeLeaf extends PageBtree {
if (entryCount == 0) { if (entryCount == 0) {
x = 0; x = 0;
} else { } else {
readAllRows();
x = find(row, false, true); x = find(row, false, true);
System.arraycopy(offsets, 0, newOffsets, 0, x); System.arraycopy(offsets, 0, newOffsets, 0, x);
System.arraycopy(rows, 0, newRows, 0, x); System.arraycopy(rows, 0, newRows, 0, x);
...@@ -108,7 +109,8 @@ class PageBtreeLeaf extends PageBtree { ...@@ -108,7 +109,8 @@ class PageBtreeLeaf extends PageBtree {
return 0; return 0;
} }
private void removeRow(int i) { private void removeRow(int i) throws SQLException {
readAllRows();
entryCount--; entryCount--;
written = false; written = false;
if (entryCount <= 0) { if (entryCount <= 0) {
...@@ -118,7 +120,6 @@ class PageBtreeLeaf extends PageBtree { ...@@ -118,7 +120,6 @@ class PageBtreeLeaf extends PageBtree {
SearchRow[] newRows = new SearchRow[entryCount]; SearchRow[] newRows = new SearchRow[entryCount];
System.arraycopy(offsets, 0, newOffsets, 0, i); System.arraycopy(offsets, 0, newOffsets, 0, i);
System.arraycopy(rows, 0, newRows, 0, i); System.arraycopy(rows, 0, newRows, 0, i);
int startNext = i > 0 ? offsets[i - 1] : index.getPageStore().getPageSize(); int startNext = i > 0 ? offsets[i - 1] : index.getPageStore().getPageSize();
int rowLength = startNext - offsets[i]; int rowLength = startNext - offsets[i];
for (int j = i; j < entryCount; j++) { for (int j = i; j < entryCount; j++) {
...@@ -186,10 +187,7 @@ class PageBtreeLeaf extends PageBtree { ...@@ -186,10 +187,7 @@ class PageBtreeLeaf extends PageBtree {
if (written) { if (written) {
return; return;
} }
// make sure rows are read readAllRows();
for (int i = 0; i < entryCount; i++) {
getRow(i);
}
data.reset(); data.reset();
data.writeInt(parentPageId); data.writeInt(parentPageId);
data.writeByte((byte) Page.TYPE_BTREE_LEAF); data.writeByte((byte) Page.TYPE_BTREE_LEAF);
...@@ -236,7 +234,7 @@ class PageBtreeLeaf extends PageBtree { ...@@ -236,7 +234,7 @@ class PageBtreeLeaf extends PageBtree {
return; return;
} }
PageBtreeNode next = (PageBtreeNode) index.getPage(parentPageId); PageBtreeNode next = (PageBtreeNode) index.getPage(parentPageId);
next.nextPage(cursor, getRow(entryCount - 1)); next.nextPage(cursor, getRow(0));
} }
public String toString() { public String toString() {
......
...@@ -19,7 +19,7 @@ import org.h2.store.DataPage; ...@@ -19,7 +19,7 @@ import org.h2.store.DataPage;
* </li><li>5-6: entry count * </li><li>5-6: entry count
* </li><li>7-10: row count of all children (-1 if not known) * </li><li>7-10: row count of all children (-1 if not known)
* </li><li>11-14: rightmost child page id * </li><li>11-14: rightmost child page id
* </li><li>15- entries: 4 bytes leaf page id, 4 bytes offset * </li><li>15- entries: 4 bytes leaf page id, 4 bytes offset to data
* </li></ul> * </li></ul>
*/ */
class PageBtreeNode extends PageBtree { class PageBtreeNode extends PageBtree {
...@@ -80,6 +80,7 @@ class PageBtreeNode extends PageBtree { ...@@ -80,6 +80,7 @@ class PageBtreeNode extends PageBtree {
System.arraycopy(childPageIds, 0, newChildPageIds, 0, x + 1); System.arraycopy(childPageIds, 0, newChildPageIds, 0, x + 1);
} }
if (entryCount > 0) { if (entryCount > 0) {
readAllRows();
System.arraycopy(offsets, 0, newOffsets, 0, x); System.arraycopy(offsets, 0, newOffsets, 0, x);
System.arraycopy(rows, 0, newRows, 0, x); System.arraycopy(rows, 0, newRows, 0, x);
if (x < entryCount) { if (x < entryCount) {
...@@ -280,7 +281,8 @@ class PageBtreeNode extends PageBtree { ...@@ -280,7 +281,8 @@ class PageBtreeNode extends PageBtree {
written = true; written = true;
} }
private void removeChild(int i) { private void removeChild(int i) throws SQLException {
readAllRows();
entryCount--; entryCount--;
written = false; written = false;
if (entryCount < 0) { if (entryCount < 0) {
...@@ -314,7 +316,7 @@ class PageBtreeNode extends PageBtree { ...@@ -314,7 +316,7 @@ class PageBtreeNode extends PageBtree {
* @param row the current row * @param row the current row
*/ */
void nextPage(PageBtreeCursor cursor, SearchRow row) throws SQLException { void nextPage(PageBtreeCursor cursor, SearchRow row) throws SQLException {
int i = find(row, true, false); int i = find(row, false, false) + 1;
if (i > entryCount) { if (i > entryCount) {
if (parentPageId == Page.ROOT) { if (parentPageId == Page.ROOT) {
cursor.setCurrent(null, 0); cursor.setCurrent(null, 0);
...@@ -322,6 +324,7 @@ class PageBtreeNode extends PageBtree { ...@@ -322,6 +324,7 @@ class PageBtreeNode extends PageBtree {
} }
PageBtreeNode next = (PageBtreeNode) index.getPage(parentPageId); PageBtreeNode next = (PageBtreeNode) index.getPage(parentPageId);
next.nextPage(cursor, getRow(entryCount - 1)); next.nextPage(cursor, getRow(entryCount - 1));
return;
} }
PageBtree page = index.getPage(childPageIds[i]); PageBtree page = index.getPage(childPageIds[i]);
PageBtreeLeaf leaf = page.getFirstLeaf(); PageBtreeLeaf leaf = page.getFirstLeaf();
......
...@@ -84,6 +84,7 @@ public class PageFreeList extends Record { ...@@ -84,6 +84,7 @@ public class PageFreeList extends Record {
public int getLastUsed() throws SQLException { public int getLastUsed() throws SQLException {
if (nextPage < store.getPageCount()) { if (nextPage < store.getPageCount()) {
PageFreeList next = getNext(); PageFreeList next = getNext();
// TODO avoid recursion
return next.getLastUsed(); return next.getLastUsed();
} }
return used.getLastSetBit() + firstAddressed; return used.getLastSetBit() + firstAddressed;
......
...@@ -26,6 +26,11 @@ import org.h2.message.Trace; ...@@ -26,6 +26,11 @@ import org.h2.message.Trace;
*/ */
public class PageInputStream extends InputStream { public class PageInputStream extends InputStream {
/**
* The number of header bytes per stream page.
*/
public static final int OVERHEAD = 10;
private PageStore store; private PageStore store;
private final Trace trace; private final Trace trace;
private int parentPage; private int parentPage;
...@@ -118,7 +123,7 @@ public class PageInputStream extends InputStream { ...@@ -118,7 +123,7 @@ public class PageInputStream extends InputStream {
remaining = store.getPageSize() - page.length(); remaining = store.getPageSize() - page.length();
} }
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
// trace.debug("pageIn.readPage " + parentPage + " next:" + nextPage); trace.debug("pageIn.readPage " + parentPage + " next:" + nextPage);
} }
} }
......
...@@ -34,11 +34,6 @@ import org.h2.value.Value; ...@@ -34,11 +34,6 @@ import org.h2.value.Value;
*/ */
public class PageLog { public class PageLog {
/**
* No operation.
*/
public static final int NO_OP = 0;
/** /**
* An undo log entry. * An undo log entry.
* Format: page id, page. * Format: page id, page.
...@@ -75,7 +70,7 @@ public class PageLog { ...@@ -75,7 +70,7 @@ public class PageLog {
private DataPage data; private DataPage data;
private long operation; private long operation;
private BitField undo = new BitField(); private BitField undo = new BitField();
private int[] reservedPages = new int[2]; private int[] reservedPages = new int[3];
PageLog(PageStore store, int firstPage) { PageLog(PageStore store, int firstPage) {
this.store = store; this.store = store;
...@@ -139,9 +134,7 @@ public class PageLog { ...@@ -139,9 +134,7 @@ public class PageLog {
break; break;
} }
pos++; pos++;
if (x == NO_OP) { if (x == UNDO) {
// nothing to do
} else if (x == UNDO) {
int pageId = in.readInt(); int pageId = in.readInt();
in.readFully(data.getBytes(), 0, store.getPageSize()); in.readFully(data.getBytes(), 0, store.getPageSize());
if (undo) { if (undo) {
...@@ -174,6 +167,11 @@ public class PageLog { ...@@ -174,6 +167,11 @@ public class PageLog {
if (undo) { if (undo) {
store.setLastCommitForSession(sessionId, id, pos); store.setLastCommitForSession(sessionId, id, pos);
} }
} else {
if (trace.isDebugEnabled()) {
trace.debug("log end");
break;
}
} }
} }
} catch (EOFException e) { } catch (EOFException e) {
...@@ -223,7 +221,7 @@ public class PageLog { ...@@ -223,7 +221,7 @@ public class PageLog {
trace.debug("log undo " + pageId); trace.debug("log undo " + pageId);
} }
undo.set(pageId); undo.set(pageId);
reservePages(2); reservePages(3);
out.write(UNDO); out.write(UNDO);
out.writeInt(pageId); out.writeInt(pageId);
out.write(page.getBytes(), 0, store.getPageSize()); out.write(page.getBytes(), 0, store.getPageSize());
...@@ -233,16 +231,16 @@ public class PageLog { ...@@ -233,16 +231,16 @@ public class PageLog {
} }
private void reservePages(int pageCount) throws SQLException { private void reservePages(int pageCount) throws SQLException {
int testIfRequired; int todoThisIsSlow;
// if (pageCount > reservedPages.length) { if (pageCount > reservedPages.length) {
// reservedPages = new int[pageCount]; reservedPages = new int[pageCount];
// } }
// for (int i = 0; i < pageCount; i++) { for (int i = 0; i < pageCount; i++) {
// reservedPages[i] = store.allocatePage(); reservedPages[i] = store.allocatePage(true);
// } }
// for (int i = 0; i < pageCount; i++) { for (int i = pageCount - 1; i >= 0; i--) {
// store.freePage(reservedPages[i], false, null); store.freePage(reservedPages[i], false, null);
// } }
} }
/** /**
...@@ -293,7 +291,7 @@ public class PageLog { ...@@ -293,7 +291,7 @@ public class PageLog {
int todoWriteIntoOutputDirectly; int todoWriteIntoOutputDirectly;
row.write(data); row.write(data);
reservePages(1 + data.length() / store.getPageSize()); reservePages(3 + data.length() / (store.getPageSize() - PageInputStream.OVERHEAD));
out.write(add ? ADD : REMOVE); out.write(add ? ADD : REMOVE);
out.writeInt(session.getId()); out.writeInt(session.getId());
......
...@@ -30,6 +30,7 @@ public class PageOutputStream extends OutputStream { ...@@ -30,6 +30,7 @@ public class PageOutputStream extends OutputStream {
private byte[] buffer = new byte[1]; private byte[] buffer = new byte[1];
private boolean needFlush; private boolean needFlush;
private final int streamId; private final int streamId;
private boolean writing;
/** /**
* Create a new page output stream. * Create a new page output stream.
...@@ -76,27 +77,35 @@ public class PageOutputStream extends OutputStream { ...@@ -76,27 +77,35 @@ public class PageOutputStream extends OutputStream {
if (len <= 0) { if (len <= 0) {
return; return;
} }
while (len >= remaining) { if (writing) {
page.write(b, off, remaining); throw Message.throwInternalError("writing while still writing");
off += remaining; }
len -= remaining; writing = true;
try { try {
nextPage = store.allocatePage(allocateAtEnd); while (len >= remaining) {
} catch (SQLException e) { page.write(b, off, remaining);
throw Message.convertToIOException(e); off += remaining;
len -= remaining;
try {
nextPage = store.allocatePage(allocateAtEnd);
} catch (SQLException e) {
throw Message.convertToIOException(e);
}
page.setPos(4);
page.writeByte((byte) type);
page.writeByte((byte) streamId);
page.writeInt(nextPage);
storePage();
parentPage = pageId;
pageId = nextPage;
initPage();
} }
page.setPos(4); page.write(b, off, len);
page.writeByte((byte) type); needFlush = true;
page.writeByte((byte) streamId); remaining -= len;
page.writeInt(nextPage); } finally {
storePage(); writing = false;
parentPage = pageId;
pageId = nextPage;
initPage();
} }
page.write(b, off, len);
needFlush = true;
remaining -= len;
} }
private void storePage() throws IOException { private void storePage() throws IOException {
......
...@@ -59,10 +59,10 @@ import org.h2.value.ValueString; ...@@ -59,10 +59,10 @@ import org.h2.value.ValueString;
*/ */
public class PageStore implements CacheWriter { public class PageStore implements CacheWriter {
// TODO check if PageLog.reservePages is required // TODO implement unlimited number of log files (TestPageStoreDb)
// TODO PageDataLeaf and Node: support random delete/add // TODO check if PageLog.reservePages is required - yes it is - change it
// TODO PageStore.openMetaIndex (add collation for indexes, // TODO PageStore.openMetaIndex (desc and nulls first / last columns support)
// desc columns support)
// TODO btree index with fixed size values doesn't need offset and so on // TODO btree index with fixed size values doesn't need offset and so on
// TODO log block allocation // TODO log block allocation
// TODO block compression: maybe http://en.wikipedia.org/wiki/LZJB // TODO block compression: maybe http://en.wikipedia.org/wiki/LZJB
...@@ -512,6 +512,9 @@ trace.setLevel(TraceSystem.DEBUG); ...@@ -512,6 +512,9 @@ trace.setLevel(TraceSystem.DEBUG);
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
trace.debug("freePage " + pageId); trace.debug("freePage " + pageId);
} }
if (lastUsedPage == pageId) {
lastUsedPage--;
}
cache.remove(pageId); cache.remove(pageId);
getFreeList().free(pageId); getFreeList().free(pageId);
if (recoveryRunning) { if (recoveryRunning) {
......
...@@ -824,9 +824,7 @@ public class Recover extends Tool implements DataHandler { ...@@ -824,9 +824,7 @@ public class Recover extends Tool implements DataHandler {
if (x < 0) { if (x < 0) {
break; break;
} }
if (x == PageLog.NO_OP) { if (x == PageLog.UNDO) {
// nothing to do
} else if (x == PageLog.UNDO) {
int pageId = in.readInt(); int pageId = in.readInt();
in.readFully(new byte[pageSize]); in.readFully(new byte[pageSize]);
writer.println("-- undo page " + pageId); writer.println("-- undo page " + pageId);
...@@ -840,6 +838,9 @@ public class Recover extends Tool implements DataHandler { ...@@ -840,6 +838,9 @@ public class Recover extends Tool implements DataHandler {
} else if (x == PageLog.COMMIT) { } else if (x == PageLog.COMMIT) {
int sessionId = in.readInt(); int sessionId = in.readInt();
writer.println("-- commit " + sessionId); writer.println("-- commit " + sessionId);
} else {
writer.println("-- end " + x);
break;
} }
} }
......
...@@ -288,6 +288,7 @@ java org.h2.test.TestAll timer ...@@ -288,6 +288,7 @@ java org.h2.test.TestAll timer
// 2009-05-15: 25 tests fail with page store (first loop) // 2009-05-15: 25 tests fail with page store (first loop)
// 2009-05-18: 18 tests fail with page store (first loop) // 2009-05-18: 18 tests fail with page store (first loop)
// 2009-05-30: 15 tests fail with page store (first loop)
// System.setProperty("h2.pageStore", "true"); // System.setProperty("h2.pageStore", "true");
/* /*
...@@ -337,6 +338,7 @@ kill -9 `jps -l | grep "org.h2.test.TestAll" | cut -d " " -f 1` ...@@ -337,6 +338,7 @@ kill -9 `jps -l | grep "org.h2.test.TestAll" | cut -d " " -f 1`
new TestRandomSQL().runTest(test); new TestRandomSQL().runTest(test);
} else if ("join".equals(args[0])) { } else if ("join".equals(args[0])) {
new TestJoin().runTest(test); new TestJoin().runTest(test);
test.endless = true;
} else if ("btree".equals(args[0])) { } else if ("btree".equals(args[0])) {
new TestBtreeIndex().runTest(test); new TestBtreeIndex().runTest(test);
} else if ("all".equals(args[0])) { } else if ("all".equals(args[0])) {
...@@ -546,6 +548,7 @@ kill -9 `jps -l | grep "org.h2.test.TestAll" | cut -d " " -f 1` ...@@ -546,6 +548,7 @@ kill -9 `jps -l | grep "org.h2.test.TestAll" | cut -d " " -f 1`
new TestRowLocks().runTest(this); new TestRowLocks().runTest(this);
// synth // synth
new TestBtreeIndex().runTest(this);
new TestCrashAPI().runTest(this); new TestCrashAPI().runTest(this);
new TestFuzzOptimizations().runTest(this); new TestFuzzOptimizations().runTest(this);
new TestRandomSQL().runTest(this); new TestRandomSQL().runTest(this);
......
...@@ -33,12 +33,36 @@ public class TestBtreeIndex extends TestBase { ...@@ -33,12 +33,36 @@ public class TestBtreeIndex extends TestBase {
public void test() throws SQLException { public void test() throws SQLException {
Random random = new Random(); Random random = new Random();
while (true) { for (int i = 0; i < getSize(1, 4); i++) {
testAddDelete();
int seed = random.nextInt(); int seed = random.nextInt();
testCase(seed); testCase(seed);
} }
} }
private void testAddDelete() throws SQLException {
deleteDb("index");
Connection conn = getConnection("index");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE TEST(ID bigint primary key)");
int count = 1000;
stat.execute("insert into test select x from system_range(1, " + count + ")");
if (!config.memory) {
conn.close();
conn = getConnection("index");
stat = conn.createStatement();
}
for (int i = 1; i < count; i++) {
ResultSet rs = stat.executeQuery("select * from test order by id");
for (int j = i; rs.next(); j++) {
assertEquals(j, rs.getInt(1));
}
stat.execute("delete from test where id =" + i);
}
stat.execute("drop all objects delete files");
conn.close();
}
public void testCase(int seed) throws SQLException { public void testCase(int seed) throws SQLException {
String old = baseDir; String old = baseDir;
baseDir = TestBase.getTestDir("index"); baseDir = TestBase.getTestDir("index");
...@@ -109,7 +133,7 @@ public class TestBtreeIndex extends TestBase { ...@@ -109,7 +133,7 @@ public class TestBtreeIndex extends TestBase {
int deleted = prepDelete.executeUpdate(); int deleted = prepDelete.executeUpdate();
if (deleted > 1) { if (deleted > 1) {
System.out.println("ERROR deleted:" + deleted); System.out.println("ERROR deleted:" + deleted);
System.out.println("new TestIndex()."); System.out.println("seed: " + seed);
} }
count -= deleted; count -= deleted;
} catch (SQLException e) { } catch (SQLException e) {
...@@ -126,12 +150,12 @@ public class TestBtreeIndex extends TestBase { ...@@ -126,12 +150,12 @@ public class TestBtreeIndex extends TestBase {
} }
if (testCount != count) { if (testCount != count) {
System.out.println("ERROR count:" + count + " testCount:" + testCount); System.out.println("ERROR count:" + count + " testCount:" + testCount);
System.out.println("new TestIndex()."); System.out.println("seed: " + seed);
} }
rs = conn.createStatement().executeQuery("SELECT text, count(*) FROM a GROUP BY text HAVING COUNT(*)>1"); rs = conn.createStatement().executeQuery("SELECT text, count(*) FROM a GROUP BY text HAVING COUNT(*)>1");
if (rs.next()) { if (rs.next()) {
System.out.println("ERROR"); System.out.println("ERROR");
System.out.println("new TestIndex()."); System.out.println("seed: " + seed);
} }
conn.close(); conn.close();
} }
......
...@@ -31,7 +31,8 @@ public class TestClearReferences extends TestBase { ...@@ -31,7 +31,8 @@ public class TestClearReferences extends TestBase {
"org.h2.tools.CompressTool.cachedBuffer", "org.h2.tools.CompressTool.cachedBuffer",
"org.h2.util.MemoryUtils.reserveMemory", "org.h2.util.MemoryUtils.reserveMemory",
"org.h2.util.NetUtils.cachedLocalAddress", "org.h2.util.NetUtils.cachedLocalAddress",
"org.h2.util.RandomUtils.cachedSecureRandom" "org.h2.util.RandomUtils.cachedSecureRandom",
"org.h2.value.CompareMode.lastUsed"
}; };
private boolean hasError; private boolean hasError;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论