提交 48ed8c60 authored 作者: Thomas Mueller's avatar Thomas Mueller

New experimental page store.

上级 c9b5b84e
...@@ -12,7 +12,6 @@ import org.h2.engine.Session; ...@@ -12,7 +12,6 @@ import org.h2.engine.Session;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.store.DataPage; import org.h2.store.DataPage;
import org.h2.store.DataPage;
import org.h2.store.PageStore; import org.h2.store.PageStore;
import org.h2.util.IntArray; import org.h2.util.IntArray;
...@@ -145,7 +144,7 @@ class PageDataLeaf extends PageData { ...@@ -145,7 +144,7 @@ class PageDataLeaf extends PageData {
array.toArray(overflowPageIds); array.toArray(overflowPageIds);
firstOverflowPageId = overflowPageIds[0]; firstOverflowPageId = overflowPageIds[0];
} }
index.getPageStore().updateRecord(this); index.getPageStore().updateRecord(this, data);
return 0; return 0;
} }
...@@ -266,7 +265,7 @@ class PageDataLeaf extends PageData { ...@@ -266,7 +265,7 @@ class PageDataLeaf extends PageData {
return true; return true;
} }
removeRow(i); removeRow(i);
index.getPageStore().updateRecord(this); index.getPageStore().updateRecord(this, data);
return false; return false;
} }
......
...@@ -12,7 +12,6 @@ import org.h2.engine.Session; ...@@ -12,7 +12,6 @@ import org.h2.engine.Session;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.store.DataPage; import org.h2.store.DataPage;
import org.h2.store.DataPage;
/** /**
* A leaf page that contains data of one or multiple rows. * A leaf page that contains data of one or multiple rows.
...@@ -84,15 +83,15 @@ class PageDataNode extends PageData { ...@@ -84,15 +83,15 @@ class PageDataNode extends PageData {
} }
int pivot = page.getKey(splitPoint - 1); int pivot = page.getKey(splitPoint - 1);
PageData page2 = page.split(splitPoint); PageData page2 = page.split(splitPoint);
index.getPageStore().updateRecord(page); index.getPageStore().updateRecord(page, page.data);
index.getPageStore().updateRecord(page2); index.getPageStore().updateRecord(page2, page2.data);
addChild(x, page2.getPageId(), pivot); addChild(x, page2.getPageId(), pivot);
int maxEntries = (index.getPageStore().getPageSize() - 15) / 8; int maxEntries = (index.getPageStore().getPageSize() - 15) / 8;
if (entryCount >= maxEntries) { if (entryCount >= maxEntries) {
int todoSplitAtLastInsertionPoint; int todoSplitAtLastInsertionPoint;
return entryCount / 2; return entryCount / 2;
} }
index.getPageStore().updateRecord(this); index.getPageStore().updateRecord(this, data);
} }
updateRowCount(1); updateRowCount(1);
return 0; return 0;
...@@ -104,7 +103,7 @@ class PageDataNode extends PageData { ...@@ -104,7 +103,7 @@ class PageDataNode extends PageData {
} }
if (rowCountStored != UNKNOWN_ROWCOUNT) { if (rowCountStored != UNKNOWN_ROWCOUNT) {
rowCountStored = UNKNOWN_ROWCOUNT; rowCountStored = UNKNOWN_ROWCOUNT;
index.getPageStore().updateRecord(this); index.getPageStore().updateRecord(this, data);
} }
} }
...@@ -134,7 +133,7 @@ class PageDataNode extends PageData { ...@@ -134,7 +133,7 @@ class PageDataNode extends PageData {
int child = childPageIds[i]; int child = childPageIds[i];
PageData p = index.getPage(child); PageData p = index.getPage(child);
p.setParentPageId(getPos()); p.setParentPageId(getPos());
index.getPageStore().updateRecord(p); index.getPageStore().updateRecord(p, p.data);
} }
} }
...@@ -232,7 +231,7 @@ class PageDataNode extends PageData { ...@@ -232,7 +231,7 @@ class PageDataNode extends PageData {
return true; return true;
} }
removeRow(at); removeRow(at);
index.getPageStore().updateRecord(this); index.getPageStore().updateRecord(this, data);
return false; return false;
} }
...@@ -258,7 +257,7 @@ class PageDataNode extends PageData { ...@@ -258,7 +257,7 @@ class PageDataNode extends PageData {
this.rowCount = rowCount; this.rowCount = rowCount;
if (rowCountStored != rowCount) { if (rowCountStored != rowCount) {
rowCountStored = rowCount; rowCountStored = rowCount;
index.getPageStore().updateRecord(this); index.getPageStore().updateRecord(this, data);
} }
} }
......
...@@ -55,11 +55,11 @@ public class PageScanIndex extends BaseIndex implements RowIndex { ...@@ -55,11 +55,11 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
return; return;
} }
this.store = database.getPageStorage(); this.store = database.getPageStorage();
if (headPos == Index.EMPTY_HEAD || headPos >= store.getPageCount()) { if (headPos == Index.EMPTY_HEAD || store.isNew()) {
// new table // new table, or the system table for a new database
headPos = store.allocatePage(); headPos = store.allocatePage();
PageDataLeaf root = new PageDataLeaf(this, headPos, Page.ROOT, store.createDataPage()); PageDataLeaf root = new PageDataLeaf(this, headPos, Page.ROOT, store.createDataPage());
store.updateRecord(root); store.updateRecord(root, root.data);
} else { } else {
lastKey = getPage(headPos).getLastKey(); lastKey = getPage(headPos).getLastKey();
rowCount = getPage(headPos).getRowCount(); rowCount = getPage(headPos).getRowCount();
...@@ -94,9 +94,9 @@ public class PageScanIndex extends BaseIndex implements RowIndex { ...@@ -94,9 +94,9 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
page2.setParentPageId(headPos); page2.setParentPageId(headPos);
PageDataNode newRoot = new PageDataNode(this, rootPageId, Page.ROOT, store.createDataPage()); PageDataNode newRoot = new PageDataNode(this, rootPageId, Page.ROOT, store.createDataPage());
newRoot.init(page1, pivot, page2); newRoot.init(page1, pivot, page2);
store.updateRecord(page1); store.updateRecord(page1, page1.data);
store.updateRecord(page2); store.updateRecord(page2, page2.data);
store.updateRecord(newRoot); store.updateRecord(newRoot, null);
root = newRoot; root = newRoot;
} }
rowCount++; rowCount++;
...@@ -185,9 +185,10 @@ public class PageScanIndex extends BaseIndex implements RowIndex { ...@@ -185,9 +185,10 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
public void truncate(Session session) throws SQLException { public void truncate(Session session) throws SQLException {
trace("truncate"); trace("truncate");
store.removeRecord(headPos); store.removeRecord(headPos);
int todoLogOldData;
int freePages; int freePages;
PageDataLeaf root = new PageDataLeaf(this, headPos, Page.ROOT, store.createDataPage()); PageDataLeaf root = new PageDataLeaf(this, headPos, Page.ROOT, store.createDataPage());
store.updateRecord(root); store.updateRecord(root, null);
rowCount = 0; rowCount = 0;
lastKey = 0; lastKey = 0;
} }
......
...@@ -24,13 +24,13 @@ import org.h2.util.IntArray; ...@@ -24,13 +24,13 @@ import org.h2.util.IntArray;
public class PageFreeList extends Record { public class PageFreeList extends Record {
private final PageStore store; private final PageStore store;
private final DataPage page; private final DataPage data;
private final IntArray array = new IntArray(); private final IntArray array = new IntArray();
private int nextPage; private int nextPage;
PageFreeList(PageStore store, int pageId, int nextPage) { PageFreeList(PageStore store, int pageId, int nextPage) {
setPos(pageId); setPos(pageId);
this.page = store.createDataPage(); this.data = store.createDataPage();
this.store = store; this.store = store;
this.nextPage = nextPage; this.nextPage = nextPage;
} }
...@@ -41,13 +41,14 @@ public class PageFreeList extends Record { ...@@ -41,13 +41,14 @@ public class PageFreeList extends Record {
* @return the page * @return the page
*/ */
int allocate() throws SQLException { int allocate() throws SQLException {
store.updateRecord(this, data);
int size = array.size(); int size = array.size();
if (size > 0) { if (size > 0) {
int x = array.get(size - 1); int x = array.get(size - 1);
array.remove(size - 1); array.remove(size - 1);
return x; return x;
} }
store.updateRecord(this); store.removeRecord(getPos());
// no more free pages in this list: // no more free pages in this list:
// set the next page (may be 0, meaning no free pages) // set the next page (may be 0, meaning no free pages)
store.setFreeListRootPage(nextPage, true, 0); store.setFreeListRootPage(nextPage, true, 0);
...@@ -63,9 +64,9 @@ public class PageFreeList extends Record { ...@@ -63,9 +64,9 @@ public class PageFreeList extends Record {
* Read the page from the disk. * Read the page from the disk.
*/ */
void read() throws SQLException { void read() throws SQLException {
store.readPage(getPos(), page); store.readPage(getPos(), data);
int p = page.readInt(); int p = data.readInt();
int t = page.readByte(); int t = data.readByte();
boolean last = (t & Page.FLAG_LAST) != 0; boolean last = (t & Page.FLAG_LAST) != 0;
t &= ~Page.FLAG_LAST; t &= ~Page.FLAG_LAST;
if (t != Page.TYPE_FREE_LIST || p != 0) { if (t != Page.TYPE_FREE_LIST || p != 0) {
...@@ -77,13 +78,13 @@ public class PageFreeList extends Record { ...@@ -77,13 +78,13 @@ public class PageFreeList extends Record {
int size; int size;
if (last) { if (last) {
nextPage = 0; nextPage = 0;
size = page.readInt(); size = data.readInt();
} else { } else {
nextPage = page.readInt(); nextPage = data.readInt();
size = getMaxSize(); size = getMaxSize();
} }
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
array.add(page.readInt()); array.add(data.readInt());
} }
} }
...@@ -93,7 +94,7 @@ public class PageFreeList extends Record { ...@@ -93,7 +94,7 @@ public class PageFreeList extends Record {
* @param pageId the page id to add * @param pageId the page id to add
*/ */
void free(int pageId) throws SQLException { void free(int pageId) throws SQLException {
store.updateRecord(this); store.updateRecord(this, data);
if (array.size() < getMaxSize()) { if (array.size() < getMaxSize()) {
array.add(pageId); array.add(pageId);
} else { } else {
...@@ -110,20 +111,20 @@ public class PageFreeList extends Record { ...@@ -110,20 +111,20 @@ public class PageFreeList extends Record {
} }
public void write(DataPage buff) throws SQLException { public void write(DataPage buff) throws SQLException {
page.reset(); data.reset();
page.writeInt(0); data.writeInt(0);
int type = Page.TYPE_FREE_LIST; int type = Page.TYPE_FREE_LIST;
if (nextPage == 0) { if (nextPage == 0) {
type |= Page.FLAG_LAST; type |= Page.FLAG_LAST;
} }
page.writeByte((byte) type); data.writeByte((byte) type);
if (nextPage != 0) { if (nextPage != 0) {
page.writeInt(nextPage); data.writeInt(nextPage);
} else { } else {
page.writeInt(array.size()); data.writeInt(array.size());
} }
for (int i = 0; i < array.size(); i++) { for (int i = 0; i < array.size(); i++) {
page.writeInt(array.get(i)); data.writeInt(array.get(i));
} }
} }
......
...@@ -6,93 +6,71 @@ ...@@ -6,93 +6,71 @@
*/ */
package org.h2.store; package org.h2.store;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.index.Page; import org.h2.index.Page;
import org.h2.message.Message;
import org.h2.util.BitField; import org.h2.util.BitField;
/** /**
* Transaction log mechanism. * Transaction log mechanism.
* The log is split in pages, the format is:
* <ul><li>0-3: parent page id
* </li><li>4-4: page type (LOG)
* </li><li>5-8: the next page (0 for end)
* </li><li>9-: data
* </li></ul>
* The data format is: * The data format is:
* <ul><li>0-0: type (0: end, 1: undo) * <ul><li>0-0: type (0: undo)
* </li><li>1-4: page id * </li><li>1-4: page id
* </li><li>5-: data * </li><li>5-: data
* </li></ul> * </li></ul>
*/ */
public class PageLog { public class PageLog {
private static final int UNDO = 0;
private PageStore store; private PageStore store;
private BitField undo = new BitField(); private BitField undo = new BitField();
private int bufferPos; private DataOutputStream out;
private int firstPage; private int firstPage;
private int nextPage;
private DataPage data;
private DataPage output;
PageLog(PageStore store, int firstPage) { PageLog(PageStore store, int firstPage) {
this.store = store; this.store = store;
this.firstPage = firstPage; this.firstPage = firstPage;
data = store.createDataPage();
output = store.createDataPage();
} }
// void open() throws SQLException { void openForWriting() {
// if (firstPage == 0) { out = new DataOutputStream(new PageOutputStream(store, 0, firstPage, Page.TYPE_LOG));
// return;
// }
// undo();
// prepareOutput();
// }
private void prepareOutput() throws SQLException {
output.reset();
output.writeInt(0);
output.writeByte((byte) Page.TYPE_LOG);
output.writeInt(store.allocatePage());
} }
private void undo() throws SQLException { public void recover() throws SQLException {
int next = firstPage; DataInputStream in = new DataInputStream(new PageInputStream(store, 0, firstPage, Page.TYPE_LOG));
while (next != 0) { DataPage data = store.createDataPage();
data = store.readPage(firstPage); try {
data.setPos(4); while (true) {
int x = in.read();
if (x < 0) {
break;
} }
if (x == UNDO) {
int pageId = in.readInt();
in.read(data.getBytes(), 0, store.getPageSize());
store.writePage(pageId, data);
} }
// void addUndo(int pageId) throws SQLException {
// if (undo.get(pageId)) {
// return;
// }
// undo.set(pageId);
// data.reset();
// data.writeByte((byte) 1);
// data.writeInt(pageId);
// DataPage p = store.readPage(pageId);
// data.write(p.getBytes(), 0, store.getPageSize());
// write(data.getBytes(), 0, data.length());
// }
private void write(byte[] data, int offset, int length) {
if (bufferPos + length > store.getPageSize()) {
while (length > 0) {
int len = Math.min(length, store.getPageSize() - bufferPos);
write(data, offset, len);
offset += len;
length -= len;
} }
return; } catch (IOException e) {
throw Message.convertIOException(e, "recovering");
}
} }
System.arraycopy(data, offset, output.getBytes(), bufferPos, length);
bufferPos += length;
// if (bufferPos != BUFFER_SIZE) {
// return;
// }
public void addUndo(int pageId, DataPage page) throws SQLException {
try {
if (undo.get(pageId)) {
return;
}
out.write(UNDO);
out.writeInt(pageId);
out.write(page.getBytes(), 0, store.getPageSize());
undo.set(pageId);
} catch (IOException e) {
throw Message.convertIOException(e, "recovering");
}
} }
} }
...@@ -32,6 +32,7 @@ import org.h2.util.ObjectArray; ...@@ -32,6 +32,7 @@ import org.h2.util.ObjectArray;
* </li><li>54-57: page number of the system table root * </li><li>54-57: page number of the system table root
* </li><li>58-61: page number of the first free list page * </li><li>58-61: page number of the first free list page
* </li><li>62-65: number of free pages * </li><li>62-65: number of free pages
* </li><li>66-69: log head page number
* </li></ul> * </li></ul>
*/ */
public class PageStore implements CacheWriter { public class PageStore implements CacheWriter {
...@@ -56,11 +57,19 @@ public class PageStore implements CacheWriter { ...@@ -56,11 +57,19 @@ public class PageStore implements CacheWriter {
private int systemRootPageId; private int systemRootPageId;
private int freeListRootPageId; private int freeListRootPageId;
private int freePageCount; private int freePageCount;
private int logRootPageId;
private PageLog log;
/** /**
* Number of pages (including free pages). * Number of pages (including free pages).
*/ */
private int pageCount; private int pageCount;
/**
* True if this
*/
private boolean isNew;
private int writeCount; private int writeCount;
private long fileLength; private long fileLength;
...@@ -97,10 +106,15 @@ public class PageStore implements CacheWriter { ...@@ -97,10 +106,15 @@ public class PageStore implements CacheWriter {
} else { } else {
setPageSize(PAGE_SIZE_DEFAULT); setPageSize(PAGE_SIZE_DEFAULT);
file = database.openFile(fileName, accessMode, false); file = database.openFile(fileName, accessMode, false);
logRootPageId = allocatePage();
writeHeader(); writeHeader();
isNew = true;
} }
log = new PageLog(this, logRootPageId);
fileLength = file.length(); fileLength = file.length();
pageCount = (int) (fileLength / pageSize); pageCount = (int) (fileLength / pageSize);
log.recover();
log.openForWriting();
} catch (SQLException e) { } catch (SQLException e) {
close(); close();
throw e; throw e;
...@@ -150,6 +164,16 @@ public class PageStore implements CacheWriter { ...@@ -150,6 +164,16 @@ public class PageStore implements CacheWriter {
systemRootPageId = fileHeader.readInt(); systemRootPageId = fileHeader.readInt();
freeListRootPageId = fileHeader.readInt(); freeListRootPageId = fileHeader.readInt();
freePageCount = fileHeader.readInt(); freePageCount = fileHeader.readInt();
logRootPageId = fileHeader.readInt();
}
/**
* Check if this page store was just created.
*
* @return true if it was
*/
public boolean isNew() {
return isNew;
} }
/** /**
...@@ -187,6 +211,7 @@ public class PageStore implements CacheWriter { ...@@ -187,6 +211,7 @@ public class PageStore implements CacheWriter {
fileHeader.writeInt(systemRootPageId); fileHeader.writeInt(systemRootPageId);
fileHeader.writeInt(freeListRootPageId); fileHeader.writeInt(freeListRootPageId);
fileHeader.writeInt(freePageCount); fileHeader.writeInt(freePageCount);
fileHeader.writeInt(logRootPageId);
file.seek(FileStore.HEADER_LENGTH); file.seek(FileStore.HEADER_LENGTH);
file.write(fileHeader.getBytes(), 0, FILE_HEADER_SIZE - FileStore.HEADER_LENGTH); file.write(fileHeader.getBytes(), 0, FILE_HEADER_SIZE - FileStore.HEADER_LENGTH);
byte[] filler = new byte[pageSize - FILE_HEADER_SIZE]; byte[] filler = new byte[pageSize - FILE_HEADER_SIZE];
...@@ -197,7 +222,7 @@ public class PageStore implements CacheWriter { ...@@ -197,7 +222,7 @@ public class PageStore implements CacheWriter {
* Close the file. * Close the file.
*/ */
public void close() throws SQLException { public void close() throws SQLException {
int todo; int todoTruncateLog;
try { try {
flush(); flush();
if (file != null) { if (file != null) {
...@@ -230,12 +255,14 @@ public class PageStore implements CacheWriter { ...@@ -230,12 +255,14 @@ public class PageStore implements CacheWriter {
* *
* @param record the record * @param record the record
*/ */
public void updateRecord(Record record) throws SQLException { public void updateRecord(Record record, DataPage old) throws SQLException {
synchronized (database) { synchronized (database) {
record.setChanged(true); record.setChanged(true);
int pos = record.getPos(); int pos = record.getPos();
cache.update(pos, record); cache.update(pos, record);
int todoLogChanges; if (old != null) {
log.addUndo(record.getPos(), old);
}
} }
} }
...@@ -382,7 +409,7 @@ public class PageStore implements CacheWriter { ...@@ -382,7 +409,7 @@ public class PageStore implements CacheWriter {
this.freeListRootPageId = pageId; this.freeListRootPageId = pageId;
if (!existing) { if (!existing) {
PageFreeList free = new PageFreeList(this, pageId, next); PageFreeList free = new PageFreeList(this, pageId, next);
updateRecord(free); updateRecord(free, null);
} }
} }
......
...@@ -56,7 +56,6 @@ public class TestPageStore extends TestBase { ...@@ -56,7 +56,6 @@ public class TestPageStore extends TestBase {
store.setPageSize(1024); store.setPageSize(1024);
store.open(); store.open();
IntArray list = new IntArray(); IntArray list = new IntArray();
int test;
int size = 270; int size = 270;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
int id = store.allocatePage(); int id = store.allocatePage();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论