提交 796e386a authored 作者: Thomas Mueller's avatar Thomas Mueller

New experimental page store.

上级 69f5efd2
......@@ -56,6 +56,11 @@ abstract class PageBtree extends Record {
*/
protected int start;
/**
* If only the position of the row is stored in the page
*/
protected boolean onlyPosition;
/**
* If the page was already written to the buffer.
*/
......@@ -123,13 +128,13 @@ abstract class PageBtree extends Record {
abstract void read() throws SQLException;
/**
* Add a row.
* Try to add a row.
*
* @param row the row
* @return 0 if successful, or the split position if the page needs to be
* split
*/
abstract int addRow(SearchRow row) throws SQLException;
abstract int addRowTry(SearchRow row) throws SQLException;
/**
* Find the first row.
......@@ -147,13 +152,9 @@ abstract class PageBtree extends Record {
* @return the row
*/
SearchRow getRow(int at) throws SQLException {
int test;
if (at < 0) {
System.out.println("stop");
}
SearchRow row = rows[at];
if (row == null) {
row = index.readRow(data, offsets[at]);
row = index.readRow(data, offsets[at], onlyPosition);
rows[at] = row;
}
return row;
......
......@@ -77,7 +77,7 @@ public class PageBtreeCursor implements Cursor {
return true;
}
public boolean previous() throws SQLException {
public boolean previous() {
i--;
int todo;
return true;
......
......@@ -11,7 +11,6 @@ import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.message.TraceSystem;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.store.DataPage;
......@@ -94,7 +93,7 @@ public class PageBtreeIndex extends BaseIndex {
}
while (true) {
PageBtree root = getPage(headPos);
int splitPoint = root.addRow(row);
int splitPoint = root.addRowTry(row);
if (splitPoint == 0) {
break;
}
......@@ -275,7 +274,7 @@ public class PageBtreeIndex extends BaseIndex {
return rowCount;
}
public void close(Session session) throws SQLException {
public void close(Session session) {
if (trace.isDebugEnabled()) {
trace.debug("close");
}
......@@ -291,10 +290,14 @@ public class PageBtreeIndex extends BaseIndex {
* @param offset the offset
* @return the row
*/
SearchRow readRow(DataPage data, int offset) throws SQLException {
SearchRow readRow(DataPage data, int offset, boolean onlyPosition) throws SQLException {
data.setPos(offset);
int pos = data.readInt();
if (onlyPosition) {
return tableData.getRow(null, pos);
}
SearchRow row = table.getTemplateSimpleRow(columns.length == 1);
row.setPos(data.readInt());
row.setPos(pos);
for (Column col : columns) {
int idx = col.getColumnId();
row.setValue(idx, data.readValue());
......@@ -307,18 +310,17 @@ public class PageBtreeIndex extends BaseIndex {
*
* @param data the data
* @param offset the offset
* @param onlyPosition whether only the position of the row is stored
* @param row the row to write
*/
void writeRow(DataPage data, int offset, SearchRow row) throws SQLException {
if (offset < 0) {
int test;
System.out.println("stop");
}
void writeRow(DataPage data, int offset, SearchRow row, boolean onlyPosition) throws SQLException {
data.setPos(offset);
data.writeInt(row.getPos());
for (Column col : columns) {
int idx = col.getColumnId();
data.writeValue(row.getValue(idx));
if (!onlyPosition) {
for (Column col : columns) {
int idx = col.getColumnId();
data.writeValue(row.getValue(idx));
}
}
}
......@@ -327,13 +329,16 @@ if (offset < 0) {
*
* @param dummy a dummy data page to calculate the size
* @param row the row
* @param onlyPosition whether only the position of the row is stored
* @return the number of bytes
*/
int getRowSize(DataPage dummy, SearchRow row) throws SQLException {
int getRowSize(DataPage dummy, SearchRow row, boolean onlyPosition) throws SQLException {
int rowsize = DataPage.LENGTH_INT;
for (Column col : columns) {
Value v = row.getValue(col.getColumnId());
rowsize += dummy.getValueLen(v);
if (!onlyPosition) {
for (Column col : columns) {
Value v = row.getValue(col.getColumnId());
rowsize += dummy.getValueLen(v);
}
}
return rowsize;
}
......
......@@ -14,30 +14,30 @@ import org.h2.store.DataPage;
import org.h2.store.PageStore;
/**
* A leaf page that contains index data.
* A b-tree leaf page that contains index data.
* Format:
* <ul><li>0-3: parent page id (0 for root)
* </li><li>4-4: page type
* </li><li>5-8: table id
* </li><li>9-10: entry count
* </li><li>overflow: 11-14: the row key
* </li><li>11-: list of key / offset pairs (4 bytes key, 2 bytes offset)
* </li><li>data
* </li></ul>
*/
class PageBtreeLeaf extends PageBtree {
private static final int KEY_OFFSET_PAIR_LENGTH = 6;
private static final int KEY_OFFSET_PAIR_START = 11;
private static final int OFFSET_LENGTH = 2;
private static final int OFFSET_START = 11;
PageBtreeLeaf(PageBtreeIndex index, int pageId, int parentPageId, DataPage data) {
super(index, pageId, parentPageId, data);
start = KEY_OFFSET_PAIR_START;
start = OFFSET_START;
}
void read() throws SQLException {
data.setPos(4);
data.readByte();
int type = data.readByte();
onlyPosition = (type & Page.FLAG_LAST) == 0;
int tableId = data.readInt();
if (tableId != index.getId()) {
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1,
......@@ -60,13 +60,17 @@ class PageBtreeLeaf extends PageBtree {
* @param row the now to add
* @return the split point of this page, or 0 if no split is required
*/
int addRow(SearchRow row) throws SQLException {
int rowLength = index.getRowSize(data, row);
int addRowTry(SearchRow row) throws SQLException {
int rowLength = index.getRowSize(data, row, onlyPosition);
int pageSize = index.getPageStore().getPageSize();
int last = entryCount == 0 ? pageSize : offsets[entryCount - 1];
if (entryCount > 0 && last - rowLength < start + KEY_OFFSET_PAIR_LENGTH) {
int todoSplitAtLastInsertionPoint;
return (entryCount / 2) + 1;
if (last - rowLength < start + OFFSET_LENGTH) {
if (entryCount > 0) {
int todoSplitAtLastInsertionPoint;
return (entryCount / 2) + 1;
}
onlyPosition = true;
rowLength = index.getRowSize(data, row, onlyPosition);
}
written = false;
int offset = last - rowLength;
......@@ -89,23 +93,12 @@ class PageBtreeLeaf extends PageBtree {
}
}
entryCount++;
start += KEY_OFFSET_PAIR_LENGTH;
start += OFFSET_LENGTH;
newOffsets[x] = offset;
newRows[x] = row;
offsets = newOffsets;
rows = newRows;
index.getPageStore().updateRecord(this, true, data);
if (offset < start) {
if (entryCount > 1) {
Message.throwInternalError();
}
// need to write the overflow page id
start += 4;
int remaining = rowLength - (pageSize - start);
// fix offset
offset = start;
offsets[x] = offset;
}
return 0;
}
......@@ -126,7 +119,7 @@ class PageBtreeLeaf extends PageBtree {
newOffsets[j] = offsets[j + 1] + rowLength;
}
System.arraycopy(rows, i + 1, newRows, i, entryCount - i);
start -= KEY_OFFSET_PAIR_LENGTH;
start -= OFFSET_LENGTH;
offsets = newOffsets;
rows = newRows;
}
......@@ -139,7 +132,7 @@ class PageBtreeLeaf extends PageBtree {
int newPageId = index.getPageStore().allocatePage();
PageBtreeLeaf p2 = new PageBtreeLeaf(index, newPageId, parentPageId, index.getPageStore().createDataPage());
for (int i = splitPoint; i < entryCount;) {
p2.addRow(getRow(splitPoint));
p2.addRowTry(getRow(splitPoint));
removeRow(splitPoint);
}
return p2;
......@@ -190,14 +183,14 @@ class PageBtreeLeaf extends PageBtree {
readAllRows();
data.reset();
data.writeInt(parentPageId);
data.writeByte((byte) Page.TYPE_BTREE_LEAF);
data.writeByte((byte) (Page.TYPE_BTREE_LEAF | (onlyPosition ? 0 : Page.FLAG_LAST)));
data.writeInt(index.getId());
data.writeShortInt(entryCount);
for (int i = 0; i < entryCount; i++) {
data.writeShortInt(offsets[i]);
}
for (int i = 0; i < entryCount; i++) {
index.writeRow(data, offsets[i], rows[i]);
index.writeRow(data, offsets[i], rows[i], onlyPosition);
}
written = true;
}
......@@ -234,7 +227,7 @@ class PageBtreeLeaf extends PageBtree {
return;
}
PageBtreeNode next = (PageBtreeNode) index.getPage(parentPageId);
next.nextPage(cursor, getRow(0));
next.nextPage(cursor, getPos());
}
public String toString() {
......
......@@ -7,13 +7,13 @@
package org.h2.index;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.message.Message;
import org.h2.result.SearchRow;
import org.h2.store.DataPage;
/**
* A leaf page that contains index data.
* A b-tree node page that contains index data.
* Data is organized as follows: [leaf 0] (largest value of leaf 0) [leaf 1]
* Format:
* <ul><li>0-3: parent page id
* </li><li>4-4: page type
......@@ -43,7 +43,9 @@ class PageBtreeNode extends PageBtree {
}
void read() {
data.setPos(5);
data.setPos(4);
int type = data.readByte();
onlyPosition = (type & Page.FLAG_LAST) == 0;
entryCount = data.readShortInt();
rowCount = rowCountStored = data.readInt();
childPageIds = new int[entryCount + 1];
......@@ -58,25 +60,39 @@ class PageBtreeNode extends PageBtree {
start = data.length();
}
private int addChildTry(SearchRow row) throws SQLException {
if (entryCount == 0) {
return 0;
}
int rowLength = index.getRowSize(data, row, onlyPosition);
int pageSize = index.getPageStore().getPageSize();
int last = entryCount == 0 ? pageSize : offsets[entryCount - 1];
if (last - rowLength < start + CHILD_OFFSET_PAIR_LENGTH) {
int todoSplitAtLastInsertionPoint;
return (entryCount / 2) + 1;
}
return 0;
}
/**
* Add a row if possible. If it is possible this method returns 0, otherwise
* Add a row. If it is possible this method returns 0, otherwise
* the split point. It is always possible to add one row.
*
* @param row the now to add
* @return the split point of this page, or 0 if no split is required
*/
private int addChild(int x, int childPageId, SearchRow row) throws SQLException {
int rowLength = index.getRowSize(data, row);
private void addChild(int x, int childPageId, SearchRow row) throws SQLException {
int rowLength = index.getRowSize(data, row, onlyPosition);
int pageSize = index.getPageStore().getPageSize();
int last = entryCount == 0 ? pageSize : offsets[entryCount - 1];
if (entryCount > 0 && last - rowLength < start + CHILD_OFFSET_PAIR_LENGTH) {
int todoSplitAtLastInsertionPoint;
return (entryCount / 2) + 1;
if (last - rowLength < start + CHILD_OFFSET_PAIR_LENGTH) {
if (entryCount > 0) {
throw Message.throwInternalError();
}
onlyPosition = true;
rowLength = index.getRowSize(data, row, onlyPosition);
}
int offset = last - rowLength;
if(offset < 0) {
throw Message.getSQLException(ErrorCode.FEATURE_NOT_SUPPORTED_1, "Wide indexes");
}
int[] newOffsets = new int[entryCount + 1];
SearchRow[] newRows = new SearchRow[entryCount + 1];
int[] newChildPageIds = new int[entryCount + 2];
......@@ -84,7 +100,6 @@ class PageBtreeNode extends PageBtree {
System.arraycopy(childPageIds, 0, newChildPageIds, 0, x + 1);
}
if (entryCount > 0) {
readAllRows();
System.arraycopy(offsets, 0, newOffsets, 0, x);
System.arraycopy(rows, 0, newRows, 0, x);
if (x < entryCount) {
......@@ -104,26 +119,25 @@ class PageBtreeNode extends PageBtree {
rows = newRows;
childPageIds = newChildPageIds;
entryCount++;
return 0;
}
int addRow(SearchRow row) throws SQLException {
int addRowTry(SearchRow row) throws SQLException {
while (true) {
int x = find(row, false, false);
PageBtree page = index.getPage(childPageIds[x]);
int splitPoint = page.addRow(row);
int splitPoint = page.addRowTry(row);
if (splitPoint == 0) {
break;
}
SearchRow pivot = page.getRow(splitPoint - 1);
int splitPoint2 = addChildTry(pivot);
if (splitPoint2 != 0) {
return splitPoint;
}
PageBtree page2 = page.split(splitPoint);
addChild(x, page2.getPageId(), pivot);
index.getPageStore().updateRecord(page, true, page.data);
index.getPageStore().updateRecord(page2, true, page2.data);
splitPoint = addChild(x, page2.getPageId(), pivot);
if (splitPoint != 0) {
int todoSplitAtLastInsertionPoint;
return splitPoint / 2;
}
index.getPageStore().updateRecord(this, true, data);
}
updateRowCount(1);
......@@ -202,7 +216,7 @@ class PageBtreeNode extends PageBtree {
boolean remove(SearchRow row) throws SQLException {
int at = find(row, false, false);
// merge is not implemented to allow concurrent usage of btrees
// merge is not implemented to allow concurrent usage
// TODO maybe implement merge
PageBtree page = index.getPage(childPageIds[at]);
boolean empty = page.remove(row);
......@@ -265,13 +279,10 @@ class PageBtreeNode extends PageBtree {
if (written) {
return;
}
// make sure rows are read
for (int i = 0; i < entryCount; i++) {
getRow(i);
}
readAllRows();
data.reset();
data.writeInt(parentPageId);
data.writeByte((byte) Page.TYPE_BTREE_NODE);
data.writeByte((byte) (Page.TYPE_BTREE_NODE | (onlyPosition ? 0 : Page.FLAG_LAST)));
data.writeShortInt(entryCount);
data.writeInt(rowCountStored);
data.writeInt(childPageIds[entryCount]);
......@@ -280,7 +291,7 @@ class PageBtreeNode extends PageBtree {
data.writeInt(offsets[i]);
}
for (int i = 0; i < entryCount; i++) {
index.writeRow(data, offsets[i], rows[i]);
index.writeRow(data, offsets[i], rows[i], onlyPosition);
}
written = true;
}
......@@ -319,16 +330,22 @@ class PageBtreeNode extends PageBtree {
* @param cursor the cursor
* @param row the current row
*/
void nextPage(PageBtreeCursor cursor, SearchRow row) throws SQLException {
int i = find(row, false, false) + 1;
void nextPage(PageBtreeCursor cursor, int pageId) throws SQLException {
int i;
// TODO maybe keep the index in the child page (transiently)
for (i = 0; i < childPageIds.length; i++) {
if (childPageIds[i] == pageId) {
i++;
break;
}
}
if (i > entryCount) {
if (parentPageId == Page.ROOT) {
cursor.setCurrent(null, 0);
return;
}
PageBtreeNode next = (PageBtreeNode) index.getPage(parentPageId);
SearchRow r = entryCount == 0 ? row : getRow(entryCount - 1);
next.nextPage(cursor, r);
next.nextPage(cursor, getPos());
return;
}
PageBtree page = index.getPage(childPageIds[i]);
......
......@@ -8,7 +8,6 @@ package org.h2.index;
import java.sql.SQLException;
import org.h2.engine.Session;
import org.h2.result.Row;
import org.h2.store.DataPage;
import org.h2.store.Record;
......@@ -97,13 +96,13 @@ abstract class PageData extends Record {
abstract void read() throws SQLException;
/**
* Add a row.
* Try to add a row.
*
* @param row the row
* @return 0 if successful, or the split position if the page needs to be
* split
*/
abstract int addRow(Row row) throws SQLException;
abstract int addRowTry(Row row) throws SQLException;
/**
* Get a cursor.
......@@ -188,6 +187,6 @@ abstract class PageData extends Record {
* @param key the key
* @return the row
*/
abstract Row getRow(Session session, int key) throws SQLException;
abstract Row getRow(int key) throws SQLException;
}
......@@ -5,10 +5,9 @@
* Initial Developer: H2 Group
*/
package org.h2.index;
import java.sql.SQLException;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.store.DataPage;
......@@ -89,7 +88,7 @@ class PageDataLeaf extends PageData {
* @param row the now to add
* @return the split point of this page, or 0 if no split is required
*/
int addRow(Row row) throws SQLException {
int addRowTry(Row row) throws SQLException {
int rowLength = row.getByteCount(data);
int pageSize = index.getPageStore().getPageSize();
int last = entryCount == 0 ? pageSize : offsets[entryCount - 1];
......@@ -245,7 +244,7 @@ class PageDataLeaf extends PageData {
int newPageId = index.getPageStore().allocatePage();
PageDataLeaf p2 = new PageDataLeaf(index, newPageId, parentPageId, index.getPageStore().createDataPage());
for (int i = splitPoint; i < entryCount;) {
p2.addRow(getRowAt(splitPoint));
p2.addRowTry(getRowAt(splitPoint));
removeRow(splitPoint);
}
return p2;
......@@ -297,7 +296,7 @@ class PageDataLeaf extends PageData {
return false;
}
Row getRow(Session session, int key) throws SQLException {
Row getRow(int key) throws SQLException {
int index = find(key);
return getRowAt(index);
}
......
......@@ -77,24 +77,24 @@ class PageDataNode extends PageData {
entryCount++;
}
int addRow(Row row) throws SQLException {
int addRowTry(Row row) throws SQLException {
while (true) {
int x = find(row.getPos());
PageData page = index.getPage(childPageIds[x]);
int splitPoint = page.addRow(row);
int splitPoint = page.addRowTry(row);
if (splitPoint == 0) {
break;
}
int pivot = page.getKey(splitPoint - 1);
PageData page2 = page.split(splitPoint);
index.getPageStore().updateRecord(page, true, page.data);
index.getPageStore().updateRecord(page2, true, page2.data);
addChild(x, page2.getPageId(), pivot);
int maxEntries = (index.getPageStore().getPageSize() - ENTRY_START) / ENTRY_LENGTH;
if (entryCount >= maxEntries) {
int todoSplitAtLastInsertionPoint;
return entryCount / 2;
}
int pivot = page.getKey(splitPoint - 1);
PageData page2 = page.split(splitPoint);
index.getPageStore().updateRecord(page, true, page.data);
index.getPageStore().updateRecord(page2, true, page2.data);
addChild(x, page2.getPageId(), pivot);
index.getPageStore().updateRecord(this, true, data);
}
updateRowCount(1);
......@@ -205,10 +205,10 @@ class PageDataNode extends PageData {
return false;
}
Row getRow(Session session, int key) throws SQLException {
Row getRow(int key) throws SQLException {
int at = find(key);
PageData page = index.getPage(childPageIds[at]);
return page.getRow(session, key);
return page.getRow(key);
}
int getRowCount() throws SQLException {
......
......@@ -108,7 +108,7 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
}
while (true) {
PageData root = getPage(headPos);
int splitPoint = root.addRow(row);
int splitPoint = root.addRowTry(row);
if (splitPoint == 0) {
break;
}
......@@ -260,7 +260,7 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
public Row getRow(Session session, int key) throws SQLException {
PageData root = getPage(headPos);
return root.getRow(session, key);
return root.getRow(key);
}
PageStore getPageStore() {
......
......@@ -66,13 +66,14 @@ import org.h2.value.ValueString;
*/
public class PageStore implements CacheWriter {
// TODO currently working on PageBtreeNode Wide indexes
// TODO implement redo log in Recover tool
// TODO TestPowerOff
// TODO PageStore.openMetaIndex (desc and nulls first / last)
// TODO PageBtreeIndex.canGetFirstOrLast
// TODO btree index with fixed size values doesn't need offset and so on
// TODO better checksums (for example, multiple fletcher)
// TODO replace CRC32
// TODO PageBtreeNode: 4 bytes offset - others use only 2
// TODO PageBtreeLeaf: why table id
// TODO log block allocation
// TODO block compression: maybe http://en.wikipedia.org/wiki/LZJB
// with RLE, specially for 0s.
......@@ -103,6 +104,7 @@ public class PageStore implements CacheWriter {
// and delay on each commit
// TODO var int: see google protocol buffers
// TODO SessionState.logId is no longer needed
// TODO PageData and PageBtree addRowTry: try to simplify
/**
* The smallest possible page size.
......
......@@ -629,4 +629,8 @@ public class Column {
return primaryKey;
}
public String toString() {
return name;
}
}
......@@ -289,7 +289,7 @@ java org.h2.test.TestAll timer
// 2009-05-15: 25 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)
// 2009-06-16: 13 tests fail with page store (first loop)
// 2009-06-19: 10 tests fail with page store (first loop)
// System.setProperty("h2.pageStore", "true");
/*
......
......@@ -178,14 +178,16 @@ public class TestPowerOff extends TestBase {
} catch (SQLException e) {
assertKnownException(e);
}
boolean deleted = false;
for (String fileName : FileLister.getDatabaseFiles(dir, dbName, false)) {
if (fileName.endsWith(Constants.SUFFIX_INDEX_FILE)) {
FileUtils.delete(fileName);
deleted = true;
if (!SysProperties.PAGE_STORE) {
boolean deleted = false;
for (String fileName : FileLister.getDatabaseFiles(dir, dbName, false)) {
if (fileName.endsWith(Constants.SUFFIX_INDEX_FILE)) {
FileUtils.delete(fileName);
deleted = true;
}
}
assertTrue(deleted);
}
assertTrue(deleted);
conn = getConnection(url);
conn.close();
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论